@masala/parser
Version:
Masala Parser
437 lines (420 loc) • 31.2 kB
HTML
<html class="default no-js">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>@masala/parser</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="assets/css/main.css">
</head>
<body>
<header>
<div class="tsd-page-toolbar">
<div class="container">
<div class="table-wrap">
<div class="table-cell" id="tsd-search" data-index="assets/js/search.js" data-base=".">
<div class="field">
<label for="tsd-search-field" class="tsd-widget search no-caption">Search</label>
<input id="tsd-search-field" type="text" />
</div>
<ul class="results">
<li class="state loading">Preparing search index...</li>
<li class="state failure">The search index is not available</li>
</ul>
<a href="index.html" class="title">@masala/parser</a>
</div>
<div class="table-cell" id="tsd-widgets">
<div id="tsd-filter">
<a href="#" class="tsd-widget options no-caption" data-toggle="options">Options</a>
<div class="tsd-filter-group">
<div class="tsd-select" id="tsd-filter-visibility">
<span class="tsd-select-label">All</span>
<ul class="tsd-select-list">
<li data-value="public">Public</li>
<li data-value="protected">Public/Protected</li>
<li data-value="private" class="selected">All</li>
</ul>
</div>
<input type="checkbox" id="tsd-filter-inherited" checked />
<label class="tsd-widget" for="tsd-filter-inherited">Inherited</label>
<input type="checkbox" id="tsd-filter-only-exported" />
<label class="tsd-widget" for="tsd-filter-only-exported">Only exported</label>
</div>
</div>
<a href="#" class="tsd-widget menu no-caption" data-toggle="menu">Menu</a>
</div>
</div>
</div>
</div>
<div class="tsd-page-title">
<div class="container">
<ul class="tsd-breadcrumb">
<li>
<a href="globals.html">Globals</a>
</li>
</ul>
<h1> @masala/parser</h1>
</div>
</div>
</header>
<div class="container container-main">
<div class="row">
<div class="col-8 col-content">
<div class="tsd-panel tsd-typography">
<h1 id="masala-parser-javascript-parser-combinators">Masala Parser: Javascript Parser Combinators</h1>
<p><a href="https://badge.fury.io/js/%40masala%2Fparser"><img src="https://badge.fury.io/js/%40masala%2Fparser.svg" alt="npm version"></a>
<a href="https://travis-ci.org/d-plaindoux/masala-parser"><img src="https://travis-ci.org/d-plaindoux/masala-parser.svg" alt="Build Status"></a>
<a href="https://coveralls.io/r/d-plaindoux/masala-parser?branch=master"><img src="https://coveralls.io/repos/d-plaindoux/masala-parser/badge.png?branch=master" alt="Coverage Status"></a>
<a href="http://github.com/badges/stability-badges"><img src="http://badges.github.io/stability-badges/dist/stable.svg" alt="stable"></a></p>
<p>Masala Parser is inspired by the paper titled:
<a href="https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/parsec-paper-letter.pdf">Direct Style Monadic Parser Combinators For The Real World</a>.</p>
<p>Masala Parser is a Javascript implementation of the Haskell <strong>Parsec</strong>.
It is plain Javascript that works in the browser, is tested with more than 450 unit tests, covering 100% of code lines.</p>
<h3 id="use-cases">Use cases</h3>
<ul>
<li>It can create a <strong>full parser from scratch</strong></li>
<li>It can extract data from a big text and <strong>replace complex regexp</strong></li>
<li>It works in any <strong>browser</strong></li>
<li>There is a good <strong>typescript</strong> type declaration</li>
<li>It can validate complete structure with <strong>variations</strong></li>
<li>It's a great starting point for parser education. It's <strong>way simpler than Lex & Yacc</strong>.</li>
<li>It's designed to be written in other languages (Python, Java, Rust) with the same interface</li>
</ul>
<p>Masala Parser keywords are <strong>simplicity</strong>, <strong>variations</strong> and <strong>maintainability</strong>. You won't
need theoretical bases on languages for extraction or validation use cases.</p>
<p>Masala Parser has relatively good performances, however Javascript is obviously not the fastest machine.</p>
<h1 id="usage">Usage</h1>
<p>With Node Js or modern build </p>
<pre><code> <span class="hljs-built_in">npm</span> install -S @masala/parser</code></pre><p>Or in the browser </p>
<ul>
<li><a href="https://github.com/d-plaindoux/masala-parser/releases">download Release</a></li>
<li><code><script src="masala-parser.min.js"/></code></li>
</ul>
<p>Check the <a href="./changelog.md">Change Log</a> if you can from a previous version.</p>
<h1 id="reference">Reference</h1>
<p>You will find an <a href="documentation/typedoc/modules/_masala_parser_d_.html">Masala Parser online reference</a>, generated from typescript interface.</p>
<h1 id="quick-examples">Quick Examples</h1>
<h2 id="hello-world">Hello World</h2>
<pre><code class="language-js"><span class="hljs-keyword">const</span> helloParser = C.string(<span class="hljs-string">'hello'</span>);
<span class="hljs-keyword">const</span> white = C.char(<span class="hljs-string">' '</span>);
<span class="hljs-keyword">const</span> worldParser = C.char(<span class="hljs-string">'world'</span>);
<span class="hljs-keyword">const</span> combinator = helloParser.then(white.rep()).then(worldParser);</code></pre>
<h2 id="floor-notation">Floor notation</h2>
<pre><code class="language-js"><span class="hljs-comment">// N: Number Bundle, C: Chars Bundle</span>
<span class="hljs-keyword">const</span> {Streams, N, C}= <span class="hljs-built_in">require</span>(<span class="hljs-string">'@masala/parser'</span>);
<span class="hljs-keyword">const</span> stream = Stream.ofString(<span class="hljs-string">'|4.6|'</span>);
<span class="hljs-keyword">const</span> floorCombinator = C.char(<span class="hljs-string">'|'</span>).drop()
.then(N.number()) <span class="hljs-comment">// we have ['|', 4.6], we drop '|'</span>
.then(C.char(<span class="hljs-string">'|'</span>).drop()) <span class="hljs-comment">// we have [4.6, '|'], we keep [4.6]</span>
.single() <span class="hljs-comment">// we had [4.6], now just 4.6</span>
.map(<span class="hljs-function"><span class="hljs-params">x</span> =></span><span class="hljs-built_in">Math</span>.floor(x));
<span class="hljs-comment">// The parser parses a stream of characters</span>
<span class="hljs-keyword">const</span> parsing = floorCombinator.parse(stream);
assertEquals( <span class="hljs-number">4</span>, parsing.value, <span class="hljs-string">'Floor parsing'</span>);</code></pre>
<h2 id="explanations">Explanations</h2>
<p>According to Wikipedia <em>"in functional programming, a parser combinator is a
higher-order function that accepts several parsers as input and returns a new
parser as its output."</em></p>
<h2 id="the-parser">The Parser</h2>
<p>Let's say we have a document :</p>
<blockquote>
<blockquote>
<blockquote>
<p>The James Bond series, by writer Ian Fleming, focuses on a fictional British Secret Service agent created in 1953, who featured him in twelve novels and two short-story collections. Since Fleming's death in 1964, eight other authors have written authorised Bond novels or novelizations: Kingsley Amis, Christopher Wood, John Gardner, Raymond Benson, Sebastian Faulks, Jeffery Deaver, William Boyd and Anthony Horowitz.</p>
</blockquote>
</blockquote>
</blockquote>
<p>The parser could fetch every names, ie two consecutive words starting with uppercase.
The parser will read through the document and aggregate a Response,
which contains a value and the current offset in the text.</p>
<p>This value will evolve when the parser will meet new characters,
but also with some function calls, such as the <code>map()</code> function.</p>
<p><img src="./documentation/parsec-monoid.png" alt=""></p>
<h2 id="the-response">The Response</h2>
<p>By definition, a Parser takes text as an input, and the Response is a structure that represents your problem.
After parsing, there are two subtypes of <code>Response</code>:</p>
<ul>
<li><code>Accept</code> when it found something. </li>
<li><code>Reject</code> if it could not.</li>
</ul>
<pre><code class="language-js">
<span class="hljs-keyword">let</span> response = C.char(<span class="hljs-string">'a'</span>).rep().parse(Streams.ofString(<span class="hljs-string">'aaaa'</span>));
assertEquals(response.value.join(<span class="hljs-string">''</span>), <span class="hljs-string">'aaaa'</span> );
assertEquals(response.offset, <span class="hljs-number">4</span> );
assertTrue(response.isAccepted());
assertTrue(response.isConsumed());
<span class="hljs-comment">// Partially accepted</span>
response = C.char(<span class="hljs-string">'a'</span>).rep().parse(Streams.ofString(<span class="hljs-string">'aabb'</span>));
assertEquals(response.value.join(<span class="hljs-string">''</span>), <span class="hljs-string">'aa'</span> );
assertEquals(response.offset, <span class="hljs-number">2</span> );
assertTrue(response.isAccepted());
assertFalse(response.isConsumed());
</code></pre>
<h2 id="building-the-parser-and-execution">Building the Parser, and execution</h2>
<p>Like a language, the parser is built then executed. With Masala, we build using other parsers.</p>
<pre><code class="language-js"><span class="hljs-keyword">const</span> helloParser = C.string(<span class="hljs-string">'hello'</span>);
<span class="hljs-keyword">const</span> white = C.char(<span class="hljs-string">' '</span>);
<span class="hljs-keyword">const</span> worldParser = C.char(<span class="hljs-string">'world'</span>);
<span class="hljs-keyword">const</span> combinator = helloParser.then(white.rep()).then(worldParser);</code></pre>
<p>There is a compiling time when you combine your parser, and an execution time when the parser
runs its <code>parse(stream)</code> function. You will have the <code>Response</code> after parsing. </p>
<p>So after building, the parser is executed against a stream of token.
For simplicity, we will use a stream of characters, which is a text :)</p>
<h2 id="hello-gandhi">Hello Gandhi</h2>
<p>The goal is check that we have Hello 'someone', then to grab that name</p>
<pre><code class="language-js"><span class="hljs-comment">// Plain old javascript</span>
<span class="hljs-keyword">const</span> {Streams, C}= <span class="hljs-built_in">require</span>(<span class="hljs-string">'@masala/parser'</span>);
<span class="hljs-keyword">var</span> helloParser = C.string(<span class="hljs-string">"Hello"</span>)
.then(C.char(<span class="hljs-string">' '</span>).rep())
.then(C.letters()) <span class="hljs-comment">// succession of A-Za-z letters</span>
.last(); <span class="hljs-comment">// keeping previous letters</span>
<span class="hljs-keyword">var</span> value = helloParser.val(<span class="hljs-string">"Hello Gandhi"</span>); <span class="hljs-comment">// val(x) is a shortcut for parse(Stream.ofString(x)).value;</span>
assertEquals(<span class="hljs-string">'Gandhi'</span>, value);</code></pre>
<h1 id="parser-combinations">Parser Combinations</h1>
<p>Let's use a real example. We combine many functions that returns a new Parser. And each new Parser
is a combination of Parsers given by the standard bundles or previous functions.</p>
<pre><code class="language-js"><span class="hljs-keyword">import</span> {Streams, N,C, F} <span class="hljs-keyword">from</span> <span class="hljs-string">'@masala/parser'</span>;
<span class="hljs-keyword">const</span> blanks = <span class="hljs-function"><span class="hljs-params">()</span>=></span>C.char(<span class="hljs-string">' '</span>).optrep();
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">operator</span>(<span class="hljs-params">symbol</span>) </span>{
<span class="hljs-keyword">return</span> blanks().drop()
.then(C.char(symbol)) <span class="hljs-comment">// '+' or '*'</span>
.then(blanks().drop())
.single();
}
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sum</span>(<span class="hljs-params"></span>) </span>{
<span class="hljs-keyword">return</span> N.integer()
.then(operator(<span class="hljs-string">'+'</span>).drop())
.then(N.integer()) <span class="hljs-comment">// then(x) creates a tuple - here, one value was dropped</span>
.map(<span class="hljs-function"><span class="hljs-params">tuple</span> =></span> tuple.at(<span class="hljs-number">0</span>) + tuple.at(<span class="hljs-number">1</span>));
}
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">multiplication</span>(<span class="hljs-params"></span>) </span>{
<span class="hljs-keyword">return</span> N.integer()
.then(operator(<span class="hljs-string">'*'</span>).drop())
.then(N.integer())
.array() <span class="hljs-comment">// we can have access to the value of the tuple</span>
.map( <span class="hljs-function">(<span class="hljs-params">[left,right]</span>)=></span> left * right); <span class="hljs-comment">// more modern js </span>
}
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">scalar</span>(<span class="hljs-params"></span>) </span>{
<span class="hljs-keyword">return</span> N.integer();
}
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">combinator</span>(<span class="hljs-params"></span>) </span>{
<span class="hljs-keyword">return</span> F.try(sum())
.or(F.try(multiplication())) <span class="hljs-comment">// or() will often work with try()</span>
.or(scalar());
}
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">parseOperation</span>(<span class="hljs-params">line</span>) </span>{
<span class="hljs-keyword">return</span> combinator().parse(Streams.ofString(line));
}
assertEquals(<span class="hljs-number">4</span>, parseOperation(<span class="hljs-string">'2 +2'</span>).value, <span class="hljs-string">'sum: '</span>);
assertEquals(<span class="hljs-number">6</span>, parseOperation(<span class="hljs-string">'2 * 3'</span>).value, <span class="hljs-string">'multiplication: '</span>);
assertEquals(<span class="hljs-number">8</span>, parseOperation(<span class="hljs-string">'8'</span>).value, <span class="hljs-string">'scalar: '</span>);</code></pre>
<p>A curry paste is an higher order ingredient made from a good combination of spices.</p>
<p><img src="./documentation/images/curry-paste.jpg" alt=""></p>
<h2 id="precedence">Precedence</h2>
<p>Precedence is a technical term for priority. Using:</p>
<pre><code class="language-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">combinator</span>(<span class="hljs-params"></span>) </span>{
<span class="hljs-keyword">return</span> F.try(sum())
.or(F.try(multiplication())) <span class="hljs-comment">// or() will often work with try()</span>
.or(scalar());
}
<span class="hljs-built_in">console</span>.info(<span class="hljs-string">'sum: '</span>,parseOperation(<span class="hljs-string">'2+2'</span>).value);</code></pre>
<p>We will give priority to sum, then multiplication, then scalar. If we had put <code>scalar()</code> first, we would have first
accepted <code>2</code>, then what could we do with <code>+2</code> alone ? It's not a valid sum ! Moreover <code>+2</code> and <code>-2</code> are acceptable scalars. </p>
<h2 id="try-x-or-y-">try(x).or(y)</h2>
<p><code>or()</code> will often be used with <code>try()</code>, that makes <a href="https://en.wikipedia.org/wiki/Backtracking">backtracking</a>
: it saves the current offset, then tries an option. And as soon that it's not satisfied, it goes back to the original
offset and use the parser inside the <code>.or(P)</code> expression.`.</p>
<p> Like Haskell's Parsec, Masala Parser can parse infinite look-ahead grammars but
performs best on predictive (LL[1]) grammars.</p>
<p>Let see how with <code>try()</code>, we can look a bit ahead of next characters, then go back:</p>
<pre><code> <span class="hljs-module-access"><span class="hljs-module"><span class="hljs-identifier">F</span>.</span></span><span class="hljs-keyword">try</span>(sum<span class="hljs-literal">()</span>).<span class="hljs-keyword">or</span>(<span class="hljs-module-access"><span class="hljs-module"><span class="hljs-identifier">F</span>.</span></span><span class="hljs-keyword">try</span>(multiplication<span class="hljs-literal">()</span>)).<span class="hljs-keyword">or</span>(scalar<span class="hljs-literal">()</span>)
<span class="hljs-comment">// try(sum()) parser in action</span>
<span class="hljs-number">2</span> *<span class="hljs-number">2</span>
..ok..ok ↑oups: go back <span class="hljs-keyword">and</span> <span class="hljs-keyword">try</span> multiplication. Should be OK.</code></pre><p>Suppose we do not <code>try()</code> but use <code>or()</code> directly:</p>
<pre><code> <span class="hljs-keyword">sum</span>().<span class="hljs-keyword">or</span>(multiplication()).<span class="hljs-keyword">or</span>(<span class="hljs-keyword">scalar</span>())
<span class="hljs-comment">// testing sum()</span>
2 *2
..ok..ok ↑<span class="hljs-comment">oups: cursor is NOT going back. So now we must test</span> <span class="hljs-comment">'*2'</span> ;
Is it (multiplication())? <span class="hljs-keyword">No</span> ;
<span class="hljs-keyword">or</span>(<span class="hljs-keyword">scalar</span>()) ? neither</code></pre><h1 id="recursion">Recursion</h1>
<p>Masala-Parser (like Parsec) is a top-down parser and doesn't like <a href="https://cs.stackexchange.com/a/9971">Left Recursion</a>.</p>
<p>However, it is a resolved problem for this kind of parsers, with a lot of documentation. You can read more on <a href="./documentation/recursion.md">recursion
with Masala</a>, and checkout examples on our Github repository
( <a href="https://github.com/d-plaindoux/masala-parser/blob/master/integration-npm/examples/recursion/aaab-lazy-recursion.js">simple recursion</a>,
or <a href="https://github.com/d-plaindoux/masala-parser/blob/master/integration-npm/examples/operations/plus-minus.js">calculous expressions</a> ).</p>
<h1 id="simple-documentation-of-core-bundles">Simple documentation of Core bundles</h1>
<h2 id="core-parser-functions">Core Parser Functions</h2>
<p>Here is a link for <a href="./documentation/parser-core-functions.md">Core functions documentation</a>.</p>
<p>It will explain <code>then()</code>, <code>drop()</code>, <code>map()</code>, <code>rep()</code>, <code>opt()</code> and other core functions of the Parser
with code examples.</p>
<p>### </p>
<h2 id="the-chars-bundle">The Chars Bundle</h2>
<p>Example: </p>
<pre><code class="language-js">C.char(<span class="hljs-string">'-'</span>)
.then(C.letters())
.then(C.char(<span class="hljs-string">'-'</span>))
<span class="hljs-comment">// accepts '-hello-' ; value is ['-','hello','-']</span>
<span class="hljs-comment">// reject '-hel lo-' because space is not a letter </span></code></pre>
<p><a href="./documentation/chars-bundle.md">General use</a></p>
<ul>
<li><code>letter()</code>: accept a european letter (and moves the cursor)</li>
<li><code>letters()</code>: accepts many letters and returns a string</li>
<li><code>letterAs(symbol)</code>: accepts a european(default), ascii, or utf8 Letter. <a href="./documentation/chars-bundle.md">More here</a></li>
<li><code>lettersAs(symbol)</code>: accepts many letters and returns a string</li>
<li><code>emoji()</code>: accept any emoji sequence. <a href="https://github.com/d-plaindoux/masala-parser/issues/86">Opened Issue</a>.</li>
<li><code>notChar(x)</code>: accept if next input is not <code>x</code></li>
<li><code>char(x)</code>: accept if next input is <code>x</code></li>
<li><code>charIn('xyz')</code>: accept if next input is <code>x</code>, <code>y</code> or <code>z</code></li>
<li><code>charNotIn('xyz')</code>: accept if next input is not <code>x</code>, <code>y</code> or <code>z</code></li>
<li><code>subString(length)</code>: accept any next <em>length</em> characters and returns the equivalent string</li>
<li><code>string(word)</code>: accept if next input is the given <code>word</code> </li>
<li><code>stringIn(words)</code>: accept if next input is the given <code>words</code> <a href="./documentation/chars-bundle.md">More here</a></li>
<li><code>notString(word)</code>: accept if next input is <em>not</em> the given <code>word</code></li>
<li><code>charLiteral()</code>: single quoted char element in C/Java : <code>'a'</code> is accepted</li>
<li><code>stringLiteral()</code>: double quoted string element in java/json: <code>"hello world"</code> is accepted</li>
<li><code>lowerCase()</code>: accept any next lower case inputs</li>
<li><code>upperCase()</code>: accept any next uppercase inputs</li>
</ul>
<p>Other example:</p>
<pre><code class="language-js">C.string(<span class="hljs-string">'Hello'</span>)
.then(C.char(<span class="hljs-string">' '</span>))
.then(C.lowerCase().rep().join(<span class="hljs-string">''</span>))
<span class="hljs-comment">// accepts Hello johnny ; value is ['Hello', ' ', 'johnny']</span>
<span class="hljs-comment">// rejects Hello Johnny : J is not lowercase ; no value</span></code></pre>
<h2 id="the-numbers-bundle">The Numbers Bundle</h2>
<ul>
<li><code>number()</code>: accept any float number, such as -2.3E+24, and returns a float </li>
<li><code>digit()</code>: accept any single digit, and returns a <strong>number</strong></li>
<li><code>digits()</code>: accept many digits, and returns a <strong>number</strong>. Warning: it does not accept <strong>+-</strong> signs symbols.</li>
<li><code>integer()</code>: accept any positive or negative integer</li>
</ul>
<h2 id="the-flow-bundle">The Flow Bundle</h2>
<p>The flow bundle will mix ingredients together.</p>
<p>For example if you have a Parser <code>p</code>, <code>F.not(p)</code> will accept anything
that does not satisfy <code>p</code></p>
<p>All of these functions will return a brand new Parser that you can combine with others.</p>
<p>Most important:</p>
<ul>
<li><code>F.try(parser).or(otherParser)</code>: Try a parser and come back to <code>otherParser</code> if failed</li>
<li><code>F.any()</code>: Accept any character (and so moves the cursor)</li>
<li><code>F.not(parser)</code>: Accept anything that is not a parser. Often used to accept until a given <em>stop</em> </li>
<li><code>F.eos()</code>: Accepted if the Parser has reached the <strong>E*<em>nd *</em>O</strong>f <strong>S</strong>tream</li>
<li><code>F.moveUntil(string|stopParser)</code>: Alternative for <strong>regex</strong>. Will traverse the document <strong>until</strong> the <em>stop parser</em><ul>
<li>returns <code>undefined</code> if <em>stop</em> is not found</li>
<li>returns all characters if <em>stop</em> is found, and set the cursor at the spot of the stop</li>
</ul>
</li>
<li><code>F.dropTo(string|stopParser)</code>: Will traverse the document <strong>including</strong> the <em>stop parser</em></li>
</ul>
<p>Others:</p>
<ul>
<li><code>F.lazy(parser, ?params)</code>: Makes a lazy evaluation. May be used for Left recursion (difficult)</li>
<li><code>F.parse(parserFunction)</code>: Create a new Parser from a function. Usually, you won't start here.</li>
<li><code>F.subStream(length)</code>: accept any next characters </li>
<li><code>F.returns(value)</code>: forces a returned value</li>
<li><code>F.error()</code>: returns an error. Parser will never be accepted</li>
<li><code>F.satisfy(predicate)</code>: check if condition is satisfied</li>
<li><code>F.startsWith(value)</code>: create a no-op parser with initial value </li>
</ul>
<h2 id="license">License</h2>
<p>Copyright (C)2016-2019 D. Plaindoux.</p>
<p>This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2, or (at
your option) any later version.</p>
<p>This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.</p>
<p>You should have received a copy of the GNU Lesser General Public
License along with this program; see the file COPYING. If not, write
to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
USA.</p>
</div>
</div>
<div class="col-4 col-menu menu-sticky-wrap menu-highlight">
<nav class="tsd-navigation primary">
<ul>
<li class="globals ">
<a href="globals.html"><em>Globals</em></a>
</li>
<li class=" tsd-kind-external-module">
<a href="modules/_masala_parser_d_.html">"masala-<wbr>parser.d"</a>
</li>
</ul>
</nav>
<nav class="tsd-navigation secondary menu-sticky">
<ul class="before-current">
</ul>
</nav>
</div>
</div>
</div>
<footer class="with-border-bottom">
<div class="container">
<h2>Legend</h2>
<div class="tsd-legend-group">
<ul class="tsd-legend">
<li class="tsd-kind-module"><span class="tsd-kind-icon">Module</span></li>
<li class="tsd-kind-object-literal"><span class="tsd-kind-icon">Object literal</span></li>
<li class="tsd-kind-variable"><span class="tsd-kind-icon">Variable</span></li>
<li class="tsd-kind-function"><span class="tsd-kind-icon">Function</span></li>
<li class="tsd-kind-function tsd-has-type-parameter"><span class="tsd-kind-icon">Function with type parameter</span></li>
<li class="tsd-kind-index-signature"><span class="tsd-kind-icon">Index signature</span></li>
<li class="tsd-kind-type-alias"><span class="tsd-kind-icon">Type alias</span></li>
</ul>
<ul class="tsd-legend">
<li class="tsd-kind-enum"><span class="tsd-kind-icon">Enumeration</span></li>
<li class="tsd-kind-enum-member"><span class="tsd-kind-icon">Enumeration member</span></li>
<li class="tsd-kind-property tsd-parent-kind-enum"><span class="tsd-kind-icon">Property</span></li>
<li class="tsd-kind-method tsd-parent-kind-enum"><span class="tsd-kind-icon">Method</span></li>
</ul>
<ul class="tsd-legend">
<li class="tsd-kind-interface"><span class="tsd-kind-icon">Interface</span></li>
<li class="tsd-kind-interface tsd-has-type-parameter"><span class="tsd-kind-icon">Interface with type parameter</span></li>
<li class="tsd-kind-constructor tsd-parent-kind-interface"><span class="tsd-kind-icon">Constructor</span></li>
<li class="tsd-kind-property tsd-parent-kind-interface"><span class="tsd-kind-icon">Property</span></li>
<li class="tsd-kind-method tsd-parent-kind-interface"><span class="tsd-kind-icon">Method</span></li>
<li class="tsd-kind-index-signature tsd-parent-kind-interface"><span class="tsd-kind-icon">Index signature</span></li>
</ul>
<ul class="tsd-legend">
<li class="tsd-kind-class"><span class="tsd-kind-icon">Class</span></li>
<li class="tsd-kind-class tsd-has-type-parameter"><span class="tsd-kind-icon">Class with type parameter</span></li>
<li class="tsd-kind-constructor tsd-parent-kind-class"><span class="tsd-kind-icon">Constructor</span></li>
<li class="tsd-kind-property tsd-parent-kind-class"><span class="tsd-kind-icon">Property</span></li>
<li class="tsd-kind-method tsd-parent-kind-class"><span class="tsd-kind-icon">Method</span></li>
<li class="tsd-kind-accessor tsd-parent-kind-class"><span class="tsd-kind-icon">Accessor</span></li>
<li class="tsd-kind-index-signature tsd-parent-kind-class"><span class="tsd-kind-icon">Index signature</span></li>
</ul>
<ul class="tsd-legend">
<li class="tsd-kind-constructor tsd-parent-kind-class tsd-is-inherited"><span class="tsd-kind-icon">Inherited constructor</span></li>
<li class="tsd-kind-property tsd-parent-kind-class tsd-is-inherited"><span class="tsd-kind-icon">Inherited property</span></li>
<li class="tsd-kind-method tsd-parent-kind-class tsd-is-inherited"><span class="tsd-kind-icon">Inherited method</span></li>
<li class="tsd-kind-accessor tsd-parent-kind-class tsd-is-inherited"><span class="tsd-kind-icon">Inherited accessor</span></li>
</ul>
<ul class="tsd-legend">
<li class="tsd-kind-property tsd-parent-kind-class tsd-is-protected"><span class="tsd-kind-icon">Protected property</span></li>
<li class="tsd-kind-method tsd-parent-kind-class tsd-is-protected"><span class="tsd-kind-icon">Protected method</span></li>
<li class="tsd-kind-accessor tsd-parent-kind-class tsd-is-protected"><span class="tsd-kind-icon">Protected accessor</span></li>
</ul>
<ul class="tsd-legend">
<li class="tsd-kind-property tsd-parent-kind-class tsd-is-private"><span class="tsd-kind-icon">Private property</span></li>
<li class="tsd-kind-method tsd-parent-kind-class tsd-is-private"><span class="tsd-kind-icon">Private method</span></li>
<li class="tsd-kind-accessor tsd-parent-kind-class tsd-is-private"><span class="tsd-kind-icon">Private accessor</span></li>
</ul>
<ul class="tsd-legend">
<li class="tsd-kind-property tsd-parent-kind-class tsd-is-static"><span class="tsd-kind-icon">Static property</span></li>
<li class="tsd-kind-call-signature tsd-parent-kind-class tsd-is-static"><span class="tsd-kind-icon">Static method</span></li>
</ul>
</div>
</div>
</footer>
<div class="container tsd-generator">
<p>Generated using <a href="http://typedoc.org/" target="_blank">TypeDoc</a></p>
</div>
<div class="overlay"></div>
<script src="assets/js/main.js"></script>
<script>if (location.protocol == 'file:') document.write('<script src="assets/js/search.js"><' + '/script>');</script>
</body>
</html>