summaryrefslogtreecommitdiff
path: root/parser.lisp
blob: a92355c791cba055466e596a7e963af038c661e5 (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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
(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 (predicate)
  (lambda (input)
    (if (input::has-data? input)
      (let ((c (input::peek-1 input)))
        (if (char= c predicate)
          (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 not-unit (predicate)
  (lambda (input)
    (if (input::has-data? input)
      (let ((c (input::peek-1 input)))
        (if (char/= c predicate)
          (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 unit-if (&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 (predicate)
  (lambda (input)
    (if (input::has-data? input (length predicate))
      (let ((c (input::peek-n input (length predicate))))
        (if (string= predicate 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."))))

(defun until-literal (predicate)
  (lambda (input)
    (let ((c (search predicate (input::input-data input) :start2 (input::input-cursor input))))
      (if c
        (let ((window (- c (input::input-cursor input))))
          (if (> window 0)
            (make-parsing :tree (input::peek-n input window) :left (input::advance input window))
            (make-failure :place input :message "Predicate not satisfied.")))
        (make-parsing :tree (input::peek-rest input) :left (input::advance-to-end 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 &key (include-separator nil))
  (comp ((v p)
         (sep (optional separator))
         (vn (if sep
               (either (separated-list p separator)
                       (fail "Value expected."))
               nothing)))
    (if include-separator
      (cons v (cons sep vn))
      (cons v vn))))