Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Website with vaguely useful documentation of most features.

  • Loading branch information...
commit 054487330dfbccbf16d20e035219008345beee61 1 parent 0377b6a
@jcoglan authored
View
3  site/config/compass.rb
@@ -0,0 +1,3 @@
+require "staticmatic/compass"
+
+project_type = :staticmatic
View
16 site/config/site.rb
@@ -0,0 +1,16 @@
+# Default is 3000
+# configuration.preview_server_port = 3000
+
+# Default is localhost
+# configuration.preview_server_host = "localhost"
+
+# Default is true
+# When false .html & index.html get stripped off generated urls
+# configuration.use_extensions_for_page_links = true
+
+# Default is an empty hash
+# configuration.sass_options = {}
+
+# Default is an empty hash
+# http://haml-lang.com/docs/yardoc/file.HAML_REFERENCE.html#options
+# configuration.haml_options = {}
View
46 site/src/layouts/default.haml
@@ -0,0 +1,46 @@
+!!!
+%html
+ %head
+ %title Canopy
+ = stylesheets
+ %body
+ .header
+ %h1
+ = link 'Canopy', '/'
+ %span
+ A PEG parser compiler for JavaScript
+
+ .main
+ .navigation
+ %ul
+ %li
+ = link 'Introduction'
+ %ul
+ %li
+ = link 'Grammar syntax', '/grammars.html'
+ %li
+ = link 'Matching strings', '/strings.html'
+ %li
+ = link 'Character classes', '/chars.html'
+ %li
+ = link 'Optional nodes', '/maybe.html'
+ %li
+ = link 'Repeated nodes', '/repetition.html'
+ %li
+ = link 'Sequences', '/sequences.html'
+ %li
+ = link 'Lookaheads', '/lookaheads.html'
+ %li
+ = link 'Ordered choices', '/choices.html'
+ %li
+ = link 'Cross-references', '/references.html'
+ %li
+ = link 'Extending nodes', '/types.html'
+
+ .content
+ =yield
+
+ .footer
+ :textile
+ Copyright © 2010–2012 "James Coglan":http://jcoglan.com. Released under
+ the MIT license.
View
34 site/src/pages/chars.haml
@@ -0,0 +1,34 @@
+:textile
+ h2. Character classes
+
+ Character classes work just like their counterparts from regular expressions,
+ in fact they literally get compiled into identical regexes in the parser.
+
+ For example, this grammar matches a single alphanumeric character:
+
+ <h6>alphanum.peg</h6>
+ <pre>grammar Alphanum
+ root <- [A-Za-z0-9]</pre>
+
+ This will parse any character matched by the class, and no others:
+
+ <pre>require('./alphanum').parse('a')
+ // -> { textValue: 'a', offset: 0, elements: [] }
+
+ require('./alphanum').parse('7')
+ // -> { textValue: '7', offset: 0, elements: [] }
+
+ require('./alphanum').parse('!')
+ Error: Line 1: expected [A-Za-z0-9]
+ !
+ ^</pre>
+
+ There is a special character class denoted by @.@ (period). This matches any
+ character.
+
+ <h6>anything.peg</h6>
+ <pre>grammar Anything
+ root <- .</pre>
+
+ <pre>require('./anything').parse('a')
+ // -> { textValue: 'a', offset: 0, elements: [] }</pre>
View
33 site/src/pages/choices.haml
@@ -0,0 +1,33 @@
+:textile
+ h2. Ordered choices
+
+ Choices are used to denote places where there is more than one possible syntax
+ for the input to follow. For example the following grammar matches either the
+ string @"hello"@ or the string @"world"@:
+
+ <h6>choice.peg</h6>
+ <pre>grammar Choice
+ root <- "hello" / "world"</pre>
+
+ The important thing about choices in PEG grammars is that they're _ordered_.
+ This means that the options are tried in order and the first choice that leads
+ to a successful parse is kept. If this choice results in a parsing error
+ later on in the input, the parser does not backtrack and try any other choices,
+ the parse simply fails.
+
+ The choice operator binds more loosely than the sequencing operator, for
+ example the following grammar matches either the string @"DouglasAdams"@ or
+ the string @"HitchhikersGuide"@:
+
+ <h6>binding.peg</h6>
+ <pre>grammar Binding
+ root <- "Douglas" "Adams" / "Hitchhikers" "Guide"</pre>
+
+ If you just want the choice to be between the nodes right next to the @/@
+ operator, you need to parenthesize them. This grammar matches
+ @"DouglasAdamsGuide"@ or @"DouglasHitchhikersGuide"@:
+
+ <h6>binding.peg</h6>
+ <pre>grammar Binding
+ root <- "Douglas" ("Adams" / "Hitchhikers") "Guide"</pre>
+
View
11 site/src/pages/download.haml
@@ -0,0 +1,11 @@
+:textile
+ The latest version is 0.2.0, released 16 May 2012. It is free software,
+ released under the MIT license. The command-line version can be installed
+ using @npm@:
+
+ <pre>$ npm install -g canopy</pre>
+
+ The download below contains the Canopy library in source and minified form,
+ including a source map. It works on both browser and CommonJS platforms.
+
+ h3. "Download Canopy v0.2.0":/assets/canopy.0-2-0.zip
View
69 site/src/pages/grammars.haml
@@ -0,0 +1,69 @@
+:textile
+ h2. Grammar syntax
+
+ Canopy grammar definitions are written using standard
+ "PEG":http://en.wikipedia.org/wiki/Parsing_expression_grammar notation and
+ stored in files with the @.peg@ extension. They _only_ specify the static
+ grammar of the language and do not contain inline processing code. However,
+ you can add additional methods to parse trees by "extending
+ nodes":/extending.html.
+
+ A grammar must have as its first line the word @grammar@, followed by one
+ space and the name of the grammar. This is followed by a list of rules that
+ define the grammar; a rule is a name, followed by @<-@, followed by the rule's
+ definition. A rule name can be any valid ASCII JavaScript variable name. The
+ first rule in the grammar is the _root_; a document must match this rule to be
+ parsed correctly.
+
+ The details of types of expressions you can use within grammar rules are
+ covered in detail in the other articles linked on the left.
+
+ For example, here's a simple grammar that matches any sequence of digits:
+
+ <h6>digits.peg</h6>
+ <pre>grammar Digits
+ digits <- [0-9]*</pre>
+
+ A grammar file is converted into a JavaScript module using the @canopy@
+ command-line tool:
+
+ <pre>$ canopy digits.peg
+ Generated parser in digits.js
+ Completed in 0.007s</pre>
+
+ Rules can contain references to other rules; this is what allows PEG parsers
+ to process recursive syntaxes. For example, this grammar matches a number
+ surrounded by any number of _matched_ parentheses - this is not possible with
+ regular expressions.
+
+ <h6>parens.peg</h6>
+ <pre>grammar Parens
+ value <- "(" value ")" / number
+ number <- [0-9]+</pre>
+
+ This generates a parser that processes the language, and throws an error on
+ invalid input:
+
+ <pre>var parens = require('./parens')
+
+ parens.parse('94')
+ // -> { textValue: '94',
+ offset: 0,
+ elements:
+ [ { textValue: '9', offset: 0, elements: [] },
+ { textValue: '4', offset: 1, elements: [] } ] }
+
+ parens.parse('(94)')
+ // -> { textValue: '(94)',
+ offset: 0,
+ elements:
+ [ { textValue: '(', offset: 0, elements: [] },
+ { textValue: '94', offset: 1, elements: [...] },
+ { textValue: ')', offset: 3, elements: [] } ] }
+
+ parens.parse('(((94)')
+ Error: Line 1: expected ")"
+ (((94)
+ ^</pre>
+
+ "Read more about cross-references":/references.html
View
19 site/src/pages/index.haml
@@ -0,0 +1,19 @@
+:textile
+ Canopy is a self-hosting "PEG":http://en.wikipedia.org/wiki/Parsing_expression_grammar
+ parser compiler for JavaScript. It lets you describe the grammar of the
+ language you're trying to parse using a simple, terse syntax, and it generates
+ a parser for the language from this definition. Its API and the parse tree
+ format it generates are heavily influenced by "Treetop":http://treetop.rubyforge.org/.
+
+ You can install the command-line tools through @npm@:
+
+ <pre>$ npm install -g canopy</pre>
+
+ Or, download the library:
+
+ h3. "Download Canopy v0.2.0":/assets/canopy.0-2-0.zip
+
+ The latest version was released 16 May 2012. It is free software, released
+ under the MIT license. The download contains the Canopy library in source and
+ minified form, including a source map. It works on both browser and CommonJS
+ platforms.
View
142 site/src/pages/introduction.haml
@@ -0,0 +1,142 @@
+:textile
+ Canopy works by converting grammar files into JavaScript code that parses the
+ language described by the grammar. The full syntax for these grammar files is
+ described by the articles listed on the left. For now, here's a quick example
+ grammar describing a language that consists of nested lists of single-digit
+ numbers and other lists:
+
+ <h6>lists.peg</h6>
+ <pre>grammar Lists
+ value <- list / number
+ list <- "[" ( value ("," value)* )? "]"
+ number <- [0-9]</pre>
+
+ h2. Compiling a grammar
+
+ Running the @canopy@ executable on this file produces a JavaScript file called
+ @lists.js@. This file contains all the parser code for the langauge.
+
+ <pre>$ canopy lists.peg
+ Generated parser in lists.js
+ Completed in 0.021s</pre>
+
+ This JavaScript file will work both in the browser and in CommonJS
+ environments. The API it exposes in the browser is named after the @grammar@
+ name at the top of the @.peg@ file, for example our @Lists@ grammar creates a
+ parser called @ListsParser@, and you'd use it like this:
+
+ <pre>var tree = ListsParser.parse('[1,[],2,[3,4,5],[[6]]]');</pre>
+
+ In a CommonJS environment the @parse()@ method is part of the exported module
+ API of the parser file:
+
+ <pre>var parser = require('./lists');
+ var tree = parser.parse('[1,[],2,[3,4,5],[[6]]]');</pre>
+
+ h2. Parse trees
+
+ The data structure produced by the parser is a representation of the text
+ whose structure follows the grammar. The top level of this structure looks
+ like this:
+
+ <pre>{ textValue: '[1,[],2,[3,4,5],[[6]]]',
+ offset: 0,
+ elements:
+ [ { textValue: '[', offset: 0, elements: [] },
+ { textValue: '1,[],2,[3,4,5],[[6]]',
+ offset: 1,
+ elements: [...] },
+ { textValue: ']', offset: 21, elements: [] } ] }</pre>
+
+ Every node in the tree has the following properties:
+
+ * @textValue@ - the text of the node, a slice of the original document
+ * @offset@ - the position in the source document where the node occurred
+ * @elements@ - an array of child nodes, which may be empty
+
+ We can navigate the tree structure via the @elements@ property of nodes. Each
+ @elements@ list takes us deeper into the tree, finding smaller sub-nodes as
+ we descend.
+
+ <pre>tree = parser.parse('[1,[],2,[3,4,5],[[6]]]')
+
+ tree.elements[0]
+ // -> { textValue: '[', offset: 0, elements: [] }
+
+ tree.elements[1].elements[0]
+ // -> { textValue: '1', offset: 1, elements: [] }
+
+ tree.elements[1].elements[1].elements[0].elements[1]
+ // -> { textValue: '[]',
+ offset: 3,
+ elements:
+ [ { textValue: '[', offset: 3, elements: [] },
+ { textValue: '', offset: 4, elements: [] },
+ { textValue: ']', offset: 4, elements: [] } ] }
+
+ tree.elements[1].elements[1].elements[1]
+ // -> { textValue: ',2',
+ offset: 5,
+ elements:
+ [ { textValue: ',', offset: 5, elements: [] },
+ { textValue: '2', offset: 6, elements: [] } ] }
+
+ tree.elements[1].elements[1].elements[3].elements[1].elements[1]
+ // -> { textValue: '[6]',
+ offset: 17,
+ elements:
+ [ { textValue: '[6]',
+ offset: 17,
+ elements: [...] },
+ { textValue: '', offset: 20, elements: [] } ] }</pre>
+
+ h2. Labelled nodes
+
+ The nodes can also have labelled children. Child nodes in a sequence can be
+ labelled in the grammar and that label is mirrored in the parse tree. Nodes
+ that come from referring to other rules are also labelled using the name of
+ the rule:
+
+ <h6>hash.peg</h6>
+ <pre>grammar Hash
+ object <- "{" string " => " number:[0-9]+ "}"
+ string <- "'" [^']* "'"</pre>
+
+ When we parse a document using this grammar, the root node has additional
+ named references to some of its child nodes, as well as the standard
+ @treeValue@, @offset@ and @elements@ properties:
+
+ <pre>tree = require('./hash').parse("{'foo' => 36}")
+
+ // -> { textValue: '{\'foo\' => 36}',
+ offset: 0,
+ elements:
+ [ { textValue: '{', offset: 0, elements: [] },
+ { textValue: '\'foo\'', offset: 1, elements: [...] },
+ { textValue: ' => ', offset: 6, elements: [] },
+ { textValue: '36', offset: 10, elements: [...] },
+ { textValue: '}', offset: 12, elements: [] } ],
+ string:
+ { textValue: '\'foo\'', offset: 1, elements: [...] },
+ number:
+ { textValue: '36', offset: 10, elements: [...] } }
+
+ tree.string.textValue
+ // -> "'foo'"
+
+ tree.number.textValue
+ // -> "36"</pre>
+
+ These labelled nodes can make it much easier to navigate the elements when
+ you're processing the parse tree.
+
+ h2. Parsing errors
+
+ If you try to use a Canopy parser to parse a document whose syntax is not
+ valid according to the grammar, an exception is raised with a helpful message
+ about where the error happened.
+
+ <pre>require('./hash').parse("{'Unix epoch' => 1970")
+ Error: Line 1: expected "}"
+ {'Unix epoch' => 1970
+ ^</pre>
View
21 site/src/pages/lookaheads.haml
@@ -0,0 +1,21 @@
+:textile
+ h2. Lookaheads
+
+ Lookaheads, denoted by the @&@ and @!@ characters, can be used to check the
+ next piece of input without consuming it. The expression @&expr@ matches if
+ the next piece of input matches @expr@, and @!expr@ matches if the input does
+ _not_ match @expr@.
+
+ For example, this rule matches the word `television` followed by a colon, but
+ does not consume the colon:
+
+ <pre> tv <- "television" &":"</pre>
+
+ This rule matches any number of non-space characters, by first checking the
+ next character using a negative lookahead and then consuming it with the
+ wildcard operator:
+
+ <pre> nonspace <- (!" " .)*</pre>
+
+ The lookahead operators do not have to be followed by strings, they can be
+ used with any other type of parsing expression.
View
33 site/src/pages/maybe.haml
@@ -0,0 +1,33 @@
+:textile
+ h2. Optional nodes
+
+ To make a node optional, follow it immediately with a @?@, with no space
+ between the node and the @?@ symbol.
+
+ Here's a grammar that matches @"future"@ or the empty string:
+
+ <h6>future.peg</h6>
+ <pre>grammar Future
+ root <- "future"?</pre>
+
+ Here's a grammar that matches any digit or the empty string:
+
+ <h6>digit.peg</h6>
+ <pre>grammar Digit
+ root <- [0-9]?</pre>
+
+ If the optional node is not found in the input, an empty syntax node is
+ returned. Remember an optional node either matches the node or nothing at all;
+ other input will cause an error.
+
+ <pre>require('./future').parse('future')
+ // -> { textValue: 'future', offset: 0, elements: [] }
+
+ require('./future').parse('')
+ // -> { textValue: '', offset: 0, elements: [] }
+
+ require('./future').parse('perfect')
+ Error: Line 1: expected "future"
+ perfect
+ ^</pre>
+
View
58 site/src/pages/references.haml
@@ -0,0 +1,58 @@
+:textile
+ h2. Cross-references
+
+ The ability to refer to other parser rules is what gives PEG parsers their
+ power. A rule can refer to any other rule in the grammar by name, and this has
+ the effect of using the named rule to match the next piece of input. Here's a
+ simplified example of using rules to match an email address:
+
+ <h6>email.peg</h6>
+ <pre>grammar Email
+ email <- username "@" host
+ username <- [a-z]+ ("." [a-z]+)*
+ host <- [a-z]+ "." ("com" / "co.uk" / "org" / "net")</pre>
+
+ <pre>require('./email').parse('bob@example.com')
+ // -> { textValue: 'bob@example.com',
+ offset: 0,
+ elements:
+ [ { textValue: 'bob', offset: 0, elements: [...] },
+ { textValue: '@', offset: 3, elements: [] },
+ { textValue: 'example.com', offset: 4, elements: [...] } ],
+ username: { textValue: 'bob', offset: 0, elements: [...] },
+ host:
+ { textValue: 'example.com',
+ offset: 4,
+ elements: [...] } }</pre>
+
+ As you can see in the above parse tree, the rules referenced by the @email@
+ rule add named nodes called @username@ and @host@ to the parse tree. This
+ gives you an easier way to traverse the tree than using the @elements@ array.
+
+ <pre>var tree = require('./email').parse('bob@example.com');
+ tree.username.textValue // -> 'bob'
+ tree.host.textValue // -> 'example.com'</pre>
+
+ References allow you to create recursive matchers, which is why PEGs can match
+ languages that regular expressions typically can't. Here's a grammar for
+ matching nested lists of numbers:
+
+ <h6>lists.peg</h6>
+ <pre>grammar Lists
+ value <- list / number
+ list <- "[" value ("," value)* "]"
+ number <- [0-9]</pre>
+
+ We can parse a string using this grammar and browse the tree it generates:
+
+ <pre>var tree = require('./lists').parse('[[1,2],3]')
+
+ tree.textValue
+ // -> '[[1,2],3]'
+
+ tree.elements[1].textValue
+ // -> '[1,2]'
+
+ tree.elements[1].elements[2].elements[0].elements[1].textValue
+ // -> '2'</pre>
+
View
30 site/src/pages/repetition.haml
@@ -0,0 +1,30 @@
+:textile
+ h2. Repeated nodes
+
+ Canopy offers the well-known @*@ and @+@ operators from regular expressions,
+ meaning "zero or more" and "one or more" respectively. Placing them after a
+ node allows the node to repeat the desired number of times.
+
+ Here's a grammar that matches the word @badger@ one or more times:
+
+ <h6>badger.peg</h6>
+ <pre>grammar Badger
+ root <- "badger"+</pre>
+
+ When a repetition matches the input, you get one node for the repetition
+ itself, and one child node for each _instance_ in the repetition. If there
+ aren't enough repetitions (for example if there's not at least one match for
+ a @+@ rule) you'll get an error.
+
+ <pre>require('./badger').parse('badgerbadgerbadger')
+ // -> { textValue: 'badgerbadgerbadger',
+ offset: 0,
+ elements:
+ [ { textValue: 'badger', offset: 0, elements: [] },
+ { textValue: 'badger', offset: 6, elements: [] },
+ { textValue: 'badger', offset: 12, elements: [] } ] }
+
+ require('./badger').parse('bad')
+ Error: Line 1: expected "badger"
+ bad
+ ^</pre>
View
86 site/src/pages/sequences.haml
@@ -0,0 +1,86 @@
+:textile
+ h2. Sequences
+
+ A sequence is just what is sounds like: one or more nodes, listed one after
+ the other, separated by at least one whitespace character. A sequence matches
+ the input if the input contains matches for each of the sequence's nodes, in
+ order.
+
+ For example, here's a grammar that matches an optional word followed by two
+ more required words:
+
+ <h6>hamlet.peg</h6>
+ <pre>grammar Hamlet
+ root <- "not "? "to be"</pre>
+
+ The sequence here is formed of two nodes: @"not "?@ and @"to be"@. Here's the
+ resulting parses of the possible inputs:
+
+ <pre>require('./hamlet').parse('to be')
+ // -> { textValue: 'to be',
+ offset: 0,
+ elements:
+ [ { textValue: '', offset: 0, elements: [] },
+ { textValue: 'to be', offset: 0, elements: [] } ] }
+
+ require('./hamlet').parse('not to be')
+ // -> { textValue: 'not to be',
+ offset: 0,
+ elements:
+ [ { textValue: 'not ', offset: 0, elements: [] },
+ { textValue: 'to be', offset: 4, elements: [] } ] }
+
+ require('./hamlet').parse('or not to be')
+ Error: Line 1: expected "to be"
+ or not to be
+ ^</pre>
+
+ h2. Labelled nodes
+
+ Sequences have a special property: their child nodes can be labelled. You can
+ explicitly add a label to any item within a sequence, and
+ "cross-references":/references.html are implicitly labelled with the name of
+ the reference. For example, take the following example that matches documents
+ that look like @{'abc' => 123}@:
+
+ <h6>hash.peg</h6>
+ <pre>grammar Hash
+ object <- "{" string " => " number:[0-9]+ "}"
+ string <- "'" [^']* "'"</pre>
+
+ The @object@ rule is a sequence containing five children:
+
+ * @"{"@
+ * @string@
+ * @" => "@
+ * @number:[0-9]+@
+ * @"}"@
+
+ The @string@ node is a reference to another rule, and @number:[0-9]+@ is a
+ labelled expression that matches one or more digits. These two children create
+ labelled nodes in the output:
+
+ <pre>tree = require('./hash').parse("{'foo' => 36}")
+
+ // -> { textValue: '{\'foo\' => 36}',
+ offset: 0,
+ elements:
+ [ { textValue: '{', offset: 0, elements: [] },
+ { textValue: '\'foo\'', offset: 1, elements: [...] },
+ { textValue: ' => ', offset: 6, elements: [] },
+ { textValue: '36', offset: 10, elements: [...] },
+ { textValue: '}', offset: 12, elements: [] } ],
+ string:
+ { textValue: '\'foo\'', offset: 1, elements: [...] },
+ number:
+ { textValue: '36', offset: 10, elements: [...] } }
+
+ tree.string.textValue
+ // -> "'foo'"
+
+ tree.number.textValue
+ // -> "36"</pre>
+
+ Here we see that @tree.string@ is the same as @tree.elements[1]@, and
+ @tree.number@ is the same as @tree.elements[3]@. These labels make it much
+ easier to navigate the tree and help keep your code readable.
View
41 site/src/pages/strings.haml
@@ -0,0 +1,41 @@
+:textile
+ h2. Matching strings
+
+ The most basic type of node in PEG grammars is the literal string match. This
+ grammar will only match the string @"I like parsers"@ and nothing else.
+
+ <h6>parsers.peg</h6>
+ <pre>grammar Parsers
+ root <- "I like parsers"</pre>
+
+ Strings are delimited with double quotes. A string node is a _terminal_ - it
+ has no child nodes.
+
+ <pre>require('./parsers').parse('I like parsers')
+ // -> { textValue: 'I like parsers',
+ offset: 0,
+ elements: [] }
+
+ require('./parsers').parse('this is not valid')
+ Error: Line 1: expected "I like parsers"
+ this is not valid
+ ^
+
+ require('./parsers').parse('I like PARSERS')
+ Error: Line 1: expected "I like parsers"
+ I like PARSERS
+ ^</pre>
+
+ As you can see from the last example, strings are case-sensitive. You can
+ create case-insensitive strings by quoting the string with backticks:
+
+ <h6>parsers.peg</h6>
+ <pre>grammar Parsers
+ root <- `I like parsers`</pre>
+
+ It will then match any casing of the input:
+
+ <pre>require('./parsers').parse('I like PARSERS')
+ // -> { textValue: 'I like PARSERS',
+ offset: 0,
+ elements: [] }</pre>
View
113 site/src/pages/types.haml
@@ -0,0 +1,113 @@
+:textile
+ h2. Extending nodes with types
+
+ The syntax nodes produced by Canopy by default are all of the same 'type'.
+ They all have a @textValue@, an @offset@ and a (possibly empty) list of
+ @elements@. But you can add your own methods to them to add new functionality
+ to the parse tree.
+
+ This is done by annotating parsing expressions with types. A type is any valid
+ JavaScript object name like @Foo.Bar@ surrounded with pointy brackets. When
+ the input matches this expression, the generated syntax node will be augmented
+ with the methods from the named type.
+
+ Let's take a simple example: matching a string literal:
+
+ <h6>strings.peg</h6>
+ <pre>grammar Strings
+ root <- "hello" <HelloNode></pre>
+
+ <h6>strings_test.js</h6>
+ <pre>var strings = require('./strings');
+
+ strings.Parser.HelloNode = {
+ upcase: function() {
+ return this.textValue.toUpperCase();
+ }
+ };
+
+ var tree = strings.parse('hello');
+ console.log(tree.upcase());</pre>
+
+ What we're doing here is saying, in the grammar, that a node matching @hello@
+ is of type @HelloNode@. Then in our JavaScript code, we add the @HelloNode@
+ module to the parser, and use the parser to process a string.
+
+ Because the string matches our typed rule, it gains the methods from the
+ @HelloNode@ module, and we can invoke those methods on the node.
+
+ Let's run this script:
+
+ <pre>$ node strings_test.js
+ HELLO</pre>
+
+ Notice how the @HelloNode@ module is added to the parser, rather than being a
+ global. This makes sure it works properly in CommonJS environments without
+ introducing global variables. In the browser, the @grammar Strings@ line means
+ the parser is referred to as @StringsParser@, and you'd add your node types to
+ that:
+
+ <pre>StringsParser.HelloNode = {
+ upcase: function() { ... }
+ };
+
+ var tree = StringsParser.parse('hello');</pre>
+
+ In the grammar syntax, type annotations bind to sequences. That is, a type
+ annotation may only appear at the end of a sequence expression, and binds
+ tighter than choice expressions.
+
+ For example the following means that a node matching the sequence @"foo"
+ "bar"@ will be augmented with the @Extension@ methods.
+
+ <h6>types.peg</h6>
+ <pre>grammar Types
+ root <- first:"foo" second:"bar" <Extension></pre>
+
+ The extension methods have access to the labelled node from the sequence.
+
+ <h6>types_test.js</h6>
+ <pre>var types = require('./types');
+
+ types.Parser.Extension = {
+ convert: function() {
+ return this.first.textValue +
+ this.second.textValue.toUpperCase();
+ }
+ };
+
+ types.parse('foobar').convert()
+ // -> 'fooBAR'</pre>
+
+ Because type annotations bind to sequences rather than to choices, the
+ following matches either the string @"abc"@ which gains the @Foo@ type, or
+ @"123"@ which gains the @Bar type@:
+
+ <pre>grammar Choice
+ root <- "a" "b" "c" <Foo> / "1" "2" "3" <Bar></pre>
+
+ If you want all the branches of a choice to be augmented with the same type,
+ you need to parenthesize the choice and place the type afterward.
+
+ <h6>choices.peg</h6>
+ <pre>grammar Choices
+ root <- (alpha / beta) <Extension>
+ alpha <- first:"a" second:"z"
+ beta <- first:"j" second:"c"</pre>
+
+ <h6>choices_test.js</h6>
+ <pre>var choices = require('./choices');
+
+ choices.Parser.Extension = {
+ convert: function() {
+ return this.first.textValue +
+ this.second.textValue.toUpperCase();
+ }
+ };
+
+ choices.parse('az').convert()
+ // -> 'aZ'
+
+ choices.parse('jc').convert()
+ // -> 'jC'</pre>
+
View
81 site/src/stylesheets/screen.sass
@@ -0,0 +1,81 @@
+body
+ background: #2d2d2d
+ color: #ddd
+ font: 15px/1.8 FreeSans, Helvetica, Arial, sans-serif
+ margin: 0
+ padding: 0
+
+.header, .footer
+ clear: both
+ margin: 0 auto
+ padding: 0 200px
+ width: 600px
+
+.footer
+ border-top: 1px solid #666
+ font-size: 80%
+ margin-bottom: 4em
+ padding: 1em 0
+ width: 600px
+
+h1
+ font-size: 400%
+ text-align: center
+ a
+ text-decoration: none
+ span
+ border-top: 1px solid #666
+ display: block
+ font-size: 20%
+ font-weight: normal
+ padding-top: 1.5em
+
+.main
+ margin: 0 auto
+ width: 1000px
+
+.navigation
+ float: left
+ position: fixed
+ width: 160px
+ ul
+ list-style: none
+ margin: 1em 0
+ padding: 0
+ a
+ color: #aaa
+ text-decoration: none
+ a:hover
+ color: #fff
+ text-decoration: underline
+
+.content
+ float: left
+ width: 600px
+ margin: 0 0 2em 200px
+
+h2
+ font-size: 1.5em
+ font-weight: normal
+ margin: 2em 0 0.5em
+
+h2:first-child
+ margin-top: 0.5em
+
+h6
+ background: #ddd
+ color: #333
+ font-size: 80%
+ margin-bottom: -1em
+ padding: 0.2em 1em
+ width: 8em
+
+pre
+ border-bottom: 1px solid #666
+ border-top: 1px solid #666
+ font-size: 80%
+ padding: 1em
+
+a
+ color: #fff
+ text-decoration: underline
Please sign in to comment.
Something went wrong with that request. Please try again.