+3
This commit is contained in:
parent
10cb608f1e
commit
14db5f99c5
9 changed files with 95 additions and 12 deletions
|
@ -3,6 +3,7 @@ export 'ast_node.dart';
|
|||
export 'attribute.dart';
|
||||
export 'binary.dart';
|
||||
export 'call.dart';
|
||||
export 'conditional.dart';
|
||||
export 'document.dart';
|
||||
export 'element.dart';
|
||||
export 'error.dart';
|
||||
|
|
27
jael/lib/src/ast/conditional.dart
Normal file
27
jael/lib/src/ast/conditional.dart
Normal file
|
@ -0,0 +1,27 @@
|
|||
import 'package:source_span/source_span.dart';
|
||||
import 'expression.dart';
|
||||
import 'token.dart';
|
||||
|
||||
class Conditional extends Expression {
|
||||
final Expression condition, ifTrue, ifFalse;
|
||||
final Token question, colon;
|
||||
|
||||
Conditional(
|
||||
this.condition, this.question, this.ifTrue, this.colon, this.ifFalse);
|
||||
|
||||
@override
|
||||
FileSpan get span {
|
||||
return condition.span
|
||||
.expand(question.span)
|
||||
.expand(ifTrue.span)
|
||||
.expand(colon.span)
|
||||
.expand(ifFalse.span);
|
||||
}
|
||||
|
||||
@override
|
||||
compute(scope) {
|
||||
return condition.compute(scope)
|
||||
? ifTrue.compute(scope)
|
||||
: ifFalse.compute(scope);
|
||||
}
|
||||
}
|
|
@ -15,6 +15,15 @@ class TextNode extends ElementChild {
|
|||
FileSpan get span => text.span;
|
||||
}
|
||||
|
||||
class ScriptTag extends ElementChild {
|
||||
final Token script_tag;
|
||||
|
||||
ScriptTag(this.script_tag);
|
||||
|
||||
@override
|
||||
FileSpan get span => script_tag.span;
|
||||
}
|
||||
|
||||
abstract class Element extends ElementChild {
|
||||
static const List<String> selfClosing = const [
|
||||
'include',
|
||||
|
|
|
@ -23,6 +23,7 @@ enum TokenType {
|
|||
slash,
|
||||
equals,
|
||||
id,
|
||||
script_tag,
|
||||
text,
|
||||
|
||||
// Keywords
|
||||
|
|
|
@ -166,6 +166,8 @@ class Renderer {
|
|||
buffer.write(child.span.text.trimRight());
|
||||
else
|
||||
buffer.write(child.span.text);
|
||||
} else if (child is ScriptTag) {
|
||||
buffer.writeln(child.script_tag.span.text);
|
||||
} else if (child is Interpolation) {
|
||||
var value = child.expression.compute(scope);
|
||||
|
||||
|
@ -176,7 +178,7 @@ class Renderer {
|
|||
buffer.write(HTML_ESCAPE.convert(value.toString()));
|
||||
}
|
||||
} else if (child is Element) {
|
||||
buffer.writeln();
|
||||
if (buffer?.lastLine?.text?.isNotEmpty == true) buffer.writeln();
|
||||
renderElement(child, buffer, scope, html5);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,9 +16,45 @@ const Map<TokenType, InfixParselet> infixParselets = const {
|
|||
TokenType.gte: const BinaryParselet(11),
|
||||
TokenType.equ: const BinaryParselet(10),
|
||||
TokenType.nequ: const BinaryParselet(10),
|
||||
TokenType.question: const ConditionalParselet(),
|
||||
TokenType.equals: const BinaryParselet(3),
|
||||
};
|
||||
|
||||
class ConditionalParselet implements InfixParselet {
|
||||
@override
|
||||
int get precedence => 4;
|
||||
|
||||
const ConditionalParselet();
|
||||
|
||||
@override
|
||||
Expression parse(Parser parser, Expression left, Token token) {
|
||||
var ifTrue = parser.parseExpression(0);
|
||||
|
||||
if (ifTrue == null) {
|
||||
parser.errors.add(new JaelError(JaelErrorSeverity.error,
|
||||
'Missing expression in conditional expression.', token.span));
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!parser.next(TokenType.colon)) {
|
||||
parser.errors.add(new JaelError(JaelErrorSeverity.error,
|
||||
'Missing ":" in conditional expression.', ifTrue.span));
|
||||
return null;
|
||||
}
|
||||
|
||||
var colon = parser.current;
|
||||
var ifFalse = parser.parseExpression(0);
|
||||
|
||||
if (ifFalse == null) {
|
||||
parser.errors.add(new JaelError(JaelErrorSeverity.error,
|
||||
'Missing expression in conditional expression.', colon.span));
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Conditional(left, token, ifTrue, colon, ifFalse);
|
||||
}
|
||||
}
|
||||
|
||||
class BinaryParselet implements InfixParselet {
|
||||
final int precedence;
|
||||
|
||||
|
|
|
@ -68,9 +68,7 @@ class Parser {
|
|||
StringLiteral implicitString() {
|
||||
if (next(TokenType.string)) {
|
||||
return prefixParselets[TokenType.string].parse(this, _current);
|
||||
} else if (next(TokenType.text)) {
|
||||
|
||||
}
|
||||
} else if (next(TokenType.text)) {}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
@ -85,8 +83,10 @@ class Parser {
|
|||
}
|
||||
var doctype = _current, html = parseIdentifier();
|
||||
if (html?.span?.text?.toLowerCase() != 'html') {
|
||||
errors.add(new JaelError(JaelErrorSeverity.error,
|
||||
'Expected "html" in doctype declaration.', html?.span ?? doctype.span));
|
||||
errors.add(new JaelError(
|
||||
JaelErrorSeverity.error,
|
||||
'Expected "html" in doctype declaration.',
|
||||
html?.span ?? doctype.span));
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -102,8 +102,10 @@ class Parser {
|
|||
}
|
||||
|
||||
if (public?.span?.text?.toLowerCase() != 'public') {
|
||||
errors.add(new JaelError(JaelErrorSeverity.error,
|
||||
'Expected "public" in doctype declaration.', public?.span ?? html.span));
|
||||
errors.add(new JaelError(
|
||||
JaelErrorSeverity.error,
|
||||
'Expected "public" in doctype declaration.',
|
||||
public?.span ?? html.span));
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -138,6 +140,7 @@ class Parser {
|
|||
parseHtmlComment() ??
|
||||
parseInterpolation() ??
|
||||
parseText() ??
|
||||
parseScriptTag() ??
|
||||
parseElement();
|
||||
|
||||
HtmlComment parseHtmlComment() =>
|
||||
|
@ -145,6 +148,9 @@ class Parser {
|
|||
|
||||
Text parseText() => next(TokenType.text) ? new Text(_current) : null;
|
||||
|
||||
ScriptTag parseScriptTag() =>
|
||||
next(TokenType.script_tag) ? new ScriptTag(_current) : null;
|
||||
|
||||
Interpolation parseInterpolation() {
|
||||
if (!next(TokenType.doubleCurlyL)) return null;
|
||||
var doubleCurlyL = _current;
|
||||
|
@ -301,7 +307,8 @@ class Parser {
|
|||
while (precedence < _nextPrecedence()) {
|
||||
_current = scanner.tokens[++_index];
|
||||
|
||||
if (_current.type == TokenType.slash && peek()?.type == TokenType.gt) {
|
||||
if (_current.type == TokenType.slash &&
|
||||
peek()?.type == TokenType.gt) {
|
||||
// Handle `/>`
|
||||
//
|
||||
// Don't register this as an infix expression.
|
||||
|
@ -314,8 +321,7 @@ class Parser {
|
|||
var newLeft = infix.parse(this, left, _current);
|
||||
|
||||
if (newLeft == null) {
|
||||
if (_current.type == TokenType.gt)
|
||||
_index--;
|
||||
if (_current.type == TokenType.gt) _index--;
|
||||
return left;
|
||||
}
|
||||
left = newLeft;
|
||||
|
|
|
@ -30,6 +30,7 @@ final Map<Pattern, TokenType> _htmlPatterns = {
|
|||
'=': TokenType.equals,
|
||||
_string1: TokenType.string,
|
||||
_string2: TokenType.string,
|
||||
new RegExp(r'<script[^>]*>[^$]*</script>'): TokenType.script_tag,
|
||||
new RegExp(r'([A-Za-z][A-Za-z0-9]*-)*([A-Za-z][A-Za-z0-9]*)'): TokenType.id,
|
||||
};
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
name: jael
|
||||
version: 1.0.0-alpha+2
|
||||
version: 1.0.0-alpha+3
|
||||
description: A simple server-side HTML templating engine for Dart.
|
||||
author: Tobe O <thosakwe@gmail.com>
|
||||
homepage: https://github.com/angel-dart/jael/tree/master/jael
|
||||
|
|
Loading…
Reference in a new issue