├── 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 " 6 | , "engines": ["node >= 0.4.0"] 7 | , "main": "./lib/jadedown.js" 8 | , "bin": { "jadedown": "./bin/jadedown" } 9 | , "devDependencies": { 10 | "jison": "0.2.11" 11 | , "expresso": "latest" 12 | } 13 | , "repository": { 14 | "type" : "git" 15 | , "url" : "http://github.com/alexyoung/jadedown.git" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/grammar.y: -------------------------------------------------------------------------------- 1 | %start file 2 | 3 | %% 4 | 5 | /* Recursively process blocks */ 6 | 7 | file 8 | : lines EOF 9 | { yy.c(yytext); return $1; } 10 | | lines P_END 11 | { yy.c('

'); } 12 | ; 13 | 14 | lines 15 | : 16 | | lines text 17 | ; 18 | 19 | text 20 | : TEXT 21 | { yy.c($1); } 22 | | PRE_TEXT 23 | { yy.ch($1); } 24 | | BLOCK_TAG 25 | { yy.c('BLOCK_TAG', $1); } 26 | | BLOCK_END 27 | { yy.c('BLOCK_END', $1); } 28 | | LINK 29 | { yy.c('LINK', $1); } 30 | | TAG 31 | { yy.c('TAG', $1); } 32 | | SPACE 33 | { yy.c($1); } 34 | | EOL 35 | { yy.c($1); } 36 | | P_START 37 | { yy.c('

'); } 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 \n
'; 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 = '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>

' 28 | , input = '`This is a `'; 29 | 30 | assert.equal(expected, jadedown(input)); 31 | } 32 | }; 33 | 34 | 35 | -------------------------------------------------------------------------------- /lib/jadedown.js: -------------------------------------------------------------------------------- 1 | function jadedown() { 2 | var parser = require('./parser').parser 3 | , result = ''; 4 | 5 | /** 6 | * Escapes brackets and ampersands. 7 | * 8 | * @param {String} Text to escape 9 | * @return {String} 10 | */ 11 | parser.yy.h = function(text) { 12 | return text.replace(/&/g,'&') 13 | .replace(//g,'>'); 15 | } 16 | 17 | /** 18 | * Runs html escape and appends to `result`. 19 | * 20 | * @param {String} Text to escape 21 | */ 22 | parser.yy.ch = function(text) { 23 | result += parser.yy.h(text); 24 | }; 25 | 26 | /** 27 | * Uses `Nodes` to render parser output. 28 | * 29 | * @param {String} Text or method name for `Nodes` 30 | * @param {Object} (optional) Object used by `Nodes` methods 31 | * @return {String} 32 | */ 33 | parser.yy.c = function() { 34 | if (arguments.length === 1) { 35 | result += arguments[0]; 36 | } else if (arguments.length === 2) { 37 | result += nodes[arguments[0]](arguments[1]); 38 | } 39 | } 40 | 41 | parser.parse(arguments[0].trim()); 42 | 43 | // TODO: The parser should catch this 44 | if (nodes.openTags.length > 0) 45 | result += nodes.popOpenTag(); 46 | 47 | return result; 48 | } 49 | 50 | if (typeof nodes === 'undefined') { 51 | var nodes = require('./nodes'); 52 | nodes.parser = jadedown; 53 | module.exports = jadedown; 54 | } 55 | -------------------------------------------------------------------------------- /test/fixtures/1.jd: -------------------------------------------------------------------------------- 1 | h1 Jadedown 2 | 3 | (http://jadedown.com)[Jadedown] is a lightweight markup language based on (http://daringfireball.net/projects/markdown/)[Markdown] and (http://jade-lang.org)[Jade]. 4 | 5 | h2 Overview 6 | 7 | # Tags take the form: `tagName#id.class1.class2(attr1="value", attr2="value 2")[innerHTLM]` 8 | # If a line starts with a recognised tag, then the line is wrapped in that tag. For example: `h3 This is a header` 9 | # If a line starts with `*` or `#` it is a unordered or ordered list 10 | # There is also shorthand for commonly used tags, like strong, em, links, and code 11 | 12 | h2 Jade Influences 13 | 14 | In Jade, a tag is written at the start of a line: 15 | 16 |
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;

' 29 | , input = '`var a = 1 * 5;`'; 30 | 31 | assert.equal(expected, jadedown(input)); 32 | }, 33 | 34 | 'test multiple code': function() { 35 | var expected = '

Given that var a = 1 * 5; and var b = Math.PI;, then:

' 36 | , input = 'Given that `var a = 1 * 5;` and `var b = Math.PI;`, then:'; 37 | 38 | assert.equal(expected, jadedown(input)); 39 | }, 40 | 41 | 'test link shorthand': function() { 42 | var expected = '

Example 1

' 43 | , input = '(http://example.com)[Example 1]'; 44 | 45 | assert.equal(expected, jadedown(input)); 46 | }, 47 | 48 | 'test multiple links': function() { 49 | var expected = '

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 ''; 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 + ''; 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:

\n
var 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:

\n
var 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:

\n
var 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:

\n
var a = 1;    a++;    console.log(a);\n
\n

Example.

' 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:

\n
var 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 = '

Example 1

' 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 = '

Example 1

' 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 = '

Example 1

' 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 = '

Example 1

' 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 = '

Example 1

' 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 = '\n'; 19 | this.popState(); 20 | return 'LIST_END'; 21 | } 22 | %} 23 | 24 |
  • <> %{ 25 | yytext = '
  • \n\n'; 26 | this.popState(); 27 | return 'EOF'; 28 | %} 29 | 30 |
  • ^"*"\s+ %{ 31 | yytext = '
  • \n
  • '; 32 | return 'LIST_ITEM'; 33 | %} 34 | 35 | ^"#"\s+ %{ 36 | this.begin('ol'); 37 | yytext = '
      \n
    1. '; 38 | return 'LIST_START'; 39 | %} 40 | 41 |
        \n+ %{ 42 | if (yytext.length > 1) { 43 | this.popState(); 44 | yytext = '\n
      \n'; 45 | return 'LIST_END'; 46 | } 47 | %} 48 | 49 |
        <> %{ 50 | yytext = '\n
      \n'; 51 | this.popState(); 52 | return 'EOF'; 53 | %} 54 | 55 |
        ^"#"\s+ %{ 56 | yytext = '\n
      1. '; 57 | return 'LIST_ITEM'; 58 | %} 59 | 60 | \(([^)]*)\)\[([^\]]*)\] %{ 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 | ^(\t|" "{4,})(.*) %{ 115 | yytext = yy.h(yy.lexer.matches[2]) 116 | return 'TEXT'; 117 | %} 118 | 119 | ^[^\s] %{ 120 | yy.lexer.unput(yytext); 121 | yy.c('\n'); 122 | this.popState(); 123 | %} 124 | 125 | ^("h"[1-6]|"bq"|"blockquote")\s+ %{ 126 | this.begin('b'); 127 | 128 | yytext = { 129 | tag: yy.lexer.matches[1] 130 | }; 131 | 132 | return 'BLOCK_TAG'; 133 | %} 134 | 135 | "*"([^*]*)"*" %{ 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 |
        ""               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 = '
      2. \n\n'; 368 | this.popState(); 369 | return 5; 370 | 371 | break; 372 | case 3: 373 | yy_.yytext = '\n
      3. '; 374 | return 20; 375 | 376 | break; 377 | case 4: 378 | this.begin('ol'); 379 | yy_.yytext = '
          \n
        1. '; 380 | return 19; 381 | 382 | break; 383 | case 5: 384 | if (yy_.yytext.length > 1) { 385 | this.popState(); 386 | yy_.yytext = '
        2. \n
        \n'; 387 | return 21; 388 | } 389 | 390 | break; 391 | case 6: 392 | yy_.yytext = '
      4. \n
      \n'; 393 | this.popState(); 394 | return 5; 395 | 396 | break; 397 | case 7: 398 | yy_.yytext = '
    2. \n
    3. '; 399 | return 20; 400 | 401 | break; 402 | case 8: 403 | yy_.yytext = { 404 | tag: 'a' 405 | , attr: 'href="' + yy.lexer.matches[1] + '"' 406 | , html: yy.lexer.matches[2] 407 | } 408 | return 12; 409 | 410 | break; 411 | case 9: 412 | var attr = yy.lexer.matches[6] 413 | , selector = yy.lexer.matches[2]; 414 | 415 | if (attr || yy.lexer.matches[8]) { 416 | yy_.yytext = { 417 | tag: yy.lexer.matches[1] 418 | , html: yy.lexer.matches[8] 419 | , attr: attr 420 | , selector: selector 421 | }; 422 | return 13; 423 | } else { 424 | return 'TEXT'; 425 | } 426 | 427 | break; 428 | case 10: 429 | // Tags in the form text[innerHTML] 430 | if (yy.lexer.matches[2]) { 431 | yy_.yytext = { 432 | tag: yy.lexer.matches[1] 433 | , html: yy.lexer.matches[3] 434 | }; 435 | return 13; 436 | } else { 437 | return 'TEXT'; 438 | } 439 | 440 | break; 441 | case 11: 442 | 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 ''; 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 + ''; 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 = '
        \n
      • '; 462 | return 19; 463 | 464 | break; 465 | case 1: 466 | if (yy_.yytext.length > 1) { 467 | yy_.yytext = '
      • \n
      \n'; 468 | this.popState(); 469 | return 21; 470 | } 471 | 472 | break; 473 | case 2: 474 | yy_.yytext = '
    4. \n\n'; 475 | this.popState(); 476 | return 5; 477 | 478 | break; 479 | case 3: 480 | yy_.yytext = '\n
    5. '; 481 | return 20; 482 | 483 | break; 484 | case 4: 485 | this.begin('ol'); 486 | yy_.yytext = '
        \n
      1. '; 487 | return 19; 488 | 489 | break; 490 | case 5: 491 | if (yy_.yytext.length > 1) { 492 | this.popState(); 493 | yy_.yytext = '
      2. \n
      \n'; 494 | return 21; 495 | } 496 | 497 | break; 498 | case 6: 499 | yy_.yytext = '
    6. \n
    \n'; 500 | this.popState(); 501 | return 5; 502 | 503 | break; 504 | case 7: 505 | yy_.yytext = '
  • \n
  • '; 506 | return 20; 507 | 508 | break; 509 | case 8: 510 | yy_.yytext = { 511 | tag: 'a' 512 | , attr: 'href="' + yy.lexer.matches[1] + '"' 513 | , html: yy.lexer.matches[2] 514 | } 515 | return 12; 516 | 517 | break; 518 | case 9: 519 | var attr = yy.lexer.matches[6] 520 | , selector = yy.lexer.matches[2]; 521 | 522 | if (attr || yy.lexer.matches[8]) { 523 | yy_.yytext = { 524 | tag: yy.lexer.matches[1] 525 | , html: yy.lexer.matches[8] 526 | , attr: attr 527 | , selector: selector 528 | }; 529 | return 13; 530 | } else { 531 | return 'TEXT'; 532 | } 533 | 534 | break; 535 | case 10: 536 | // Tags in the form text[innerHTML] 537 | if (yy.lexer.matches[2]) { 538 | yy_.yytext = { 539 | tag: yy.lexer.matches[1] 540 | , html: yy.lexer.matches[3] 541 | }; 542 | return 13; 543 | } else { 544 | return 'TEXT'; 545 | } 546 | 547 | break; 548 | case 11: 549 | 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 | --------------------------------------------------------------------------------