summaryrefslogtreecommitdiff
path: root/base.lisp
blob: 12ef7fb3184ede058e01fc152f48cfc435c35b20 (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
(in-package #:monparser)

(defstruct parsing
  tree
  start
  end)

(defstruct failure
  place
  (message "")
  (priority 0))

(defmethod print-object ((obj failure) stream)
  (if (failure-place obj)
    (let ((linecol (str:line-and-column (cursed:data (failure-place obj))
                                        (cursed:index (failure-place obj)))))
      (format stream "~a:~a: ~a~&~a~&"
              (car linecol) (cdr linecol) (failure-message obj) (failure-place obj)))
    (format stream "~a~&" (failure-message obj))))

(defun new (tree)
  (lambda (input)
    (make-parsing :tree tree :start input :end input)))

(defun fail (message &key (priority 1))
  (lambda (input)
    (make-failure :place input :message message :priority priority)))

(defun bind (parser f)
  (lambda (input)
    (let ((r (funcall parser input)))
      (cond ((parsing-p r)
             (funcall (funcall f r) (parsing-end r)))
            ((failure-p r) r)
            (t (error (format nil "Invalid return value: ~a" r)))))))

(defmacro comp (bindings &body body)
  (if (null bindings)
    `(new (progn ,@body))
    (let ((var (first (car bindings)))
          (parser (second (car bindings)))
          (unused (gensym)))
      (cond ((symbolp var)
             (if (string= (symbol-name var) "_")
               `(bind ,parser
                      (lambda (&rest ,unused)
                        (declare (ignore ,unused))
                        (comp ,(cdr bindings) ,@body)))
               `(bind ,parser
                      (lambda (,var &rest ,unused)
                        (let ((,var (parsing-tree ,var)))
                          (declare (ignore ,unused))
                          (comp ,(cdr bindings) ,@body))))))
            ((and (listp var) (= (length var) 1) (symbolp (car var)))
             `(bind ,parser
                    (lambda (,(first var) &rest ,unused)
                      (declare (ignore ,unused))
                      (comp ,(cdr bindings) ,@body))))
            (t (error "Binding must be either a symbol or a list of one symbol."))))))