Skip to content

Commit b7c2a23

Browse files
author
Brian Mock
authored
Merge pull request #176 from /issues/174
Fixes #174 improves examples
2 parents 9c3325b + 6be049b commit b7c2a23

File tree

3 files changed

+58
-31
lines changed

3 files changed

+58
-31
lines changed

examples/lisp.js

+15-17
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,12 @@ let P = require('../');
77

88
///////////////////////////////////////////////////////////////////////
99

10-
// A little helper to wrap a parser with optional whitespace. Helper functions
11-
// that take a parser can be passed to the .thru(wrapper) method.
12-
function spaced(parser) {
13-
return P.optWhitespace
14-
.then(parser)
15-
.skip(P.optWhitespace);
16-
}
1710

1811
let Lisp = P.createLanguage({
12+
13+
// An expression is just any of the other values we make in the language. Note
14+
// that because we're using `.createLanguage` here we can reference other
15+
// parsers off of the argument to our function. `r` is short for `rules` here.
1916
Expression: function(r) {
2017
return P.alt(
2118
r.Symbol,
@@ -24,8 +21,8 @@ let Lisp = P.createLanguage({
2421
);
2522
},
2623

27-
// The basic parsers (usually the ones described via regexp) should have a
28-
// description for error message purposes.
24+
// The basic parsers (usually the ones described via regexp) should have a
25+
// description for error message purposes.
2926
Symbol: function() {
3027
return P.regexp(/[a-zA-Z_-][a-zA-Z0-9_-]*/)
3128
.desc('symbol');
@@ -39,18 +36,19 @@ let Lisp = P.createLanguage({
3936
.desc('number');
4037
},
4138

42-
// `.then` throws away the first value, and `.skip` throws away the second
43-
// `.value, so we're left with just the `Expression.thru(spaced).many()` part as
44-
// the `.yielded value from this parser.
39+
// `.trim(P.optWhitespace)` removes whitespace from both sides, then `.many()`
40+
// repeats the expression zero or more times. Finally, `.wrap(...)` removes
41+
// the '(' and ')' from both sides of the list.
4542
List: function(r) {
46-
return P.string('(')
47-
.then(r.Expression.thru(spaced).many())
48-
.skip(P.string(')'));
43+
return r.Expression
44+
.trim(P.optWhitespace)
45+
.many()
46+
.wrap(P.string('('), P.string(')'));
4947
},
5048

51-
// Let's remember to throw away whitesapce at the top level of the parser.
49+
// A file in Lisp is generally just zero or more expressions.
5250
File: function(r) {
53-
return r.Expression.thru(spaced).many();
51+
return r.Expression.trim(P.optWhitespace).many();
5452
}
5553
});
5654

examples/math.js

+3-8
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,7 @@ let P = require('..');
1717

1818
///////////////////////////////////////////////////////////////////////
1919

20-
// Returns a new parser that ignores whitespace before and after the parser.
21-
function spaced(parser) {
22-
return P.optWhitespace
23-
.then(parser)
24-
.skip(P.optWhitespace);
25-
}
20+
let _ = P.optWhitespace;
2621

2722
// Operators should allow whitespace around them, but not require it. This
2823
// helper combines multiple operators together with names.
@@ -33,7 +28,7 @@ function spaced(parser) {
3328
// whitespace, and gives back the word "Add" or "Sub" instead of the character.
3429
function operators(ops) {
3530
let keys = Object.keys(ops).sort();
36-
let ps = keys.map(k => P.string(ops[k]).thru(spaced).result(k));
31+
let ps = keys.map(k => P.string(ops[k]).trim(_).result(k));
3732
return P.alt.apply(null, ps);
3833
}
3934

@@ -166,7 +161,7 @@ let tableParser =
166161
// keep it in a table instead of nesting it all manually.
167162

168163
// This is our version of a math expression.
169-
let MyMath = spaced(tableParser);
164+
let MyMath = tableParser.trim(_);
170165

171166
///////////////////////////////////////////////////////////////////////
172167

examples/python-ish.js

+40-6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,29 @@ let P = require('..');
77

88
///////////////////////////////////////////////////////////////////////
99

10+
// LIMITATIONS: Python allows not only multiline blocks, but inline blocks too.
11+
//
12+
// if x == y: print("nice")
13+
//
14+
// vs.
15+
//
16+
// if x == y:
17+
// print("nice")
18+
//
19+
// This parser only supports the multiline indented form.
20+
21+
// NOTE: This is a hack and is not recommended. Maintaining state throughout
22+
// Parsimmon parsers is not reliable since backtracking may occur, leaving your
23+
// state inaccurate. See the relevant GitHub issue for discussion.
24+
//
25+
// https://github.com/jneen/parsimmon/issues/158
26+
//
27+
function indentPeek() {
28+
return indentStack[indentStack.length - 1];
29+
}
30+
31+
let indentStack = [0];
32+
1033
let Pythonish = P.createLanguage({
1134
// If this were actually Python, "Block" wouldn't be a statement on its own,
1235
// but rather "If" and "While" would be statements that used "Block" inside.
@@ -29,18 +52,29 @@ let Pythonish = P.createLanguage({
2952
// indentation in front of it.
3053
Block: r =>
3154
P.seq(
32-
P.string('block:\n'),
33-
P.regexp(/[ ]+/),
55+
P.string('block:\n').then(P.regexp(/[ ]+/)),
3456
r.Statement
3557
).chain(args => {
3658
// `.chain` is called after a parser succeeds. It returns the next parser
3759
// to use for parsing. This allows subsequent parsing to be dependent on
3860
// previous text.
3961
let [indent, statement] = args;
62+
let indentSize = indent.length;
63+
let currentSize = indentPeek();
64+
// Indentation must be deeper than the current block context. Otherwise
65+
// you could indent *less* for a block and it would still work. This is
66+
// not how any language I know of works.
67+
if (indentSize <= currentSize) {
68+
return P.fail('at least ' + currentSize + ' spaces');
69+
}
70+
indentStack.push(indentSize);
4071
return P.string(indent)
4172
.then(r.Statement)
4273
.many()
43-
.map(statements => [statement].concat(statements));
74+
.map(statements => {
75+
indentStack.pop();
76+
return [statement].concat(statements);
77+
});
4478
})
4579
.node('Block'),
4680
});
@@ -53,9 +87,9 @@ block:
5387
b()
5488
c()
5589
block:
56-
d()
57-
e()
58-
f()
90+
d()
91+
e()
92+
f()
5993
block:
6094
g()
6195
h()

0 commit comments

Comments
 (0)