Artifact Content

Artifact 08758a57949dcfce9873684c39936e56688f1b8d:

Wiki page [Getting Started] by User 2016-11-20 04:57:45.
D 2016-11-20T04:57:45.632
L Getting\sStarted
P 4c046c3d91b47577b8a97ed3a5770823de1d83c3
U User
W 10645
<h1><i>Getting Started</i></h1>

<p><font size="3">So, you are interested in using PComb? Doing so is very easy.
Start by taking a good look at the demo app in the <i>app/</i>&nbsp;subfolder,
and when you are ready, here are the steps:</font></p>

<p><font size="3">1. Include the file <i>pcomb.ts</i> in &nbsp;your project. If
it is a TypeScript project, adjust your build script to make sure that it gets
compiled along with any other dependencies.</font></p>

<p><font size="3">

<span style="color: rgb(17, 17, 17);"><font face="Arial">2. In any source file
that is going to use PComb for parsing, add a reference or an import statement
to include it, like this:&nbsp;</font></span>

<span style="color: rgb(17, 17, 17); font-family: Arial;"><b>import * as pcomb
from "../src/pcomb" </b>Of course, the path will be different depending on how
you have your project files laid out.</span></font></p>

<p>

<span style="color: rgb(17, 17, 17); font-family: Arial;"><font size="3">3. You
will need to provide two classes: one that implements the <i>pcomb.ParserInput</i>
interface and one that implements the <i>pcomb.ParserOutput</i> interface The
class that implements <i>pcomb.ParserInput</i> doesn't need to do much: just
provide the one property and one method required by the interface. <i>pcomb.ParserOutput</i>
on the other hand, should implement the required fields, but it also needs to
store whatever kind of state is needed by your application to do its work.</font></span></p>

<p>

<span style="color: rgb(17, 17, 17); font-family: Arial;"><font size="3">Here
is the <i>ParserInput</i> from the demo app:</font></span></p>
<pre><font size="3" face="Courier New">class ChattyInput implements pcomb.ParserInput {
</font><pre><font face="Courier New"><font size="3">    text: string;
</font><font size="3">    copy(): pcomb.ParserInput {
</font><font size="3">        let newInput = new ChattyInput();</font></font></pre><pre><font face="Courier New"><font size="3">        newInput.text = this.text;</font><font size="3">
</font><font size="3">        return newInput;
</font><font size="3">    }
</font><font size="3">}</font></font></pre></pre>
<pre><font size="3"><font face="Arial">And here is the <i>ParserOutput:</i></font></font></pre>
<pre><font size="3" face="Courier New">class ChattyData implements pcomb.ParserOutput {
</font><pre><font face="Courier New"><font size="3">    matched: string[];
</font><font size="3">    leftOperand: number;
</font><font size="3">    rightOperand: number;
</font><font size="3">    operator: MathOps;
</font><font size="3">    accumulator: number = 0;</font><font size="3">
</font><font size="3">    copy(): ChattyData {
</font><font size="3">        let newData = new ChattyData();<br>


</font><p><font size="3">        if (newData.matched) {
</font><font size="3">            newData.matched = this.matched.slice();
</font><font size="3">        } else {
</font><font size="3">            newData.matched = Array&lt;string&gt;();
</font><font size="3">        }
</font></p></font><p><font face="Courier New"><font size="3">        newData.leftOperand = this.leftOperand;
</font><font size="3">        newData.rightOperand = this.rightOperand;
</font><font size="3">        newData.operator = this.operator;
</font><font size="3">        newData.accumulator = this.accumulator;</font><font size="3">
</font><font size="3">        return newData;
</font><font size="3">    }
</font><font size="3">}</font></font></p></pre></pre>

<p><font face="Arial" size="3">As you can see, the <i>ParserOutput</i>
implementing class adds a number of fields -- <i>leftOperand, rightOperand,
operator</i> and <i>accumulator</i> -- that are used to store the data that is
collected by the parsers that the app defines. This tutorial will cover the
mechanism for getting data into the object later.</font></p>

<p><font face="Arial" size="3">4. Now it is time to actually create a parser.
Most begin with one or more <i>lit</i>&nbsp;parsers; they match a fixed,
caseless string of one-or-more charatcers. Again taking an example from the
demo app, let's create parsers that represent the arithmetic operators for
addition, subtraction, multiplication and division.</font></p>
<pre><font size="3" face="Courier New">let operandPlus: pcomb.Parser = p</font><font face="Courier New"><font size="3">comb.lit("+");</font><span style="font-size: medium;">&nbsp;</span></font></pre>

<p><font face="Arial" size="3">Here we are declaring a variable of type <i>pcomb.Parser</i>
and call the <i>lit</i>&nbsp;function to create a parser that will match a plus
sign. That is all well and good, but nothing will happen aside from parsing
continuing. What we want is for the <i>operator</i>&nbsp;field in our
ChattyData object to be set correctly. To do that, we will need to define a <i>ParserAction.</i></font></p>

<p><font face="Arial" size="3">5. Add a ParserAction function that can be
called by the parser that matches math operators:</font></p>
<pre><font face="Courier New"><font size="3">&nbsp; &nbsp; private operandSet(matched: string, output: pcomb.ParserOutput): pcomb.ParserOutput {
</font><font size="3">&nbsp; &nbsp; &nbsp; &nbsp; let result: ChattyData = &lt;ChattyData&gt;output.copy();</font><font size="3"><br>


</font><p><font size="3">&nbsp; &nbsp; &nbsp; &nbsp; switch (matched) {
</font><font size="3">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case "+":
</font><font size="3">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case "plus":
</font><font size="3">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; result.operator = MathOps.Add;
</font><font size="3">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break;
</font><font size="3">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case "-":
</font><font size="3">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case "minus":
</font><font size="3">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; result.operator = MathOps.Subtract;
</font><font size="3">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break;
</font><font size="3">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case "*":
</font><font size="3">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case "times":
</font><font size="3">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; result.operator = MathOps.Multipy;
</font><font size="3">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break;
</font><font size="3">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case "/":
</font><font size="3">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case "divided by":
</font><font size="3">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; result.operator = MathOps.Divide;
</font><font size="3">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break;
</font><font size="3">&nbsp; &nbsp; &nbsp; &nbsp; }</font><font size="3"><br>


</font></p></font><p><font face="Courier New"><font size="3">&nbsp; &nbsp; &nbsp; &nbsp; return result;
</font><font size="3">&nbsp; &nbsp; }</font></font></p></pre>

<p><font face="Arial" size="3">Note that this function matches the signature of
the <i>ParserAction</i>&nbsp;type as defined in pcomb.ts:</font></p>
<pre><font face="Courier New" size="2">export type ParserAction = (matchedText: string, output: ParserOutput) =&gt; ParserOutput;</font></pre>

<p><font face="Arial" size="3">It is a function that takes two parameters: <i>matchedText,</i>&nbsp;and
<i>output.</i>&nbsp;The first holds the actual text that was matched by the
parser. This may seem trivial; after all you are attaching the function to a
parser and you know what the parser matches, right? Well, a <i>ParserAction</i>&nbsp;can
be attached to a parser of arbitrary complexity, and it is possible that one of
very many different patterns will have been matched, so knowing what was
matched is necessary for writing anything other than the most trivial function.</font></p>

<p><font face="Arial" size="3">The <i>output</i>&nbsp;parameter is the current
state of the parse: all of the texts matched to date as well as whatever
application-specific data has been collected. You can see that the first thing
that the function does is create of copy of that which is passed in, and it
makes all changes to and returns that copy.</font></p>

<p><font face="Arial" size="3">The function matches our expected value of <i>+</i>,
but it also knows about the synonym <i>plus</i>&nbsp;and all the other
operators and their synonyms. However, we haven't yet written a parser that
will match any of those.</font></p>

<p><font face="Arial" size="3">&nbsp;First, let's rewrite our definition of <i>operandPlus</i>&nbsp;so
the action will be called:</font></p>
<pre><font size="3" face="Courier New">let operandPlus: pcomb.Parser = p</font><font face="Courier New"><font size="3">comb.lit("+", this.operandSet);</font></font></pre>

<p><font face="Arial" size="3">Now we can rewrite our parser so that it will
match either <i>+</i>&nbsp;or <i>plus</i>:</font></p><font face="Courier New" size="2">let
operandPlus: pcomb.Parser = pcomb.or([pcomb.lit("+", this.operandSet),
pcomb.lit("plus", this.operandSet)]); </font>

<p><font face="Arial" size="3">Since the <i>or</i>&nbsp;combinator can take a
<i>ParserAction,</i>&nbsp;we can rewrite the above to eliminate redundancy:</font></p><font face="Courier New" size="2">let
operandPlus: pcomb.Parser = pcomb.or([pcomb.lit("+"), pcomb.lit("plus")],
this.operandSet); </font>

<div><font face="Courier New" size="2"><br>
</font></div>

<p><font face="Arial" size="3">6. Run the parser against some text. While our
currently-defined parser is trivially and not very useful, we can still give it
some text to parse. The <i>pcomb.Parse</i> method is called like this:</font></p><font face="Courier New" size="2">let
result = pcomb.Parse(operandPlus, text, new ChattyInput(), new ChattyData());
</font>

<p><font face="Arial" size="3">It returns a <i>ParseResult</i>&nbsp;type:</font></p><font face="Courier New" size="3">export
type ParseResult = [boolean, ParserInput, ParserOutput]; </font>

<p><font face="Arial" size="3">We can tell if the parser matched the input text
by examining <i>result[0]</i>, which will be <i>true</i>&nbsp;if it did and <i>false</i>&nbsp;otherwise.
The second returned value, a <i>ParserInput</i>&nbsp;holds&nbsp;the state of
the input data once the parser is done consuming it, and the third value, a <i>ParserOutput</i>&nbsp;has
all the distinct matched values from all of the parsers that ran, as well as
all of the application-specific information that was retained.</font></p>

<p><font face="Arial" size="3">7. Parse, parse, parse.</font></p>

Z 84ae170eae499f4aac1b69c3ab4f3b43