diff --git a/Makefile b/Makefile
index 80dee91..9c6000a 100644
--- a/Makefile
+++ b/Makefile
@@ -5,14 +5,18 @@ lib_objects:= $(subst src/, lib/, $(lib_sources:%.coffee=%.js))
default: build
-build: $(lib_objects)
+build: $(lib_objects) lib/tumble.js
+
+lib/tumble.js: src/tumble.peg
+ ./node_modules/.bin/pegjs src/tumble.peg lib/tumble.js
$(lib_objects): lib/%.js: src/%.coffee
@mkdir -p $(@D)
coffee -o $(@D) -c $<
-test: test/[0-9]*_mocha.coffee
+test: test/[0-9]*_mocha.coffee lib/tumble.js lib/parser.js
./node_modules/.bin/mocha -C --compilers coffee:coffee-script -u tdd $<
+
clean:
rm -fr lib
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e9ef878
--- /dev/null
+++ b/README.md
@@ -0,0 +1,108 @@
+Tumble is an implementation of the Tumblr parser/compiler/renderer,
+with keyword substitutions suitable to my needs on my story website.
+The idea is that the database side will produce an object consisting
+of the title of a series
+
+
+block:Series
+ Must be found within a block:toc
+ It must contain the special tag {titles} as a child tag.
+
+ This will be rendered if this is a series page.
+
+block:Subseries
+
+ Must be found within a block:toc
+ It must contain the special tag {titles} as a child tag.
+
+ This will be rendered for any subseries of the main series. This
+ will recurse to a maximum depth of four. Users can use clever CSS
+ to make this look awesome. There may be settings in the database
+ that prevent subseries recursion deliberately.
+
+block:Story
+ Has two meanings, based on context.
+
+ If it is found in the *document* (i.e. *not* within a , it is treated at a story block,
+ and will only be rendered if this is a story page.
+
+ If it is found in a *series* or *subseries* block, it's contents
+ are rendered during the rendering of a series or subseries for
+ each story found.
+
+block:Next
+ Valid only in a block:story
+ Will render if there is a "next" story in the parent series.
+
+block:Prev
+ Valid only in a block:story
+ Will render if there is a "previous" story in the parent series.
+
+block:Title
+ Will render if there is a title in the current context
+
+block:Blurb
+ Will render if there is a blurb.
+
+block:Excerpt
+ Will render if there is an excerpt
+
+Variables:
+
+Title
+ The title in the current context.
+
+Body
+ The body of the story. Only available in top-level block:story.
+
+SeriesTitle
+ The title of the series in the current context.
+
+MainSeriesTitle
+ The title of the top-level series for the current page.
+
+AuthorsName
+
+Blurb
+ Valid in a series or series/story
+
+Excerpt
+ Valid in a series or series/story
+
+URL
+ Context-sensitive. In a Story, refers to the URL used to access
+ that story. In a Next or Prev, refers to the URL of the next or
+ previous story, respectively.
+
+SeriesURL
+
+MainSeriesURL
+
+AuthorsURL
+
+# {block:Series}{URL}{Title}{Contents}{/block:Series}
+
+# > Handle these first
+
+{block:IfSeries}
+ {Title}
+ {Description}
+ {block:TableOfContents}
+ {block:Story}{URL}{Title}{/block:Story}
+ {/block:TableOfContents}
+{/block:IfSeries}
+
+The important trick here is that the TableOfContents will be recursed
+wherever the {Contents} block is seen, up to a maximum depth of four.
+
+{block:IfStory}
+ {Title}
+ {Body}
+ {block:Next}{URL}{Title}{/block:Next}
+ {block:Prev}{URL}{Title}{/block:Prev}
+ {Pubdate}
+ {block:IfLicense}{License}{/block:IfLicense}
+ {Copyright}
+{block:/IfStory}
+
+
diff --git a/package.json b/package.json
index 3e2519b..bce0bcf 100644
--- a/package.json
+++ b/package.json
@@ -1,29 +1,37 @@
{
"name": "Tumble",
- "description": "An implementation of a parser for Tumbler.",
- "author": {
- "name": "Elf M. Sternberg"
- },
+ "description": "Trivial reimplementation of Tumbler template parser",
"version": "0.0.1",
- "keywords": ["parser", "coffeescript"],
- "licenses": [{
- "type": "ARR",
- "url": "http://elfsternberg.com/home/elfsternberg/repos/Tumble/LICENSE"
- }],
- "dependencies": {
- "coffee-script": "1.x.x",
- "reparse-coffeescript": "git://github.com/elfsternberg/reparse-coffeescript#master"
+ "author": {
+ "name": "Kenneth \"Elf\" M. Sternberg",
+ "email": "elf.sternberg@gmail.com",
+ "url": "http://elfsternberg.com"
},
- "devDependencies": {
- "docco": "0.3.x",
- "mocha": "1.8.x",
- "chai": "1.5.x"
+ "repository": {
+ "type": "git",
+ "url": "ssh://elfstenberg@elfsternberg.com/home/elfsternberg/repos/tumble.git"
},
- "directories": {
- "lib": "./lib"
- },
- "main": "./lib/tumble",
+ "licenses": [
+ {
+ "type": "PRIVATE"
+ }
+ ],
+ "main": "lib/tumble",
"engines": {
"node": ">= 0.6.0"
- }
+ },
+ "scripts": {
+ "test": "make test"
+ },
+ "dependencies": {
+ "underscore": "1.4.x"
+ },
+ "devDependencies": {
+ "pegjs": "0.7.x",
+ "mocha": "1.8.x",
+ "chai": "1.5.x",
+ "coffee-script": "1.6.x",
+ "docco": "0.3.x"
+ },
+ "keywords": []
}
diff --git a/src/parser.coffee b/src/parser.coffee
new file mode 100644
index 0000000..60fd90d
--- /dev/null
+++ b/src/parser.coffee
@@ -0,0 +1,35 @@
+tumble = require('./tumble')
+util = require('util')
+
+module.exports = (template) ->
+
+ ast = tumble.parse(template)
+ console.log(util.inspect(ast, null, null))
+ # Using the AST, return a function that will render each component
+ # of the AST out, as long as the data provided to the AST makes
+ # sens.
+ #
+
+ (content) ->
+ console.log(content)
+ subtypes = (name) ->
+ return 'cond'
+
+ handler = (obj) ->
+ isLegal = (name) -> true
+ {
+ 'text': () ->
+ obj.content
+ 'variable': () ->
+ return '' if not (isLegal(obj.content) and content.hasOwnProperty(obj.content))
+ content[obj.content]
+
+ 'block': () ->
+ return '' if not (isLegal(obj.content) and content.hasOwnProperty(obj.content))
+ {
+ 'cond': () -> if obj.content then handler(obj.content) else ''
+ 'loop': () -> (handler(o) for o in obj.content)
+ }[subtypes(obj.name)]()
+ }[obj.type]()
+
+ (handler(i) for i in ast).join("")
diff --git a/src/tumble.coffee b/src/tumble.coffee
deleted file mode 100644
index 91674c5..0000000
--- a/src/tumble.coffee
+++ /dev/null
@@ -1,14 +0,0 @@
-# _ _ _ _ _____ _ _
-# /_\ | |__ __| |_ _ _ __ _ __| ||_ _| _ _ __ | |__| |_ _
-# / _ \| '_ (_-< _| '_/ _` / _| _|| || || | ' \| '_ \ | '_|
-# /_/ \_\_.__/__/\__|_| \__,_\__|\__||_| \_,_|_|_|_|_.__/_|_|
-#
-
-# Built on top of the basic parser-combinator for Coffeescript, this
-# defines a parser for the Tumblr engine, assuming the following:
-
-ReParse = require('reparse-coffeescript/lib/reparse').ReParse
-
-class AbstractTumbler extends ReParse
-
-module.exports = class extends AbstractTumbler
diff --git a/src/tumble.peg b/src/tumble.peg
new file mode 100644
index 0000000..6fae776
--- /dev/null
+++ b/src/tumble.peg
@@ -0,0 +1,57 @@
+{
+
+
+
+}
+
+document
+ = p:part* { return p }
+
+part
+ = text / variable / block
+
+text
+ = b:(!tag c:. {return c})+
+ { return { type: "text", content: b.join('') }; }
+
+variable "variable"
+ = t:tag_start rd
+ { return { type: "variable", content: t }; }
+
+tag_start "tag_start"
+ = ld n:tagname
+ { return n; }
+
+tagname "tagname"
+ = t:[a-zA-Z]+
+ { return t.join(''); }
+
+block "block"
+ = t:block_tag_start p:part* n:block_end_tag
+ &{ return t == n }
+ { return { type: "block", name: n, content: p }; }
+
+block_tag_start "tag_start"
+ = ld "block:" n:tagname rd
+ { return n; }
+
+block_end_tag
+ = ld "/block:" n:tagname rd
+ { return n; }
+
+tag
+ = ld (!rd !eol [a-zA-Z\:\/])+ rd
+
+ld
+ = "{"
+
+rd
+ = "}"
+
+eol
+ = "\n"
+ / "\r\n"
+ / "\r"
+ / "\u2028"
+ / "\u2029"
+
diff --git a/test/01_basics_mocha.coffee b/test/01_basics_mocha.coffee
index d984573..ed08e56 100644
--- a/test/01_basics_mocha.coffee
+++ b/test/01_basics_mocha.coffee
@@ -3,39 +3,45 @@ assert = chai.assert
expect = chai.expect
should = chai.should()
-Tumbler = require('../lib/tumble')
-
-tumbl = new Tumbler()
+tumble = require('../lib/parser')
test_data = [
{
'input': '',
- 'output': ''
+ 'output': '',
+ 'description': "no input"
}
{
'input': '',
'output': '',
+ 'description': "just text"
}
{
'input': '
{name}
'
'output': 'Elf Sternberg
'
- 'data': {'name': 'Elf Sternberg'}
+ 'data': {'name': 'Elf Sternberg'},
+ 'description': "a simple substitution"
}
{
'input': '{title} {name}
'
'output': 'Mr. Elf Sternberg
'
- 'data': {'name': 'Elf Sternberg', 'title': 'Mr.'}
+ 'data': {'name': 'Elf Sternberg', 'title': 'Mr.'},
+ 'description': "two simple substitutions"
}
{
'input': '