├── lib
├── browser
│ ├── end.js
│ └── start.js
├── grammar.y
├── jadedown.js
├── nodes.js
├── lexer.l
└── parser.js
├── .gitignore
├── bin
└── jadedown
├── TODO.md
├── test
├── fixtures
│ ├── 2.jd
│ └── 1.jd
├── preformatted.test.js
├── shorthand.test.js
├── blocks.test.js
└── tags.test.js
├── Makefile
├── package.json
├── README.md
└── jadedown.js
/lib/browser/end.js:
--------------------------------------------------------------------------------
1 | window.jadedown = jadedown;
2 | })();
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .DS_Store
3 | tmp/*
4 | *.sw?
5 | lib-cov
6 |
--------------------------------------------------------------------------------
/bin/jadedown:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | var jadedown = require(__dirname + '/../lib/jadedown')
4 | , fs = require('fs');
5 |
6 | // TODO: Stream?
7 | console.log(jadedown(fs.readFileSync(process.argv[2], 'utf8')));
8 |
--------------------------------------------------------------------------------
/TODO.md:
--------------------------------------------------------------------------------
1 | * Allow escaped brackets in link shorthand and tags
2 | * Decide how to manage paragraph insertion
3 | * Remove list tags from lexer and use Nodes
4 | * Make parser able to always close open tags
5 | * Parse/preserve Windows line endings
6 |
7 |
--------------------------------------------------------------------------------
/test/fixtures/2.jd:
--------------------------------------------------------------------------------
1 | h2 Inline (http://example.com)[links] should work in headers
2 |
3 | # And *things like bold* should work in lists
4 | # And _italic_
5 | # Good job
6 |
7 | More:
8 |
9 | * And *things like bold* should work in lists
10 | * And _italic_
11 | * Again, (http://example.com/2)[links] should work
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | build:
2 | @node_modules/.bin/jison lib/grammar.y lib/lexer.l
3 | @mv grammar.js lib/parser.js
4 |
5 | clean:
6 | @rm lib/parser.js
7 |
8 | test: build
9 | @node_modules/.bin/expresso
10 |
11 | browser: build
12 | @cat lib/browser/start.js lib/nodes.js lib/parser.js lib/jadedown.js lib/browser/end.js > jadedown.js
13 |
--------------------------------------------------------------------------------
/lib/browser/start.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | /**
3 | * Jadedown for browsers.
4 | *
5 | * Usage:
6 | *
7 | *
8 | *
9 | * jadedown('*Example*');
10 | *
11 | */
12 | var exports = {};
13 |
14 | function require(m) {
15 | if (m === './parser') {
16 | return exports;
17 | }
18 | }
19 |
20 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | { "name": "jadedown"
2 | , "description": "A lightweight markup language"
3 | , "version": "0.0.3"
4 | , "url": "http://github.com/alexyoung/jadedown"
5 | , "author": "Alex R. Young
'); } 38 | | P_END 39 | { yy.c('
\n'); } 40 | | PRE_START 41 | { yy.c($1); } 42 | | PRE_END 43 | { yy.c($1); } 44 | | LIST_START 45 | { yy.c($1); } 46 | | LIST_ITEM 47 | { yy.c($1); } 48 | | LIST_END 49 | { yy.c($1); } 50 | ; 51 | 52 | -------------------------------------------------------------------------------- /test/preformatted.test.js: -------------------------------------------------------------------------------- 1 | var jadedown = require('./../lib/jadedown') 2 | , assert = require('assert'); 3 | 4 | module.exports = { 5 | 'test HTML is escaped in pre tags': function() { 6 | var expected = 'This is a <tag>' 7 | , input = '
This is a'; 8 | 9 | assert.equal(expected, jadedown(input)); 10 | }, 11 | 12 | 'test HTML is escaped in multiline pre tags': function() { 13 | var expected = '
Example:
\n\nThis is a <tag>\n' 14 | , input = 'Example:\n
\nThis is a'; 15 | assert.equal(expected, jadedown(input)); 16 | }, 17 | 18 | 'test HTML is escaped in code tags': function() { 19 | // I'm not sure if this should insert paragraphs or not 20 | var expected = '\n
This is a <tag>'
21 | , input = 'This is a ';
22 |
23 | assert.equal(expected, jadedown(input));
24 | },
25 |
26 | 'test HTML is escaped in code shorthand': function() {
27 | var expected = 'This is a <tag>
17 | h3 This is a header 18 |19 | 20 | This has been retained in Jadedown. 21 | 22 | Jade uses CSS selectors and brackets to contain attributes: 23 | 24 |
25 | h3.className(style="color: blue") This is a header 26 |27 | 28 | Jadedown uses the same conventions. 29 | 30 | h2 Shorthand 31 | 32 | * Code can be written with backticks
`var a = 1;`
33 | * Asterisks and underscores are used for bold and italic
34 | * Other lines are converted to paragraphs
35 | * Unordered lists start with asterisks
36 | * Ordered lists start with a hash
37 |
38 | h2 Plans
39 |
40 | # Inline tags
41 | # Tags with classes
42 | # Tags with IDs
43 | # Full test suite
44 | # Benchmarks
45 |
46 | And this is an image:
47 |
48 | img(src="http://example.com/image.png")
49 |
50 |
--------------------------------------------------------------------------------
/test/shorthand.test.js:
--------------------------------------------------------------------------------
1 | var jadedown = require('./../lib/jadedown')
2 | , fs = require('fs')
3 | , assert = require('assert');
4 |
5 | module.exports = {
6 | 'test strong': function() {
7 | var expected = 'Example 1
' 8 | , input = '*Example 1*'; 9 | 10 | assert.equal(expected, jadedown(input)); 11 | }, 12 | 13 | 'test multiple strong': function() { 14 | var expected = 'Important: The lazy fox
' 15 | , input = '*Important*: The *lazy fox*'; 16 | 17 | assert.equal(expected, jadedown(input)); 18 | }, 19 | 20 | 'test em': function() { 21 | var expected = 'Example 1
' 22 | , input = '_Example 1_'; 23 | 24 | assert.equal(expected, jadedown(input)); 25 | }, 26 | 27 | 'test code': function() { 28 | var expected = 'var a = 1 * 5;
Given that var a = 1 * 5; and var b = Math.PI;, then:
Example 1 and Example 2 are links
' 50 | , input = '(http://example.com)[Example 1] and (http://example.com/2)[Example 2] are links'; 51 | 52 | assert.equal(expected, jadedown(input)); 53 | } 54 | }; 55 | 56 | -------------------------------------------------------------------------------- /lib/nodes.js: -------------------------------------------------------------------------------- 1 | function aliases(tag) { 2 | switch (tag) { 3 | case 'bq': 4 | return 'blockquote'; 5 | break; 6 | 7 | default: 8 | return tag; 9 | } 10 | } 11 | 12 | function Nodes() { 13 | this.openTags = []; 14 | } 15 | 16 | Nodes.prototype = { 17 | pushOpenTag: function(tag) { 18 | this.openTags.push(tag); 19 | }, 20 | 21 | popOpenTag: function() { 22 | return '' + this.openTags.pop() + '>'; 23 | }, 24 | 25 | makeAttr: function($1) { 26 | var attr = [] 27 | , id = $1.selector ? $1.selector.match(/#([^.([]*)/) : null 28 | , classes = $1.selector; 29 | 30 | if (id && id[1]) { 31 | attr.push('id="' + id[1] + '"'); 32 | classes = classes.replace(/#([^.([]*)/, ''); 33 | } 34 | 35 | if (classes) { 36 | classes = classes.split(/\./); 37 | if (classes.length > 0) { 38 | attr.push('class="' + classes.join(' ').trim() + '"'); 39 | } 40 | } 41 | 42 | if ($1.attr) { 43 | attr.push($1.attr); 44 | } 45 | 46 | if (attr.length === 0) { 47 | return ''; 48 | } else { 49 | return ' ' + attr.join(' '); 50 | } 51 | }, 52 | 53 | makeTag: function($1) { 54 | var attr = this.makeAttr($1); 55 | $1.tag = aliases($1.tag); 56 | 57 | if (!$1.html) { 58 | this.pushOpenTag($1.tag); 59 | // TODO: self-closing, plus on/off for xhtml/html 60 | var end = $1.tag === 'img' ? '/>' : '>'; 61 | return '<' + $1.tag + attr + end; 62 | } else { 63 | return '<' + $1.tag + attr + '>' + $1.html + '' + $1.tag + '>'; 64 | } 65 | }, 66 | 67 | TAG: function($1) { 68 | return this.makeTag($1); 69 | }, 70 | 71 | LINK: function($1) { 72 | return this.makeTag($1); 73 | }, 74 | 75 | BLOCK_TAG: function($1) { 76 | return this.makeTag($1); 77 | }, 78 | 79 | BLOCK_END: function() { 80 | return this.popOpenTag() + '\n'; 81 | } 82 | }; 83 | 84 | var nodes = new Nodes(); 85 | 86 | if (typeof module !== 'undefined') { 87 | module.exports = nodes; 88 | } 89 | -------------------------------------------------------------------------------- /test/blocks.test.js: -------------------------------------------------------------------------------- 1 | var jadedown = require('./../lib/jadedown') 2 | , fs = require('fs') 3 | , assert = require('assert'); 4 | 5 | module.exports = { 6 | 'test line start': function() { 7 | var expected = 'This is a quote' 8 | , input = 'blockquote This is a quote\n'; 9 | 10 | assert.equal(expected, jadedown(input)); 11 | }, 12 | 13 | 'test line start and inline': function() { 14 | var expected = '
This is a quote' 15 | , input = 'blockquote This is a _quote_\n'; 16 | 17 | assert.equal(expected, jadedown(input)); 18 | }, 19 | 20 | 'test space indentation becomes code': function() { 21 | var expected = '
Here is some code:
\nvar a = 1;\n'
22 | , input = 'Here is some code:\n var a = 1;\n';
23 |
24 | assert.equal(expected, jadedown(input));
25 | },
26 |
27 | 'test extra space indentation becomes code': function() {
28 | var expected = 'Here is some code:
\nvar a = 1;\n'
29 | , input = 'Here is some code:\n var a = 1;\n';
30 |
31 | assert.equal(expected, jadedown(input));
32 | },
33 |
34 | 'test multiple lines of code': function() {
35 | var expected = 'Here is some code:
\nvar a = 1;\nfunction() { return a * 100; }\n'
36 | , input = 'Here is some code:\n var a = 1;\n function() { return a * 100; }\n';
37 |
38 | assert.equal(expected, jadedown(input));
39 | },
40 |
41 | 'test indented code tags are closed correctly': function() {
42 | var expected = 'Here is some code:
\nvar a = 1; a++; console.log(a);\n\nExample.
' 43 | , input = 'Here is some code:\n var a = 1; a++; console.log(a);\nExample.\n'; 44 | 45 | assert.equal(expected, jadedown(input)); 46 | }, 47 | 48 | 'test tab indentation becomes code': function() { 49 | var expected = 'Here is some code:
\nvar a = 1;\n'
50 | , input = 'Here is some code:\n\tvar a = 1;\n';
51 |
52 | assert.equal(expected, jadedown(input));
53 | }
54 |
55 | // TODO: Blocks with IDs, classes, etc.
56 | };
57 |
--------------------------------------------------------------------------------
/test/tags.test.js:
--------------------------------------------------------------------------------
1 | var jadedown = require('./../lib/jadedown')
2 | , fs = require('fs')
3 | , assert = require('assert');
4 |
5 | module.exports = {
6 | 'test tags': function() {
7 | var expected = 'Example:
Example 1' 8 | , input = 'Example: blockquote[Example 1]'; 9 | 10 | assert.equal(expected, jadedown(input)); 11 | }, 12 | 13 | 'test tags with attributes': function() { 14 | var expected = '' 15 | , input = 'a(href="http://example.com")[Example 1]'; 16 | 17 | assert.equal(expected, jadedown(input)); 18 | }, 19 | 20 | 'test tags with a class': function() { 21 | var expected = '' 22 | , input = 'a.example(href="http://example.com")[Example 1]'; 23 | 24 | assert.equal(expected, jadedown(input)); 25 | }, 26 | 27 | 'test tags with an id': function() { 28 | var expected = '' 29 | , input = 'a#nav(href="http://example.com")[Example 1]'; 30 | 31 | assert.equal(expected, jadedown(input)); 32 | }, 33 | 34 | 'test tags with classes': function() { 35 | var expected = '' 36 | , input = 'a.example1.example2(href="http://example.com")[Example 1]'; 37 | 38 | assert.equal(expected, jadedown(input)); 39 | }, 40 | 41 | 'test tags with classes and an id': function() { 42 | var expected = '' 43 | , input = 'a#id.example1.example2(href="http://example.com")[Example 1]'; 44 | 45 | assert.equal(expected, jadedown(input)); 46 | }, 47 | 48 | 'test multiple tags with classes and an id': function() { 49 | var expected = '' 50 | , input = ''; 51 | 52 | expected += '
'; 53 | expected += 'Link 1: Example 1 '; 54 | expected += 'and link 2 Example 2
'; 55 | 56 | input += 'Link 1: a#id.example1.example2(href="http://example.com")[Example 1] '; 57 | input += 'and link 2 a#id2.example1.example2(href="http://example.com/2")[Example 2]'; 58 | 59 | assert.equal(expected, jadedown(input)); 60 | } 61 | }; 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Jadedown 2 | 3 | An *experiment* to combine [Jade](http://jade-lang.com/) and [Markdown](http://daringfireball.net/projects/markdown/). 4 | 5 | This has been developed purely for fun to see what I can do with Jade's convenient selector-based syntax. 6 | 7 | ## Syntax 8 | 9 | Basic principles: 10 | 11 | * Tags take the form: `tagName#id.class1.class2(attr1="value", attr2="value 2")[innerHTLM]` 12 | * If a line starts with a recognised tag, then the line is wrapped in that tag. For example: `h3 This is a header` 13 | * If a line starts with `*` or `#` it is a unordered or ordered list 14 | * There is also shorthand for commonly used tags, like strong, em, links, and code 15 | * Content in pre-formatted tags (pre, code) is escaped 16 | 17 | Shorthand: 18 | 19 | * Code can be written with backticks: `var a = 1;` 20 | * Code blocks can be created by using four spaces or a tab at the start of each line 21 | * Asterisks and underscores are used for bold and italic 22 | * Other lines are converted to paragraphs 23 | * Unordered lists start with asterisks 24 | * Ordered lists start with a hash 25 | 26 | ## Examples 27 | 28 | There are some rough examples I used to develop the basic lexer and parser; these can be found in `test/fixtures/`. 29 | 30 | ## Installation 31 | 32 | As an npm module: 33 | 34 | npm install -g jadedown 35 | 36 | There's also a browser version in jadedown.js. 37 | 38 | 39 | 42 | 43 | ## Usage 44 | 45 | This library comes with a command-line script: 46 | 47 | jadedown file.jd 48 | 49 | Or as a CommonJS module: 50 | 51 | var jadedown = require('jadedown'); 52 | jadedown('h3 Example'); 53 | 54 | ## License 55 | 56 | Copyright (C) 2011 by Alex R. Young 57 | 58 | Permission is hereby granted, free of charge, to any person obtaining a copy 59 | of this software and associated documentation files (the "Software"), to deal 60 | in the Software without restriction, including without limitation the rights 61 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 62 | copies of the Software, and to permit persons to whom the Software is 63 | furnished to do so, subject to the following conditions: 64 | 65 | The above copyright notice and this permission notice shall be included in 66 | all copies or substantial portions of the Software. 67 | 68 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 69 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 70 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 71 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 72 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 73 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 74 | THE SOFTWARE. 75 | 76 | -------------------------------------------------------------------------------- /lib/lexer.l: -------------------------------------------------------------------------------- 1 | %x b 2 | %x p 3 | %x pre 4 | %x preblock 5 | %x li 6 | %x ol 7 | 8 | %% 9 | 10 | ^"*"\s+ %{ 11 | this.begin('li'); 12 | yytext = '\(([^)]*)\)\[([^\]]*)\] %{ 61 | yytext = { 62 | tag: 'a' 63 | , attr: 'href="' + yy.lexer.matches[1] + '"' 64 | , html: yy.lexer.matches[2] 65 | } 66 | return 'LINK'; 67 | %} 68 | 69 |
([a-zA-Z0-9]+)(((\.|"#")([^ (]*))+)?\(([^)]*)\)(\[([^\]]*)\])? %{ 70 | var attr = yy.lexer.matches[6] 71 | , selector = yy.lexer.matches[2]; 72 | 73 | if (attr || yy.lexer.matches[8]) { 74 | yytext = { 75 | tag: yy.lexer.matches[1] 76 | , html: yy.lexer.matches[8] 77 | , attr: attr 78 | , selector: selector 79 | }; 80 | return 'TAG'; 81 | } else { 82 | return 'TEXT'; 83 | } 84 | %} 85 | 86 |
([a-zA-Z0-9]+)(\[([^\]]*)\]) %{ 87 | // Tags in the form text[innerHTML] 88 | if (yy.lexer.matches[2]) { 89 | yytext = { 90 | tag: yy.lexer.matches[1] 91 | , html: yy.lexer.matches[3] 92 | }; 93 | return 'TAG'; 94 | } else { 95 | return 'TEXT'; 96 | } 97 | %} 98 | 99 | ^(\t|" "{4,})(.*) %{ 100 | yytext = '
' + yy.h(yy.lexer.matches[2])
101 | this.begin('preblock');
102 | return 'TEXT';
103 | %}
104 |
105 | \n+ %{
106 | return 'EOL';
107 | %}
108 |
109 | <> %{
110 | yytext = ' \n';
111 | return 'EOF';
112 | %}
113 |
114 | "*"([^*]*)"*" %{ 136 | yytext = { 137 | tag: 'strong' 138 | , html: yy.lexer.matches[1] 139 | }; 140 | return 'TAG'; 141 | %} 142 |
"_"([^_]*)"_" %{ 143 | yytext = { 144 | tag: 'em' 145 | , html: yy.lexer.matches[1] 146 | }; 147 | return 'TAG'; 148 | %} 149 |
"`"([^`]*)"`" %{ 150 | yytext = { 151 | tag: 'code' 152 | , html: yy.h(yy.lexer.matches[1]) 153 | }; 154 | return 'TAG'; 155 | %} 156 | \s return 'SPACE'; 157 | "<"("pre"|"code")">" this.begin('pre'); return 'PRE_START'; 158 |
""("pre"|"code")">" this.popState(); return 'PRE_END';
159 | . return 'PRE_TEXT';
160 | \n+ return 'TEXT';
161 | \n+ this.popState(); return 'BLOCK_END';
162 | . return 'TEXT';
163 | \n+ this.popState(); return 'P_END';
164 | \n+ return 'TEXT';
165 | ^. this.begin('p'); yy.lexer.unput(yytext); return 'P_START';
166 |
. return 'TEXT';
167 | \n this.popState(); return 'EOL';
168 | <> return 'EOF';
169 | <> return 'P_END';
170 |
171 | %%
172 |
--------------------------------------------------------------------------------
/lib/parser.js:
--------------------------------------------------------------------------------
1 | /* Jison generated parser */
2 | var grammar = (function(){
3 | var parser = {trace: function trace() { },
4 | yy: {},
5 | symbols_: {"error":2,"file":3,"lines":4,"EOF":5,"P_END":6,"text":7,"TEXT":8,"PRE_TEXT":9,"BLOCK_TAG":10,"BLOCK_END":11,"LINK":12,"TAG":13,"SPACE":14,"EOL":15,"P_START":16,"PRE_START":17,"PRE_END":18,"LIST_START":19,"LIST_ITEM":20,"LIST_END":21,"$accept":0,"$end":1},
6 | terminals_: {2:"error",5:"EOF",6:"P_END",8:"TEXT",9:"PRE_TEXT",10:"BLOCK_TAG",11:"BLOCK_END",12:"LINK",13:"TAG",14:"SPACE",15:"EOL",16:"P_START",17:"PRE_START",18:"PRE_END",19:"LIST_START",20:"LIST_ITEM",21:"LIST_END"},
7 | productions_: [0,[3,2],[3,2],[4,0],[4,2],[7,1],[7,1],[7,1],[7,1],[7,1],[7,1],[7,1],[7,1],[7,1],[7,1],[7,1],[7,1],[7,1],[7,1],[7,1]],
8 | performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) {
9 |
10 | var $0 = $$.length - 1;
11 | switch (yystate) {
12 | case 1: yy.c(yytext); return $$[$0-1];
13 | break;
14 | case 2: yy.c('
');
15 | break;
16 | case 5: yy.c($$[$0]);
17 | break;
18 | case 6: yy.ch($$[$0]);
19 | break;
20 | case 7: yy.c('BLOCK_TAG', $$[$0]);
21 | break;
22 | case 8: yy.c('BLOCK_END', $$[$0]);
23 | break;
24 | case 9: yy.c('LINK', $$[$0]);
25 | break;
26 | case 10: yy.c('TAG', $$[$0]);
27 | break;
28 | case 11: yy.c($$[$0]);
29 | break;
30 | case 12: yy.c($$[$0]);
31 | break;
32 | case 13: yy.c('');
33 | break;
34 | case 14: yy.c('
\n');
35 | break;
36 | case 15: yy.c($$[$0]);
37 | break;
38 | case 16: yy.c($$[$0]);
39 | break;
40 | case 17: yy.c($$[$0]);
41 | break;
42 | case 18: yy.c($$[$0]);
43 | break;
44 | case 19: yy.c($$[$0]);
45 | break;
46 | }
47 | },
48 | table: [{3:1,4:2,5:[2,3],6:[2,3],8:[2,3],9:[2,3],10:[2,3],11:[2,3],12:[2,3],13:[2,3],14:[2,3],15:[2,3],16:[2,3],17:[2,3],18:[2,3],19:[2,3],20:[2,3],21:[2,3]},{1:[3]},{5:[1,3],6:[1,4],7:5,8:[1,6],9:[1,7],10:[1,8],11:[1,9],12:[1,10],13:[1,11],14:[1,12],15:[1,13],16:[1,14],17:[1,15],18:[1,16],19:[1,17],20:[1,18],21:[1,19]},{1:[2,1]},{1:[2,2],5:[2,14],6:[2,14],8:[2,14],9:[2,14],10:[2,14],11:[2,14],12:[2,14],13:[2,14],14:[2,14],15:[2,14],16:[2,14],17:[2,14],18:[2,14],19:[2,14],20:[2,14],21:[2,14]},{5:[2,4],6:[2,4],8:[2,4],9:[2,4],10:[2,4],11:[2,4],12:[2,4],13:[2,4],14:[2,4],15:[2,4],16:[2,4],17:[2,4],18:[2,4],19:[2,4],20:[2,4],21:[2,4]},{5:[2,5],6:[2,5],8:[2,5],9:[2,5],10:[2,5],11:[2,5],12:[2,5],13:[2,5],14:[2,5],15:[2,5],16:[2,5],17:[2,5],18:[2,5],19:[2,5],20:[2,5],21:[2,5]},{5:[2,6],6:[2,6],8:[2,6],9:[2,6],10:[2,6],11:[2,6],12:[2,6],13:[2,6],14:[2,6],15:[2,6],16:[2,6],17:[2,6],18:[2,6],19:[2,6],20:[2,6],21:[2,6]},{5:[2,7],6:[2,7],8:[2,7],9:[2,7],10:[2,7],11:[2,7],12:[2,7],13:[2,7],14:[2,7],15:[2,7],16:[2,7],17:[2,7],18:[2,7],19:[2,7],20:[2,7],21:[2,7]},{5:[2,8],6:[2,8],8:[2,8],9:[2,8],10:[2,8],11:[2,8],12:[2,8],13:[2,8],14:[2,8],15:[2,8],16:[2,8],17:[2,8],18:[2,8],19:[2,8],20:[2,8],21:[2,8]},{5:[2,9],6:[2,9],8:[2,9],9:[2,9],10:[2,9],11:[2,9],12:[2,9],13:[2,9],14:[2,9],15:[2,9],16:[2,9],17:[2,9],18:[2,9],19:[2,9],20:[2,9],21:[2,9]},{5:[2,10],6:[2,10],8:[2,10],9:[2,10],10:[2,10],11:[2,10],12:[2,10],13:[2,10],14:[2,10],15:[2,10],16:[2,10],17:[2,10],18:[2,10],19:[2,10],20:[2,10],21:[2,10]},{5:[2,11],6:[2,11],8:[2,11],9:[2,11],10:[2,11],11:[2,11],12:[2,11],13:[2,11],14:[2,11],15:[2,11],16:[2,11],17:[2,11],18:[2,11],19:[2,11],20:[2,11],21:[2,11]},{5:[2,12],6:[2,12],8:[2,12],9:[2,12],10:[2,12],11:[2,12],12:[2,12],13:[2,12],14:[2,12],15:[2,12],16:[2,12],17:[2,12],18:[2,12],19:[2,12],20:[2,12],21:[2,12]},{5:[2,13],6:[2,13],8:[2,13],9:[2,13],10:[2,13],11:[2,13],12:[2,13],13:[2,13],14:[2,13],15:[2,13],16:[2,13],17:[2,13],18:[2,13],19:[2,13],20:[2,13],21:[2,13]},{5:[2,15],6:[2,15],8:[2,15],9:[2,15],10:[2,15],11:[2,15],12:[2,15],13:[2,15],14:[2,15],15:[2,15],16:[2,15],17:[2,15],18:[2,15],19:[2,15],20:[2,15],21:[2,15]},{5:[2,16],6:[2,16],8:[2,16],9:[2,16],10:[2,16],11:[2,16],12:[2,16],13:[2,16],14:[2,16],15:[2,16],16:[2,16],17:[2,16],18:[2,16],19:[2,16],20:[2,16],21:[2,16]},{5:[2,17],6:[2,17],8:[2,17],9:[2,17],10:[2,17],11:[2,17],12:[2,17],13:[2,17],14:[2,17],15:[2,17],16:[2,17],17:[2,17],18:[2,17],19:[2,17],20:[2,17],21:[2,17]},{5:[2,18],6:[2,18],8:[2,18],9:[2,18],10:[2,18],11:[2,18],12:[2,18],13:[2,18],14:[2,18],15:[2,18],16:[2,18],17:[2,18],18:[2,18],19:[2,18],20:[2,18],21:[2,18]},{5:[2,19],6:[2,19],8:[2,19],9:[2,19],10:[2,19],11:[2,19],12:[2,19],13:[2,19],14:[2,19],15:[2,19],16:[2,19],17:[2,19],18:[2,19],19:[2,19],20:[2,19],21:[2,19]}],
49 | defaultActions: {3:[2,1]},
50 | parseError: function parseError(str, hash) {
51 | throw new Error(str);
52 | },
53 | parse: function parse(input) {
54 | var self = this,
55 | stack = [0],
56 | vstack = [null], // semantic value stack
57 | lstack = [], // location stack
58 | table = this.table,
59 | yytext = '',
60 | yylineno = 0,
61 | yyleng = 0,
62 | recovering = 0,
63 | TERROR = 2,
64 | EOF = 1;
65 |
66 | //this.reductionCount = this.shiftCount = 0;
67 |
68 | this.lexer.setInput(input);
69 | this.lexer.yy = this.yy;
70 | this.yy.lexer = this.lexer;
71 | if (typeof this.lexer.yylloc == 'undefined')
72 | this.lexer.yylloc = {};
73 | var yyloc = this.lexer.yylloc;
74 | lstack.push(yyloc);
75 |
76 | if (typeof this.yy.parseError === 'function')
77 | this.parseError = this.yy.parseError;
78 |
79 | function popStack (n) {
80 | stack.length = stack.length - 2*n;
81 | vstack.length = vstack.length - n;
82 | lstack.length = lstack.length - n;
83 | }
84 |
85 | function lex() {
86 | var token;
87 | token = self.lexer.lex() || 1; // $end = 1
88 | // if token isn't its numeric value, convert
89 | if (typeof token !== 'number') {
90 | token = self.symbols_[token] || token;
91 | }
92 | return token;
93 | };
94 |
95 | var symbol, preErrorSymbol, state, action, a, r, yyval={},p,len,newState, expected;
96 | while (true) {
97 | // retreive state number from top of stack
98 | state = stack[stack.length-1];
99 |
100 | // use default actions if available
101 | if (this.defaultActions[state]) {
102 | action = this.defaultActions[state];
103 | } else {
104 | if (symbol == null)
105 | symbol = lex();
106 | // read action for current state and first input
107 | action = table[state] && table[state][symbol];
108 | }
109 |
110 | // handle parse error
111 | if (typeof action === 'undefined' || !action.length || !action[0]) {
112 |
113 | if (!recovering) {
114 | // Report error
115 | expected = [];
116 | for (p in table[state]) if (this.terminals_[p] && p > 2) {
117 | expected.push("'"+this.terminals_[p]+"'");
118 | }
119 | var errStr = '';
120 | if (this.lexer.showPosition) {
121 | errStr = 'Parse error on line '+(yylineno+1)+":\n"+this.lexer.showPosition()+'\nExpecting '+expected.join(', ');
122 | } else {
123 | errStr = 'Parse error on line '+(yylineno+1)+": Unexpected " +
124 | (symbol == 1 /*EOF*/ ? "end of input" :
125 | ("'"+(this.terminals_[symbol] || symbol)+"'"));
126 | }
127 | this.parseError(errStr,
128 | {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected});
129 | }
130 |
131 | // just recovered from another error
132 | if (recovering == 3) {
133 | if (symbol == EOF) {
134 | throw new Error(errStr || 'Parsing halted.');
135 | }
136 |
137 | // discard current lookahead and grab another
138 | yyleng = this.lexer.yyleng;
139 | yytext = this.lexer.yytext;
140 | yylineno = this.lexer.yylineno;
141 | yyloc = this.lexer.yylloc;
142 | symbol = lex();
143 | }
144 |
145 | // try to recover from error
146 | while (1) {
147 | // check for error recovery rule in this state
148 | if ((TERROR.toString()) in table[state]) {
149 | break;
150 | }
151 | if (state == 0) {
152 | throw new Error(errStr || 'Parsing halted.');
153 | }
154 | popStack(1);
155 | state = stack[stack.length-1];
156 | }
157 |
158 | preErrorSymbol = symbol; // save the lookahead token
159 | symbol = TERROR; // insert generic error symbol as new lookahead
160 | state = stack[stack.length-1];
161 | action = table[state] && table[state][TERROR];
162 | recovering = 3; // allow 3 real symbols to be shifted before reporting a new error
163 | }
164 |
165 | // this shouldn't happen, unless resolve defaults are off
166 | if (action[0] instanceof Array && action.length > 1) {
167 | throw new Error('Parse Error: multiple actions possible at state: '+state+', token: '+symbol);
168 | }
169 |
170 | switch (action[0]) {
171 |
172 | case 1: // shift
173 | //this.shiftCount++;
174 |
175 | stack.push(symbol);
176 | vstack.push(this.lexer.yytext);
177 | lstack.push(this.lexer.yylloc);
178 | stack.push(action[1]); // push state
179 | symbol = null;
180 | if (!preErrorSymbol) { // normal execution/no error
181 | yyleng = this.lexer.yyleng;
182 | yytext = this.lexer.yytext;
183 | yylineno = this.lexer.yylineno;
184 | yyloc = this.lexer.yylloc;
185 | if (recovering > 0)
186 | recovering--;
187 | } else { // error just occurred, resume old lookahead f/ before error
188 | symbol = preErrorSymbol;
189 | preErrorSymbol = null;
190 | }
191 | break;
192 |
193 | case 2: // reduce
194 | //this.reductionCount++;
195 |
196 | len = this.productions_[action[1]][1];
197 |
198 | // perform semantic action
199 | yyval.$ = vstack[vstack.length-len]; // default to $$ = $1
200 | // default location, uses first token for firsts, last for lasts
201 | yyval._$ = {
202 | first_line: lstack[lstack.length-(len||1)].first_line,
203 | last_line: lstack[lstack.length-1].last_line,
204 | first_column: lstack[lstack.length-(len||1)].first_column,
205 | last_column: lstack[lstack.length-1].last_column
206 | };
207 | r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack);
208 |
209 | if (typeof r !== 'undefined') {
210 | return r;
211 | }
212 |
213 | // pop off stack
214 | if (len) {
215 | stack = stack.slice(0,-1*len*2);
216 | vstack = vstack.slice(0, -1*len);
217 | lstack = lstack.slice(0, -1*len);
218 | }
219 |
220 | stack.push(this.productions_[action[1]][0]); // push nonterminal (reduce)
221 | vstack.push(yyval.$);
222 | lstack.push(yyval._$);
223 | // goto new state = table[STATE][NONTERMINAL]
224 | newState = table[stack[stack.length-2]][stack[stack.length-1]];
225 | stack.push(newState);
226 | break;
227 |
228 | case 3: // accept
229 | return true;
230 | }
231 |
232 | }
233 |
234 | return true;
235 | }};/* Jison generated lexer */
236 | var lexer = (function(){var lexer = ({EOF:1,
237 | parseError:function parseError(str, hash) {
238 | if (this.yy.parseError) {
239 | this.yy.parseError(str, hash);
240 | } else {
241 | throw new Error(str);
242 | }
243 | },
244 | setInput:function (input) {
245 | this._input = input;
246 | this._more = this._less = this.done = false;
247 | this.yylineno = this.yyleng = 0;
248 | this.yytext = this.matched = this.match = '';
249 | this.conditionStack = ['INITIAL'];
250 | this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0};
251 | return this;
252 | },
253 | input:function () {
254 | var ch = this._input[0];
255 | this.yytext+=ch;
256 | this.yyleng++;
257 | this.match+=ch;
258 | this.matched+=ch;
259 | var lines = ch.match(/\n/);
260 | if (lines) this.yylineno++;
261 | this._input = this._input.slice(1);
262 | return ch;
263 | },
264 | unput:function (ch) {
265 | this._input = ch + this._input;
266 | return this;
267 | },
268 | more:function () {
269 | this._more = true;
270 | return this;
271 | },
272 | pastInput:function () {
273 | var past = this.matched.substr(0, this.matched.length - this.match.length);
274 | return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, "");
275 | },
276 | upcomingInput:function () {
277 | var next = this.match;
278 | if (next.length < 20) {
279 | next += this._input.substr(0, 20-next.length);
280 | }
281 | return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, "");
282 | },
283 | showPosition:function () {
284 | var pre = this.pastInput();
285 | var c = new Array(pre.length + 1).join("-");
286 | return pre + this.upcomingInput() + "\n" + c+"^";
287 | },
288 | next:function () {
289 | if (this.done) {
290 | return this.EOF;
291 | }
292 | if (!this._input) this.done = true;
293 |
294 | var token,
295 | match,
296 | col,
297 | lines;
298 | if (!this._more) {
299 | this.yytext = '';
300 | this.match = '';
301 | }
302 | var rules = this._currentRules();
303 | for (var i=0;i < rules.length; i++) {
304 | match = this._input.match(this.rules[rules[i]]);
305 | if (match) {
306 | lines = match[0].match(/\n.*/g);
307 | if (lines) this.yylineno += lines.length;
308 | this.yylloc = {first_line: this.yylloc.last_line,
309 | last_line: this.yylineno+1,
310 | first_column: this.yylloc.last_column,
311 | last_column: lines ? lines[lines.length-1].length-1 : this.yylloc.last_column + match[0].length}
312 | this.yytext += match[0];
313 | this.match += match[0];
314 | this.matches = match;
315 | this.yyleng = this.yytext.length;
316 | this._more = false;
317 | this._input = this._input.slice(match[0].length);
318 | this.matched += match[0];
319 | token = this.performAction.call(this, this.yy, this, rules[i],this.conditionStack[this.conditionStack.length-1]);
320 | if (token) return token;
321 | else return;
322 | }
323 | }
324 | if (this._input === "") {
325 | return this.EOF;
326 | } else {
327 | this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(),
328 | {text: "", token: null, line: this.yylineno});
329 | }
330 | },
331 | lex:function lex() {
332 | var r = this.next();
333 | if (typeof r !== 'undefined') {
334 | return r;
335 | } else {
336 | return this.lex();
337 | }
338 | },
339 | begin:function begin(condition) {
340 | this.conditionStack.push(condition);
341 | },
342 | popState:function popState() {
343 | return this.conditionStack.pop();
344 | },
345 | _currentRules:function _currentRules() {
346 | return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules;
347 | }});
348 | lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
349 |
350 | var YYSTATE=YY_START
351 | switch($avoiding_name_collisions) {
352 | case 0:
353 | this.begin('li');
354 | yy_.yytext = '\n- ';
355 | return 19;
356 |
357 | break;
358 | case 1:
359 | if (yy_.yytext.length > 1) {
360 | yy_.yytext = '
\n
\n';
361 | this.popState();
362 | return 21;
363 | }
364 |
365 | break;
366 | case 2:
367 | yy_.yytext = '
' + yy.h(yy.lexer.matches[2])
443 | this.begin('preblock');
444 | return 8;
445 |
446 | break;
447 | case 12:
448 | return 15;
449 |
450 | break;
451 | case 13:
452 | yy_.yytext = '\n';
453 | return 5;
454 |
455 | break;
456 | case 14:
457 | yy_.yytext = yy.h(yy.lexer.matches[2])
458 | return 8;
459 |
460 | break;
461 | case 15:
462 | yy.lexer.unput(yy_.yytext);
463 | yy.c('\n');
464 | this.popState();
465 |
466 | break;
467 | case 16:
468 | this.begin('b');
469 |
470 | yy_.yytext = {
471 | tag: yy.lexer.matches[1]
472 | };
473 |
474 | return 10;
475 |
476 | break;
477 | case 17:
478 | yy_.yytext = {
479 | tag: 'strong'
480 | , html: yy.lexer.matches[1]
481 | };
482 | return 13;
483 |
484 | break;
485 | case 18:
486 | yy_.yytext = {
487 | tag: 'em'
488 | , html: yy.lexer.matches[1]
489 | };
490 | return 13;
491 |
492 | break;
493 | case 19:
494 | yy_.yytext = {
495 | tag: 'code'
496 | , html: yy.h(yy.lexer.matches[1])
497 | };
498 | return 13;
499 |
500 | break;
501 | case 20:return 14;
502 | break;
503 | case 21:this.begin('pre'); return 17;
504 | break;
505 | case 22:this.popState(); return 18;
506 | break;
507 | case 23:return 9;
508 | break;
509 | case 24:return 8;
510 | break;
511 | case 25:this.popState(); return 11;
512 | break;
513 | case 26:return 8;
514 | break;
515 | case 27:this.popState(); return 6;
516 | break;
517 | case 28:return 8;
518 | break;
519 | case 29:this.begin('p'); yy.lexer.unput(yy_.yytext); return 16;
520 | break;
521 | case 30:return 8;
522 | break;
523 | case 31:this.popState(); return 15;
524 | break;
525 | case 32:return 5;
526 | break;
527 | case 33:return 6;
528 | break;
529 | }
530 | };
531 | lexer.rules = [/^^\*\s+/,/^\n+/,/^$/,/^^\*\s+/,/^^#\s+/,/^\n+/,/^$/,/^^#\s+/,/^\(([^)]*)\)\[([^\]]*)\]/,/^([a-zA-Z0-9]+)(((\.|#)([^ (]*))+)?\(([^)]*)\)(\[([^\]]*)\])?/,/^([a-zA-Z0-9]+)(\[([^\]]*)\])/,/^^(\t| {4,})(.*)/,/^\n+/,/^$/,/^^(\t| {4,})(.*)/,/^^[^\s]/,/^^(h[1-6]|bq|blockquote)\s+/,/^\*([^*]*)\*/,/^_([^_]*)_\b/,/^`([^`]*)`/,/^\s\b/,/^<(pre|code)>/,/^<\/(pre|code)>/,/^./,/^\n+/,/^\n+/,/^./,/^\n+/,/^\n+/,/^^./,/^./,/^\n\b/,/^$/,/^$/];
532 | lexer.conditions = {"b":{"rules":[8,9,10,17,18,19,25,26,31,32],"inclusive":false},"p":{"rules":[8,9,10,17,18,19,27,30,33],"inclusive":false},"pre":{"rules":[22,23,24],"inclusive":false},"preblock":{"rules":[12,13,14,15],"inclusive":false},"li":{"rules":[1,2,3,8,9,10,17,18,19,26],"inclusive":false},"ol":{"rules":[5,6,7,8,9,10,17,18,19,26],"inclusive":false},"INITIAL":{"rules":[0,4,11,16,20,21,28,29,31,32],"inclusive":true}};return lexer;})()
533 | parser.lexer = lexer;
534 | return parser;
535 | })();
536 | if (typeof require !== 'undefined' && typeof exports !== 'undefined') {
537 | exports.parser = grammar;
538 | exports.parse = function () { return grammar.parse.apply(grammar, arguments); }
539 | exports.main = function commonjsMain(args) {
540 | if (!args[1])
541 | throw new Error('Usage: '+args[0]+' FILE');
542 | if (typeof process !== 'undefined') {
543 | var source = require('fs').readFileSync(require('path').join(process.cwd(), args[1]), "utf8");
544 | } else {
545 | var cwd = require("file").path(require("file").cwd());
546 | var source = cwd.join(args[1]).read({charset: "utf-8"});
547 | }
548 | return exports.parser.parse(source);
549 | }
550 | if (typeof module !== 'undefined' && require.main === module) {
551 | exports.main(typeof process !== 'undefined' ? process.argv.slice(1) : require("system").args);
552 | }
553 | }
--------------------------------------------------------------------------------
/jadedown.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | /**
3 | * Jadedown for browsers.
4 | *
5 | * Usage:
6 | *
7 | *
8 | *
9 | * jadedown('*Example*');
10 | *
11 | */
12 | var exports = {};
13 |
14 | function require(m) {
15 | if (m === './parser') {
16 | return exports;
17 | }
18 | }
19 |
20 | function aliases(tag) {
21 | switch (tag) {
22 | case 'bq':
23 | return 'blockquote';
24 | break;
25 |
26 | default:
27 | return tag;
28 | }
29 | }
30 |
31 | function Nodes() {
32 | this.openTags = [];
33 | }
34 |
35 | Nodes.prototype = {
36 | pushOpenTag: function(tag) {
37 | this.openTags.push(tag);
38 | },
39 |
40 | popOpenTag: function() {
41 | return '' + this.openTags.pop() + '>';
42 | },
43 |
44 | makeAttr: function($1) {
45 | var attr = []
46 | , id = $1.selector ? $1.selector.match(/#([^.([]*)/) : null
47 | , classes = $1.selector;
48 |
49 | if (id && id[1]) {
50 | attr.push('id="' + id[1] + '"');
51 | classes = classes.replace(/#([^.([]*)/, '');
52 | }
53 |
54 | if (classes) {
55 | classes = classes.split(/\./);
56 | if (classes.length > 0) {
57 | attr.push('class="' + classes.join(' ').trim() + '"');
58 | }
59 | }
60 |
61 | if ($1.attr) {
62 | attr.push($1.attr);
63 | }
64 |
65 | if (attr.length === 0) {
66 | return '';
67 | } else {
68 | return ' ' + attr.join(' ');
69 | }
70 | },
71 |
72 | makeTag: function($1) {
73 | var attr = this.makeAttr($1);
74 | $1.tag = aliases($1.tag);
75 |
76 | if (!$1.html) {
77 | this.pushOpenTag($1.tag);
78 | // TODO: self-closing, plus on/off for xhtml/html
79 | var end = $1.tag === 'img' ? '/>' : '>';
80 | return '<' + $1.tag + attr + end;
81 | } else {
82 | return '<' + $1.tag + attr + '>' + $1.html + '' + $1.tag + '>';
83 | }
84 | },
85 |
86 | TAG: function($1) {
87 | return this.makeTag($1);
88 | },
89 |
90 | LINK: function($1) {
91 | return this.makeTag($1);
92 | },
93 |
94 | BLOCK_TAG: function($1) {
95 | return this.makeTag($1);
96 | },
97 |
98 | BLOCK_END: function() {
99 | return this.popOpenTag() + '\n';
100 | }
101 | };
102 |
103 | var nodes = new Nodes();
104 |
105 | if (typeof module !== 'undefined') {
106 | module.exports = nodes;
107 | }
108 | /* Jison generated parser */
109 | var grammar = (function(){
110 | var parser = {trace: function trace() { },
111 | yy: {},
112 | symbols_: {"error":2,"file":3,"lines":4,"EOF":5,"P_END":6,"text":7,"TEXT":8,"PRE_TEXT":9,"BLOCK_TAG":10,"BLOCK_END":11,"LINK":12,"TAG":13,"SPACE":14,"EOL":15,"P_START":16,"PRE_START":17,"PRE_END":18,"LIST_START":19,"LIST_ITEM":20,"LIST_END":21,"$accept":0,"$end":1},
113 | terminals_: {2:"error",5:"EOF",6:"P_END",8:"TEXT",9:"PRE_TEXT",10:"BLOCK_TAG",11:"BLOCK_END",12:"LINK",13:"TAG",14:"SPACE",15:"EOL",16:"P_START",17:"PRE_START",18:"PRE_END",19:"LIST_START",20:"LIST_ITEM",21:"LIST_END"},
114 | productions_: [0,[3,2],[3,2],[4,0],[4,2],[7,1],[7,1],[7,1],[7,1],[7,1],[7,1],[7,1],[7,1],[7,1],[7,1],[7,1],[7,1],[7,1],[7,1],[7,1]],
115 | performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) {
116 |
117 | var $0 = $$.length - 1;
118 | switch (yystate) {
119 | case 1: yy.c(yytext); return $$[$0-1];
120 | break;
121 | case 2: yy.c('');
122 | break;
123 | case 5: yy.c($$[$0]);
124 | break;
125 | case 6: yy.ch($$[$0]);
126 | break;
127 | case 7: yy.c('BLOCK_TAG', $$[$0]);
128 | break;
129 | case 8: yy.c('BLOCK_END', $$[$0]);
130 | break;
131 | case 9: yy.c('LINK', $$[$0]);
132 | break;
133 | case 10: yy.c('TAG', $$[$0]);
134 | break;
135 | case 11: yy.c($$[$0]);
136 | break;
137 | case 12: yy.c($$[$0]);
138 | break;
139 | case 13: yy.c(''); 140 | break; 141 | case 14: yy.c('
\n'); 142 | break; 143 | case 15: yy.c($$[$0]); 144 | break; 145 | case 16: yy.c($$[$0]); 146 | break; 147 | case 17: yy.c($$[$0]); 148 | break; 149 | case 18: yy.c($$[$0]); 150 | break; 151 | case 19: yy.c($$[$0]); 152 | break; 153 | } 154 | }, 155 | table: [{3:1,4:2,5:[2,3],6:[2,3],8:[2,3],9:[2,3],10:[2,3],11:[2,3],12:[2,3],13:[2,3],14:[2,3],15:[2,3],16:[2,3],17:[2,3],18:[2,3],19:[2,3],20:[2,3],21:[2,3]},{1:[3]},{5:[1,3],6:[1,4],7:5,8:[1,6],9:[1,7],10:[1,8],11:[1,9],12:[1,10],13:[1,11],14:[1,12],15:[1,13],16:[1,14],17:[1,15],18:[1,16],19:[1,17],20:[1,18],21:[1,19]},{1:[2,1]},{1:[2,2],5:[2,14],6:[2,14],8:[2,14],9:[2,14],10:[2,14],11:[2,14],12:[2,14],13:[2,14],14:[2,14],15:[2,14],16:[2,14],17:[2,14],18:[2,14],19:[2,14],20:[2,14],21:[2,14]},{5:[2,4],6:[2,4],8:[2,4],9:[2,4],10:[2,4],11:[2,4],12:[2,4],13:[2,4],14:[2,4],15:[2,4],16:[2,4],17:[2,4],18:[2,4],19:[2,4],20:[2,4],21:[2,4]},{5:[2,5],6:[2,5],8:[2,5],9:[2,5],10:[2,5],11:[2,5],12:[2,5],13:[2,5],14:[2,5],15:[2,5],16:[2,5],17:[2,5],18:[2,5],19:[2,5],20:[2,5],21:[2,5]},{5:[2,6],6:[2,6],8:[2,6],9:[2,6],10:[2,6],11:[2,6],12:[2,6],13:[2,6],14:[2,6],15:[2,6],16:[2,6],17:[2,6],18:[2,6],19:[2,6],20:[2,6],21:[2,6]},{5:[2,7],6:[2,7],8:[2,7],9:[2,7],10:[2,7],11:[2,7],12:[2,7],13:[2,7],14:[2,7],15:[2,7],16:[2,7],17:[2,7],18:[2,7],19:[2,7],20:[2,7],21:[2,7]},{5:[2,8],6:[2,8],8:[2,8],9:[2,8],10:[2,8],11:[2,8],12:[2,8],13:[2,8],14:[2,8],15:[2,8],16:[2,8],17:[2,8],18:[2,8],19:[2,8],20:[2,8],21:[2,8]},{5:[2,9],6:[2,9],8:[2,9],9:[2,9],10:[2,9],11:[2,9],12:[2,9],13:[2,9],14:[2,9],15:[2,9],16:[2,9],17:[2,9],18:[2,9],19:[2,9],20:[2,9],21:[2,9]},{5:[2,10],6:[2,10],8:[2,10],9:[2,10],10:[2,10],11:[2,10],12:[2,10],13:[2,10],14:[2,10],15:[2,10],16:[2,10],17:[2,10],18:[2,10],19:[2,10],20:[2,10],21:[2,10]},{5:[2,11],6:[2,11],8:[2,11],9:[2,11],10:[2,11],11:[2,11],12:[2,11],13:[2,11],14:[2,11],15:[2,11],16:[2,11],17:[2,11],18:[2,11],19:[2,11],20:[2,11],21:[2,11]},{5:[2,12],6:[2,12],8:[2,12],9:[2,12],10:[2,12],11:[2,12],12:[2,12],13:[2,12],14:[2,12],15:[2,12],16:[2,12],17:[2,12],18:[2,12],19:[2,12],20:[2,12],21:[2,12]},{5:[2,13],6:[2,13],8:[2,13],9:[2,13],10:[2,13],11:[2,13],12:[2,13],13:[2,13],14:[2,13],15:[2,13],16:[2,13],17:[2,13],18:[2,13],19:[2,13],20:[2,13],21:[2,13]},{5:[2,15],6:[2,15],8:[2,15],9:[2,15],10:[2,15],11:[2,15],12:[2,15],13:[2,15],14:[2,15],15:[2,15],16:[2,15],17:[2,15],18:[2,15],19:[2,15],20:[2,15],21:[2,15]},{5:[2,16],6:[2,16],8:[2,16],9:[2,16],10:[2,16],11:[2,16],12:[2,16],13:[2,16],14:[2,16],15:[2,16],16:[2,16],17:[2,16],18:[2,16],19:[2,16],20:[2,16],21:[2,16]},{5:[2,17],6:[2,17],8:[2,17],9:[2,17],10:[2,17],11:[2,17],12:[2,17],13:[2,17],14:[2,17],15:[2,17],16:[2,17],17:[2,17],18:[2,17],19:[2,17],20:[2,17],21:[2,17]},{5:[2,18],6:[2,18],8:[2,18],9:[2,18],10:[2,18],11:[2,18],12:[2,18],13:[2,18],14:[2,18],15:[2,18],16:[2,18],17:[2,18],18:[2,18],19:[2,18],20:[2,18],21:[2,18]},{5:[2,19],6:[2,19],8:[2,19],9:[2,19],10:[2,19],11:[2,19],12:[2,19],13:[2,19],14:[2,19],15:[2,19],16:[2,19],17:[2,19],18:[2,19],19:[2,19],20:[2,19],21:[2,19]}], 156 | defaultActions: {3:[2,1]}, 157 | parseError: function parseError(str, hash) { 158 | throw new Error(str); 159 | }, 160 | parse: function parse(input) { 161 | var self = this, 162 | stack = [0], 163 | vstack = [null], // semantic value stack 164 | lstack = [], // location stack 165 | table = this.table, 166 | yytext = '', 167 | yylineno = 0, 168 | yyleng = 0, 169 | recovering = 0, 170 | TERROR = 2, 171 | EOF = 1; 172 | 173 | //this.reductionCount = this.shiftCount = 0; 174 | 175 | this.lexer.setInput(input); 176 | this.lexer.yy = this.yy; 177 | this.yy.lexer = this.lexer; 178 | if (typeof this.lexer.yylloc == 'undefined') 179 | this.lexer.yylloc = {}; 180 | var yyloc = this.lexer.yylloc; 181 | lstack.push(yyloc); 182 | 183 | if (typeof this.yy.parseError === 'function') 184 | this.parseError = this.yy.parseError; 185 | 186 | function popStack (n) { 187 | stack.length = stack.length - 2*n; 188 | vstack.length = vstack.length - n; 189 | lstack.length = lstack.length - n; 190 | } 191 | 192 | function lex() { 193 | var token; 194 | token = self.lexer.lex() || 1; // $end = 1 195 | // if token isn't its numeric value, convert 196 | if (typeof token !== 'number') { 197 | token = self.symbols_[token] || token; 198 | } 199 | return token; 200 | }; 201 | 202 | var symbol, preErrorSymbol, state, action, a, r, yyval={},p,len,newState, expected; 203 | while (true) { 204 | // retreive state number from top of stack 205 | state = stack[stack.length-1]; 206 | 207 | // use default actions if available 208 | if (this.defaultActions[state]) { 209 | action = this.defaultActions[state]; 210 | } else { 211 | if (symbol == null) 212 | symbol = lex(); 213 | // read action for current state and first input 214 | action = table[state] && table[state][symbol]; 215 | } 216 | 217 | // handle parse error 218 | if (typeof action === 'undefined' || !action.length || !action[0]) { 219 | 220 | if (!recovering) { 221 | // Report error 222 | expected = []; 223 | for (p in table[state]) if (this.terminals_[p] && p > 2) { 224 | expected.push("'"+this.terminals_[p]+"'"); 225 | } 226 | var errStr = ''; 227 | if (this.lexer.showPosition) { 228 | errStr = 'Parse error on line '+(yylineno+1)+":\n"+this.lexer.showPosition()+'\nExpecting '+expected.join(', '); 229 | } else { 230 | errStr = 'Parse error on line '+(yylineno+1)+": Unexpected " + 231 | (symbol == 1 /*EOF*/ ? "end of input" : 232 | ("'"+(this.terminals_[symbol] || symbol)+"'")); 233 | } 234 | this.parseError(errStr, 235 | {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected}); 236 | } 237 | 238 | // just recovered from another error 239 | if (recovering == 3) { 240 | if (symbol == EOF) { 241 | throw new Error(errStr || 'Parsing halted.'); 242 | } 243 | 244 | // discard current lookahead and grab another 245 | yyleng = this.lexer.yyleng; 246 | yytext = this.lexer.yytext; 247 | yylineno = this.lexer.yylineno; 248 | yyloc = this.lexer.yylloc; 249 | symbol = lex(); 250 | } 251 | 252 | // try to recover from error 253 | while (1) { 254 | // check for error recovery rule in this state 255 | if ((TERROR.toString()) in table[state]) { 256 | break; 257 | } 258 | if (state == 0) { 259 | throw new Error(errStr || 'Parsing halted.'); 260 | } 261 | popStack(1); 262 | state = stack[stack.length-1]; 263 | } 264 | 265 | preErrorSymbol = symbol; // save the lookahead token 266 | symbol = TERROR; // insert generic error symbol as new lookahead 267 | state = stack[stack.length-1]; 268 | action = table[state] && table[state][TERROR]; 269 | recovering = 3; // allow 3 real symbols to be shifted before reporting a new error 270 | } 271 | 272 | // this shouldn't happen, unless resolve defaults are off 273 | if (action[0] instanceof Array && action.length > 1) { 274 | throw new Error('Parse Error: multiple actions possible at state: '+state+', token: '+symbol); 275 | } 276 | 277 | switch (action[0]) { 278 | 279 | case 1: // shift 280 | //this.shiftCount++; 281 | 282 | stack.push(symbol); 283 | vstack.push(this.lexer.yytext); 284 | lstack.push(this.lexer.yylloc); 285 | stack.push(action[1]); // push state 286 | symbol = null; 287 | if (!preErrorSymbol) { // normal execution/no error 288 | yyleng = this.lexer.yyleng; 289 | yytext = this.lexer.yytext; 290 | yylineno = this.lexer.yylineno; 291 | yyloc = this.lexer.yylloc; 292 | if (recovering > 0) 293 | recovering--; 294 | } else { // error just occurred, resume old lookahead f/ before error 295 | symbol = preErrorSymbol; 296 | preErrorSymbol = null; 297 | } 298 | break; 299 | 300 | case 2: // reduce 301 | //this.reductionCount++; 302 | 303 | len = this.productions_[action[1]][1]; 304 | 305 | // perform semantic action 306 | yyval.$ = vstack[vstack.length-len]; // default to $$ = $1 307 | // default location, uses first token for firsts, last for lasts 308 | yyval._$ = { 309 | first_line: lstack[lstack.length-(len||1)].first_line, 310 | last_line: lstack[lstack.length-1].last_line, 311 | first_column: lstack[lstack.length-(len||1)].first_column, 312 | last_column: lstack[lstack.length-1].last_column 313 | }; 314 | r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack); 315 | 316 | if (typeof r !== 'undefined') { 317 | return r; 318 | } 319 | 320 | // pop off stack 321 | if (len) { 322 | stack = stack.slice(0,-1*len*2); 323 | vstack = vstack.slice(0, -1*len); 324 | lstack = lstack.slice(0, -1*len); 325 | } 326 | 327 | stack.push(this.productions_[action[1]][0]); // push nonterminal (reduce) 328 | vstack.push(yyval.$); 329 | lstack.push(yyval._$); 330 | // goto new state = table[STATE][NONTERMINAL] 331 | newState = table[stack[stack.length-2]][stack[stack.length-1]]; 332 | stack.push(newState); 333 | break; 334 | 335 | case 3: // accept 336 | return true; 337 | } 338 | 339 | } 340 | 341 | return true; 342 | }};/* Jison generated lexer */ 343 | var lexer = (function(){var lexer = ({EOF:1, 344 | parseError:function parseError(str, hash) { 345 | if (this.yy.parseError) { 346 | this.yy.parseError(str, hash); 347 | } else { 348 | throw new Error(str); 349 | } 350 | }, 351 | setInput:function (input) { 352 | this._input = input; 353 | this._more = this._less = this.done = false; 354 | this.yylineno = this.yyleng = 0; 355 | this.yytext = this.matched = this.match = ''; 356 | this.conditionStack = ['INITIAL']; 357 | this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0}; 358 | return this; 359 | }, 360 | input:function () { 361 | var ch = this._input[0]; 362 | this.yytext+=ch; 363 | this.yyleng++; 364 | this.match+=ch; 365 | this.matched+=ch; 366 | var lines = ch.match(/\n/); 367 | if (lines) this.yylineno++; 368 | this._input = this._input.slice(1); 369 | return ch; 370 | }, 371 | unput:function (ch) { 372 | this._input = ch + this._input; 373 | return this; 374 | }, 375 | more:function () { 376 | this._more = true; 377 | return this; 378 | }, 379 | pastInput:function () { 380 | var past = this.matched.substr(0, this.matched.length - this.match.length); 381 | return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, ""); 382 | }, 383 | upcomingInput:function () { 384 | var next = this.match; 385 | if (next.length < 20) { 386 | next += this._input.substr(0, 20-next.length); 387 | } 388 | return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, ""); 389 | }, 390 | showPosition:function () { 391 | var pre = this.pastInput(); 392 | var c = new Array(pre.length + 1).join("-"); 393 | return pre + this.upcomingInput() + "\n" + c+"^"; 394 | }, 395 | next:function () { 396 | if (this.done) { 397 | return this.EOF; 398 | } 399 | if (!this._input) this.done = true; 400 | 401 | var token, 402 | match, 403 | col, 404 | lines; 405 | if (!this._more) { 406 | this.yytext = ''; 407 | this.match = ''; 408 | } 409 | var rules = this._currentRules(); 410 | for (var i=0;i < rules.length; i++) { 411 | match = this._input.match(this.rules[rules[i]]); 412 | if (match) { 413 | lines = match[0].match(/\n.*/g); 414 | if (lines) this.yylineno += lines.length; 415 | this.yylloc = {first_line: this.yylloc.last_line, 416 | last_line: this.yylineno+1, 417 | first_column: this.yylloc.last_column, 418 | last_column: lines ? lines[lines.length-1].length-1 : this.yylloc.last_column + match[0].length} 419 | this.yytext += match[0]; 420 | this.match += match[0]; 421 | this.matches = match; 422 | this.yyleng = this.yytext.length; 423 | this._more = false; 424 | this._input = this._input.slice(match[0].length); 425 | this.matched += match[0]; 426 | token = this.performAction.call(this, this.yy, this, rules[i],this.conditionStack[this.conditionStack.length-1]); 427 | if (token) return token; 428 | else return; 429 | } 430 | } 431 | if (this._input === "") { 432 | return this.EOF; 433 | } else { 434 | this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(), 435 | {text: "", token: null, line: this.yylineno}); 436 | } 437 | }, 438 | lex:function lex() { 439 | var r = this.next(); 440 | if (typeof r !== 'undefined') { 441 | return r; 442 | } else { 443 | return this.lex(); 444 | } 445 | }, 446 | begin:function begin(condition) { 447 | this.conditionStack.push(condition); 448 | }, 449 | popState:function popState() { 450 | return this.conditionStack.pop(); 451 | }, 452 | _currentRules:function _currentRules() { 453 | return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules; 454 | }}); 455 | lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) { 456 | 457 | var YYSTATE=YY_START 458 | switch($avoiding_name_collisions) { 459 | case 0: 460 | this.begin('li'); 461 | yy_.yytext = '' + yy.h(yy.lexer.matches[2])
550 | this.begin('preblock');
551 | return 8;
552 |
553 | break;
554 | case 12:
555 | return 15;
556 |
557 | break;
558 | case 13:
559 | yy_.yytext = '\n';
560 | return 5;
561 |
562 | break;
563 | case 14:
564 | yy_.yytext = yy.h(yy.lexer.matches[2])
565 | return 8;
566 |
567 | break;
568 | case 15:
569 | yy.lexer.unput(yy_.yytext);
570 | yy.c('\n');
571 | this.popState();
572 |
573 | break;
574 | case 16:
575 | this.begin('b');
576 |
577 | yy_.yytext = {
578 | tag: yy.lexer.matches[1]
579 | };
580 |
581 | return 10;
582 |
583 | break;
584 | case 17:
585 | yy_.yytext = {
586 | tag: 'strong'
587 | , html: yy.lexer.matches[1]
588 | };
589 | return 13;
590 |
591 | break;
592 | case 18:
593 | yy_.yytext = {
594 | tag: 'em'
595 | , html: yy.lexer.matches[1]
596 | };
597 | return 13;
598 |
599 | break;
600 | case 19:
601 | yy_.yytext = {
602 | tag: 'code'
603 | , html: yy.h(yy.lexer.matches[1])
604 | };
605 | return 13;
606 |
607 | break;
608 | case 20:return 14;
609 | break;
610 | case 21:this.begin('pre'); return 17;
611 | break;
612 | case 22:this.popState(); return 18;
613 | break;
614 | case 23:return 9;
615 | break;
616 | case 24:return 8;
617 | break;
618 | case 25:this.popState(); return 11;
619 | break;
620 | case 26:return 8;
621 | break;
622 | case 27:this.popState(); return 6;
623 | break;
624 | case 28:return 8;
625 | break;
626 | case 29:this.begin('p'); yy.lexer.unput(yy_.yytext); return 16;
627 | break;
628 | case 30:return 8;
629 | break;
630 | case 31:this.popState(); return 15;
631 | break;
632 | case 32:return 5;
633 | break;
634 | case 33:return 6;
635 | break;
636 | }
637 | };
638 | lexer.rules = [/^^\*\s+/,/^\n+/,/^$/,/^^\*\s+/,/^^#\s+/,/^\n+/,/^$/,/^^#\s+/,/^\(([^)]*)\)\[([^\]]*)\]/,/^([a-zA-Z0-9]+)(((\.|#)([^ (]*))+)?\(([^)]*)\)(\[([^\]]*)\])?/,/^([a-zA-Z0-9]+)(\[([^\]]*)\])/,/^^(\t| {4,})(.*)/,/^\n+/,/^$/,/^^(\t| {4,})(.*)/,/^^[^\s]/,/^^(h[1-6]|bq|blockquote)\s+/,/^\*([^*]*)\*/,/^_([^_]*)_\b/,/^`([^`]*)`/,/^\s\b/,/^<(pre|code)>/,/^<\/(pre|code)>/,/^./,/^\n+/,/^\n+/,/^./,/^\n+/,/^\n+/,/^^./,/^./,/^\n\b/,/^$/,/^$/];
639 | lexer.conditions = {"b":{"rules":[8,9,10,17,18,19,25,26,31,32],"inclusive":false},"p":{"rules":[8,9,10,17,18,19,27,30,33],"inclusive":false},"pre":{"rules":[22,23,24],"inclusive":false},"preblock":{"rules":[12,13,14,15],"inclusive":false},"li":{"rules":[1,2,3,8,9,10,17,18,19,26],"inclusive":false},"ol":{"rules":[5,6,7,8,9,10,17,18,19,26],"inclusive":false},"INITIAL":{"rules":[0,4,11,16,20,21,28,29,31,32],"inclusive":true}};return lexer;})()
640 | parser.lexer = lexer;
641 | return parser;
642 | })();
643 | if (typeof require !== 'undefined' && typeof exports !== 'undefined') {
644 | exports.parser = grammar;
645 | exports.parse = function () { return grammar.parse.apply(grammar, arguments); }
646 | exports.main = function commonjsMain(args) {
647 | if (!args[1])
648 | throw new Error('Usage: '+args[0]+' FILE');
649 | if (typeof process !== 'undefined') {
650 | var source = require('fs').readFileSync(require('path').join(process.cwd(), args[1]), "utf8");
651 | } else {
652 | var cwd = require("file").path(require("file").cwd());
653 | var source = cwd.join(args[1]).read({charset: "utf-8"});
654 | }
655 | return exports.parser.parse(source);
656 | }
657 | if (typeof module !== 'undefined' && require.main === module) {
658 | exports.main(typeof process !== 'undefined' ? process.argv.slice(1) : require("system").args);
659 | }
660 | }function jadedown() {
661 | var parser = require('./parser').parser
662 | , result = '';
663 |
664 | /**
665 | * Escapes brackets and ampersands.
666 | *
667 | * @param {String} Text to escape
668 | * @return {String}
669 | */
670 | parser.yy.h = function(text) {
671 | return text.replace(/&/g,'&')
672 | .replace(//g,'>');
674 | }
675 |
676 | /**
677 | * Runs html escape and appends to `result`.
678 | *
679 | * @param {String} Text to escape
680 | */
681 | parser.yy.ch = function(text) {
682 | result += parser.yy.h(text);
683 | };
684 |
685 | /**
686 | * Uses `Nodes` to render parser output.
687 | *
688 | * @param {String} Text or method name for `Nodes`
689 | * @param {Object} (optional) Object used by `Nodes` methods
690 | * @return {String}
691 | */
692 | parser.yy.c = function() {
693 | if (arguments.length === 1) {
694 | result += arguments[0];
695 | } else if (arguments.length === 2) {
696 | result += nodes[arguments[0]](arguments[1]);
697 | }
698 | }
699 |
700 | parser.parse(arguments[0].trim());
701 |
702 | // TODO: The parser should catch this
703 | if (nodes.openTags.length > 0)
704 | result += nodes.popOpenTag();
705 |
706 | return result;
707 | }
708 |
709 | if (typeof nodes === 'undefined') {
710 | var nodes = require('./nodes');
711 | nodes.parser = jadedown;
712 | module.exports = jadedown;
713 | }
714 | window.jadedown = jadedown;
715 | })();
716 |
--------------------------------------------------------------------------------