Skip to content

Commit bf038b1

Browse files
committed
refactor(Scope): Pull ASTParser out of Scope into it's own library
For dart-archive#1086
1 parent 46878c3 commit bf038b1

File tree

5 files changed

+272
-249
lines changed

5 files changed

+272
-249
lines changed

lib/change_detection/ast_parser.dart

+262
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
library angular.change_detection.ast_parser;
2+
3+
import 'dart:collection';
4+
5+
import 'package:angular/core/parser/syntax.dart' as syntax;
6+
import 'package:angular/core/parser/parser.dart';
7+
import 'package:angular/core/formatter.dart';
8+
import 'package:angular/change_detection/watch_group.dart';
9+
import 'package:angular/change_detection/change_detection.dart';
10+
import 'package:angular/core/parser/utils.dart';
11+
12+
class _FunctionChain {
13+
final Function fn;
14+
_FunctionChain _next;
15+
16+
_FunctionChain(fn()): fn = fn {
17+
assert(fn != null);
18+
}
19+
}
20+
21+
class AstParser {
22+
final Parser _parser;
23+
int _id = 0;
24+
final ExpressionVisitor _visitor;
25+
26+
AstParser(this._parser, ClosureMap closureMap)
27+
: _visitor = new ExpressionVisitor(closureMap);
28+
29+
AST call(String input, {FormatterMap formatters,
30+
bool collection: false }) {
31+
_visitor.formatters = formatters;
32+
AST contextRef = _visitor.contextRef;
33+
try {
34+
var exp = _parser(input);
35+
return collection ? _visitor.visitCollection(exp) : _visitor.visit(exp);
36+
} finally {
37+
_visitor.contextRef = contextRef;
38+
_visitor.formatters = null;
39+
}
40+
}
41+
}
42+
43+
class ExpressionVisitor implements syntax.Visitor {
44+
static final ContextReferenceAST scopeContextRef = new ContextReferenceAST();
45+
final ClosureMap _closureMap;
46+
AST contextRef = scopeContextRef;
47+
48+
49+
ExpressionVisitor(this._closureMap);
50+
51+
AST ast;
52+
FormatterMap formatters;
53+
54+
AST visit(syntax.Expression exp) {
55+
exp.accept(this);
56+
assert(ast != null);
57+
try {
58+
return ast;
59+
} finally {
60+
ast = null;
61+
}
62+
}
63+
64+
AST visitCollection(syntax.Expression exp) => new CollectionAST(visit(exp));
65+
AST _mapToAst(syntax.Expression expression) => visit(expression);
66+
67+
List<AST> _toAst(List<syntax.Expression> expressions) =>
68+
expressions.map(_mapToAst).toList();
69+
70+
Map<Symbol, AST> _toAstMap(Map<String, syntax.Expression> expressions) {
71+
if (expressions.isEmpty) return const {};
72+
Map<Symbol, AST> result = new Map<Symbol, AST>();
73+
expressions.forEach((String name, syntax.Expression expression) {
74+
result[_closureMap.lookupSymbol(name)] = _mapToAst(expression);
75+
});
76+
return result;
77+
}
78+
79+
void visitCallScope(syntax.CallScope exp) {
80+
List<AST> positionals = _toAst(exp.arguments.positionals);
81+
Map<Symbol, AST> named = _toAstMap(exp.arguments.named);
82+
ast = new MethodAST(contextRef, exp.name, positionals, named);
83+
}
84+
void visitCallMember(syntax.CallMember exp) {
85+
List<AST> positionals = _toAst(exp.arguments.positionals);
86+
Map<Symbol, AST> named = _toAstMap(exp.arguments.named);
87+
ast = new MethodAST(visit(exp.object), exp.name, positionals, named);
88+
}
89+
void visitAccessScope(syntax.AccessScope exp) {
90+
ast = new FieldReadAST(contextRef, exp.name);
91+
}
92+
void visitAccessMember(syntax.AccessMember exp) {
93+
ast = new FieldReadAST(visit(exp.object), exp.name);
94+
}
95+
void visitBinary(syntax.Binary exp) {
96+
ast = new PureFunctionAST(exp.operation,
97+
_operationToFunction(exp.operation),
98+
[visit(exp.left), visit(exp.right)]);
99+
}
100+
void visitPrefix(syntax.Prefix exp) {
101+
ast = new PureFunctionAST(exp.operation,
102+
_operationToFunction(exp.operation),
103+
[visit(exp.expression)]);
104+
}
105+
void visitConditional(syntax.Conditional exp) {
106+
ast = new PureFunctionAST('?:', _operation_ternary,
107+
[visit(exp.condition), visit(exp.yes),
108+
visit(exp.no)]);
109+
}
110+
void visitAccessKeyed(syntax.AccessKeyed exp) {
111+
ast = new ClosureAST('[]', _operation_bracket,
112+
[visit(exp.object), visit(exp.key)]);
113+
}
114+
void visitLiteralPrimitive(syntax.LiteralPrimitive exp) {
115+
ast = new ConstantAST(exp.value);
116+
}
117+
void visitLiteralString(syntax.LiteralString exp) {
118+
ast = new ConstantAST(exp.value);
119+
}
120+
void visitLiteralArray(syntax.LiteralArray exp) {
121+
List<AST> items = _toAst(exp.elements);
122+
ast = new PureFunctionAST('[${items.join(', ')}]', new ArrayFn(), items);
123+
}
124+
125+
void visitLiteralObject(syntax.LiteralObject exp) {
126+
List<String> keys = exp.keys;
127+
List<AST> values = _toAst(exp.values);
128+
assert(keys.length == values.length);
129+
var kv = <String>[];
130+
for (var i = 0; i < keys.length; i++) {
131+
kv.add('${keys[i]}: ${values[i]}');
132+
}
133+
ast = new PureFunctionAST('{${kv.join(', ')}}', new MapFn(keys), values);
134+
}
135+
136+
void visitFormatter(syntax.Formatter exp) {
137+
if (formatters == null) {
138+
throw new Exception("No formatters have been registered");
139+
}
140+
Function formatterFunction = formatters(exp.name);
141+
List<AST> args = [visitCollection(exp.expression)];
142+
args.addAll(_toAst(exp.arguments).map((ast) => new CollectionAST(ast)));
143+
ast = new PureFunctionAST('|${exp.name}',
144+
new _FormatterWrapper(formatterFunction, args.length), args);
145+
}
146+
147+
// TODO(misko): this is a corner case. Choosing not to implement for now.
148+
void visitCallFunction(syntax.CallFunction exp) {
149+
_notSupported("function's returing functions");
150+
}
151+
void visitAssign(syntax.Assign exp) {
152+
_notSupported('assignement');
153+
}
154+
void visitLiteral(syntax.Literal exp) {
155+
_notSupported('literal');
156+
}
157+
void visitExpression(syntax.Expression exp) {
158+
_notSupported('?');
159+
}
160+
void visitChain(syntax.Chain exp) {
161+
_notSupported(';');
162+
}
163+
164+
void _notSupported(String name) {
165+
throw new StateError("Can not watch expression containing '$name'.");
166+
}
167+
}
168+
169+
Function _operationToFunction(String operation) {
170+
switch(operation) {
171+
case '!' : return _operation_negate;
172+
case '+' : return _operation_add;
173+
case '-' : return _operation_subtract;
174+
case '*' : return _operation_multiply;
175+
case '/' : return _operation_divide;
176+
case '~/' : return _operation_divide_int;
177+
case '%' : return _operation_remainder;
178+
case '==' : return _operation_equals;
179+
case '!=' : return _operation_not_equals;
180+
case '<' : return _operation_less_then;
181+
case '>' : return _operation_greater_then;
182+
case '<=' : return _operation_less_or_equals_then;
183+
case '>=' : return _operation_greater_or_equals_then;
184+
case '^' : return _operation_power;
185+
case '&' : return _operation_bitwise_and;
186+
case '&&' : return _operation_logical_and;
187+
case '||' : return _operation_logical_or;
188+
default: throw new StateError(operation);
189+
}
190+
}
191+
192+
_operation_negate(value) => !toBool(value);
193+
_operation_add(left, right) => autoConvertAdd(left, right);
194+
_operation_subtract(left, right) => (left != null && right != null) ? left - right : (left != null ? left : (right != null ? 0 - right : 0));
195+
_operation_multiply(left, right) => (left == null || right == null) ? null : left * right;
196+
_operation_divide(left, right) => (left == null || right == null) ? null : left / right;
197+
_operation_divide_int(left, right) => (left == null || right == null) ? null : left ~/ right;
198+
_operation_remainder(left, right) => (left == null || right == null) ? null : left % right;
199+
_operation_equals(left, right) => left == right;
200+
_operation_not_equals(left, right) => left != right;
201+
_operation_less_then(left, right) => (left == null || right == null) ? null : left < right;
202+
_operation_greater_then(left, right) => (left == null || right == null) ? null : left > right;
203+
_operation_less_or_equals_then(left, right) => (left == null || right == null) ? null : left <= right;
204+
_operation_greater_or_equals_then(left, right) => (left == null || right == null) ? null : left >= right;
205+
_operation_power(left, right) => (left == null || right == null) ? null : left ^ right;
206+
_operation_bitwise_and(left, right) => (left == null || right == null) ? null : left & right;
207+
// TODO(misko): these should short circuit the evaluation.
208+
_operation_logical_and(left, right) => toBool(left) && toBool(right);
209+
_operation_logical_or(left, right) => toBool(left) || toBool(right);
210+
211+
_operation_ternary(condition, yes, no) => toBool(condition) ? yes : no;
212+
_operation_bracket(obj, key) => obj == null ? null : obj[key];
213+
214+
class ArrayFn extends FunctionApply {
215+
// TODO(misko): figure out why do we need to make a copy?
216+
apply(List args) => new List.from(args);
217+
}
218+
219+
class MapFn extends FunctionApply {
220+
final List<String> keys;
221+
222+
MapFn(this.keys);
223+
224+
Map apply(List values) {
225+
// TODO(misko): figure out why do we need to make a copy instead of reusing instance?
226+
assert(values.length == keys.length);
227+
return new Map.fromIterables(keys, values);
228+
}
229+
}
230+
231+
class _FormatterWrapper extends FunctionApply {
232+
final Function formatterFn;
233+
final List args;
234+
final List<Watch> argsWatches;
235+
_FormatterWrapper(this.formatterFn, length):
236+
args = new List(length),
237+
argsWatches = new List(length);
238+
239+
apply(List values) {
240+
for (var i=0; i < values.length; i++) {
241+
var value = values[i];
242+
var lastValue = args[i];
243+
if (!identical(value, lastValue)) {
244+
if (value is CollectionChangeRecord) {
245+
args[i] = (value as CollectionChangeRecord).iterable;
246+
} else if (value is MapChangeRecord) {
247+
args[i] = (value as MapChangeRecord).map;
248+
} else {
249+
args[i] = value;
250+
}
251+
}
252+
}
253+
var value = Function.apply(formatterFn, args);
254+
if (value is Iterable) {
255+
// Since formatters are pure we can guarantee that this well never change.
256+
// By wrapping in UnmodifiableListView we can hint to the dirty checker
257+
// and short circuit the iterator.
258+
value = new UnmodifiableListView(value);
259+
}
260+
return value;
261+
}
262+
}

lib/core/formatter.dart

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
part of angular.core_internal;
1+
library angular.core_internal.formatter_map;
22

3+
import 'package:di/di.dart';
4+
import 'package:angular/core/annotation_src.dart';
5+
import 'package:angular/core/registry.dart';
36

47
/**
58
* Registry of formatters at runtime.

lib/core/module_internal.dart

+3-2
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,16 @@ import 'package:angular/core/annotation_src.dart';
1515

1616
import 'package:angular/change_detection/watch_group.dart';
1717
export 'package:angular/change_detection/watch_group.dart';
18+
import 'package:angular/change_detection/ast_parser.dart';
1819
import 'package:angular/change_detection/change_detection.dart';
1920
import 'package:angular/change_detection/dirty_checking_change_detector.dart';
21+
import 'package:angular/core/formatter.dart';
22+
export 'package:angular/core/formatter.dart';
2023
import 'package:angular/core/parser/utils.dart';
21-
import 'package:angular/core/parser/syntax.dart' as syntax;
2224
import 'package:angular/core/registry.dart';
2325

2426
part "cache.dart";
2527
part "exception_handler.dart";
26-
part 'formatter.dart';
2728
part "interpolate.dart";
2829
part "scope.dart";
2930
part "zone.dart";

0 commit comments

Comments
 (0)