Stripped the parser down to its essentials. Will now provide simple
rendering tools for the envisioned basics: IF, LOOP, CONTEXT, TEMPLATE, VARIABLE/METHOD, and TEXT. The idea will ultimately be a two-step: a second parser for the language we'll be parsing(!), so that you'll be able to say: document = if:story if:series if:page And be able to say with confidence that the document being read conforms to the language.
This commit is contained in:
		
							parent
							
								
									da5506f1df
								
							
						
					
					
						commit
						7d7bf35d63
					
				|  | @ -1,6 +1,7 @@ | ||||||
| *# | *# | ||||||
| .#* | .#* | ||||||
| *~ | *~ | ||||||
|  | *.orig | ||||||
| npm-debug.log | npm-debug.log | ||||||
| node_modules/* | node_modules/* | ||||||
| lib/ | lib/ | ||||||
|  |  | ||||||
							
								
								
									
										260
									
								
								src/tumble.peg
								
								
								
								
							
							
						
						
									
										260
									
								
								src/tumble.peg
								
								
								
								
							|  | @ -1,223 +1,33 @@ | ||||||
| { | // -*- mode: javascript -*- | ||||||
|     var _ = require('underscore'), |  | ||||||
|         depth = 0, |  | ||||||
|         parts, variable, conditional, descendant,  |  | ||||||
|         iterative, templatize, renderer, sections; |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     var Contexter = function(c) { |  | ||||||
|         this.content = c |  | ||||||
|         this.stack = [c]; |  | ||||||
|         this.templates = {}; |  | ||||||
|         this.depth = 0; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     _.extend(Contexter.prototype, { |  | ||||||
|         has_any: function(name) { |  | ||||||
|             return _.find(this.stack, function(o) { return _.has(o, name); }); |  | ||||||
|         }, |  | ||||||
| 
 |  | ||||||
|         has: function(name) { |  | ||||||
|             if (typeof this.stack[0][name] != 'undefined') { |  | ||||||
|                 return this.stack[0][name]; |  | ||||||
|             } |  | ||||||
|             return null; |  | ||||||
|         }, |  | ||||||
|         |  | ||||||
|         get: function(name) { |  | ||||||
|             var p = this.has_any(name); |  | ||||||
|             if (p && (_.isString(p[name]) || _.isNumber(p[name]))) { |  | ||||||
|                 return p[name]; |  | ||||||
|             } |  | ||||||
|             if (arguments.length > 1) { |  | ||||||
|                 return arguments[1]; |  | ||||||
|             } |  | ||||||
|             return ''; |  | ||||||
|         }, |  | ||||||
| 
 |  | ||||||
|         once: function(obj, cb) { |  | ||||||
|             this.stack.unshift(obj); |  | ||||||
|             if (this.stack.length > 10) { |  | ||||||
|                 throw new Error("Maxmimum nesting depth reached"); |  | ||||||
|             } |  | ||||||
|             var r = cb(this); |  | ||||||
|             this.stack.shift(); |  | ||||||
|             return r; |  | ||||||
|         }, |  | ||||||
| 
 |  | ||||||
|         if: function(name, cb) { |  | ||||||
|             var p = this.has_any(name); |  | ||||||
|             if (p && p[name]) { |  | ||||||
|                 return cb(this); |  | ||||||
|             } |  | ||||||
|             return ""; |  | ||||||
|         }, |  | ||||||
|          |  | ||||||
|         descend: function(name, cb) { |  | ||||||
|             var p = this.has(name); |  | ||||||
|             if (p && _.isObject(p)) { |  | ||||||
|                 return this.once(p, cb); |  | ||||||
|             } |  | ||||||
|             return ""; |  | ||||||
|         }, |  | ||||||
|          |  | ||||||
|         many: function(name, cb) { |  | ||||||
|             var ps = this.has(name), |  | ||||||
|             _this = this; |  | ||||||
|             if (ps && _.isArray(ps)) { |  | ||||||
|                 return _.map(ps, function(p) { |  | ||||||
|                     return _this.once(p, cb); |  | ||||||
|                 }).join(""); |  | ||||||
|             } |  | ||||||
|             return ""; |  | ||||||
|         }, |  | ||||||
| 
 |  | ||||||
|         templatize: function(name, cb) { |  | ||||||
|             this.templates[name] = cb; |  | ||||||
|             return ""; |  | ||||||
|         }, |  | ||||||
| 
 |  | ||||||
|         template_render: function(name) { |  | ||||||
|             if (this.templates[name] && _.isFunction(this.templates[name])) { |  | ||||||
|                 return this.templates[name](this); |  | ||||||
|             } |  | ||||||
|             return ""; |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
| 
 |  | ||||||
|     sections = function(ps, content) { |  | ||||||
|         return _.map(ps, function(p) { return p(content); }).join(""); |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     parts = function(ps) { |  | ||||||
|         return function(content) { |  | ||||||
|             var context = new Contexter(content); |  | ||||||
|             return sections(ps, context); |  | ||||||
|         } |  | ||||||
|     }; |  | ||||||
|      |  | ||||||
|     text = function(ps) { |  | ||||||
|         var t = ps.join(""); |  | ||||||
|         return function(content) { |  | ||||||
|             return t; |  | ||||||
|         }; |  | ||||||
|     }; |  | ||||||
|      |  | ||||||
|     variable = function(t) { |  | ||||||
|         return function(content) {  |  | ||||||
|             return content.get(t, "");  |  | ||||||
|         }; |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     // TODO: Yeah, there's a code smell below.   |  | ||||||
|      |  | ||||||
|     conditional = function(t, ps) { |  | ||||||
|         return function(content) { |  | ||||||
|             return content.if(t, function(c) { |  | ||||||
|                 return sections(ps, content); |  | ||||||
|             }); |  | ||||||
|         } |  | ||||||
|     }; |  | ||||||
|      |  | ||||||
|     descendant = function(t, ps) { |  | ||||||
|         return function(content) { |  | ||||||
|             return content.descend(t, function(c) { |  | ||||||
|                 return sections(ps, content); |  | ||||||
|             }); |  | ||||||
|         } |  | ||||||
|     }; |  | ||||||
|      |  | ||||||
|     iterative = function(t, ps) {  |  | ||||||
|         return function(content) { |  | ||||||
|             return content.many(t, function(c) { |  | ||||||
|                 return sections(ps, content); |  | ||||||
|             }); |  | ||||||
|         } |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     templatize = function(t, ps) { |  | ||||||
|         return function(content) { |  | ||||||
|             content.templatize(t, function(content) { |  | ||||||
|                 return sections(ps, content); |  | ||||||
|             }); |  | ||||||
|             return ""; |  | ||||||
|         } |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     renderer = function(t) { |  | ||||||
|         return function(content) { |  | ||||||
|             return content.template_render(t); |  | ||||||
|         } |  | ||||||
|     }; |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| document | document | ||||||
|     = ps:document_part* {  |     = ps:part*  | ||||||
|         return parts(ps); |       { return ps; } | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| ifblock_tag_start "tag_start" |  | ||||||
|     =  ld "if:" n:tagname rd |  | ||||||
|     { return n; } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| ifblock_tag_end |  | ||||||
|     =  ld "/if:" n:tagname rd |  | ||||||
|     { return n; } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| blockblock_tag_start |  | ||||||
|     =  ld "block:" n:tagname rd |  | ||||||
|     { return n; } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| blockblock_tag_end |  | ||||||
|     =  ld "/block:" n:tagname rd |  | ||||||
|     { return n; } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| loopblock_tag_start "tag_start" |  | ||||||
|     =  ld "many:" n:tagname rd |  | ||||||
|     { return n; } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| loopblock_tag_end |  | ||||||
|     =  ld "/many:" n:tagname rd |  | ||||||
|     { return n; } |  | ||||||
| 
 |  | ||||||
| template_tag_start "tag_start" |  | ||||||
|     =  ld "template:" n:tagname rd |  | ||||||
|     { return n; } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| template_tag_end |  | ||||||
|     =  ld "/template:" n:tagname rd |  | ||||||
|     { return n; } |  | ||||||
| 
 | 
 | ||||||
|  | part | ||||||
|  |     = block / variable / text | ||||||
| 
 | 
 | ||||||
| tag_start "tag_start" | tag_start "tag_start" | ||||||
|     =   ld n:tagname |     =  ld b:tagname ":" n:tagname rd | ||||||
|     { return n; } |     { return {type: b, name: n }; } | ||||||
| 
 | 
 | ||||||
|  | tag_end | ||||||
|  |     =  ld '/' b:tagname ":" n:tagname rd  | ||||||
|  |     { return {type: b, name: n }; } | ||||||
| 
 | 
 | ||||||
| tagname "tagname" | tagname "tagname" | ||||||
|     = t:[a-zA-Z]+ |     = t:[a-zA-Z]+ | ||||||
|     { return t.join(''); } |     { return t.join(''); } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| tag | tag | ||||||
|     = ld (!rd !eol [a-zA-Z\:\/])+ rd |     = ld (!rd !eol [a-zA-Z\:\/])+ rd | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| ld | ld | ||||||
|     = "{" |     = "{" | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| rd | rd | ||||||
|     = "}" |     = "}" | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| eol | eol | ||||||
|     = "\n" |     = "\n" | ||||||
|     / "\r\n" |     / "\r\n" | ||||||
|  | @ -225,51 +35,17 @@ eol | ||||||
|     / "\u2028" |     / "\u2028" | ||||||
|     / "\u2029" |     / "\u2029" | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| text | text | ||||||
|     = bs:(!tag c:. {return c})+  |     = bs:(!tag c:. {return c})+  | ||||||
|       { return text(bs); } |       { return { unit: 'text', content: bs.join('') } } | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| renderer |  | ||||||
|     = ld "render:" n:tagname rd   |  | ||||||
|       { return renderer(n); } |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| variable "variable" | variable "variable" | ||||||
|     = t:tag_start rd  |     = ld t:tagname rd  | ||||||
|       &{ return (t != "render") } |       &{ return (t !== "render") } | ||||||
|       { return variable(t); } |       { return { unit: 'variable', name: t }; } | ||||||
| 
 |  | ||||||
| document_part |  | ||||||
|     = renderer / template / iterative / descendant / conditional / variable / text |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| simple_part |  | ||||||
|     = descendant / variable / conditional / text |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| conditional |  | ||||||
|     = t:ifblock_tag_start ps:simple_part* n:ifblock_tag_end |  | ||||||
|       &{ return (t == n) }  |  | ||||||
|       { return conditional(t, ps); } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| template |  | ||||||
|     = t:template_tag_start ps:simple_part* n:template_tag_end |  | ||||||
|       &{ return (t == n) }  |  | ||||||
|       { return templatize(t, ps); } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| descendant |  | ||||||
|     = t:blockblock_tag_start ps:simple_part* n:blockblock_tag_end |  | ||||||
|       &{ return (t == n) } |  | ||||||
|       { return descendant(t, ps); } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| iterative |  | ||||||
|     = t:loopblock_tag_start ps:simple_part* n:loopblock_tag_end |  | ||||||
|       &{ return (t == n) } |  | ||||||
|       { return iterative(t, ps); } |  | ||||||
| 
 | 
 | ||||||
|  | block | ||||||
|  |     = t:tag_start ps:part* n:tag_end | ||||||
|  |       &{ return (t.type == n.type) && (t.name == n.name) }  | ||||||
|  |       { return {unit: 'block', type:t.type, name:t.name, content: ps } } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue