diff options
Diffstat (limited to 'zencoding-mode.el')
-rw-r--r-- | zencoding-mode.el | 158 |
1 files changed, 110 insertions, 48 deletions
diff --git a/zencoding-mode.el b/zencoding-mode.el index f5c98be..312cd6c 100644 --- a/zencoding-mode.el +++ b/zencoding-mode.el @@ -116,7 +116,17 @@ ;; Zen coding parsers (defun zencoding-expr (input) - "Parse a zen coding expression. This pretty much defines precedence." + "Parse a zen coding expression with optional filters." + (zencoding-pif (zencoding-parse "\\(.*?\\)|" 2 "expr|filter" it) + (let ((input (elt it 1)) + (filters (elt it 2))) + (zencoding-pif (zencoding-extract-filters filters) + (zencoding-filter input it) + it)) + (zencoding-filter input (zencoding-default-filter)))) + +(defun zencoding-subexpr (input) + "Parse a zen coding expression with no filter. This pretty much defines precedence." (zencoding-run zencoding-siblings it (zencoding-run zencoding-parent-child @@ -129,6 +139,28 @@ it '(error "no match, expecting ( or a-zA-Z0-9"))))))) +(defun zencoding-extract-filters (input) + "Extract filters from expression." + (zencoding-pif (zencoding-parse "\\([^\\|]+?\\)|" 2 "" it) + (let ((filter-name (elt it 1)) + (more-filters (elt it 2))) + (zencoding-pif (zencoding-extract-filters more-filters) + (cons filter-name it) + it)) + (zencoding-parse "\\([^\\|]+\\)" 1 "filter name" `(,(elt it 1))))) + +(defun zencoding-filter (input filters) + "Construct AST with specified filters." + (zencoding-pif (zencoding-subexpr input) + (let ((result (car it)) + (rest (cdr it))) + `((,filters ,result) . ,rest)) + it)) + +(defun zencoding-default-filter () + "Default filter(s) to be used if none is specified." + '("html")) + (defun zencoding-multiplier (input) (zencoding-por zencoding-pexpr zencoding-tag (let ((multiplier expr)) @@ -221,7 +253,7 @@ (defun zencoding-pexpr (input) "A zen coding expression with parentheses around it." (zencoding-parse "(" 1 "(" - (zencoding-run zencoding-expr + (zencoding-run zencoding-subexpr (zencoding-aif (zencoding-regex ")" input '(0 1)) `(,expr . ,(elt it 1)) '(error "expecting `)'"))))) @@ -248,13 +280,13 @@ (defun zencoding-child-sans (parent input) (zencoding-parse ">" 1 ">" - (zencoding-run zencoding-expr + (zencoding-run zencoding-subexpr it '(error "expected child")))) (defun zencoding-child (parent input) (zencoding-parse ">" 1 ">" - (zencoding-run zencoding-expr + (zencoding-run zencoding-subexpr (let ((child expr)) `((parent-child ,parent ,child) . ,input)) '(error "expected child")))) @@ -271,9 +303,9 @@ (zencoding-run zencoding-sibling (let ((parent expr)) (zencoding-parse "\\+" 1 "+" - (zencoding-run zencoding-expr + (zencoding-run zencoding-subexpr (let ((child expr)) - `((zencoding-siblings ,parent ,child) . ,input)) + `((sibling ,parent ,child) . ,input)) (zencoding-expand parent input)))) '(error "expected first sibling"))) @@ -285,16 +317,15 @@ (defun zencoding-expand (parent input) "Parse an e+ expression, where e is an expandable tag" - (let ((parent-tag (car (elt parent 1)))) - (let ((expandable (member parent-tag zencoding-expandable-tags))) - (if expandable - (let ((expansion (zencoding-child parent (concat (cadr expandable))))) - (zencoding-pif (zencoding-parse "+\\(.*\\)" 1 "+expr" - (zencoding-expr (elt it 1))) - `((zencoding-siblings ,(car expansion) ,(car it))) - expansion) - ) - '(error "expected second sibling"))))) + (let* ((parent-tag (car (elt parent 1))) + (expandable (member parent-tag zencoding-expandable-tags))) + (if expandable + (let ((expansion (zencoding-child parent (concat (cadr expandable))))) + (zencoding-pif (zencoding-parse "+\\(.*\\)" 1 "+expr" + (zencoding-subexpr (elt it 1))) + `((sibling ,(car expansion) ,(car it))) + expansion)) + '(error "expected second sibling")))) (defun zencoding-name (input) "Parse a class or identifier name, e.g. news, footer, mainimage" @@ -323,7 +354,7 @@ '(error "expected class"))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Zen coding transformer from AST to HTML +;; Zen coding transformer from AST to string (defvar zencoding-inline-tags '("a" @@ -343,7 +374,7 @@ (defvar zencoding-block-tags '("p")) -(defvar zencoding-closed-tags +(defvar zencoding-self-closing-tags '("br" "img" "input")) @@ -357,21 +388,43 @@ "Function to execute when expanding a leaf node in the Zencoding AST.") -(defun zencoding-make-tag (tag &optional content) +(defvar zencoding-filters + '("html" (zencoding-primary-filter zencoding-make-html-tag))) + +(defun zencoding-primary-filter (input proc) + "Process filter that needs to be executed first, ie. not given output from other filter." + (if (listp input) + (let ((tag-maker (cadr proc))) + (zencoding-transform-ast input tag-maker)) + nil)) + +(defun zencoding-process-filter (filters input) + "Process filters, chain one filter output as the input of the next filter." + (let ((filter-data (member (car filters) zencoding-filters)) + (more-filters (cdr filters))) + (if filter-data + (let* ((proc (cadr filter-data)) + (fun (car proc)) + (filter-output (funcall fun input proc))) + (if more-filters + (zencoding-process-filter more-filters filter-output) + filter-output)) + nil))) + +(defun zencoding-make-html-tag (tag &optional content) (let* ((name (caar tag)) (has-body? (or content (and (cdar tag) - (not (member name zencoding-closed-tags))))) + (not (member name zencoding-self-closing-tags))))) (lf (if (or (and content (string-match "\n" content)) (member name zencoding-block-tags) (and (> (length name) 1) (not (member name zencoding-inline-tags)))) "\n" "")) - (props (apply 'concat (mapcar - (lambda (prop) - (concat " " (symbol-name (car prop)) - "=\"" (cadr prop) "\"")) - (cadr tag))))) + (props (apply 'concat (mapcar (lambda (prop) + (concat " " (symbol-name (car prop)) + "=\"" (cadr prop) "\"")) + (cadr tag))))) (concat lf "<" name props (if has-body? (concat ">" lf @@ -380,22 +433,31 @@ (funcall zencoding-leaf-function) "")) lf "</" name ">" lf) - "/>")))) + "/>\n")))) + +(defun zencoding-transform (ast-with-filters) + (let ((filters (car ast-with-filters)) + (ast (cadr ast-with-filters))) + (zencoding-process-filter filters ast))) -(defun zencoding-transform (ast) +(defun zencoding-transform-ast (ast tag-maker) (let ((type (car ast))) (cond ((eq type 'list) - (mapconcat 'zencoding-transform (cadr ast) "")) + (mapconcat (lexical-let ((make-tag-fun tag-maker)) + #'(lambda (sub-ast) + (zencoding-transform-ast sub-ast make-tag-fun))) + (cadr ast) + "")) ((eq type 'tag) - (zencoding-make-tag (cdr ast))) + (funcall tag-maker (cdr ast))) ((eq type 'parent-child) (let ((parent (cdadr ast)) - (children (zencoding-transform (caddr ast)))) - (zencoding-make-tag parent children))) - ((eq type 'zencoding-siblings) - (let ((sib1 (zencoding-transform (cadr ast))) - (sib2 (zencoding-transform (caddr ast)))) + (children (zencoding-transform-ast (caddr ast) tag-maker))) + (funcall tag-maker parent children))) + ((eq type 'sibling) + (let ((sib1 (zencoding-transform-ast (cadr ast) tag-maker)) + (sib2 (zencoding-transform-ast (caddr ast) tag-maker))) (concat sib1 sib2)))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -412,14 +474,14 @@ ("#q.x" "\n<div id=\"q\" class=\"x\">\n\n</div>\n") ("#q.x.y.z" "\n<div id=\"q\" class=\"x y z\">\n\n</div>\n") ;; Empty tags - ("a/" "<a/>") - ("a/.x" "<a class=\"x\"/>") - ("a/#q.x" "<a id=\"q\" class=\"x\"/>") - ("a/#q.x.y.z" "<a id=\"q\" class=\"x y z\"/>") + ("a/" "<a/>\n") + ("a/.x" "<a class=\"x\"/>\n") + ("a/#q.x" "<a id=\"q\" class=\"x\"/>\n") + ("a/#q.x.y.z" "<a id=\"q\" class=\"x y z\"/>\n") ;; Self-closing tags - ("input type=text" "\n<input type=\"text\"/>") - ("img" "\n<img/>") - ("img>metadata/*2" "\n<img>\n\n<metadata/>\n<metadata/>\n</img>\n") + ("input type=text" "\n<input type=\"text\"/>\n") + ("img" "\n<img/>\n") + ("img>metadata/*2" "\n<img>\n\n<metadata/>\n\n<metadata/>\n\n</img>\n") ;; Siblings ("a+b" "<a></a><b></b>") ("a+b+c" "<a></a><b></b><c></c>") @@ -446,12 +508,12 @@ ;; Multiplication ("a*1" "<a></a>") ("a*2" "<a></a><a></a>") - ("a/*2" "<a/><a/>") + ("a/*2" "<a/>\n<a/>\n") ("a*2+b*2" "<a></a><a></a><b></b><b></b>") ("a*2>b*2" "<a><b></b><b></b></a><a><b></b><b></b></a>") ("a>b*2" "<a><b></b><b></b></a>") ("a#q.x>b#q.x*2" "<a id=\"q\" class=\"x\"><b id=\"q\" class=\"x\"></b><b id=\"q\" class=\"x\"></b></a>") - ("a#q.x>b/#q.x*2" "<a id=\"q\" class=\"x\"><b id=\"q\" class=\"x\"/><b id=\"q\" class=\"x\"/></a>") + ("a#q.x>b/#q.x*2" "\n<a id=\"q\" class=\"x\">\n<b id=\"q\" class=\"x\"/>\n<b id=\"q\" class=\"x\"/>\n\n</a>\n") ;; Properties ("a x" "<a x=\"\"></a>") ("a x=" "<a x=\"\"></a>") @@ -462,11 +524,11 @@ ("a x m" "<a x=\"\" m=\"\"></a>") ("a x= m=\"\"" "<a x=\"\" m=\"\"></a>") ("a x=y m=l" "<a x=\"y\" m=\"l\"></a>") - ("a/ x=y m=l" "<a x=\"y\" m=\"l\"/>") + ("a/ x=y m=l" "<a x=\"y\" m=\"l\"/>\n") ("a#foo x=y m=l" "<a id=\"foo\" x=\"y\" m=\"l\"></a>") ("a.foo x=y m=l" "<a class=\"foo\" x=\"y\" m=\"l\"></a>") ("a#foo.bar.mu x=y m=l" "<a id=\"foo\" class=\"bar mu\" x=\"y\" m=\"l\"></a>") - ("a/#foo.bar.mu x=y m=l" "<a id=\"foo\" class=\"bar mu\" x=\"y\" m=\"l\"/>") + ("a/#foo.bar.mu x=y m=l" "<a id=\"foo\" class=\"bar mu\" x=\"y\" m=\"l\"/>\n") ("a x=y+b" "<a x=\"y\"></a><b></b>") ("a x=y+b x=y" "<a x=\"y\"></a><b x=\"y\"></b>") ("a x=y>b" "<a x=\"y\"><b></b></a>") @@ -496,7 +558,6 @@ tests) (concat (number-to-string (length tests)) " tests performed. All OK."))) - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Zencoding minor mode @@ -778,8 +839,9 @@ accept it or skip it." (overlay-end zencoding-preview-input))) (ast (car (zencoding-expr string)))) (when (not (eq ast 'error)) - (zencoding-prettify (zencoding-transform ast) - indent)))) + (let ((output (zencoding-transform ast))) + (when output + (zencoding-prettify output indent)))))) (defun zencoding-update-preview (indent) (let* ((pretty (zencoding-preview-transformed indent)) |