Skip to content
This repository has been archived by the owner on Feb 22, 2018. It is now read-only.

feat(parser): Better error messages for bad functions. #26

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 23 additions & 10 deletions lib/parser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -500,14 +500,25 @@ class Parser {
return null;
}

/**
* Token savers are synchronous lists that allows Parser functions to
* access the tokens parsed during some amount of time. They are useful
* for printing helpful debugging messages.
*/
List<List<Token>> tokenSavers = [];
List<Token> saveTokens() { var n = []; tokenSavers.add(n); return n; }
stopSavingTokens(x) { if (!tokenSavers.remove(x)) { throw "bad token saver"; } return x; }
tokensText(List x) => x.map((x) => x.text).join();

Token expect([String e1, String e2, String e3, String e4]){
Token token = peek(e1, e2, e3, e4);
if (token != null) {
// TODO json
// if (json && !token.json) {
// throwError("is not valid json", token);
// }
tokens.removeAt(0);
var consumed = tokens.removeAt(0);
tokenSavers.forEach((ts) => ts.add(consumed));
return token;
}
return null;
Expand All @@ -520,17 +531,12 @@ class Parser {
}
}





var filterChain = null;
var functionCall, arrayDeclaration, objectIndex, fieldAccess, object;



ParsedFn primary() {
var primary;
var ts = saveTokens();
if (expect('(') != null) {
primary = filterChain();
consume(')');
Expand All @@ -539,7 +545,7 @@ class Parser {
} else if (expect('{') != null) {
primary = object();
} else {
var token = expect();
Token token = expect();
primary = token.primaryFn;
if (primary == null) {
throw "not impl error";
Expand All @@ -551,7 +557,7 @@ class Parser {
var next, context;
while ((next = expect('(', '[', '.')) != null) {
if (next.text == '(') {
primary = functionCall(primary);
primary = functionCall(primary, tokensText(ts.sublist(0, ts.length-1)));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: add spaces around -

context = null;
} else if (next.text == '[') {
context = primary;
Expand All @@ -563,6 +569,7 @@ class Parser {
throw "Impossible.. what?";
}
}
stopSavingTokens(ts);
return primary;
}

Expand Down Expand Up @@ -706,7 +713,7 @@ class Parser {
}
}

functionCall = (fn) {
functionCall = (fn, fnName) {
var argsFn = [];
if (peekToken().text != ')') {
do {
Expand All @@ -720,6 +727,12 @@ class Parser {
args.add(argsFn[i](self, locals));
}
var userFn = fn(self, locals);
if (userFn == null) {
throw "Undefined function $fnName";
}
if (userFn is! Function) {
throw "$fnName is not a function";
}
return relaxFnApply(userFn, args);
});
};
Expand Down
53 changes: 41 additions & 12 deletions test/parser_spec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,6 @@ main() {
});
});


describe('parse', () {
var scope;
eval(String text) => Parser.parse(text)(scope, null);
Expand Down Expand Up @@ -588,19 +587,49 @@ main() {
expect(Parser.parse('str')(data)).toEqual('dole');
});

it('should support map getters from superclass', () {
InheritedMapData mapData = new InheritedMapData();
expect(Parser.parse('notmixed')(mapData)).toEqual('mapped-notmixed');
});
it('should support map getters from superclass', () {
InheritedMapData mapData = new InheritedMapData();
expect(Parser.parse('notmixed')(mapData)).toEqual('mapped-notmixed');
});

it('should support map getters from mixins', () {
MixedMapData data = new MixedMapData();
expect(Parser.parse('str')(data)).toEqual('mapped-str');
});

it('should gracefully handle bad containsKey', () {
expect(Parser.parse('str')(new BadContainsKeys())).toEqual('member');
});

it('should support map getters from mixins', () {
MixedMapData data = new MixedMapData();
expect(Parser.parse('str')(data)).toEqual('mapped-str');
});
it('should parse functions for object indices', () {
expect(Parser.parse('a[x()]()')({'a': [()=>6], 'x': () => 0})).toEqual(6);
});

it('should gracefully handle bad containsKey', () {
expect(Parser.parse('str')(new BadContainsKeys())).toEqual('member');
});
it('should fail gracefully when missing a function', () {
expect(() {
Parser.parse('doesNotExist()')({});
}).toThrow('Undefined function doesNotExist');

expect(() {
Parser.parse('exists(doesNotExist())')({'exists': () => true});
}).toThrow('Undefined function doesNotExist');

expect(() {
Parser.parse('doesNotExists(exists())')({'exists': () => true});
}).toThrow('Undefined function doesNotExist');

expect(() {
Parser.parse('a[0]()')({'a': [4]});
}).toThrow('a[0] is not a function');

expect(() {
Parser.parse('a[x()]()')({'a': [4], 'x': () => 0});
}).toThrow('a[x()] is not a function');

expect(() {
Parser.parse('{}()')({});
}).toThrow('{} is not a function');
});
});

describe('assignable', () {
Expand Down