meta-sexp is implemented using sevaral transformation methods. Therefore, besides builtin grammar transformators coming with meta-sexp by default, you are allowed to add your own transformation methods too.
Project is released under BSD license and currently maintained by Volkan YAZICI.
Download ASDF package from http://www.students.itu.edu.tr/~yazicivo/files/meta-sexp.tar.gz
(Tarball comes with the latest .git source tree. Feel free to play with it.)
Inspired by src/parser.lisp of core-server project at http://core.gen.tr/.
Idea is based on the META language discussed in `Pragmatic Parsing in Common Lisp' paper of Henry G. Baker [ACM Lisp Pointers 4, 2 (Apr/Jun 1991), 3-15]
In most of the time, you'll need to define your own parsers using CREATE-PARSER-CONTEXT methods and DEFRULE, DEFRENDERER macros.
In a rule or renderer body, if supplied, ATTACHMENT argument will get bound to ATTACHMENT keyword given to CREATE-PARSER-CONTEXT.
In some certain situations, you may also need to use DEFATOM too. See atoms.lisp for DEFATOM examples.
Here is a tiny example:
(defrule integer? (&aux (sign 1) d (num 0)) ()
(:? (:or (:and "-" (:assign sign -1))
"+"))
(:+ (:assign d (:type digit?))
(:assign num (+ (* num 10)
(- (char-code d) #.(char-code #\0)))))
(:return (* sign num)))
(integer? (create-parser-context "+123")) ==> 123
(integer? (create-parser-context "-123")) ==> -123
(defrule in-wonderland? () ()
"META-SEXP"
(progn
(format t "META-SEXP in Wonderland!")
(meta (:type space?)
"in Wonderland!"))
(:return t))
(in-wonderland? (create-parser-context :data "META-SEXP in Wonderland!"))
META-SEXP in Wonderland!
==> T
(in-wonderland? (create-parser-context :data "META-SEXP in Fooland!"))
META-SEXP in Wonderland!
==> NIL
Here's a complete example with renderers and attachments.
(defrenderer internal-link! (label &optional text) (attachment)
(format attachment "~a"
label (if (empty-char-accum-p text) label text)))
(defrule internal-link? (&aux (ref (make-char-accum)) (text (make-char-accum))) ()
"[["
(:+ (:not (:or "]]" (:type (or white-space? newline?))))
(:char-push ref))
(:? (:* (:type (or white-space? newline?)))
(:+ (:not "]]")
(:char-push text)))
"]]"
(:render internal-link! ref text))
(defrule wiki-markup? (&aux c) (attachment)
(:* (:or (:rule internal-link?)
(:and (:assign c (:read-atom))
(write-char c attachment))))
(get-output-stream-string attachment))
(wiki-markup? (create-parser-context
"foo bar [[ref text]] and [[just-ref]] here."
:attachment (make-string-output-stream)))
==> "foo bar text and just-ref here."
What's the role of ATTACHMENT slot given to CREATE-PARSER-CONTEXT (or specified as a keyword while making an instance of PARSER-CONTEXT class)? Think it as a state storage unit between passes to defined rules and renderers. (For instance, in our above example, ATTACHMENT used as a common output stream.) Yes, it is possible to let this problem get solved by the programmer, e.g. via global variables. But this approach yields to another problem: thread safety. Anyway, that was the best that I can come up with; if you have any other ideas, I'd be happy to hear them.
ALNUM? ALPHA? GRAPHIC? ASCII? BIT? DIGIT? EXTENDED? LOWER? NEWLINE? SPACE? TAB? UPPER? WHITE-SPACE?
If a form doesn't start with any of the above keywords, there're three possiblities remaining:
When you're in the third situation, to be able to get your META s-expressions compiled again, use META keyword. (See the second example in the Quick Introduction.)
Every transformation process issued by meta-sexp is controlled by TRANSFORM-GRAMMAR methods.
(defgeneric transform-grammar (ctx in-meta directive &optional args) (:documentation "META grammar transformation methods."))
To introduce a new transformation directive, just create a new TRANSFORM-GRAMMAR method with related lambda list specializers. For instance, consider how :AND and :NOT directive transformations are implemented:
(defmethod transform-grammar
(ctx (in-meta (eql t)) (directive (eql :and)) &optional args)
`(and ,@(mapcar #'(lambda (form) (transform-grammar ctx t form))
args)))
(defmethod transform-grammar
(ctx (in-meta (eql t)) (directive (eql :not)) &optional args)
(transform-grammar
ctx t :checkpoint
`((not ,(transform-grammar ctx t (car args))))))
Also pay attention how meta-sexp handles unrecognized transformation directives:
(defmethod transform-grammar (ctx in-meta directive &optional args)
"The most unspecific transformation method."
(declare (ignore args))
(cond
((and in-meta (consp directive) (keywordp (car directive)))
(transform-grammar ctx t (car directive) (cdr directive)))
((and (not in-meta) (consp directive) (eql 'meta (car directive)))
(transform-grammar ctx t :and (cdr directive)))
((consp directive)
(mapcar #'(lambda (form) (transform-grammar ctx nil form))
directive))
(t directive)))
With similar patterns, you can introduce new transformation directives to meta-sexp.
This page is linked from: meta
CLiki pages can be edited by anyone at any time. Imagine a fearsomely comprehensive disclaimer of liability. Now fear, comprehensively