Check-in [a38e69bf5f]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:[5661b375d3] Made all of the code changes and the existing tests pass. Still need to write new tests to confirm that the ParserActions are run by the and and or combinators.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:a38e69bf5f8809574a7a637c076c8f14aeb306be
User & Date: adamc 2016-10-21 01:57:25
Context
2016-10-22
16:53
[6fb63b4ba7] Added code for the new combinator and tests for it. check-in: ff3f039849 user: adamc tags: trunk
2016-10-21
01:57
[5661b375d3] Made all of the code changes and the existing tests pass. Still need to write new tests to confirm that the ParserActions are run by the and and or combinators. check-in: a38e69bf5f user: adamc tags: trunk
2016-10-20
03:17
Continuing to add more parsing goodness to the demo app. check-in: 0d12361d03 user: adamc tags: trunk
Changes

Changes to app/chattymath.ts.

153
154
155
156
157
158
159


















160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
                break;
            case "twnety":
                result.accumulator += 20;
                break;
            case "thirty":
                result.accumulator += 30;
                break;


















        }

        return result;
    }
    constructor() {
        this.operandPlus = pcomb.or(pcomb.lit("+", this.operandSet),
            pcomb.lit("plus", this.operandSet));
        this.operandMinus = pcomb.or(pcomb.lit("-", this.operandSet),
            pcomb.lit("minus", this.operandSet));
        this.operandMultiply = pcomb.or(pcomb.lit("*", this.operandSet),
            pcomb.lit("times", this.operandSet));
        this.operandDivide = pcomb.or(pcomb.lit("/", this.operandSet),
            pcomb.lit("divided by", this.operandSet));

        this.operand = pcomb.or(this.operandPlus,
            this.operandMinus,
            this.operandMultiply,
            this.operandDivide);

        this.singleDigitNumbers = pcomb.or(
            pcomb.lit("one"),
            pcomb.lit("two"),
            pcomb.lit("three"),
            pcomb.lit("four"),
            pcomb.lit("five"),
            pcomb.lit("six"),
            pcomb.lit("seven"),
            pcomb.lit("eight"),
            pcomb.lit("nine")
        );

        this.numberParser = pcomb.all(pcomb.numeric(), this.numberSet);

        this.formulaParser = pcomb.and(
            pcomb.opt(pcomb.lit("what does")),
            pcomb.opt(pcomb.lit("what is")),
            this.numberParser,
            this.operand,
            this.numberParser,
            pcomb.opt(pcomb.or(pcomb.lit("="), pcomb.lit("equal"))));

        this.parsers = new pcomb.ParserSet(new ChattyInput(), new ChattyData());
    }

    ask(text: string): string {
        let theAnswer = "sorry, but I couldn't understand that";
        let parseResult = this.parsers.parse(this.formulaParser, text);







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>





|
|
|
|
|
|
|
|

|


|

|








|




|





|







153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
                break;
            case "twnety":
                result.accumulator += 20;
                break;
            case "thirty":
                result.accumulator += 30;
                break;
            case "forty":
                result.accumulator += 40;
                break;
            case "fifty":
                result.accumulator += 50;
                break;
            case "sixty":
                result.accumulator += 60;
                break;
            case "seventy":
                result.accumulator += 70;
                break;
            case "eighty":
                result.accumulator += 80;
                break;
            case "ninety":
                result.accumulator += 90;
                break;
        }

        return result;
    }
    constructor() {
        this.operandPlus = pcomb.or([pcomb.lit("+", this.operandSet),
            pcomb.lit("plus", this.operandSet)]);
        this.operandMinus = pcomb.or([pcomb.lit("-", this.operandSet),
            pcomb.lit("minus", this.operandSet)]);
        this.operandMultiply = pcomb.or([pcomb.lit("*", this.operandSet),
            pcomb.lit("times", this.operandSet)]);
        this.operandDivide = pcomb.or([pcomb.lit("/", this.operandSet),
            pcomb.lit("divided by", this.operandSet)]);

        this.operand = pcomb.or([this.operandPlus,
            this.operandMinus,
            this.operandMultiply,
            this.operandDivide]);

        this.singleDigitNumbers = pcomb.or([
            pcomb.lit("one"),
            pcomb.lit("two"),
            pcomb.lit("three"),
            pcomb.lit("four"),
            pcomb.lit("five"),
            pcomb.lit("six"),
            pcomb.lit("seven"),
            pcomb.lit("eight"),
            pcomb.lit("nine")]
        );

        this.numberParser = pcomb.all(pcomb.numeric(), this.numberSet);

        this.formulaParser = pcomb.and([
            pcomb.opt(pcomb.lit("what does")),
            pcomb.opt(pcomb.lit("what is")),
            this.numberParser,
            this.operand,
            this.numberParser,
            pcomb.opt(pcomb.or([pcomb.lit("="), pcomb.lit("equal")]))]);

        this.parsers = new pcomb.ParserSet(new ChattyInput(), new ChattyData());
    }

    ask(text: string): string {
        let theAnswer = "sorry, but I couldn't understand that";
        let parseResult = this.parsers.parse(this.formulaParser, text);

Changes to src/pcomb.ts.

57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76






77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
..
99
100
101
102
103
104
105




106
107
108
109
110
111
112
    }

    return foundMatch;
}

/** Execute all of the given parsers, in order, until one of them
     returns true. A matching parser terminates parsing immediately. */
export function or(...parsers: Parser[]): Parser {
    return (input: ParserInput, output: ParserOutput, action?: ParserAction): ParseResult => {
        let finalResult: ParseResult = [false, input.copy(), output.copy()];

        for (let i = 0; i < parsers.length; i++) {
            let result = parsers[i](input.copy(), output.copy());
            if (result[0]) { // Parsing successful, finish up and exit
                finalResult[0] = result[0];
                finalResult[1] = result[1];
                finalResult[2] = result[2];
                break;
            }
        }







        return finalResult;
    };
}

/** Execute all of the listed parsers, as long as the previous 
    parser returns true. A non-matching parser terminates
    parsing immediately. */
export function and(...parsers: Parser[]): Parser {
    return (input: ParserInput, output: ParserOutput, action?: ParserAction): ParseResult => {
        let result: ParseResult = [true, input.copy(), output.copy()];
        result[0] = true;

        let tryInput = input.copy();
        let tryOutput = output.copy();

................................................................................
            } else {
                tryInput = tryParseResult[1];
                tryOutput = tryParseResult[2];
            }
        }

        if (result[0]) { // All parsers finished successfully.




            result[1] = tryInput;
            result[2] = tryOutput;
        }

        return result;
    };
}







|












>
>
>
>
>
>








|







 







>
>
>
>







57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
...
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
    }

    return foundMatch;
}

/** Execute all of the given parsers, in order, until one of them
     returns true. A matching parser terminates parsing immediately. */
export function or(parsers: Parser[], outerAction?: ParserAction): Parser {
    return (input: ParserInput, output: ParserOutput, action?: ParserAction): ParseResult => {
        let finalResult: ParseResult = [false, input.copy(), output.copy()];

        for (let i = 0; i < parsers.length; i++) {
            let result = parsers[i](input.copy(), output.copy());
            if (result[0]) { // Parsing successful, finish up and exit
                finalResult[0] = result[0];
                finalResult[1] = result[1];
                finalResult[2] = result[2];
                break;
            }
        }

        if (finalResult[0]) {
            if (outerAction) {
                finalResult[2] = outerAction(lastMatched(finalResult[2]), finalResult[2]);
            }
        }

        return finalResult;
    };
}

/** Execute all of the listed parsers, as long as the previous 
    parser returns true. A non-matching parser terminates
    parsing immediately. */
export function and(parsers: Parser[], outerAction?: ParserAction): Parser {
    return (input: ParserInput, output: ParserOutput, action?: ParserAction): ParseResult => {
        let result: ParseResult = [true, input.copy(), output.copy()];
        result[0] = true;

        let tryInput = input.copy();
        let tryOutput = output.copy();

................................................................................
            } else {
                tryInput = tryParseResult[1];
                tryOutput = tryParseResult[2];
            }
        }

        if (result[0]) { // All parsers finished successfully.
            if (outerAction) {
                result[2] = outerAction(lastMatched(result[2]), result[2]);
            }

            result[1] = tryInput;
            result[2] = tryOutput;
        }

        return result;
    };
}

Changes to tests/test.ts.

69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
...
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
...
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
...
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
...
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
        this.litValue = pcomb.lit("value");
        this.litValueOther = pcomb.lit("othervalue");
        this.litColon = pcomb.lit(":");
        this.litSemicolon = pcomb.lit(";");
        this.litSpace = pcomb.lit(" ");
        this.optWsParser = pcomb.opt(this.litSpace);
        this.allStoreName = pcomb.any(null, this.storeNameValue);
        this.nameValuePair = pcomb.and(this.litName, this.litColon, this.litValue);
        this.eitherNameValuePair = pcomb.or(this.litValue, this.litValueOther);
        this.nameValuePair2 = pcomb.and(this.litName, this.litColon, this.eitherNameValuePair);
        this.optParser = pcomb.and(
            this.litName, this.optWsParser, this.litColon,
            this.optWsParser, this.litValue);
        this.anyParser1 = pcomb.and(this.litName, this.litColon, this.allStoreName);
        this.anyParser2 = pcomb.and(this.litName, this.litColon, pcomb.any(this.litSemicolon), this.litSemicolon);
        this.numericParser = pcomb.numeric();
        this.numericSequence = pcomb.all(this.numericParser);

        // Declare functions that are used in testing in this way to prevent tsUnit from throwing errors
        // related to them.
        this.storeNameValue = function (matched: string, output: TestOutput) {
            let newOutput = output.copy();
................................................................................
        this.areIdentical(tryResult[2].matched[0], "name");
        this.areIdentical(tryResult[2].matched[1], ":");
        this.areIdentical(tryResult[2].matched[2], "adam");
        this.areIdentical(tryResult[2].matched[3], ";");
    }

    actionAnyTest() {
        let testParser = pcomb.and(
            this.litName,
            this.litColon,
            pcomb.any(this.litSemicolon, this.storeNameValue),
            this.litSemicolon
        );

        let tryResult = this.parsers.parse(testParser, "name:adamC;");
        this.isTrue(tryResult[0]);
        this.areIdentical(tryResult[2].matched.length, 4);
        this.areIdentical(tryResult[2].matched[0], "name");
        this.areIdentical(tryResult[2].matched[1], ":");
        this.areIdentical(tryResult[2].matched[2], "adamC");
        let foo: TestOutput = <TestOutput>tryResult[2];
        this.areIdentical(foo.name, "adamC");
    }

    actionLitTest() {
        let altParser = pcomb.and(
            this.litName,
            this.litColon,
            pcomb.lit("adam", this.dumbGrabber)
        );

        let tryResult = this.parsers.parse(altParser, "name:adam");
        this.isTrue(tryResult[0]);
        this.areIdentical(tryResult[2].matched.length, 3);
        this.areIdentical(tryResult[2].matched[0], "name");
        this.areIdentical(tryResult[2].matched[1], ":");
................................................................................
        this.areIdentical(tryResult[2].matched[2], "adam");
        let foo: TestOutput = <TestOutput>tryResult[2];
        this.areIdentical(foo.name, "adam");
    }

    actionOptTest() {
        let wsParser = pcomb.opt(this.litSpace, this.recordWhitespace);
        let testParser = pcomb.and(
            this.litName,
            wsParser,
            this.litColon,
            wsParser,
            this.litValue
        );

        let tryResult1 = this.parsers.parse(testParser, "name : value");
        this.isTrue(tryResult1[0]);
        this.areIdentical(tryResult1[2].matched.length, 5);
        this.areIdentical(tryResult1[2].matched[0], "name");
        this.areIdentical(tryResult1[2].matched[1], " ");
................................................................................
        let tryParser = pcomb.all(pcomb.whitespace());
        let tryResult = this.parsers.parse(tryParser, "   ");
        this.isTrue(tryResult[0]);
        this.areIdentical(tryResult[1].text.length, 0);
        this.areIdentical(tryResult[2].matched.length, 1);
        this.areIdentical(tryResult[2].matched[0].length, 3);

        tryParser = pcomb.and(this.litName, pcomb.whitespace(), this.litValue);
        tryResult = this.parsers.parse(tryParser, "name value");
        this.isTrue(tryResult[0]);
        this.areIdentical(tryResult[1].text.length, 0);
        this.areIdentical(tryResult[2].matched.length, 3);
        this.areIdentical(tryResult[2].matched[0], "name");
        this.areIdentical(tryResult[2].matched[1].length, 1);
        this.areIdentical(tryResult[2].matched[2], "value");
................................................................................
        this.isTrue(tryResult[0]);
        this.areIdentical(tryResult[1].text.length, 0);
        this.areIdentical(tryResult[2].matched.length, 3);
        this.areIdentical(tryResult[2].matched[0], "name");
        this.areIdentical(tryResult[2].matched[1].length, 1);
        this.areIdentical(tryResult[2].matched[2], "value");

        tryParser = pcomb.and(this.litName, pcomb.all(pcomb.whitespace()), this.litValue);
        tryResult = this.parsers.parse(tryParser, "name\n \t\v \rvalue");
        this.isTrue(tryResult[0]);
        this.areIdentical(tryResult[1].text.length, 0);
        this.areIdentical(tryResult[2].matched.length, 3);
        this.areIdentical(tryResult[2].matched[0], "name");
        this.areIdentical(tryResult[2].matched[1].length, 6);
        this.areIdentical(tryResult[2].matched[2], "value");
    }
}








|
|
|
|

|
|
|







 







|



|













|


|







 







|




|







 







|







 







|










69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
...
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
...
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
...
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
...
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
        this.litValue = pcomb.lit("value");
        this.litValueOther = pcomb.lit("othervalue");
        this.litColon = pcomb.lit(":");
        this.litSemicolon = pcomb.lit(";");
        this.litSpace = pcomb.lit(" ");
        this.optWsParser = pcomb.opt(this.litSpace);
        this.allStoreName = pcomb.any(null, this.storeNameValue);
        this.nameValuePair = pcomb.and([this.litName, this.litColon, this.litValue]);
        this.eitherNameValuePair = pcomb.or([this.litValue, this.litValueOther]);
        this.nameValuePair2 = pcomb.and([this.litName, this.litColon, this.eitherNameValuePair]);
        this.optParser = pcomb.and([
            this.litName, this.optWsParser, this.litColon,
            this.optWsParser, this.litValue]);
        this.anyParser1 = pcomb.and([this.litName, this.litColon, this.allStoreName]);
        this.anyParser2 = pcomb.and([this.litName, this.litColon, pcomb.any(this.litSemicolon), this.litSemicolon]);
        this.numericParser = pcomb.numeric();
        this.numericSequence = pcomb.all(this.numericParser);

        // Declare functions that are used in testing in this way to prevent tsUnit from throwing errors
        // related to them.
        this.storeNameValue = function (matched: string, output: TestOutput) {
            let newOutput = output.copy();
................................................................................
        this.areIdentical(tryResult[2].matched[0], "name");
        this.areIdentical(tryResult[2].matched[1], ":");
        this.areIdentical(tryResult[2].matched[2], "adam");
        this.areIdentical(tryResult[2].matched[3], ";");
    }

    actionAnyTest() {
        let testParser = pcomb.and([
            this.litName,
            this.litColon,
            pcomb.any(this.litSemicolon, this.storeNameValue),
            this.litSemicolon]
        );

        let tryResult = this.parsers.parse(testParser, "name:adamC;");
        this.isTrue(tryResult[0]);
        this.areIdentical(tryResult[2].matched.length, 4);
        this.areIdentical(tryResult[2].matched[0], "name");
        this.areIdentical(tryResult[2].matched[1], ":");
        this.areIdentical(tryResult[2].matched[2], "adamC");
        let foo: TestOutput = <TestOutput>tryResult[2];
        this.areIdentical(foo.name, "adamC");
    }

    actionLitTest() {
        let altParser = pcomb.and([
            this.litName,
            this.litColon,
            pcomb.lit("adam", this.dumbGrabber)]
        );

        let tryResult = this.parsers.parse(altParser, "name:adam");
        this.isTrue(tryResult[0]);
        this.areIdentical(tryResult[2].matched.length, 3);
        this.areIdentical(tryResult[2].matched[0], "name");
        this.areIdentical(tryResult[2].matched[1], ":");
................................................................................
        this.areIdentical(tryResult[2].matched[2], "adam");
        let foo: TestOutput = <TestOutput>tryResult[2];
        this.areIdentical(foo.name, "adam");
    }

    actionOptTest() {
        let wsParser = pcomb.opt(this.litSpace, this.recordWhitespace);
        let testParser = pcomb.and([
            this.litName,
            wsParser,
            this.litColon,
            wsParser,
            this.litValue]
        );

        let tryResult1 = this.parsers.parse(testParser, "name : value");
        this.isTrue(tryResult1[0]);
        this.areIdentical(tryResult1[2].matched.length, 5);
        this.areIdentical(tryResult1[2].matched[0], "name");
        this.areIdentical(tryResult1[2].matched[1], " ");
................................................................................
        let tryParser = pcomb.all(pcomb.whitespace());
        let tryResult = this.parsers.parse(tryParser, "   ");
        this.isTrue(tryResult[0]);
        this.areIdentical(tryResult[1].text.length, 0);
        this.areIdentical(tryResult[2].matched.length, 1);
        this.areIdentical(tryResult[2].matched[0].length, 3);

        tryParser = pcomb.and([this.litName, pcomb.whitespace(), this.litValue]);
        tryResult = this.parsers.parse(tryParser, "name value");
        this.isTrue(tryResult[0]);
        this.areIdentical(tryResult[1].text.length, 0);
        this.areIdentical(tryResult[2].matched.length, 3);
        this.areIdentical(tryResult[2].matched[0], "name");
        this.areIdentical(tryResult[2].matched[1].length, 1);
        this.areIdentical(tryResult[2].matched[2], "value");
................................................................................
        this.isTrue(tryResult[0]);
        this.areIdentical(tryResult[1].text.length, 0);
        this.areIdentical(tryResult[2].matched.length, 3);
        this.areIdentical(tryResult[2].matched[0], "name");
        this.areIdentical(tryResult[2].matched[1].length, 1);
        this.areIdentical(tryResult[2].matched[2], "value");

        tryParser = pcomb.and([this.litName, pcomb.all(pcomb.whitespace()), this.litValue]);
        tryResult = this.parsers.parse(tryParser, "name\n \t\v \rvalue");
        this.isTrue(tryResult[0]);
        this.areIdentical(tryResult[1].text.length, 0);
        this.areIdentical(tryResult[2].matched.length, 3);
        this.areIdentical(tryResult[2].matched[0], "name");
        this.areIdentical(tryResult[2].matched[1].length, 6);
        this.areIdentical(tryResult[2].matched[2], "value");
    }
}