Index: packages/petitparser/test/petitparser_test.dart |
diff --git a/packages/petitparser/test/petitparser_test.dart b/packages/petitparser/test/petitparser_test.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..2883014e52977a0d1e360f4ae2d55804f57df1d0 |
--- /dev/null |
+++ b/packages/petitparser/test/petitparser_test.dart |
@@ -0,0 +1,1556 @@ |
+library petitparser.test.core_test; |
+ |
+import 'dart:math' as math; |
+import 'package:test/test.dart' hide anyOf; |
+ |
+import 'package:petitparser/petitparser.dart'; |
+ |
+void expectSuccess(Parser parser, dynamic input, dynamic expected, |
+ [int position]) { |
+ var result = parser.parse(input); |
+ expect(result.isSuccess, isTrue); |
+ expect(result.isFailure, isFalse); |
+ expect(result.value, expected); |
+ expect(result.position, position != null ? position : input.length); |
+} |
+ |
+void expectFailure(Parser parser, dynamic input, |
+ [int position = 0, String message]) { |
+ var result = parser.parse(input); |
+ expect(result.isFailure, isTrue); |
+ expect(result.isSuccess, isFalse); |
+ expect(result.position, position); |
+ if (message != null) { |
+ expect(result.message, message); |
+ } |
+} |
+ |
+class ListGrammarDefinition extends GrammarDefinition { |
+ start() => ref(list).end(); |
+ list() => ref(element) & char(',') & ref(list) | ref(element); |
+ element() => digit().plus().flatten(); |
+} |
+ |
+class ListParserDefinition extends ListGrammarDefinition { |
+ element() => super.element().map((value) => int.parse(value)); |
+} |
+ |
+class TokenizedListGrammarDefinition extends GrammarDefinition { |
+ start() => ref(list).end(); |
+ list() => ref(element) & ref(token, char(',')) & ref(list) | ref(element); |
+ element() => ref(token, digit().plus()); |
+ token(p) => p.flatten().trim(); |
+} |
+ |
+class BuggedGrammarDefinition extends GrammarDefinition { |
+ start() => epsilon(); |
+ |
+ directRecursion1() => ref(directRecursion1); |
+ |
+ indirectRecursion1() => ref(indirectRecursion2); |
+ indirectRecursion2() => ref(indirectRecursion3); |
+ indirectRecursion3() => ref(indirectRecursion1); |
+ |
+ delegation1() => ref(delegation2); |
+ delegation2() => ref(delegation3); |
+ delegation3() => epsilon(); |
+} |
+ |
+class LambdaGrammarDefinition extends GrammarDefinition { |
+ start() => ref(expression).end(); |
+ expression() => ref(variable) | ref(abstraction) | ref(application); |
+ |
+ variable() => (letter() & word().star()).flatten().trim(); |
+ abstraction() => token('\\') & ref(variable) & token('.') & ref(expression); |
+ application() => token('(') & ref(expression) & ref(expression) & token(')'); |
+ |
+ token(value) => char(value).trim(); |
+} |
+ |
+class ExpressionGrammarDefinition extends GrammarDefinition { |
+ start() => ref(terms).end(); |
+ terms() => ref(addition) | ref(factors); |
+ |
+ addition() => ref(factors).separatedBy(token(char('+') | char('-'))); |
+ factors() => ref(multiplication) | ref(power); |
+ |
+ multiplication() => ref(power).separatedBy(token(char('*') | char('/'))); |
+ power() => ref(primary).separatedBy(char('^').trim()); |
+ |
+ primary() => ref(number) | ref(parentheses); |
+ number() => token(char('-').optional() & digit().plus() & (char('.') & digit().plus()).optional()); |
+ |
+ parentheses() => token('(') & ref(terms) & token(')'); |
+ token(value) => value is String ? char(value).trim() : value.flatten().trim(); |
+} |
+ |
+class PluggableCompositeParser extends CompositeParser { |
+ final Function _function; |
+ |
+ PluggableCompositeParser(this._function) : super(); |
+ |
+ void initialize() { |
+ _function(this); |
+ } |
+} |
+ |
+main() { |
+ group('parsers', () { |
+ test('and()', () { |
+ var parser = char('a').and(); |
+ expectSuccess(parser, 'a', 'a', 0); |
+ expectFailure(parser, 'b', 0, '"a" expected'); |
+ expectFailure(parser, ''); |
+ }); |
+ test('or() operator', () { |
+ var parser = char('a') | char('b'); |
+ expectSuccess(parser, 'a', 'a'); |
+ expectSuccess(parser, 'b', 'b'); |
+ expectFailure(parser, 'c'); |
+ expectFailure(parser, ''); |
+ }); |
+ test('or() of two', () { |
+ var parser = char('a').or(char('b')); |
+ expectSuccess(parser, 'a', 'a'); |
+ expectSuccess(parser, 'b', 'b'); |
+ expectFailure(parser, 'c'); |
+ expectFailure(parser, ''); |
+ }); |
+ test('or() of three', () { |
+ var parser = char('a').or(char('b')).or(char('c')); |
+ expectSuccess(parser, 'a', 'a'); |
+ expectSuccess(parser, 'b', 'b'); |
+ expectSuccess(parser, 'c', 'c'); |
+ expectFailure(parser, 'd'); |
+ expectFailure(parser, ''); |
+ }); |
+ test('end()', () { |
+ var parser = char('a').end(); |
+ expectFailure(parser, '', 0, '"a" expected'); |
+ expectSuccess(parser, 'a', 'a'); |
+ expectFailure(parser, 'aa', 1, 'end of input expected'); |
+ }); |
+ test('epsilon()', () { |
+ var parser = epsilon(); |
+ expectSuccess(parser, '', null); |
+ expectSuccess(parser, 'a', null, 0); |
+ }); |
+ test('failure()', () { |
+ var parser = failure('failure'); |
+ expectFailure(parser, '', 0, 'failure'); |
+ expectFailure(parser, 'a', 0, 'failure'); |
+ }); |
+ test('flatten()', () { |
+ var parser = digit().plus().flatten(); |
+ expectFailure(parser, ''); |
+ expectFailure(parser, 'a'); |
+ expectSuccess(parser, '1', '1'); |
+ expectSuccess(parser, '12', '12'); |
+ expectSuccess(parser, '123', '123'); |
+ expectSuccess(parser, '1234', '1234'); |
+ }); |
+ test('token() on string', () { |
+ var parser = digit().plus().token(); |
+ expectFailure(parser, ''); |
+ expectFailure(parser, 'a'); |
+ var token = parser.parse('123').value; |
+ expect(token.value, ['1', '2', '3']); |
+ expect(token.buffer, '123'); |
+ expect(token.start, 0); |
+ expect(token.stop, 3); |
+ expect(token.input, '123'); |
+ expect(token.length, 3); |
+ expect(token.line, 1); |
+ expect(token.column, 1); |
+ expect(token.toString(), 'Token[1:1]: [1, 2, 3]'); |
+ }); |
+ test('token() on list', () { |
+ var parser = any().plus().token(); |
+ var token = parser.parse([1, 2, 3]).value; |
+ expect(token.value, [1, 2, 3]); |
+ expect(token.buffer, [1, 2, 3]); |
+ expect(token.start, 0); |
+ expect(token.stop, 3); |
+ expect(token.input, [1, 2, 3]); |
+ expect(token.length, 3); |
+ expect(token.toString(), 'Token[0]: [1, 2, 3]'); |
+ }); |
+ test('map()', () { |
+ var parser = digit().map((String each) { |
+ return each.codeUnitAt(0) - '0'.codeUnitAt(0); |
+ }); |
+ expectSuccess(parser, '1', 1); |
+ expectSuccess(parser, '4', 4); |
+ expectSuccess(parser, '9', 9); |
+ expectFailure(parser, ''); |
+ expectFailure(parser, 'a'); |
+ }); |
+ test('pick(1)', () { |
+ var parser = digit().seq(letter()).pick(1); |
+ expectSuccess(parser, '1a', 'a'); |
+ expectSuccess(parser, '2b', 'b'); |
+ expectFailure(parser, ''); |
+ expectFailure(parser, '1', 1, 'letter expected'); |
+ expectFailure(parser, '12', 1, 'letter expected'); |
+ }); |
+ test('pick(-1)', () { |
+ var parser = digit().seq(letter()).pick(-1); |
+ expectSuccess(parser, '1a', 'a'); |
+ expectSuccess(parser, '2b', 'b'); |
+ expectFailure(parser, ''); |
+ expectFailure(parser, '1', 1, 'letter expected'); |
+ expectFailure(parser, '12', 1, 'letter expected'); |
+ }); |
+ test('permute([1, 0])', () { |
+ var parser = digit().seq(letter()).permute([1, 0]); |
+ expectSuccess(parser, '1a', ['a', '1']); |
+ expectSuccess(parser, '2b', ['b', '2']); |
+ expectFailure(parser, ''); |
+ expectFailure(parser, '1', 1, 'letter expected'); |
+ expectFailure(parser, '12', 1, 'letter expected'); |
+ }); |
+ test('permute([-1, 0])', () { |
+ var parser = digit().seq(letter()).permute([-1, 0]); |
+ expectSuccess(parser, '1a', ['a', '1']); |
+ expectSuccess(parser, '2b', ['b', '2']); |
+ expectFailure(parser, ''); |
+ expectFailure(parser, '1', 1, 'letter expected'); |
+ expectFailure(parser, '12', 1, 'letter expected'); |
+ }); |
+ test('not()', () { |
+ var parser = char('a').not('not "a" expected'); |
+ expectFailure(parser, 'a', 0, 'not "a" expected'); |
+ expectSuccess(parser, 'b', null, 0); |
+ expectSuccess(parser, '', null); |
+ }); |
+ test('neg()', () { |
+ var parser = digit().neg('no digit expected'); |
+ expectFailure(parser, '1', 0, 'no digit expected'); |
+ expectFailure(parser, '9', 0, 'no digit expected'); |
+ expectSuccess(parser, 'a', 'a'); |
+ expectSuccess(parser, ' ', ' '); |
+ expectFailure(parser, '', 0, 'input expected'); |
+ }); |
+ test('optional()', () { |
+ var parser = char('a').optional(); |
+ expectSuccess(parser, 'a', 'a'); |
+ expectSuccess(parser, 'b', null, 0); |
+ expectSuccess(parser, '', null); |
+ }); |
+ test('plus()', () { |
+ var parser = char('a').plus(); |
+ expectFailure(parser, '', 0, '"a" expected'); |
+ expectSuccess(parser, 'a', ['a']); |
+ expectSuccess(parser, 'aa', ['a', 'a']); |
+ expectSuccess(parser, 'aaa', ['a', 'a', 'a']); |
+ }); |
+ test('plusGreedy()', () { |
+ var parser = word().plusGreedy(digit()); |
+ expectFailure(parser, '', 0, 'letter or digit expected'); |
+ expectFailure(parser, 'a', 1, 'digit expected'); |
+ expectFailure(parser, 'ab', 1, 'digit expected'); |
+ expectFailure(parser, '1', 1, 'digit expected'); |
+ expectSuccess(parser, 'a1', ['a'], 1); |
+ expectSuccess(parser, 'ab1', ['a', 'b'], 2); |
+ expectSuccess(parser, 'abc1', ['a', 'b', 'c'], 3); |
+ expectSuccess(parser, '12', ['1'], 1); |
+ expectSuccess(parser, 'a12', ['a', '1'], 2); |
+ expectSuccess(parser, 'ab12', ['a', 'b', '1'], 3); |
+ expectSuccess(parser, 'abc12', ['a', 'b', 'c', '1'], 4); |
+ expectSuccess(parser, '123', ['1', '2'], 2); |
+ expectSuccess(parser, 'a123', ['a', '1', '2'], 3); |
+ expectSuccess(parser, 'ab123', ['a', 'b', '1', '2'], 4); |
+ expectSuccess(parser, 'abc123', ['a', 'b', 'c', '1', '2'], 5); |
+ }); |
+ test('plusLazy()', () { |
+ var parser = word().plusLazy(digit()); |
+ expectFailure(parser, ''); |
+ expectFailure(parser, 'a', 1, 'digit expected'); |
+ expectFailure(parser, 'ab', 2, 'digit expected'); |
+ expectFailure(parser, '1', 1, 'digit expected'); |
+ expectSuccess(parser, 'a1', ['a'], 1); |
+ expectSuccess(parser, 'ab1', ['a', 'b'], 2); |
+ expectSuccess(parser, 'abc1', ['a', 'b', 'c'], 3); |
+ expectSuccess(parser, '12', ['1'], 1); |
+ expectSuccess(parser, 'a12', ['a'], 1); |
+ expectSuccess(parser, 'ab12', ['a', 'b'], 2); |
+ expectSuccess(parser, 'abc12', ['a', 'b', 'c'], 3); |
+ expectSuccess(parser, '123', ['1'], 1); |
+ expectSuccess(parser, 'a123', ['a'], 1); |
+ expectSuccess(parser, 'ab123', ['a', 'b'], 2); |
+ expectSuccess(parser, 'abc123', ['a', 'b', 'c'], 3); |
+ }); |
+ test('times()', () { |
+ var parser = char('a').times(2); |
+ expectFailure(parser, '', 0, '"a" expected'); |
+ expectFailure(parser, 'a', 1, '"a" expected'); |
+ expectSuccess(parser, 'aa', ['a', 'a']); |
+ expectSuccess(parser, 'aaa', ['a', 'a'], 2); |
+ }); |
+ test('repeat()', () { |
+ var parser = char('a').repeat(2, 3); |
+ expectFailure(parser, '', 0, '"a" expected'); |
+ expectFailure(parser, 'a', 1, '"a" expected'); |
+ expectSuccess(parser, 'aa', ['a', 'a']); |
+ expectSuccess(parser, 'aaa', ['a', 'a', 'a']); |
+ expectSuccess(parser, 'aaaa', ['a', 'a', 'a'], 3); |
+ }); |
+ test('repeat() unbounded', () { |
+ var input = new List.filled(100000, 'a'); |
+ var parser = char('a').repeat(2, unbounded); |
+ expectSuccess(parser, input.join(), input); |
+ }); |
+ test('repeatGreedy()', () { |
+ var parser = word().repeatGreedy(digit(), 2, 4); |
+ expectFailure(parser, '', 0, 'letter or digit expected'); |
+ expectFailure(parser, 'a', 1, 'letter or digit expected'); |
+ expectFailure(parser, 'ab', 2, 'digit expected'); |
+ expectFailure(parser, 'abc', 2, 'digit expected'); |
+ expectFailure(parser, 'abcd', 2, 'digit expected'); |
+ expectFailure(parser, 'abcde', 2, 'digit expected'); |
+ expectFailure(parser, '1', 1, 'letter or digit expected'); |
+ expectFailure(parser, 'a1', 2, 'digit expected'); |
+ expectSuccess(parser, 'ab1', ['a', 'b'], 2); |
+ expectSuccess(parser, 'abc1', ['a', 'b', 'c'], 3); |
+ expectSuccess(parser, 'abcd1', ['a', 'b', 'c', 'd'], 4); |
+ expectFailure(parser, 'abcde1', 2, 'digit expected'); |
+ expectFailure(parser, '12', 2, 'digit expected'); |
+ expectSuccess(parser, 'a12', ['a', '1'], 2); |
+ expectSuccess(parser, 'ab12', ['a', 'b', '1'], 3); |
+ expectSuccess(parser, 'abc12', ['a', 'b', 'c', '1'], 4); |
+ expectSuccess(parser, 'abcd12', ['a', 'b', 'c', 'd'], 4); |
+ expectFailure(parser, 'abcde12', 2, 'digit expected'); |
+ expectSuccess(parser, '123', ['1', '2'], 2); |
+ expectSuccess(parser, 'a123', ['a', '1', '2'], 3); |
+ expectSuccess(parser, 'ab123', ['a', 'b', '1', '2'], 4); |
+ expectSuccess(parser, 'abc123', ['a', 'b', 'c', '1'], 4); |
+ expectSuccess(parser, 'abcd123', ['a', 'b', 'c', 'd'], 4); |
+ expectFailure(parser, 'abcde123', 2, 'digit expected'); |
+ }); |
+ test('repeatGreedy() unbounded', () { |
+ var inputLetter = new List.filled(100000, 'a'); |
+ var inputDigit = new List.filled(100000, '1'); |
+ var parser = word().repeatGreedy(digit(), 2, unbounded); |
+ expectSuccess(parser, inputLetter.join() + '1', inputLetter, inputLetter.length); |
+ expectSuccess(parser, inputDigit.join() + '1', inputDigit, inputDigit.length); |
+ }); |
+ test('repeatLazy()', () { |
+ var parser = word().repeatLazy(digit(), 2, 4); |
+ expectFailure(parser, '', 0, 'letter or digit expected'); |
+ expectFailure(parser, 'a', 1, 'letter or digit expected'); |
+ expectFailure(parser, 'ab', 2, 'digit expected'); |
+ expectFailure(parser, 'abc', 3, 'digit expected'); |
+ expectFailure(parser, 'abcd', 4, 'digit expected'); |
+ expectFailure(parser, 'abcde', 4, 'digit expected'); |
+ expectFailure(parser, '1', 1, 'letter or digit expected'); |
+ expectFailure(parser, 'a1', 2, 'digit expected'); |
+ expectSuccess(parser, 'ab1', ['a', 'b'], 2); |
+ expectSuccess(parser, 'abc1', ['a', 'b', 'c'], 3); |
+ expectSuccess(parser, 'abcd1', ['a', 'b', 'c', 'd'], 4); |
+ expectFailure(parser, 'abcde1', 4, 'digit expected'); |
+ expectFailure(parser, '12', 2, 'digit expected'); |
+ expectSuccess(parser, 'a12', ['a', '1'], 2); |
+ expectSuccess(parser, 'ab12', ['a', 'b'], 2); |
+ expectSuccess(parser, 'abc12', ['a', 'b', 'c'], 3); |
+ expectSuccess(parser, 'abcd12', ['a', 'b', 'c', 'd'], 4); |
+ expectFailure(parser, 'abcde12', 4, 'digit expected'); |
+ expectSuccess(parser, '123', ['1', '2'], 2); |
+ expectSuccess(parser, 'a123', ['a', '1'], 2); |
+ expectSuccess(parser, 'ab123', ['a', 'b'], 2); |
+ expectSuccess(parser, 'abc123', ['a', 'b', 'c'], 3); |
+ expectSuccess(parser, 'abcd123', ['a', 'b', 'c', 'd'], 4); |
+ expectFailure(parser, 'abcde123', 4, 'digit expected'); |
+ }); |
+ test('repeatLazy() unbounded', () { |
+ var input = new List.filled(100000, 'a'); |
+ var parser = word().repeatLazy(digit(), 2, unbounded); |
+ expectSuccess(parser, input.join() + '1111', input, input.length); |
+ }); |
+ test('separatedBy()', () { |
+ var parser = char('a').separatedBy(char('b')); |
+ expectFailure(parser, '', 0, '"a" expected'); |
+ expectSuccess(parser, 'a', ['a']); |
+ expectSuccess(parser, 'ab', ['a'], 1); |
+ expectSuccess(parser, 'aba', ['a', 'b', 'a']); |
+ expectSuccess(parser, 'abab', ['a', 'b', 'a'], 3); |
+ expectSuccess(parser, 'ababa', ['a', 'b', 'a', 'b', 'a']); |
+ expectSuccess(parser, 'ababab', ['a', 'b', 'a', 'b', 'a'], 5); |
+ }); |
+ test('separatedBy() without separators', () { |
+ var parser = char('a').separatedBy(char('b'), includeSeparators: false); |
+ expectFailure(parser, '', 0, '"a" expected'); |
+ expectSuccess(parser, 'a', ['a']); |
+ expectSuccess(parser, 'ab', ['a'], 1); |
+ expectSuccess(parser, 'aba', ['a', 'a']); |
+ expectSuccess(parser, 'abab', ['a', 'a'], 3); |
+ expectSuccess(parser, 'ababa', ['a', 'a', 'a']); |
+ expectSuccess(parser, 'ababab', ['a', 'a', 'a'], 5); |
+ }); |
+ test('separatedBy() separator at end', () { |
+ var parser = |
+ char('a').separatedBy(char('b'), optionalSeparatorAtEnd: true); |
+ expectFailure(parser, '', 0, '"a" expected'); |
+ expectSuccess(parser, 'a', ['a']); |
+ expectSuccess(parser, 'ab', ['a', 'b']); |
+ expectSuccess(parser, 'aba', ['a', 'b', 'a']); |
+ expectSuccess(parser, 'abab', ['a', 'b', 'a', 'b']); |
+ expectSuccess(parser, 'ababa', ['a', 'b', 'a', 'b', 'a']); |
+ expectSuccess(parser, 'ababab', ['a', 'b', 'a', 'b', 'a', 'b']); |
+ }); |
+ test('separatedBy() without separators & separator at end', () { |
+ var parser = char('a').separatedBy(char('b'), |
+ includeSeparators: false, optionalSeparatorAtEnd: true); |
+ expectFailure(parser, '', 0, '"a" expected'); |
+ expectSuccess(parser, 'a', ['a']); |
+ expectSuccess(parser, 'ab', ['a']); |
+ expectSuccess(parser, 'aba', ['a', 'a']); |
+ expectSuccess(parser, 'abab', ['a', 'a']); |
+ expectSuccess(parser, 'ababa', ['a', 'a', 'a']); |
+ expectSuccess(parser, 'ababab', ['a', 'a', 'a']); |
+ }); |
+ test('seq() operator', () { |
+ var parser = char('a') & char('b'); |
+ expectSuccess(parser, 'ab', ['a', 'b']); |
+ expectFailure(parser, ''); |
+ expectFailure(parser, 'x'); |
+ expectFailure(parser, 'a', 1); |
+ expectFailure(parser, 'ax', 1); |
+ }); |
+ test('seq() of two', () { |
+ var parser = char('a').seq(char('b')); |
+ expectSuccess(parser, 'ab', ['a', 'b']); |
+ expectFailure(parser, ''); |
+ expectFailure(parser, 'x'); |
+ expectFailure(parser, 'a', 1); |
+ expectFailure(parser, 'ax', 1); |
+ }); |
+ test('seq() of three', () { |
+ var parser = char('a').seq(char('b')).seq(char('c')); |
+ expectSuccess(parser, 'abc', ['a', 'b', 'c']); |
+ expectFailure(parser, ''); |
+ expectFailure(parser, 'x'); |
+ expectFailure(parser, 'a', 1); |
+ expectFailure(parser, 'ax', 1); |
+ expectFailure(parser, 'ab', 2); |
+ expectFailure(parser, 'abx', 2); |
+ }); |
+ test('star()', () { |
+ var parser = char('a').star(); |
+ expectSuccess(parser, '', []); |
+ expectSuccess(parser, 'a', ['a']); |
+ expectSuccess(parser, 'aa', ['a', 'a']); |
+ expectSuccess(parser, 'aaa', ['a', 'a', 'a']); |
+ }); |
+ test('starGreedy()', () { |
+ var parser = word().starGreedy(digit()); |
+ expectFailure(parser, '', 0, 'digit expected'); |
+ expectFailure(parser, 'a', 0, 'digit expected'); |
+ expectFailure(parser, 'ab', 0, 'digit expected'); |
+ expectSuccess(parser, '1', [], 0); |
+ expectSuccess(parser, 'a1', ['a'], 1); |
+ expectSuccess(parser, 'ab1', ['a', 'b'], 2); |
+ expectSuccess(parser, 'abc1', ['a', 'b', 'c'], 3); |
+ expectSuccess(parser, '12', ['1'], 1); |
+ expectSuccess(parser, 'a12', ['a', '1'], 2); |
+ expectSuccess(parser, 'ab12', ['a', 'b', '1'], 3); |
+ expectSuccess(parser, 'abc12', ['a', 'b', 'c', '1'], 4); |
+ expectSuccess(parser, '123', ['1', '2'], 2); |
+ expectSuccess(parser, 'a123', ['a', '1', '2'], 3); |
+ expectSuccess(parser, 'ab123', ['a', 'b', '1', '2'], 4); |
+ expectSuccess(parser, 'abc123', ['a', 'b', 'c', '1', '2'], 5); |
+ }); |
+ test('starLazy()', () { |
+ var parser = word().starLazy(digit()); |
+ expectFailure(parser, ''); |
+ expectFailure(parser, 'a', 1, 'digit expected'); |
+ expectFailure(parser, 'ab', 2, 'digit expected'); |
+ expectSuccess(parser, '1', [], 0); |
+ expectSuccess(parser, 'a1', ['a'], 1); |
+ expectSuccess(parser, 'ab1', ['a', 'b'], 2); |
+ expectSuccess(parser, 'abc1', ['a', 'b', 'c'], 3); |
+ expectSuccess(parser, '12', [], 0); |
+ expectSuccess(parser, 'a12', ['a'], 1); |
+ expectSuccess(parser, 'ab12', ['a', 'b'], 2); |
+ expectSuccess(parser, 'abc12', ['a', 'b', 'c'], 3); |
+ expectSuccess(parser, '123', [], 0); |
+ expectSuccess(parser, 'a123', ['a'], 1); |
+ expectSuccess(parser, 'ab123', ['a', 'b'], 2); |
+ expectSuccess(parser, 'abc123', ['a', 'b', 'c'], 3); |
+ }); |
+ test('trim()', () { |
+ var parser = char('a').trim(); |
+ expectSuccess(parser, 'a', 'a'); |
+ expectSuccess(parser, ' a', 'a'); |
+ expectSuccess(parser, 'a ', 'a'); |
+ expectSuccess(parser, ' a ', 'a'); |
+ expectSuccess(parser, ' a', 'a'); |
+ expectSuccess(parser, 'a ', 'a'); |
+ expectSuccess(parser, ' a ', 'a'); |
+ expectFailure(parser, '', 0, '"a" expected'); |
+ expectFailure(parser, 'b', 0, '"a" expected'); |
+ expectFailure(parser, ' b', 1, '"a" expected'); |
+ expectFailure(parser, ' b', 2, '"a" expected'); |
+ }); |
+ test('trim() both', () { |
+ var parser = char('a').trim(char('*')); |
+ expectSuccess(parser, 'a', 'a'); |
+ expectSuccess(parser, '*a', 'a'); |
+ expectSuccess(parser, 'a*', 'a'); |
+ expectSuccess(parser, '*a*', 'a'); |
+ expectSuccess(parser, '**a', 'a'); |
+ expectSuccess(parser, 'a**', 'a'); |
+ expectSuccess(parser, '**a**', 'a'); |
+ expectFailure(parser, '', 0, '"a" expected'); |
+ expectFailure(parser, 'b', 0, '"a" expected'); |
+ expectFailure(parser, '*b', 1, '"a" expected'); |
+ expectFailure(parser, '**b', 2, '"a" expected'); |
+ }); |
+ test('trim() left/right', () { |
+ var parser = char('a').trim(char('*'), char('#')); |
+ expectSuccess(parser, 'a', 'a'); |
+ expectSuccess(parser, '*a', 'a'); |
+ expectSuccess(parser, 'a#', 'a'); |
+ expectSuccess(parser, '*a#', 'a'); |
+ expectSuccess(parser, '**a', 'a'); |
+ expectSuccess(parser, 'a##', 'a'); |
+ expectSuccess(parser, '**a##', 'a'); |
+ expectFailure(parser, '', 0, '"a" expected'); |
+ expectFailure(parser, 'b', 0, '"a" expected'); |
+ expectFailure(parser, '*b', 1, '"a" expected'); |
+ expectFailure(parser, '**b', 2, '"a" expected'); |
+ expectFailure(parser, '#a', 0, '"a" expected'); |
+ expectSuccess(parser, 'a*', 'a', 1); |
+ }); |
+ test('undefined()', () { |
+ var parser = undefined(); |
+ expectFailure(parser, '', 0, 'undefined parser'); |
+ expectFailure(parser, 'a', 0, 'undefined parser'); |
+ parser.set(char('a')); |
+ expectSuccess(parser, 'a', 'a'); |
+ }); |
+ test('setable()', () { |
+ var parser = char('a').settable(); |
+ expectSuccess(parser, 'a', 'a'); |
+ expectFailure(parser, 'b', 0, '"a" expected'); |
+ expectFailure(parser, ''); |
+ }); |
+ }); |
+ group('characters', () { |
+ test('anyOf()', () { |
+ var parser = anyOf('uncopyrightable'); |
+ expectSuccess(parser, 'c', 'c'); |
+ expectSuccess(parser, 'g', 'g'); |
+ expectSuccess(parser, 'h', 'h'); |
+ expectSuccess(parser, 'i', 'i'); |
+ expectSuccess(parser, 'o', 'o'); |
+ expectSuccess(parser, 'p', 'p'); |
+ expectSuccess(parser, 'r', 'r'); |
+ expectSuccess(parser, 't', 't'); |
+ expectSuccess(parser, 'y', 'y'); |
+ expectFailure(parser, 'x', 0, 'any of "uncopyrightable" expected'); |
+ }); |
+ test('noneOf()', () { |
+ var parser = noneOf('uncopyrightable'); |
+ expectSuccess(parser, 'x', 'x'); |
+ expectFailure(parser, 'c', 0, 'none of "uncopyrightable" expected'); |
+ expectFailure(parser, 'g', 0, 'none of "uncopyrightable" expected'); |
+ expectFailure(parser, 'h', 0, 'none of "uncopyrightable" expected'); |
+ expectFailure(parser, 'i', 0, 'none of "uncopyrightable" expected'); |
+ expectFailure(parser, 'o', 0, 'none of "uncopyrightable" expected'); |
+ expectFailure(parser, 'p', 0, 'none of "uncopyrightable" expected'); |
+ expectFailure(parser, 'r', 0, 'none of "uncopyrightable" expected'); |
+ expectFailure(parser, 't', 0, 'none of "uncopyrightable" expected'); |
+ expectFailure(parser, 'y', 0, 'none of "uncopyrightable" expected'); |
+ }); |
+ test('char() with number', () { |
+ var parser = char(97, 'lowercase a'); |
+ expectSuccess(parser, 'a', 'a'); |
+ expectFailure(parser, 'b', 0, 'lowercase a'); |
+ expectFailure(parser, ''); |
+ }); |
+ test('char() invalid', () { |
+ expect(() => char('ab'), throwsArgumentError); |
+ }); |
+ test('digit()', () { |
+ var parser = digit(); |
+ expectSuccess(parser, '1', '1'); |
+ expectSuccess(parser, '9', '9'); |
+ expectFailure(parser, 'a', 0, 'digit expected'); |
+ expectFailure(parser, ''); |
+ }); |
+ test('letter()', () { |
+ var parser = letter(); |
+ expectSuccess(parser, 'a', 'a'); |
+ expectSuccess(parser, 'X', 'X'); |
+ expectFailure(parser, '0', 0, 'letter expected'); |
+ expectFailure(parser, ''); |
+ }); |
+ test('lowercase()', () { |
+ var parser = lowercase(); |
+ expectSuccess(parser, 'a', 'a'); |
+ expectSuccess(parser, 'z', 'z'); |
+ expectFailure(parser, 'A', 0, 'lowercase letter expected'); |
+ expectFailure(parser, '0', 0, 'lowercase letter expected'); |
+ expectFailure(parser, ''); |
+ }); |
+ test('pattern() with single', () { |
+ var parser = pattern('abc'); |
+ expectSuccess(parser, 'a', 'a'); |
+ expectSuccess(parser, 'b', 'b'); |
+ expectSuccess(parser, 'c', 'c'); |
+ expectFailure(parser, 'd', 0, '[abc] expected'); |
+ expectFailure(parser, ''); |
+ }); |
+ test('pattern() with range', () { |
+ var parser = pattern('a-c'); |
+ expectSuccess(parser, 'a', 'a'); |
+ expectSuccess(parser, 'b', 'b'); |
+ expectSuccess(parser, 'c', 'c'); |
+ expectFailure(parser, 'd', 0, '[a-c] expected'); |
+ expectFailure(parser, ''); |
+ }); |
+ test('pattern() with overlapping range', () { |
+ var parser = pattern('b-da-c'); |
+ expectSuccess(parser, 'a', 'a'); |
+ expectSuccess(parser, 'b', 'b'); |
+ expectSuccess(parser, 'c', 'c'); |
+ expectSuccess(parser, 'd', 'd'); |
+ expectFailure(parser, 'e', 0, '[b-da-c] expected'); |
+ expectFailure(parser, '', 0, '[b-da-c] expected'); |
+ }); |
+ test('pattern() with adjacent range', () { |
+ var parser = pattern('c-ea-c'); |
+ expectSuccess(parser, 'a', 'a'); |
+ expectSuccess(parser, 'b', 'b'); |
+ expectSuccess(parser, 'c', 'c'); |
+ expectSuccess(parser, 'd', 'd'); |
+ expectSuccess(parser, 'e', 'e'); |
+ expectFailure(parser, 'f', 0, '[c-ea-c] expected'); |
+ expectFailure(parser, '', 0, '[c-ea-c] expected'); |
+ }); |
+ test('pattern() with prefix range', () { |
+ var parser = pattern('a-ea-c'); |
+ expectSuccess(parser, 'a', 'a'); |
+ expectSuccess(parser, 'b', 'b'); |
+ expectSuccess(parser, 'c', 'c'); |
+ expectSuccess(parser, 'd', 'd'); |
+ expectSuccess(parser, 'e', 'e'); |
+ expectFailure(parser, 'f', 0, '[a-ea-c] expected'); |
+ expectFailure(parser, '', 0, '[a-ea-c] expected'); |
+ }); |
+ test('pattern() with postfix range', () { |
+ var parser = pattern('a-ec-e'); |
+ expectSuccess(parser, 'a', 'a'); |
+ expectSuccess(parser, 'b', 'b'); |
+ expectSuccess(parser, 'c', 'c'); |
+ expectSuccess(parser, 'd', 'd'); |
+ expectSuccess(parser, 'e', 'e'); |
+ expectFailure(parser, 'f', 0, '[a-ec-e] expected'); |
+ expectFailure(parser, '', 0, '[a-ec-e] expected'); |
+ }); |
+ test('pattern() with repeated range', () { |
+ var parser = pattern('a-ea-e'); |
+ expectSuccess(parser, 'a', 'a'); |
+ expectSuccess(parser, 'b', 'b'); |
+ expectSuccess(parser, 'c', 'c'); |
+ expectSuccess(parser, 'd', 'd'); |
+ expectSuccess(parser, 'e', 'e'); |
+ expectFailure(parser, 'f', 0, '[a-ea-e] expected'); |
+ expectFailure(parser, '', 0, '[a-ea-e] expected'); |
+ }); |
+ test('pattern() with composed range', () { |
+ var parser = pattern('ac-df-'); |
+ expectSuccess(parser, 'a', 'a'); |
+ expectSuccess(parser, 'c', 'c'); |
+ expectSuccess(parser, 'd', 'd'); |
+ expectSuccess(parser, 'f', 'f'); |
+ expectSuccess(parser, '-', '-'); |
+ expectFailure(parser, 'b', 0, '[ac-df-] expected'); |
+ expectFailure(parser, 'e', 0, '[ac-df-] expected'); |
+ expectFailure(parser, 'g', 0, '[ac-df-] expected'); |
+ expectFailure(parser, ''); |
+ }); |
+ test('pattern() with negated single', () { |
+ var parser = pattern('^a'); |
+ expectSuccess(parser, 'b', 'b'); |
+ expectFailure(parser, 'a', 0, '[^a] expected'); |
+ expectFailure(parser, ''); |
+ }); |
+ test('pattern() with negated range', () { |
+ var parser = pattern('^a-c'); |
+ expectSuccess(parser, 'd', 'd'); |
+ expectFailure(parser, 'a', 0, '[^a-c] expected'); |
+ expectFailure(parser, 'b', 0, '[^a-c] expected'); |
+ expectFailure(parser, 'c', 0, '[^a-c] expected'); |
+ expectFailure(parser, ''); |
+ }); |
+ test('range()', () { |
+ var parser = range('e', 'o'); |
+ expectSuccess(parser, 'e', 'e'); |
+ expectSuccess(parser, 'i', 'i'); |
+ expectSuccess(parser, 'o', 'o'); |
+ expectFailure(parser, 'p', 0, 'e..o expected'); |
+ expectFailure(parser, 'd', 0, 'e..o expected'); |
+ expectFailure(parser, ''); |
+ }); |
+ test('uppercase()', () { |
+ var parser = uppercase(); |
+ expectSuccess(parser, 'A', 'A'); |
+ expectSuccess(parser, 'Z', 'Z'); |
+ expectFailure(parser, 'a', 0, 'uppercase letter expected'); |
+ expectFailure(parser, '0', 0, 'uppercase letter expected'); |
+ expectFailure(parser, ''); |
+ }); |
+ test('whitespace()', () { |
+ var parser = whitespace(); |
+ expectSuccess(parser, ' ', ' '); |
+ expectSuccess(parser, '\t', '\t'); |
+ expectSuccess(parser, '\r', '\r'); |
+ expectSuccess(parser, '\f', '\f'); |
+ expectFailure(parser, 'z', 0, 'whitespace expected'); |
+ expectFailure(parser, ''); |
+ }); |
+ test('whitespace() unicode', () { |
+ var string = new String.fromCharCodes([ |
+ 0x09, |
+ 0x0A, |
+ 0x0B, |
+ 0x0C, |
+ 0x0D, |
+ 0x20, |
+ 0x85, |
+ 0xA0, |
+ 0x1680, |
+ 0x180E, |
+ 0x2000, |
+ 0x2001, |
+ 0x2002, |
+ 0x2003, |
+ 0x2004, |
+ 0x2005, |
+ 0x2006, |
+ 0x2007, |
+ 0x2008, |
+ 0x2009, |
+ 0x200A, |
+ 0x2028, |
+ 0x2029, |
+ 0x202F, |
+ 0x205F, |
+ 0x3000, |
+ 0xFEFF |
+ ]); |
+ var parser = whitespace().star().flatten().end(); |
+ expectSuccess(parser, string, string); |
+ }); |
+ test('word()', () { |
+ var parser = word(); |
+ expectSuccess(parser, 'a', 'a'); |
+ expectSuccess(parser, 'z', 'z'); |
+ expectSuccess(parser, 'A', 'A'); |
+ expectSuccess(parser, 'Z', 'Z'); |
+ expectSuccess(parser, '0', '0'); |
+ expectSuccess(parser, '9', '9'); |
+ expectSuccess(parser, '_', '_'); |
+ expectFailure(parser, '-', 0, 'letter or digit expected'); |
+ expectFailure(parser, ''); |
+ }); |
+ }); |
+ group('predicates', () { |
+ test('any()', () { |
+ var parser = any(); |
+ expectSuccess(parser, 'a', 'a'); |
+ expectSuccess(parser, 'b', 'b'); |
+ expectFailure(parser, '', 0, 'input expected'); |
+ }); |
+ test('anyIn()', () { |
+ var parser = anyIn(['a', 'b']); |
+ expectSuccess(parser, 'a', 'a'); |
+ expectSuccess(parser, 'b', 'b'); |
+ expectFailure(parser, 'c'); |
+ expectFailure(parser, ''); |
+ }); |
+ test('string()', () { |
+ var parser = string('foo'); |
+ expectSuccess(parser, 'foo', 'foo'); |
+ expectFailure(parser, ''); |
+ expectFailure(parser, 'f'); |
+ expectFailure(parser, 'fo'); |
+ expectFailure(parser, 'Foo'); |
+ }); |
+ test('stringIgnoreCase()', () { |
+ var parser = stringIgnoreCase('foo'); |
+ expectSuccess(parser, 'foo', 'foo'); |
+ expectSuccess(parser, 'FOO', 'FOO'); |
+ expectSuccess(parser, 'fOo', 'fOo'); |
+ expectFailure(parser, ''); |
+ expectFailure(parser, 'f'); |
+ expectFailure(parser, 'Fo'); |
+ }); |
+ }); |
+ group('token', () { |
+ var parser = any().map((value) => value.codeUnitAt(0)).token().star(); |
+ var buffer = '1\r12\r\n123\n1234'; |
+ var result = parser.parse(buffer).value; |
+ test('value', () { |
+ var expected = [49, 13, 49, 50, 13, 10, 49, 50, 51, 10, 49, 50, 51, 52]; |
+ expect(result.map((token) => token.value), expected); |
+ }); |
+ test('buffer', () { |
+ var expected = new List.filled(buffer.length, buffer); |
+ expect(result.map((token) => token.buffer), expected); |
+ }); |
+ test('start', () { |
+ var expected = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]; |
+ expect(result.map((token) => token.start), expected); |
+ }); |
+ test('stop', () { |
+ var expected = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]; |
+ expect(result.map((token) => token.stop), expected); |
+ }); |
+ test('length', () { |
+ var expected = new List.filled(buffer.length, 1); |
+ expect(result.map((token) => token.length), expected); |
+ }); |
+ test('line', () { |
+ var expected = [1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4]; |
+ expect(result.map((token) => token.line), expected); |
+ }); |
+ test('column', () { |
+ var expected = [1, 2, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4]; |
+ expect(result.map((token) => token.column), expected); |
+ }); |
+ test('input', () { |
+ var expected = ['1', '\r', '1', '2', '\r', '\n', '1', '2', '3', '\n', '1', '2', '3', '4']; |
+ expect(result.map((token) => token.input), expected); |
+ }); |
+ test('unique', () { |
+ expect(new Set.from(result).length, result.length); |
+ }); |
+ test('equals', () { |
+ for (var i = 0; i < result.length; i++) { |
+ for (var j = 0; j < result.length; j++) { |
+ var condition = i == j ? isTrue : isFalse; |
+ expect(result[i] == result[j], condition); |
+ expect(result[i].hashCode == result[j].hashCode, condition); |
+ } |
+ } |
+ }); |
+ }); |
+ group('context', () { |
+ var buffer = 'a\nc'; |
+ var context = new Context(buffer, 0); |
+ test('context', () { |
+ expect(context.buffer, buffer); |
+ expect(context.position, 0); |
+ expect(context.toString(), 'Context[1:1]'); |
+ }); |
+ test('success', () { |
+ var success = context.success('result'); |
+ expect(success.buffer, buffer); |
+ expect(success.position, 0); |
+ expect(success.value, 'result'); |
+ expect(success.message, isNull); |
+ expect(success.isSuccess, isTrue); |
+ expect(success.isFailure, isFalse); |
+ expect(success.toString(), 'Success[1:1]: result'); |
+ }); |
+ test('success with position', () { |
+ var success = context.success('result', 2); |
+ expect(success.buffer, buffer); |
+ expect(success.position, 2); |
+ expect(success.value, 'result'); |
+ expect(success.message, isNull); |
+ expect(success.isSuccess, isTrue); |
+ expect(success.isFailure, isFalse); |
+ expect(success.toString(), 'Success[2:1]: result'); |
+ }); |
+ test('failure', () { |
+ var failure = context.failure('error'); |
+ expect(failure.buffer, buffer); |
+ expect(failure.position, 0); |
+ try { |
+ failure.value; |
+ fail('Expected ParserError to be thrown'); |
+ } on ParserError catch (error) { |
+ expect(error.failure, same(failure)); |
+ expect(error.toString(), 'error at 1:1'); |
+ } |
+ expect(failure.message, 'error'); |
+ expect(failure.isSuccess, isFalse); |
+ expect(failure.isFailure, isTrue); |
+ expect(failure.toString(), 'Failure[1:1]: error'); |
+ }); |
+ test('failure with position', () { |
+ var failure = context.failure('error', 2); |
+ expect(failure.buffer, buffer); |
+ expect(failure.position, 2); |
+ try { |
+ failure.value; |
+ fail('Expected ParserError to be thrown'); |
+ } on ParserError catch (error) { |
+ expect(error.failure, same(failure)); |
+ expect(error.toString(), 'error at 2:1'); |
+ } |
+ expect(failure.message, 'error'); |
+ expect(failure.isSuccess, isFalse); |
+ expect(failure.isFailure, isTrue); |
+ expect(failure.toString(), 'Failure[2:1]: error'); |
+ }); |
+ }); |
+ group('parsing', () { |
+ test('parse()', () { |
+ var parser = char('a'); |
+ expect(parser.parse('a').isSuccess, isTrue); |
+ expect(parser.parse('b').isSuccess, isFalse); |
+ }); |
+ test('accept()', () { |
+ var parser = char('a'); |
+ expect(parser.accept('a'), isTrue); |
+ expect(parser.accept('b'), isFalse); |
+ }); |
+ test('matches()', () { |
+ var parser = digit().seq(digit()).flatten(); |
+ expect(parser.matches('a123b45'), ['12', '23', '45']); |
+ }); |
+ test('matchesSkipping()', () { |
+ var parser = digit().seq(digit()).flatten(); |
+ expect(parser.matchesSkipping('a123b45'), ['12', '45']); |
+ }); |
+ }); |
+ group('examples', () { |
+ final identifier = letter().seq(word().star()).flatten(); |
+ final number = char('-') |
+ .optional() |
+ .seq(digit().plus()) |
+ .seq(char('.').seq(digit().plus()).optional()) |
+ .flatten(); |
+ final quoted = char('"') |
+ .seq(char('"').neg().star()) |
+ .seq(char('"')) |
+ .flatten(); |
+ final keyword = string('return') |
+ .seq(whitespace().plus().flatten()) |
+ .seq(identifier.or(number).or(quoted)) |
+ .map((list) => list.last); |
+ final javadoc = string('/**') |
+ .seq(string('*/').neg().star()) |
+ .seq(string('*/')) |
+ .flatten(); |
+ test('valid identifier', () { |
+ expectSuccess(identifier, 'a', 'a'); |
+ expectSuccess(identifier, 'a1', 'a1'); |
+ expectSuccess(identifier, 'a12', 'a12'); |
+ expectSuccess(identifier, 'ab', 'ab'); |
+ expectSuccess(identifier, 'a1b', 'a1b'); |
+ }); |
+ test('incomplete identifier', () { |
+ expectSuccess(identifier, 'a=', 'a', 1); |
+ expectSuccess(identifier, 'a1-', 'a1', 2); |
+ expectSuccess(identifier, 'a12+', 'a12', 3); |
+ expectSuccess(identifier, 'ab ', 'ab', 2); |
+ }); |
+ test('invalid identifier', () { |
+ expectFailure(identifier, '', 0, 'letter expected'); |
+ expectFailure(identifier, '1', 0, 'letter expected'); |
+ expectFailure(identifier, '1a', 0, 'letter expected'); |
+ }); |
+ test('positive number', () { |
+ expectSuccess(number, '1', '1'); |
+ expectSuccess(number, '12', '12'); |
+ expectSuccess(number, '12.3', '12.3'); |
+ expectSuccess(number, '12.34', '12.34'); |
+ }); |
+ test('negative number', () { |
+ expectSuccess(number, '-1', '-1'); |
+ expectSuccess(number, '-12', '-12'); |
+ expectSuccess(number, '-12.3', '-12.3'); |
+ expectSuccess(number, '-12.34', '-12.34'); |
+ }); |
+ test('incomplete number', () { |
+ expectSuccess(number, '1..', '1', 1); |
+ expectSuccess(number, '12-', '12', 2); |
+ expectSuccess(number, '12.3.', '12.3', 4); |
+ expectSuccess(number, '12.34.', '12.34', 5); |
+ }); |
+ test('invalid number', () { |
+ expectFailure(number, '', 0, 'digit expected'); |
+ expectFailure(number, '-', 1, 'digit expected'); |
+ expectFailure(number, '-x', 1, 'digit expected'); |
+ expectFailure(number, '.', 0, 'digit expected'); |
+ expectFailure(number, '.1', 0, 'digit expected'); |
+ }); |
+ test('valid string', () { |
+ expectSuccess(quoted, '""', '""'); |
+ expectSuccess(quoted, '"a"', '"a"'); |
+ expectSuccess(quoted, '"ab"', '"ab"'); |
+ expectSuccess(quoted, '"abc"', '"abc"'); |
+ }); |
+ test('incomplete string', () { |
+ expectSuccess(quoted, '""x', '""', 2); |
+ expectSuccess(quoted, '"a"x', '"a"', 3); |
+ expectSuccess(quoted, '"ab"x', '"ab"', 4); |
+ expectSuccess(quoted, '"abc"x', '"abc"', 5); |
+ }); |
+ test('invalid string', () { |
+ expectFailure(quoted, '"', 1, '""" expected'); |
+ expectFailure(quoted, '"a', 2, '""" expected'); |
+ expectFailure(quoted, '"ab', 3, '""" expected'); |
+ expectFailure(quoted, 'a"', 0, '""" expected'); |
+ expectFailure(quoted, 'ab"', 0, '""" expected'); |
+ }); |
+ test('return statement', () { |
+ expectSuccess(keyword, 'return f', 'f'); |
+ expectSuccess(keyword, 'return f', 'f'); |
+ expectSuccess(keyword, 'return foo', 'foo'); |
+ expectSuccess(keyword, 'return foo', 'foo'); |
+ expectSuccess(keyword, 'return 1', '1'); |
+ expectSuccess(keyword, 'return 1', '1'); |
+ expectSuccess(keyword, 'return -2.3', '-2.3'); |
+ expectSuccess(keyword, 'return -2.3', '-2.3'); |
+ expectSuccess(keyword, 'return "a"', '"a"'); |
+ expectSuccess(keyword, 'return "a"', '"a"'); |
+ }); |
+ test('invalid statement', () { |
+ expectFailure(keyword, 'retur f', 0, 'return expected'); |
+ expectFailure(keyword, 'return1', 6, 'whitespace expected'); |
+ expectFailure(keyword, 'return _', 8, '""" expected'); |
+ }); |
+ test('javadoc', () { |
+ expectSuccess(javadoc, '/** foo */', '/** foo */'); |
+ expectSuccess(javadoc, '/** * * */', '/** * * */'); |
+ }); |
+ }); |
+ group('copying, matching, replacing', () { |
+ void verify(Parser parser) { |
+ var copy = parser.copy(); |
+ // check copying |
+ expect(copy, isNot(same(parser))); |
+ expect(copy.toString(), parser.toString()); |
+ expect(copy.runtimeType, parser.runtimeType); |
+ expect(copy.children, |
+ pairwiseCompare(parser.children, identical, 'same children')); |
+ // check equality |
+ expect(copy.isEqualTo(copy), isTrue); |
+ expect(parser.isEqualTo(parser), isTrue); |
+ expect(copy.isEqualTo(parser), isTrue); |
+ expect(parser.isEqualTo(copy), isTrue); |
+ // check replacing |
+ var replaced = new List(); |
+ for (var i = 0; i < copy.children.length; i++) { |
+ var source = copy.children[i], |
+ target = any(); |
+ copy.replace(source, target); |
+ expect(copy.children[i], same(target)); |
+ replaced.add(target); |
+ } |
+ expect(copy.children, |
+ pairwiseCompare(replaced, identical, 'replaced children')); |
+ } |
+ test('any()', () => verify(any())); |
+ test('and()', () => verify(digit().and())); |
+ test('char()', () => verify(char('a'))); |
+ test('digit()', () => verify(digit())); |
+ test('delegate()', () => verify(new DelegateParser(any()))); |
+ test('end()', () => verify(digit().end())); |
+ test('epsilon()', () => verify(epsilon())); |
+ test('failure()', () => verify(failure())); |
+ test('flatten()', () => verify(digit().flatten())); |
+ test('map()', () => verify(digit().map((a) => a))); |
+ test('not()', () => verify(digit().not())); |
+ test('optional()', () => verify(digit().optional())); |
+ test('or()', () => verify(digit().or(word()))); |
+ test('plus()', () => verify(digit().plus())); |
+ test('plusGreedy()', () => verify(digit().plusGreedy(word()))); |
+ test('plusLazy()', () => verify(digit().plusLazy(word()))); |
+ test('repeat()', () => verify(digit().repeat(2, 3))); |
+ test('repeatGreedy()', () => verify(digit().repeatGreedy(word(), 2, 3))); |
+ test('repeatLazy()', () => verify(digit().repeatLazy(word(), 2, 3))); |
+ test('seq()', () => verify(digit().seq(word()))); |
+ test('setable()', () => verify(digit().settable())); |
+ test('star()', () => verify(digit().star())); |
+ test('starGreedy()', () => verify(digit().starGreedy(word()))); |
+ test('starLazy()', () => verify(digit().starLazy(word()))); |
+ test('string()', () => verify(string('ab'))); |
+ test('times()', () => verify(digit().times(2))); |
+ test('token()', () => verify(digit().token())); |
+ test('trim()', () => verify(digit().trim(char('a'), char('b')))); |
+ test('undefined()', () => verify(undefined())); |
+ }); |
+ group('regressions', () { |
+ test('flatten().trim()', () { |
+ var parser = word().plus().flatten().trim(); |
+ expectSuccess(parser, 'ab1', 'ab1'); |
+ expectSuccess(parser, ' ab1 ', 'ab1'); |
+ expectSuccess(parser, ' ab1 ', 'ab1'); |
+ }); |
+ test('trim().flatten()', () { |
+ var parser = word().plus().trim().flatten(); |
+ expectSuccess(parser, 'ab1', 'ab1'); |
+ expectSuccess(parser, ' ab1 ', ' ab1 '); |
+ expectSuccess(parser, ' ab1 ', ' ab1 '); |
+ }); |
+ }); |
+ group('definition', () { |
+ var grammarDefinition = new ListGrammarDefinition(); |
+ var parserDefinition = new ListParserDefinition(); |
+ var tokenDefinition = new TokenizedListGrammarDefinition(); |
+ var buggedDefinition = new BuggedGrammarDefinition(); |
+ |
+ test('reference without parameters', () { |
+ var firstReference = grammarDefinition.ref(grammarDefinition.start); |
+ var secondReference = grammarDefinition.ref(grammarDefinition.start); |
+ expect(firstReference, isNot(same(secondReference))); |
+ expect(firstReference == secondReference, isTrue); |
+ }); |
+ test('reference with different production', () { |
+ var firstReference = grammarDefinition.ref(grammarDefinition.start); |
+ var secondReference = grammarDefinition.ref(grammarDefinition.element); |
+ expect(firstReference, isNot(same(secondReference))); |
+ expect(firstReference == secondReference, isFalse); |
+ }); |
+ test('reference with same parameters', () { |
+ var firstReference = grammarDefinition.ref(grammarDefinition.start, 'a'); |
+ var secondReference = grammarDefinition.ref(grammarDefinition.start, 'a'); |
+ expect(firstReference, isNot(same(secondReference))); |
+ expect(firstReference == secondReference, isTrue); |
+ }); |
+ test('reference with different parameters', () { |
+ var firstReference = grammarDefinition.ref(grammarDefinition.start, 'a'); |
+ var secondReference = grammarDefinition.ref(grammarDefinition.start, 'b'); |
+ expect(firstReference, isNot(same(secondReference))); |
+ expect(firstReference == secondReference, isFalse); |
+ }); |
+ test('grammar', () { |
+ var parser = grammarDefinition.build(); |
+ expectSuccess(parser, '1,2', ['1', ',', '2']); |
+ expectSuccess(parser, '1,2,3', ['1', ',', ['2', ',', '3']]); |
+ }); |
+ test('parser', () { |
+ var parser = parserDefinition.build(); |
+ expectSuccess(parser, '1,2', [1, ',', 2]); |
+ expectSuccess(parser, '1,2,3', [1, ',', [2, ',', 3]]); |
+ }); |
+ test('token', () { |
+ var parser = tokenDefinition.build(); |
+ expectSuccess(parser, '1, 2', ['1', ',', '2']); |
+ expectSuccess(parser, '1, 2, 3', ['1', ',', ['2', ',', '3']]); |
+ }); |
+ test('direct recursion', () { |
+ expect(() => |
+ buggedDefinition.build(start: buggedDefinition.directRecursion1), |
+ throwsStateError); |
+ }); |
+ test('indirect recursion', () { |
+ expect(() => buggedDefinition.build( |
+ start: buggedDefinition.indirectRecursion1), throwsStateError); |
+ expect(() => buggedDefinition.build( |
+ start: buggedDefinition.indirectRecursion2), throwsStateError); |
+ expect(() => buggedDefinition.build( |
+ start: buggedDefinition.indirectRecursion3), throwsStateError); |
+ }); |
+ test('delegation', () { |
+ expect(buggedDefinition.build( |
+ start: buggedDefinition.delegation1) is EpsilonParser, isTrue); |
+ expect(buggedDefinition.build( |
+ start: buggedDefinition.delegation2) is EpsilonParser, isTrue); |
+ expect(buggedDefinition.build( |
+ start: buggedDefinition.delegation3) is EpsilonParser, isTrue); |
+ }); |
+ test('lambda example', () { |
+ var definition = new LambdaGrammarDefinition(); |
+ var parser = definition.build(); |
+ expect(parser.accept('x'), isTrue); |
+ expect(parser.accept('xy'), isTrue); |
+ expect(parser.accept('x12'), isTrue); |
+ expect(parser.accept('\\x.y'), isTrue); |
+ expect(parser.accept('\\x.\\y.z'), isTrue); |
+ expect(parser.accept('(x x)'), isTrue); |
+ expect(parser.accept('(x y)'), isTrue); |
+ expect(parser.accept('(x (y z))'), isTrue); |
+ expect(parser.accept('((x y) z)'), isTrue); |
+ }); |
+ test('expression example', () { |
+ var definition = new ExpressionGrammarDefinition(); |
+ var parser = definition.build(); |
+ expect(parser.accept('1'), isTrue); |
+ expect(parser.accept('12'), isTrue); |
+ expect(parser.accept('1.23'), isTrue); |
+ expect(parser.accept('-12.3'), isTrue); |
+ expect(parser.accept('1 + 2'), isTrue); |
+ expect(parser.accept('1 + 2 + 3'), isTrue); |
+ expect(parser.accept('1 - 2'), isTrue); |
+ expect(parser.accept('1 - 2 - 3'), isTrue); |
+ expect(parser.accept('1 * 2'), isTrue); |
+ expect(parser.accept('1 * 2 * 3'), isTrue); |
+ expect(parser.accept('1 / 2'), isTrue); |
+ expect(parser.accept('1 / 2 / 3'), isTrue); |
+ expect(parser.accept('1 ^ 2'), isTrue); |
+ expect(parser.accept('1 ^ 2 ^ 3'), isTrue); |
+ expect(parser.accept('1 + (2 * 3)'), isTrue); |
+ expect(parser.accept('(1 + 2) * 3'), isTrue); |
+ }); |
+ }); |
+ group('expression', () { |
+ Parser build({attachAction: true}) { |
+ var action = attachAction ? (func) => func : (func) => null; |
+ var root = failure().settable(); |
+ var builder = new ExpressionBuilder(); |
+ builder.group() |
+ ..primitive(char('(').trim().seq(root).seq(char(')').trim()).pick(1)) |
+ ..primitive(digit().plus().seq(char('.').seq(digit().plus()).optional()) |
+ .flatten().trim().map((a) => double.parse(a))); |
+ builder.group() |
+ ..prefix(char('-').trim(), action((op, a) => -a)); |
+ builder.group() |
+ ..postfix(string('++').trim(), action((a, op) => ++a)) |
+ ..postfix(string('--').trim(), action((a, op) => --a)); |
+ builder.group() |
+ ..right(char('^').trim(), action((a, op, b) => math.pow(a, b))); |
+ builder.group() |
+ ..left(char('*').trim(), action((a, op, b) => a * b)) |
+ ..left(char('/').trim(), action((a, op, b) => a / b)); |
+ builder.group() |
+ ..left(char('+').trim(), action((a, op, b) => a + b)) |
+ ..left(char('-').trim(), action((a, op, b) => a - b)); |
+ root.set(builder.build()); |
+ return root.end(); |
+ } |
+ var epsilon = 1e-5; |
+ var evaluator = build(attachAction: true); |
+ var parser = build(attachAction: false); |
+ test('number', () { |
+ expect(evaluator.parse('0').value, closeTo(0, epsilon)); |
+ expect(evaluator.parse('0.0').value, closeTo(0, epsilon)); |
+ expect(evaluator.parse('1').value, closeTo(1, epsilon)); |
+ expect(evaluator.parse('1.2').value, closeTo(1.2, epsilon)); |
+ expect(evaluator.parse('34').value, closeTo(34, epsilon)); |
+ expect(evaluator.parse('34.7').value, closeTo(34.7, epsilon)); |
+ expect(evaluator.parse('56.78').value, closeTo(56.78, epsilon)); |
+ }); |
+ test('number negative', () { |
+ expect(evaluator.parse('-1').value, closeTo(-1, epsilon)); |
+ expect(evaluator.parse('-1.2').value, closeTo(-1.2, epsilon)); |
+ }); |
+ test('number parse', () { |
+ expect(parser.parse('0').value, 0); |
+ expect(parser.parse('-1').value, ['-', 1]); |
+ }); |
+ test('add', () { |
+ expect(evaluator.parse('1 + 2').value, closeTo(3, epsilon)); |
+ expect(evaluator.parse('2 + 1').value, closeTo(3, epsilon)); |
+ expect(evaluator.parse('1 + 2.3').value, closeTo(3.3, epsilon)); |
+ expect(evaluator.parse('2.3 + 1').value, closeTo(3.3, epsilon)); |
+ expect(evaluator.parse('1 + -2').value, closeTo(-1, epsilon)); |
+ expect(evaluator.parse('-2 + 1').value, closeTo(-1, epsilon)); |
+ }); |
+ test('add many', () { |
+ expect(evaluator.parse('1').value, closeTo(1, epsilon)); |
+ expect(evaluator.parse('1 + 2').value, closeTo(3, epsilon)); |
+ expect(evaluator.parse('1 + 2 + 3').value, closeTo(6, epsilon)); |
+ expect(evaluator.parse('1 + 2 + 3 + 4').value, closeTo(10, epsilon)); |
+ expect(evaluator.parse('1 + 2 + 3 + 4 + 5').value, closeTo(15, epsilon)); |
+ }); |
+ test('add parse', () { |
+ expect(parser.parse('1 + 2 + 3').value, [[1, '+', 2], '+', 3]); |
+ }); |
+ test('sub', () { |
+ expect(evaluator.parse('1 - 2').value, closeTo(-1, epsilon)); |
+ expect(evaluator.parse('1.2 - 1.2').value, closeTo(0, epsilon)); |
+ expect(evaluator.parse('1 - -2').value, closeTo(3, epsilon)); |
+ expect(evaluator.parse('-1 - -2').value, closeTo(1, epsilon)); |
+ }); |
+ test('sub many', () { |
+ expect(evaluator.parse('1').value, closeTo(1, epsilon)); |
+ expect(evaluator.parse('1 - 2').value, closeTo(-1, epsilon)); |
+ expect(evaluator.parse('1 - 2 - 3').value, closeTo(-4, epsilon)); |
+ expect(evaluator.parse('1 - 2 - 3 - 4').value, closeTo(-8, epsilon)); |
+ expect(evaluator.parse('1 - 2 - 3 - 4 - 5').value, closeTo(-13, epsilon)); |
+ }); |
+ test('sub parse', () { |
+ expect(parser.parse('1 - 2 - 3').value, [[1, '-', 2], '-', 3]); |
+ }); |
+ test('mul', () { |
+ expect(evaluator.parse('2 * 3').value, closeTo(6, epsilon)); |
+ expect(evaluator.parse('2 * -4').value, closeTo(-8, epsilon)); |
+ }); |
+ test('mul many', () { |
+ expect(evaluator.parse('1 * 2').value, closeTo(2, epsilon)); |
+ expect(evaluator.parse('1 * 2 * 3').value, closeTo(6, epsilon)); |
+ expect(evaluator.parse('1 * 2 * 3 * 4').value, closeTo(24, epsilon)); |
+ expect(evaluator.parse('1 * 2 * 3 * 4 * 5').value, closeTo(120, epsilon)); |
+ }); |
+ test('mul parse', () { |
+ expect(parser.parse('1 * 2 * 3').value, [[1, '*', 2], '*', 3]); |
+ }); |
+ test('div', () { |
+ expect(evaluator.parse('12 / 3').value, closeTo(4, epsilon)); |
+ expect(evaluator.parse('-16 / -4').value, closeTo(4, epsilon)); |
+ }); |
+ test('div many', () { |
+ expect(evaluator.parse('100 / 2').value, closeTo(50, epsilon)); |
+ expect(evaluator.parse('100 / 2 / 2').value, closeTo(25, epsilon)); |
+ expect(evaluator.parse('100 / 2 / 2 / 5').value, closeTo(5, epsilon)); |
+ expect(evaluator.parse('100 / 2 / 2 / 5 / 5').value, closeTo(1, epsilon)); |
+ }); |
+ test('mul parse', () { |
+ expect(parser.parse('1 / 2 / 3').value, [[1, '/', 2], '/', 3]); |
+ }); |
+ test('pow', () { |
+ expect(evaluator.parse('2 ^ 3').value, closeTo(8, epsilon)); |
+ expect(evaluator.parse('-2 ^ 3').value, closeTo(-8, epsilon)); |
+ expect(evaluator.parse('-2 ^ -3').value, closeTo(-0.125, epsilon)); |
+ }); |
+ test('pow many', () { |
+ expect(evaluator.parse('4 ^ 3').value, closeTo(64, epsilon)); |
+ expect(evaluator.parse('4 ^ 3 ^ 2').value, closeTo(262144, epsilon)); |
+ expect(evaluator.parse('4 ^ 3 ^ 2 ^ 1').value, closeTo(262144, epsilon)); |
+ expect(evaluator.parse('4 ^ 3 ^ 2 ^ 1 ^ 0').value, closeTo(262144, epsilon)); |
+ }); |
+ test('pow parse', () { |
+ expect(parser.parse('1 ^ 2 ^ 3').value, [1, '^', [2, '^', 3]]); |
+ }); |
+ test('parens', () { |
+ expect(evaluator.parse('(1)').value, closeTo(1, epsilon)); |
+ expect(evaluator.parse('(1 + 2)').value, closeTo(3, epsilon)); |
+ expect(evaluator.parse('((1))').value, closeTo(1, epsilon)); |
+ expect(evaluator.parse('((1 + 2))').value, closeTo(3, epsilon)); |
+ expect(evaluator.parse('2 * (3 + 4)').value, closeTo(14, epsilon)); |
+ expect(evaluator.parse('(2 + 3) * 4').value, closeTo(20, epsilon)); |
+ expect(evaluator.parse('6 / (2 + 4)').value, closeTo(1, epsilon)); |
+ expect(evaluator.parse('(2 + 6) / 2').value, closeTo(4, epsilon)); |
+ }); |
+ test('priority', () { |
+ expect(evaluator.parse('2 * 3 + 4').value, closeTo(10, epsilon)); |
+ expect(evaluator.parse('2 + 3 * 4').value, closeTo(14, epsilon)); |
+ expect(evaluator.parse('6 / 3 + 4').value, closeTo(6, epsilon)); |
+ expect(evaluator.parse('2 + 6 / 2').value, closeTo(5, epsilon)); |
+ }); |
+ test('priority parse', () { |
+ expect(parser.parse('2 * 3 + 4').value, [[2.0, '*', 3.0], '+', 4.0]); |
+ expect(parser.parse('2 + 3 * 4').value, [2.0, '+', [3.0, '*', 4.0]]); |
+ }); |
+ test('postfix add', () { |
+ expect(evaluator.parse('0++').value, closeTo(1, epsilon)); |
+ expect(evaluator.parse('0++++').value, closeTo(2, epsilon)); |
+ expect(evaluator.parse('0++++++').value, closeTo(3, epsilon)); |
+ expect(evaluator.parse('0+++1').value, closeTo(2, epsilon)); |
+ expect(evaluator.parse('0+++++1').value, closeTo(3, epsilon)); |
+ expect(evaluator.parse('0+++++++1').value, closeTo(4, epsilon)); |
+ }); |
+ test('postfix sub', () { |
+ expect(evaluator.parse('1--').value, closeTo(0, epsilon)); |
+ expect(evaluator.parse('2----').value, closeTo(0, epsilon)); |
+ expect(evaluator.parse('3------').value, closeTo(0, epsilon)); |
+ expect(evaluator.parse('2---1').value, closeTo(0, epsilon)); |
+ expect(evaluator.parse('3-----1').value, closeTo(0, epsilon)); |
+ expect(evaluator.parse('4-------1').value, closeTo(0, epsilon)); |
+ }); |
+ test('prefix negate', () { |
+ expect(evaluator.parse('1').value, closeTo(1, epsilon)); |
+ expect(evaluator.parse('-1').value, closeTo(-1, epsilon)); |
+ expect(evaluator.parse('--1').value, closeTo(1, epsilon)); |
+ expect(evaluator.parse('---1').value, closeTo(-1, epsilon)); |
+ }); |
+ }); |
+ group('tutorial', () { |
+ test('simple grammar', () { |
+ var id = letter().seq(letter().or(digit()).star()); |
+ var id1 = id.parse('yeah'); |
+ var id2 = id.parse('f12'); |
+ expect(id1.value, ['y', ['e', 'a', 'h']]); |
+ expect(id2.value, ['f', ['1', '2']]); |
+ var id3 = id.parse('123'); |
+ expect(id3.message, 'letter expected'); |
+ expect(id3.position, 0); |
+ expect(id.accept('foo'), isTrue); |
+ expect(id.accept('123'), isFalse); |
+ }); |
+ test('different parsers', () { |
+ var id = letter().seq(word().star()).flatten(); |
+ var matches = id.matchesSkipping('foo 123 bar4'); |
+ expect(matches, ['foo', 'bar4']); |
+ }); |
+ test('complicated grammar', () { |
+ var number = digit().plus().flatten().trim().map(int.parse); |
+ var term = undefined(); |
+ var prod = undefined(); |
+ var prim = undefined(); |
+ term.set(prod.seq(char('+').trim()).seq(term).map((values) { |
+ return values[0] + values[2]; |
+ }).or(prod)); |
+ prod.set(prim.seq(char('*').trim()).seq(prod).map((values) { |
+ return values[0] * values[2]; |
+ }).or(prim)); |
+ prim.set(char('(').trim().seq(term).seq(char(')'.trim())).map((values) { |
+ return values[1]; |
+ }).or(number)); |
+ var start = term.end(); |
+ expect(7, start.parse('1 + 2 * 3').value); |
+ expect(9, start.parse('(1 + 2) * 3').value); |
+ }); |
+ }); |
+ group('composite (deprecated)', () { |
+ test('start', () { |
+ var parser = new PluggableCompositeParser((self) { |
+ self.def('start', char('a')); |
+ }); |
+ expectSuccess(parser, 'a', 'a', 1); |
+ expectFailure(parser, 'b', 0, '"a" expected'); |
+ expectFailure(parser, ''); |
+ }); |
+ test('circular', () { |
+ var parser = new PluggableCompositeParser((self) { |
+ self.def('start', self.ref('loop').or(char('b'))); |
+ self.def('loop', char('a').seq(self.ref('start'))); |
+ }); |
+ expect(parser.accept('b'), isTrue); |
+ expect(parser.accept('ab'), isTrue); |
+ expect(parser.accept('aab'), isTrue); |
+ expect(parser.accept('aaab'), isTrue); |
+ }); |
+ test('redefine parser', () { |
+ var parser = new PluggableCompositeParser((self) { |
+ self.def('start', char('b')); |
+ self.redef('start', char('a')); |
+ }); |
+ expectSuccess(parser, 'a', 'a', 1); |
+ expectFailure(parser, 'b', 0, '"a" expected'); |
+ expectFailure(parser, ''); |
+ }); |
+ test('redefine function', () { |
+ var parser = new PluggableCompositeParser((self) { |
+ var b = char('b'); |
+ self.def('start', b); |
+ self.redef('start', (old) { |
+ expect(b, old); |
+ return char('a'); |
+ }); |
+ }); |
+ expectSuccess(parser, 'a', 'a', 1); |
+ expectFailure(parser, 'b', 0, '"a" expected'); |
+ expectFailure(parser, ''); |
+ }); |
+ test('define completed', () { |
+ var parser = new PluggableCompositeParser((self) { |
+ self.def('start', char('a')); |
+ }); |
+ expect(() => parser.def('other', char('b')), throws); |
+ expect(() => parser.redef('start', char('b')), throws); |
+ expect(() => parser.action('start', (each) => each), throws); |
+ }); |
+ test('reference completed', () { |
+ var parsers = { |
+ 'start': char('a'), |
+ 'for_b': char('b'), |
+ 'for_c': char('c') |
+ }; |
+ var parser = new PluggableCompositeParser((self) { |
+ for (var key in parsers.keys) { |
+ self.def(key, parsers[key]); |
+ } |
+ }); |
+ for (var key in parsers.keys) { |
+ expect(parsers[key], parser[key]); |
+ expect(parsers[key], parser.ref(key)); |
+ } |
+ }); |
+ test('reference unknown', () { |
+ var parser = new PluggableCompositeParser((self) { |
+ self.def('start', char('a')); |
+ }); |
+ try { |
+ parser.ref('star1'); |
+ fail('Expected UndefinedProductionError to be thrown'); |
+ } on UndefinedProductionError catch (error) { |
+ expect(error.toString(), 'Undefined production: star1'); |
+ } |
+ }); |
+ test('duplicated start', () { |
+ new PluggableCompositeParser((self) { |
+ self.def('start', char('a')); |
+ try { |
+ self.def('start', char('b')); |
+ fail('Expected UndefinedProductionError to be thrown'); |
+ } on RedefinedProductionError catch (error) { |
+ expect(error.toString(), 'Redefined production: start'); |
+ } |
+ }); |
+ }); |
+ test('undefined start', () { |
+ expect(() => new PluggableCompositeParser((self) {}), throws); |
+ }); |
+ test('undefined redef', () { |
+ new PluggableCompositeParser((self) { |
+ self.def('start', char('a')); |
+ expect(() => self.redef('star1', char('b')), throws); |
+ }); |
+ }); |
+ test('example (lambda)', () { |
+ var parser = new PluggableCompositeParser((self) { |
+ self.def('start', self.ref('expression').end()); |
+ self.def('variable', letter().seq(word().star()).flatten().trim()); |
+ self.def('expression', self |
+ .ref('variable') |
+ .or(self.ref('abstraction')) |
+ .or(self.ref('application'))); |
+ self.def('abstraction', char('\\') |
+ .trim() |
+ .seq(self.ref('variable')) |
+ .seq(char('.').trim()) |
+ .seq(self.ref('expression'))); |
+ self.def('application', char('(') |
+ .trim() |
+ .seq(self.ref('expression')) |
+ .seq(self.ref('expression')) |
+ .seq(char(')').trim())); |
+ }); |
+ expect(parser.accept('x'), isTrue); |
+ expect(parser.accept('xy'), isTrue); |
+ expect(parser.accept('x12'), isTrue); |
+ expect(parser.accept('\\x.y'), isTrue); |
+ expect(parser.accept('\\x.\\y.z'), isTrue); |
+ expect(parser.accept('(x x)'), isTrue); |
+ expect(parser.accept('(x y)'), isTrue); |
+ expect(parser.accept('(x (y z))'), isTrue); |
+ expect(parser.accept('((x y) z)'), isTrue); |
+ }); |
+ test('example (expression)', () { |
+ var parser = new PluggableCompositeParser((self) { |
+ self.def('start', self.ref('terms').end()); |
+ self.def('terms', self.ref('addition').or(self.ref('factors'))); |
+ self.def('addition', |
+ self.ref('factors').separatedBy(char('+').or(char('-')).trim())); |
+ self.def('factors', self.ref('multiplication').or(self.ref('power'))); |
+ self.def('multiplication', |
+ self.ref('power').separatedBy(char('*').or(char('/')).trim())); |
+ self.def('power', self.ref('primary').separatedBy(char('^').trim())); |
+ self.def('primary', self.ref('number').or(self.ref('parentheses'))); |
+ self.def('number', char('-') |
+ .optional() |
+ .seq(digit().plus()) |
+ .seq(char('.').seq(digit().plus()).optional()) |
+ .flatten() |
+ .trim()); |
+ self.def('parentheses', |
+ char('(').trim().seq(self.ref('terms')).seq(char(')').trim())); |
+ }); |
+ expect(parser.accept('1'), isTrue); |
+ expect(parser.accept('12'), isTrue); |
+ expect(parser.accept('1.23'), isTrue); |
+ expect(parser.accept('-12.3'), isTrue); |
+ expect(parser.accept('1 + 2'), isTrue); |
+ expect(parser.accept('1 + 2 + 3'), isTrue); |
+ expect(parser.accept('1 - 2'), isTrue); |
+ expect(parser.accept('1 - 2 - 3'), isTrue); |
+ expect(parser.accept('1 * 2'), isTrue); |
+ expect(parser.accept('1 * 2 * 3'), isTrue); |
+ expect(parser.accept('1 / 2'), isTrue); |
+ expect(parser.accept('1 / 2 / 3'), isTrue); |
+ expect(parser.accept('1 ^ 2'), isTrue); |
+ expect(parser.accept('1 ^ 2 ^ 3'), isTrue); |
+ expect(parser.accept('1 + (2 * 3)'), isTrue); |
+ expect(parser.accept('(1 + 2) * 3'), isTrue); |
+ }); |
+ }); |
+} |