summaryrefslogtreecommitdiff
path: root/parser.lisp
blob: 989938bf26576bb37ad44d9be7fda46d82454fab (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
(in-package #:parser)

(defun run (p input)
  (let ((r (funcall p input)))
    (if (parsing-p r)
      (parsing-tree r)
      (input::generate-report (failure-place r) (failure-message r)))))

(defstruct parsing
  tree
  left)

(defstruct failure
  place
  message)

(defstruct (normal-failure (:include failure)))

(defstruct (critical-failure (:include failure)))

(defun new (tree)
  (lambda (input)
    (make-parsing :tree tree :left input)))

(defun bind (p f)
  (lambda (input)
    (let ((r (funcall p input)))
      (if (parsing-p r)
        (funcall (funcall f (parsing-tree r)) (parsing-left r))
        r))))

(defun discarding-bind (p q)
  (lambda (input)
    (let ((r (funcall p input)))
      (if (parsing-p r)
        (funcall q (parsing-left r))
        r))))

(defun fail (&optional (message "Unknown error."))
  (lambda (input)
    (make-critical-failure :place input :message message)))

(defun either (first-parser second-parser &rest other-parsers)
  (lambda (input)
    (labels ((either-rec (body)
                         (if (cdr body)
                           (let ((r (funcall (car body) input)))
                             (if (normal-failure-p r)
                               (either-rec (cdr body))
                               r))
                           (funcall (car body) input))))
      (either-rec (cons first-parser (cons second-parser other-parsers))))))

(defun unit (&optional (predicate #'characterp))
  (lambda (input)
    (if (input::has-data? input)
      (let ((c (input::peek-1 input)))
        (if (funcall predicate c)
          (make-parsing :tree c :left (input::advance input))
          (make-normal-failure :place input :message "Predicate not satisfied.")))
      (make-normal-failure :place input :message "Reached end of input."))))

(defun literal (str)
  (lambda (input)
    (if (input::has-data? input (length str))
      (let ((c (input::peek-n input (length str))))
        (if (string= str c)
          (make-parsing :tree c :left (input::advance input (length c)))
          (make-normal-failure :place input :message "Predicate not satisfied.")))
      (make-normal-failure :place input :message "Reached end of input."))))

(defmacro comp (bindings &body body)
  (if (null bindings)
    `(new (progn ,@body))
    (let ((v (first (car bindings)))
          (p (second (car bindings))))
      (if (eq v '_)
        `(discarding-bind ,p (comp ,(cdr bindings) ,@body))
        `(bind ,p (lambda (,v) (comp ,(cdr bindings) ,@body)))))))

(defparameter nothing
  (new nil))

(defun optional (p)
  (either p nothing))

(defun many (p)
  (comp ((x p)
         (xs (optional (many p))))
    (cons x xs)))

(defun separated-list (p separator)
  (comp ((v p)
         (sep (optional (the-char separator)))
         (vn (if sep
               (either (separated-list p separator)
                       (fail "Value expected."))
               nothing)))
    (cons v vn)))