Artifact Content

Artifact 682b700009613e07de3ae7d7f1472fc407d89b13:

Wiki page [Getting Started] by User 2016-11-17 03:26:34.
D 2016-11-17T03:26:34.139
L Getting\sStarted
P aca42b1524a5690be437133ccd721576fc47c96d
U User
W 9339
<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>


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


<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. 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>
<pre><font size="3" face="Courier New"><span style="font-size: medium; color: rgb(17, 17, 17);">let operandPlus: pcomb.Parser =&nbsp;</span>pcomb.or([pcomb.lit("+", this.operandSet), pcomb.lit("plus", this.operandSet)]);</font></pre>

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

Z f7adf1f9e49c76d0d8ca10bd6c94d829