aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcolin <colin@cicadas.surf>2023-08-03 20:15:42 -0700
committercolin <colin@cicadas.surf>2023-08-03 20:15:42 -0700
commitf242a709095fc660b51aec8c2590cadc89f144ba (patch)
treeccfc9c3f2abdcbec1ecb9ec3fe62b8d3a993966b
parent7dcb51ab0ec2dadd3728a3872a56cb64c1e0c23a (diff)
Manualmaster
-rw-r--r--README.org133
-rw-r--r--grammars.lisp2
2 files changed, 128 insertions, 7 deletions
diff --git a/README.org b/README.org
index 7abb546..5083f42 100644
--- a/README.org
+++ b/README.org
@@ -1,9 +1,8 @@
* Argot: A System For Grammar-Driven Macros
The ~argot~ system provides a macro-defining-macro called
-~deflanguage~. The ~deflanguage~ macro accepts a context-free grammar,
-augmented with parse handlers, and defines a macro whose body is
-parsed and expanded according to that augmented grammar.
+~deflanguage~, which accepts a context-free grammar that is used to
+parse the body calls to the defined macro.
** A brief tour
@@ -57,9 +56,9 @@ This defines a cacluator language that you can use like so:
#+end_src
-The symbol ~calc~ also has a function docstring. If you are in slime,
-you can evoke ~M-x slime-documentation~ with your cursor over the
-~calc~ symbol and see this:
+The symbol ~calc~ also has a function docstring. If you are using
+slime, you can evoke ~M-x slime-documentation~ with your cursor over
+the ~calc~ symbol to see this:
#+begin_src
@@ -91,3 +90,125 @@ PATTERN* Zero or more PATTERN
** User Guide
+The body of a ~deflanguage~ looks like this:
+
+~(deflanguage NAME (&key (DOCUMENTATION "")) START-RULE &body RULES)~
+
+~NAME~ is a symbol. This symbol becomes the name the macro
+you're defining.
+
+~START-RULE~ and ~RULES~ are *rule definition forms*, each of which
+looks like this:
+
+~(NONTERMINAL :match PATTERN [:if PREDICATE] [:then ACTION])~
+
+A nonterminal is a symbol whose name is surrounded by angle brackets:
+e.g. ~<RULE1>~ or ~<FOOBAR>~.
+
+*** Pattern Expressions
+
+There are two kinds of patterns. First, any nonterminal counts as a
+valid pattern. Every other pattern is a list whose ~CAR~ is a keyword
+and whose ~CDR~ varies depending on the value of the ~CAR~.
+
+ | PATTERN | MATCHES |
+ |----------------------+--------------------------------------------------|
+ | ~(:seq . PATTERNS)~ | Matches a sequence of patterns, results |
+ | | in the sequence of results. |
+ |----------------------+--------------------------------------------------|
+ | ~(:? PATTERN)~ | Optional match of PATTERN. Always succeeds. |
+ | | Succeeds with NIL if PATTERN doesn't match. |
+ |----------------------+--------------------------------------------------|
+ | ~(:* PATTERN)~ | Zero or more of PATTERN, results in sequence |
+ | | of matches. Always succeeds. |
+ |----------------------+--------------------------------------------------|
+ | ~(:+ PATTERN)~ | One ore more of PATTERN, results in a sequence. |
+ |----------------------+--------------------------------------------------|
+ | ~(:or . PATTERNS)~ | Matches one of PATTERNS, checked left to right. |
+ |----------------------+--------------------------------------------------|
+ | ~(:= . LITERAL)~ | Literal pattern matches. They match exactly |
+ | ~(:seq= . LITERALS)~ | their arguments (according to EQUALP). These |
+ | ~(:?= LITERAL)~ | variants behave like their counterparts above, |
+ | ~(:*= LITERAL)~ | except with literal value matches instead of |
+ | ~(:+= LITERAL)~ | pattern expressions. |
+ | ~(:or= . LITERALS)~ | |
+ |----------------------+--------------------------------------------------|
+ | ~(:@ VAR PATTERN)~ | Variable Binding. Matches PATTERN and binds the |
+ | | result to VAR, which is in-scope for the body of |
+ | | :IF and :THEN clauses (see below). |
+ |----------------------+--------------------------------------------------|
+ | ~(:{} LANGUAGE)~ | Match a list of TOKENS using a grammar named |
+ | | by LANGUAGE. I.e this lets you compose languages |
+ | | defined with DEFLANGUAGE. This is also the only |
+ | | way to parse sublists in the TOKENS list. |
+ |----------------------+--------------------------------------------------|
+ | ~(:item)~ | Matches any token in TOKENS. |
+ |----------------------+--------------------------------------------------|
+ | ~(:eof)~ | Excplicitly matches the end of the TOKENS list. |
+ |----------------------+--------------------------------------------------|
+
+
+*** IF clauses
+
+An ~IF~ clause lets the user check the values of a particular match
+against a predicate. If the predicate is ~NIL~, the match fails.
+
+An ~IF~ clause can be either a function designator or an arbitrary
+S-EXPRESSION.
+
+**** Example 1: Function Designator IF Clauses
+
+#+begin_src
+
+(<rule1>
+ :match (:item)
+ :if symbolp)
+
+#+end_src
+
+This would check that the token returned by matching against the
+`(:item)` pattern is a symbol.
+
+**** Example 2: Expression IF Clauses
+
+#+begin_src
+
+(<rule2>
+ :match (:seq (:= index-of) (:@ idx (:item)) (:@ str (:item)))
+ :if (and (integerp idx) (stringp str)))
+
+#+end_src
+
+This would match sequences like ~INDEX-OF 3 "Hello"~ and it would
+ensure that ~3~, which gets bound to ~idx~ is an integer, and that
+~"Hello"~ , which is bound to ~str~ , is a string.
+
+E.g. ~INDEX-OF "Hello" 4~ would fail to match.
+
+*** THEN clauses
+
+A ~THEN~ clause lets users transform a match result. Just like an ~IF~
+clause, it can be either a function designator or an arbitrary expression.
+
+**** Example
+
+#+begin_src
+
+(<rule3>
+ :match (:seq
+ (:= :foo)
+ (:@ part1 <rule4>)
+ (:= :bar)
+ (:@ part2 <rule5>))
+ :if (and (good? part1) (also-good? part2))
+ :then (list part1 part2))
+
+#+end_src
+
+When ~<rule3>~ succeeds, it returns a list of two values.
+
+*** The start rule
+
+The very first rule in a ~deflanguage~ body is the ~START-RULE~. On a
+successful parse of the ~TOKENS~, whatever the start rule results in is
+what the macro being defined by ~deflanguage~ will expand in to.
diff --git a/grammars.lisp b/grammars.lisp
index 38f1c2f..533b7ce 100644
--- a/grammars.lisp
+++ b/grammars.lisp
@@ -110,7 +110,7 @@ any pattern fails the whole parse fails."
(defun literals->patterns (literals)
(loop :for l :in literals :collect (list := l)))
-(defun literals-equal? (a b) (eq a b))
+(defun literals-equal? (a b) (equalp a b))
(defun parse-literal (literal)
(if (literals-equal? (next-token) literal)