Added DSX syntax to Jael
This commit is contained in:
parent
0e600a1e59
commit
3402e5a0db
10 changed files with 109 additions and 28 deletions
7
.idea/runConfigurations/tests_in_dsx_test_dart.xml
Normal file
7
.idea/runConfigurations/tests_in_dsx_test_dart.xml
Normal file
|
@ -0,0 +1,7 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="tests in dsx_test.dart" type="DartTestRunConfigurationType" factoryName="Dart Test" singleton="true" nameIsGenerated="true">
|
||||
<option name="filePath" value="$PROJECT_DIR$/jael/test/render/dsx_test.dart" />
|
||||
<option name="testRunnerOptions" value="-j4" />
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
1
jael.iml
1
jael.iml
|
@ -13,6 +13,7 @@
|
|||
<content url="file://$MODULE_DIR$/dsx" />
|
||||
<content url="file://$MODULE_DIR$/dsx_generator" />
|
||||
<content url="file://$MODULE_DIR$/jael">
|
||||
<excludeFolder url="file://$MODULE_DIR$/jael/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/jael/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/jael/build" />
|
||||
</content>
|
||||
|
|
|
@ -34,7 +34,7 @@ void myFunction() {
|
|||
''';
|
||||
|
||||
var buf = new CodeBuffer();
|
||||
var document = jael.parseDocument(template, sourceUrl: 'test.jl');
|
||||
var document = jael.parseDocument(template, sourceUrl: 'test.jl', asDSX: false);
|
||||
var scope = new SymbolTable(values: {
|
||||
'profile': {
|
||||
'avatar': 'thosakwe.png',
|
||||
|
|
|
@ -33,8 +33,13 @@ abstract class Element extends ElementChild {
|
|||
];
|
||||
|
||||
Identifier get tagName;
|
||||
|
||||
Iterable<Attribute> get attributes;
|
||||
|
||||
Iterable<ElementChild> get children;
|
||||
|
||||
Attribute getAttribute(String name) =>
|
||||
attributes.firstWhere((a) => a.name == name, orElse: () => null);
|
||||
}
|
||||
|
||||
class SelfClosingElement extends Element {
|
||||
|
|
|
@ -34,8 +34,8 @@ enum TokenType {
|
|||
*/
|
||||
lBracket,
|
||||
rBracket,
|
||||
doubleCurlyL,
|
||||
doubleCurlyR,
|
||||
lDoubleCurly,
|
||||
rDoubleCurly,
|
||||
lCurly,
|
||||
rCurly,
|
||||
lParen,
|
||||
|
|
|
@ -7,8 +7,8 @@ import 'text/scanner.dart';
|
|||
|
||||
/// Parses a Jael document.
|
||||
Document parseDocument(String text,
|
||||
{sourceUrl, void onError(JaelError error)}) {
|
||||
var scanner = scan(text, sourceUrl: sourceUrl);
|
||||
{sourceUrl, bool asDSX: false, void onError(JaelError error)}) {
|
||||
var scanner = scan(text, sourceUrl: sourceUrl, asDSX: asDSX);
|
||||
|
||||
//scanner.tokens.forEach(print);
|
||||
|
||||
|
@ -16,7 +16,7 @@ Document parseDocument(String text,
|
|||
scanner.errors.forEach(onError);
|
||||
else if (scanner.errors.isNotEmpty) throw scanner.errors.first;
|
||||
|
||||
var parser = new Parser(scanner);
|
||||
var parser = new Parser(scanner, asDSX: asDSX);
|
||||
var doc = parser.parseDocument();
|
||||
|
||||
if (parser.errors.isNotEmpty && onError != null)
|
||||
|
|
|
@ -5,11 +5,12 @@ import 'scanner.dart';
|
|||
class Parser {
|
||||
final List<JaelError> errors = [];
|
||||
final Scanner scanner;
|
||||
final bool asDSX;
|
||||
|
||||
Token _current;
|
||||
int _index = -1;
|
||||
|
||||
Parser(this.scanner);
|
||||
Parser(this.scanner, {this.asDSX: false});
|
||||
|
||||
Token get current => _current;
|
||||
|
||||
|
@ -148,7 +149,7 @@ class Parser {
|
|||
Text parseText() => next(TokenType.text) ? new Text(_current) : null;
|
||||
|
||||
Interpolation parseInterpolation() {
|
||||
if (!next(TokenType.doubleCurlyL)) return null;
|
||||
if (!next(asDSX ? TokenType.lCurly : TokenType.lDoubleCurly)) return null;
|
||||
var doubleCurlyL = _current;
|
||||
|
||||
var expression = parseExpression(0);
|
||||
|
@ -159,9 +160,10 @@ class Parser {
|
|||
return null;
|
||||
}
|
||||
|
||||
if (!next(TokenType.doubleCurlyR)) {
|
||||
if (!next(asDSX ? TokenType.rCurly : TokenType.rDoubleCurly)) {
|
||||
var expected = asDSX ? '}' : '}}';
|
||||
errors.add(new JaelError(JaelErrorSeverity.error,
|
||||
'Missing closing "}}" in interpolation.', expression.span));
|
||||
'Missing closing "$expected" in interpolation.', expression.span));
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -260,7 +262,8 @@ class Parser {
|
|||
if (tagName2.name != tagName.name) {
|
||||
errors.add(new JaelError(
|
||||
JaelErrorSeverity.error,
|
||||
'Mismatched closing tags. Expected "${tagName.span.text}"; got "${tagName2.name}" instead.',
|
||||
'Mismatched closing tags. Expected "${tagName.span
|
||||
.text}"; got "${tagName2.name}" instead.',
|
||||
lt2.span));
|
||||
return null;
|
||||
}
|
||||
|
@ -291,21 +294,41 @@ class Parser {
|
|||
|
||||
if (next(TokenType.equals)) {
|
||||
equals = _current;
|
||||
} else if (next(TokenType.nequ)) {
|
||||
} else if (!asDSX && next(TokenType.nequ)) {
|
||||
nequ = _current;
|
||||
} else {
|
||||
return new Attribute(id, string, null, null, null);
|
||||
}
|
||||
|
||||
var value = parseExpression(0);
|
||||
if (!asDSX) {
|
||||
var value = parseExpression(0);
|
||||
|
||||
if (value == null) {
|
||||
errors.add(new JaelError(JaelErrorSeverity.error,
|
||||
'Missing expression in attribute.', equals?.span ?? nequ.span));
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Attribute(id, string, equals, nequ, value);
|
||||
} else {
|
||||
// Find either a string, or an interpolation.
|
||||
var value = implicitString();
|
||||
|
||||
if (value != null) {
|
||||
return new Attribute(id, string, equals, nequ, value);
|
||||
}
|
||||
|
||||
var interpolation = parseInterpolation();
|
||||
|
||||
if (interpolation != null) {
|
||||
return new Attribute(
|
||||
id, string, equals, nequ, interpolation.expression);
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
errors.add(new JaelError(JaelErrorSeverity.error,
|
||||
'Missing expression in attribute.', equals?.span ?? nequ.span));
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Attribute(id, string, equals, nequ, value);
|
||||
}
|
||||
|
||||
Expression parseExpression(int precedence) {
|
||||
|
|
|
@ -12,7 +12,8 @@ final RegExp _string1 = new RegExp(
|
|||
final RegExp _string2 = new RegExp(
|
||||
r'"((\\(["\\/bfnrt]|(u[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])))|([^"\\]))*"');
|
||||
|
||||
Scanner scan(String text, {sourceUrl}) => new _Scanner(text, sourceUrl)..scan();
|
||||
Scanner scan(String text, {sourceUrl, bool asDSX: false}) =>
|
||||
new _Scanner(text, sourceUrl)..scan(asDSX: asDSX);
|
||||
|
||||
abstract class Scanner {
|
||||
List<JaelError> get errors;
|
||||
|
@ -24,8 +25,8 @@ final RegExp _htmlComment = new RegExp(r'<!--[^$]*-->');
|
|||
|
||||
final Map<Pattern, TokenType> _expressionPatterns = {
|
||||
//final Map<Pattern, TokenType> _htmlPatterns = {
|
||||
'{{': TokenType.doubleCurlyL,
|
||||
'{{-': TokenType.doubleCurlyL,
|
||||
'{{': TokenType.lDoubleCurly,
|
||||
'{{-': TokenType.lDoubleCurly,
|
||||
|
||||
//
|
||||
_htmlComment: TokenType.htmlComment,
|
||||
|
@ -42,7 +43,7 @@ final Map<Pattern, TokenType> _expressionPatterns = {
|
|||
//};
|
||||
|
||||
//final Map<Pattern, TokenType> _expressionPatterns = {
|
||||
'}}': TokenType.doubleCurlyR,
|
||||
'}}': TokenType.rDoubleCurly,
|
||||
|
||||
// Keywords
|
||||
'new': TokenType.$new,
|
||||
|
@ -92,10 +93,10 @@ class _Scanner implements Scanner {
|
|||
_scanner = new SpanScanner(text, sourceUrl: sourceUrl);
|
||||
}
|
||||
|
||||
void scan() {
|
||||
void scan({bool asDSX: false}) {
|
||||
while (!_scanner.isDone) {
|
||||
if (state == _ScannerState.html) {
|
||||
scanHtml();
|
||||
scanHtml(asDSX);
|
||||
} else if (state == _ScannerState.freeText) {
|
||||
// Just keep parsing until we hit "</"
|
||||
var start = _scanner.state, end = start;
|
||||
|
@ -104,8 +105,8 @@ class _Scanner implements Scanner {
|
|||
// Skip through comments
|
||||
if (_scanner.scan(_htmlComment)) continue;
|
||||
|
||||
// Break on {{
|
||||
if (_scanner.matches('{{')) {
|
||||
// Break on {{ or {
|
||||
if (_scanner.matches(asDSX ? '{' : '{{')) {
|
||||
state = _ScannerState.html;
|
||||
//_scanner.position--;
|
||||
break;
|
||||
|
@ -169,7 +170,7 @@ class _Scanner implements Scanner {
|
|||
}
|
||||
}
|
||||
|
||||
void scanHtml() {
|
||||
void scanHtml(bool asDSX) {
|
||||
var brackets = new Queue<Token>();
|
||||
|
||||
do {
|
||||
|
@ -247,7 +248,8 @@ class _Scanner implements Scanner {
|
|||
break;
|
||||
}
|
||||
}
|
||||
} else if (token.type == TokenType.doubleCurlyR) {
|
||||
} else if (token.type ==
|
||||
(asDSX ? TokenType.rCurly : TokenType.rDoubleCurly)) {
|
||||
state = _ScannerState.freeText;
|
||||
break;
|
||||
}
|
||||
|
|
43
jael/test/render/dsx_test.dart
Normal file
43
jael/test/render/dsx_test.dart
Normal file
|
@ -0,0 +1,43 @@
|
|||
import 'package:jael/jael.dart';
|
||||
import 'package:symbol_table/symbol_table.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
test('attributes', () {
|
||||
var doc = parseDSX('''
|
||||
<foo bar="baz" yes={no} />
|
||||
''');
|
||||
|
||||
var foo = doc.root as SelfClosingElement;
|
||||
expect(foo.tagName.name, 'foo');
|
||||
expect(foo.attributes, hasLength(2));
|
||||
expect(foo.getAttribute('bar'), isNotNull);
|
||||
expect(foo.getAttribute('yes'), isNotNull);
|
||||
expect(foo.getAttribute('bar').value.compute(null), 'baz');
|
||||
expect(
|
||||
foo
|
||||
.getAttribute('yes')
|
||||
.value
|
||||
.compute(new SymbolTable(values: {'no': 'maybe'})),
|
||||
'maybe');
|
||||
});
|
||||
|
||||
test('children', () {
|
||||
var doc = parseDSX('''
|
||||
<foo bar="baz" yes={no}>
|
||||
<bar>{24 * 3}</bar>
|
||||
</foo>
|
||||
''');
|
||||
|
||||
var bar = doc.root.children.first as RegularElement;
|
||||
expect(bar.tagName.name, 'bar');
|
||||
|
||||
var interpolation = bar.children.first as Interpolation;
|
||||
expect(interpolation.expression.compute(null), 24 * 3);
|
||||
});
|
||||
}
|
||||
|
||||
Document parseDSX(String text) {
|
||||
return parseDocument(text,
|
||||
sourceUrl: 'test.dsx', asDSX: true, onError: (e) => throw e);
|
||||
}
|
|
@ -64,13 +64,13 @@ main() {
|
|||
expect(tokens[6], isToken(TokenType.number, '2'));
|
||||
expect(tokens[7], isToken(TokenType.gt));
|
||||
expect(tokens[8], isToken(TokenType.text, 'three'));
|
||||
expect(tokens[9], isToken(TokenType.doubleCurlyL));
|
||||
expect(tokens[9], isToken(TokenType.lDoubleCurly));
|
||||
expect(tokens[10], isToken(TokenType.id, 'four'));
|
||||
expect(tokens[11], isToken(TokenType.gt));
|
||||
expect(tokens[12], isToken(TokenType.id, 'five'));
|
||||
expect(tokens[13], isToken(TokenType.dot));
|
||||
expect(tokens[14], isToken(TokenType.id, 'six'));
|
||||
expect(tokens[15], isToken(TokenType.doubleCurlyR));
|
||||
expect(tokens[15], isToken(TokenType.rDoubleCurly));
|
||||
expect(tokens[16], isToken(TokenType.lt));
|
||||
expect(tokens[17], isToken(TokenType.slash));
|
||||
expect(tokens[18], isToken(TokenType.id, 'ul'));
|
||||
|
|
Loading…
Reference in a new issue