forked from nikodemus/esrap
-
Notifications
You must be signed in to change notification settings - Fork 3
/
example-sexp.lisp
98 lines (63 loc) · 1.98 KB
/
example-sexp.lisp
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
;;;; Esrap example: a simple S-expression grammar
(require :esrap-liquid)
(defpackage :sexp-grammar
(:use :cl :esrap-liquid))
(in-package :sexp-grammar)
;;; A semantic predicate for filtering out double quotes.
(defun not-doublequote (char)
(not (eql #\" char)))
(defun not-integer (string)
(when (find-if-not #'digit-char-p string)
t))
;;; Utility rules.
(defrule whitespace ()
(postimes (|| #\space #\tab #\newline))
nil)
(defrule alphanumeric ()
(pred #'alphanumericp character))
(defrule string-char ()
(|| (pred #'not-doublequote character)
(list (v #\\) (v #\"))))
;;; Here we go: an S-expression is either a list or an atom, with possibly leading whitespace.
(defrule sexp ()
(? whitespace)
(list (|| magic list atom)
(cons match-start match-end)))
(defrule magic ()
(if (eq * :use-magic)
(progn (v "foobar")
:magic)
(fail-parse "No room for magic in this world")))
(defrule list ()
(v #\()
(let ((res `(,(v sexp) ,. (times sexp))))
(? whitespace)
(v #\))
res))
(defrule atom ()
(|| string integer symbol))
(defrule string ()
(text (progm #\" (times string-char) #\")))
(defrule integer ()
(parse-integer (text (postimes (|| "0" "1" "2" "3" "4" "5" "6" "7" "8" "9")))
:radix 10))
(defrule symbol ()
(intern (text (pred #'not-integer (postimes alphanumeric)))))
;;;; Try these
(parse 'sexp "FOO123")
(parse 'sexp "123")
(parse 'sexp "\"foo\"")
(parse 'sexp " ( 1 2 3 (FOO\"foo\"123 ) )")
(parse 'sexp "foobar")
(let ((* :use-magic))
(parse 'sexp "foobar"))
;; (describe-grammar 'sexp)
;; (trace-rule 'sexp :recursive t)
(parse 'sexp "(foo bar 1 quux)")
;; (untrace-rule 'sexp :recursive t)
;; (defparameter *orig* (rule-expression (find-rule 'sexp)))
;; (change-rule 'sexp '(and (? whitespace) (or list symbol)))
(parse 'sexp "(foo bar quux)")
(parse 'sexp "(foo bar 1 quux)" :junk-allowed t)
;; (change-rule 'sexp *orig*)
(parse 'sexp "(foo bar 1 quux)" :junk-allowed t)