summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--base.lisp32
-rw-r--r--core.lisp63
-rw-r--r--cursor.lisp40
-rw-r--r--extra.lisp9
-rw-r--r--main.lisp17
-rw-r--r--monparser.asd8
-rw-r--r--test.lisp62
7 files changed, 177 insertions, 54 deletions
diff --git a/base.lisp b/base.lisp
index 12ef7fb..e8aac7d 100644
--- a/base.lisp
+++ b/base.lisp
@@ -10,27 +10,39 @@
(message "")
(priority 0))
+(defun line-and-column (str index)
+ (let ((line 1) (column 1))
+ (dotimes (i index)
+ (let ((c (char str i)))
+ (case c
+ (#\Newline
+ (incf line)
+ (setf column 1))
+ (t (incf column)))))
+ (cons line column)))
+
(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)))))
+ (let ((linecol (line-and-column (data (failure-place obj))
+ (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)
+ (lambda (start input)
+ (declare (ignore start))
(make-failure :place input :message message :priority priority)))
+(defun new (tree)
+ (lambda (start input)
+ (make-parsing :tree tree :start start :end input)))
+
(defun bind (parser f)
- (lambda (input)
- (let ((r (funcall parser input)))
+ (lambda (start input)
+ (let ((r (funcall parser input input)))
(cond ((parsing-p r)
- (funcall (funcall f r) (parsing-end r)))
+ (funcall (funcall f r) start (parsing-end r)))
((failure-p r) r)
(t (error (format nil "Invalid return value: ~a" r)))))))
diff --git a/core.lisp b/core.lisp
index a9e9246..833eb41 100644
--- a/core.lisp
+++ b/core.lisp
@@ -3,6 +3,14 @@
(defparameter nothing
(new nil))
+(defun normalize (sym expression)
+ (nsubst-if sym
+ (lambda (x)
+ (and (symbolp x)
+ (string-equal (symbol-name x)
+ (symbol-name sym))))
+ expression))
+
(defmacro unit (&optional predicate)
(cond ((null predicate)
(setf predicate 't))
@@ -14,29 +22,31 @@
(if (eq (car predicate) 'function)
(setf predicate `(funcall ,predicate it))
(setf predicate
- (symbol:normalize 'it predicate))))
+ (normalize 'it predicate))))
(t (error (format nil "Invalid predicate: ~a." predicate))))
- `(lambda (input)
- (if (cursed:has-data? input)
- (let ((it (cursed:peek input)))
+ `(lambda (start input)
+ (declare (ignore start))
+ (if (has-data? input)
+ (let ((it (peek input)))
(if ,predicate
(make-parsing :tree it
:start input
- :end (cursed:advance input))
+ :end (advance input))
(make-failure :place input
:message (format nil "Expected: ~a, Got: ~:c." ',predicate it))))
(make-failure :place input
:message (format nil "Reached end of input. Expected: ~a." ',predicate)))))
(defun one-of (first-parser second-parser &rest other-parsers)
- (lambda (input)
+ (lambda (start input)
+ (declare (ignore start))
(let ((parsers (cons first-parser (cons second-parser other-parsers)))
(result (make-failure :place input)))
(dolist (p parsers)
- (let ((r (funcall p input)))
+ (let ((r (funcall p input input)))
(cond ((parsing-p r)
(when (or (not (parsing-p result))
- (> (cursed:distance (parsing-end result)
+ (> (distance (parsing-end result)
(parsing-end r))
0))
(setf result r)))
@@ -46,7 +56,7 @@
(failure-priority result))))
(when (or (> priority-cmp 0)
(and (= priority-cmp 0)
- (>= (cursed:distance (failure-place result)
+ (>= (distance (failure-place result)
(failure-place r))
0)))
(setf result r)))))
@@ -57,19 +67,22 @@
(one-of p nothing))
(defun many (p &key all)
- (lambda (input)
- (let* ((result '())
- (input-left input)
- (last-failure
- (do ((r (funcall p input-left) (funcall p input-left))) ((failure-p r) r)
- (when (parsing-p r)
- (setf input-left (parsing-end r))
- (when (parsing-tree r)
- (push (parsing-tree r) result))))))
- (if (or (not result)
- (and result all (cursed:has-data? (failure-place last-failure))))
- (make-failure :place (failure-place last-failure)
- :message (failure-message last-failure))
- (make-parsing :tree (reverse result)
- :start input
- :end input-left)))))
+ (lambda (start input)
+ (declare (ignore start))
+ (let* ((result '()))
+ (do ((r (funcall p input input)
+ (funcall p (parsing-end r) (parsing-end r))))
+ ((or (failure-p r)
+ (= (index (parsing-start r))
+ (index (parsing-end r))))
+ nil)
+ (push r result))
+ (cond ((not result)
+ (make-failure :place input
+ :message "No matches."))
+ ((and all (has-data? (parsing-end (first result))))
+ (make-failure :place (parsing-end (first result))
+ :message "Input not exausted."))
+ (t (make-parsing :tree (reverse (mapcar (lambda (x) (parsing-tree x)) result))
+ :start input
+ :end (parsing-end (first result))))))))
diff --git a/cursor.lisp b/cursor.lisp
new file mode 100644
index 0000000..321ec3b
--- /dev/null
+++ b/cursor.lisp
@@ -0,0 +1,40 @@
+(in-package #:monparser)
+
+(defclass text ()
+ ((index :type (unsigned-byte 44) :initarg :index :accessor index :initform 0)
+ (data :type simple-string :initarg :data :reader data :initform "")))
+
+(defun has-data? (cursor)
+ (< (index cursor) (length (data cursor))))
+
+(defun peek (cursor)
+ (char (data cursor)
+ (index cursor)))
+
+(defun advance (cursor)
+ (make-instance 'text
+ :data (data cursor)
+ :index (+ (index cursor) 1)))
+
+(defun distance (from to)
+ (- (index to)
+ (index from)))
+
+(defun context-window (str index &key (side-length 20))
+ (let ((begin (max (- index side-length) 0))
+ (end (min (+ index side-length) (length str)))
+ (result '()))
+ (push (subseq str (1+ index) end) result)
+ (push (elt str index) result)
+ (push (subseq str begin index) result)
+ result))
+
+(defmethod print-object ((obj text) stream)
+ (print-unreadable-object (obj stream :type t)
+ (let ((str (if (has-data? obj)
+ (format nil "~{~a~a~a~}"
+ (context-window (data obj)
+ (index obj)
+ :side-length 10))
+ "END OF DATA")))
+ (format stream "~s" (substitute #\~ #\Newline str)))))
diff --git a/extra.lisp b/extra.lisp
index 81776a7..024bd3b 100644
--- a/extra.lisp
+++ b/extra.lisp
@@ -1,13 +1,8 @@
(in-package #:monparser)
(defparameter whitespace
- (many (unit #'char:whitespace?)))
-
-(defparameter end-of-input
- (lambda (input)
- (if (cursed:has-data? input)
- (make-failure :place input :message "Didn't reach end of input.")
- (make-parsing :tree nil :start input :end input))))
+ (many (unit (or (char= it #\Space)
+ (not (graphic-char-p it))))))
(defun repeat (p min &optional (max 0))
(if (> min 0)
diff --git a/main.lisp b/main.lisp
index 6cc0007..6d6ef8e 100644
--- a/main.lisp
+++ b/main.lisp
@@ -1,17 +1,16 @@
(in-package #:monparser)
(defun parse (parser data)
- (let* ((result (funcall parser
- (make-instance 'cursed:text
- :data data))))
- (if (parsing-p result)
- (let ((finished? (not (cursed:has-data? (parsing-end result)))))
- (values (parsing-tree result) finished?))
- result)))
+ (if (typep data 'string)
+ (funcall parser
+ (make-instance 'text :data data)
+ (make-instance 'text :data data))
+ (error "Only string parsing is allowed.")))
(defun append-on-failure (p message)
- (lambda (input)
- (let ((result (funcall p input)))
+ (lambda (start input)
+ (declare (ignore start))
+ (let ((result (funcall p input input)))
(if (failure-p result)
(make-failure :place (failure-place result)
:message (concatenate 'string message (failure-message result))
diff --git a/monparser.asd b/monparser.asd
index 37e47b1..f9ee915 100644
--- a/monparser.asd
+++ b/monparser.asd
@@ -1,9 +1,11 @@
-(defsystem #:monparser
+(defsystem "monparser"
:serial t
- :depends-on (#:utils #:cursed)
+ :depends-on ()
:components
((:file "package")
+ (:file "cursor")
(:file "base")
(:file "core")
(:file "extra")
- (:file "main")))
+ (:file "main")
+ (:file "test")))
diff --git a/test.lisp b/test.lisp
new file mode 100644
index 0000000..b0ffdc2
--- /dev/null
+++ b/test.lisp
@@ -0,0 +1,62 @@
+(in-package #:monparser)
+
+(defmacro deftest (title &body tests)
+ (let ((result '(progn)))
+ (push `(format t "~a:~&" ,title) result)
+ (dolist (test tests)
+ (let ((parser (first test))
+ (input (second test)))
+ (push `(format t "~a~&" (parse ,parser ,input)) result)))
+ (push '(format t "~%") result)
+ (reverse result)))
+
+(defun test ()
+ (deftest "unit"
+ ((unit) "hello")
+ ((unit #\h) "hello"))
+ (deftest "many"
+ ((many (unit #\h)) "hello")
+ ((many (unit #\h)) "hhhhhhhello")
+ ((many (unit #\h)) "ello"))
+ (deftest "optional many"
+ ((optional (many (unit #\h))) "hello")
+ ((optional (many (unit #\h))) "hhhhhhhello")
+ ((optional (many (unit #\h))) "ello"))
+ (deftest "many optional"
+ ((many (optional (unit #\h))) "hello")
+ ((many (optional (unit #\h))) "hhhhhhhello")
+ ((many (optional (unit #\h))) "ello"))
+ (deftest "until literal"
+ ((comp ((prefix (optional (literal "zy")))
+ (match (if prefix
+ (fail "Reached prefix")
+ (unit))))
+ match)
+ "zy")
+ ((comp (((prefix) (optional (literal "zy")))
+ ((match) (if (parsing-tree prefix)
+ (fail "Reached prefix")
+ (progn
+ (format t "prefix start: ~a, end: ~a~&"
+ (parsing-start prefix)
+ (parsing-end prefix))
+ (unit)))))
+ (format t "match start: ~a, end: ~a~&"
+ (parsing-start match)
+ (parsing-end match))
+ match)
+ "ezy")
+ ((many (comp ((prefix (optional (literal "zy")))
+ (match (if prefix
+ (fail "Reached prefix")
+ (unit))))
+ match))
+ "the quick brown fox jumps over the lazy dog.")
+ ((optional
+ (many
+ (comp ((prefix (optional (literal "zy")))
+ (match (if prefix
+ (fail "Reached prefix")
+ (unit))))
+ match)))
+ "the quick brown fox jumps over the lazy dog.")))