null-coalescing
This commit is contained in:
parent
f16f2918a3
commit
7c07ad3aa3
10 changed files with 73 additions and 14 deletions
|
@ -1,6 +1,6 @@
|
|||
import 'package:source_span/source_span.dart';
|
||||
|
||||
class JaelError {
|
||||
class JaelError extends Error {
|
||||
final JaelErrorSeverity severity;
|
||||
final String message;
|
||||
final FileSpan span;
|
||||
|
|
|
@ -1,8 +1,27 @@
|
|||
import 'package:source_span/source_span.dart';
|
||||
import 'package:symbol_table/symbol_table.dart';
|
||||
import 'ast_node.dart';
|
||||
import 'token.dart';
|
||||
|
||||
abstract class Expression extends AstNode {
|
||||
compute(SymbolTable scope);
|
||||
}
|
||||
|
||||
abstract class Literal extends Expression {}
|
||||
|
||||
class Negation extends Expression {
|
||||
final Token exclamation;
|
||||
final Expression expression;
|
||||
|
||||
Negation(this.exclamation, this.expression);
|
||||
|
||||
@override
|
||||
FileSpan get span {
|
||||
return exclamation.span.expand(expression.span);
|
||||
}
|
||||
|
||||
@override
|
||||
compute(SymbolTable scope) {
|
||||
return !(expression.compute(scope));
|
||||
}
|
||||
}
|
|
@ -20,6 +20,8 @@ class Identifier extends Expression {
|
|||
default:
|
||||
var symbol = scope.resolve(name);
|
||||
if (symbol == null) {
|
||||
if (scope.resolve('!strict!')?.value == false)
|
||||
return null;
|
||||
throw new ArgumentError(
|
||||
'The name "$name" does not exist in this scope.');
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ enum TokenType {
|
|||
colon,
|
||||
comma,
|
||||
dot,
|
||||
exclamation,
|
||||
percent,
|
||||
plus,
|
||||
minus,
|
||||
|
|
|
@ -27,7 +27,13 @@ Document parseDocument(String text,
|
|||
class Renderer {
|
||||
const Renderer();
|
||||
|
||||
void render(Document document, CodeBuffer buffer, SymbolTable scope) {
|
||||
/// Renders a [document] into the [buffer] as HTML.
|
||||
///
|
||||
/// If [strictResolution] is `false` (default: `true`), then undefined identifiers will return `null`
|
||||
/// instead of throwing.
|
||||
void render(Document document, CodeBuffer buffer, SymbolTable scope, {bool strictResolution: true}) {
|
||||
scope.add('!strict!', value: strictResolution != false);
|
||||
|
||||
if (document.doctype != null) buffer.writeln(document.doctype.span.text);
|
||||
renderElement(
|
||||
document.root, buffer, scope, document.doctype?.public == null);
|
||||
|
|
|
@ -18,6 +18,7 @@ const Map<TokenType, InfixParselet> infixParselets = const {
|
|||
TokenType.nequ: const BinaryParselet(10),
|
||||
TokenType.question: const ConditionalParselet(),
|
||||
TokenType.equals: const BinaryParselet(3),
|
||||
TokenType.elvis: const BinaryParselet(3),
|
||||
};
|
||||
|
||||
class ConditionalParselet implements InfixParselet {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
part of jael.src.text.parselet;
|
||||
|
||||
const Map<TokenType, PrefixParselet> prefixParselets = const {
|
||||
TokenType.exclamation: const NotParselet(),
|
||||
TokenType.$new: const NewParselet(),
|
||||
TokenType.number: const NumberParselet(),
|
||||
TokenType.hex: const HexParselet(),
|
||||
|
@ -11,6 +12,22 @@ const Map<TokenType, PrefixParselet> prefixParselets = const {
|
|||
TokenType.lParen: const ParenthesisParselet(),
|
||||
};
|
||||
|
||||
class NotParselet implements PrefixParselet {
|
||||
const NotParselet();
|
||||
|
||||
@override
|
||||
Expression parse(Parser parser, Token token) {
|
||||
var expression = parser.parseExpression(0);
|
||||
|
||||
if (expression == null) {
|
||||
parser.errors.add(new JaelError(JaelErrorSeverity.error,
|
||||
'Missing expression after "!" in negation expression.', token.span));
|
||||
}
|
||||
|
||||
return new Negation(token, expression);
|
||||
}
|
||||
}
|
||||
|
||||
class NewParselet implements PrefixParselet {
|
||||
const NewParselet();
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ import '../ast/ast.dart';
|
|||
|
||||
final RegExp _whitespace = new RegExp(r'[ \n\r\t]+');
|
||||
|
||||
final RegExp _id = new RegExp(r'(([A-Za-z][A-Za-z0-9]*-)*([A-Za-z][A-Za-z0-9]*))');
|
||||
final RegExp _string1 = new RegExp(
|
||||
r"'((\\(['\\/bfnrt]|(u[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])))|([^'\\]))*'");
|
||||
final RegExp _string2 = new RegExp(
|
||||
|
@ -31,7 +32,7 @@ final Map<Pattern, TokenType> _htmlPatterns = {
|
|||
'!=': TokenType.nequ,
|
||||
_string1: TokenType.string,
|
||||
_string2: TokenType.string,
|
||||
new RegExp(r'(([A-Za-z][A-Za-z0-9]*-)*([A-Za-z][A-Za-z0-9]*))'): TokenType.id,
|
||||
_id: TokenType.id,
|
||||
};
|
||||
|
||||
final Map<Pattern, TokenType> _expressionPatterns = {
|
||||
|
@ -45,7 +46,10 @@ final Map<Pattern, TokenType> _expressionPatterns = {
|
|||
':': TokenType.colon,
|
||||
',': TokenType.comma,
|
||||
'.': TokenType.dot,
|
||||
'??': TokenType.elvis,
|
||||
'?.': TokenType.elvis_dot,
|
||||
'=': TokenType.equals,
|
||||
'!': TokenType.exclamation,
|
||||
'-': TokenType.minus,
|
||||
'%': TokenType.percent,
|
||||
'+': TokenType.plus,
|
||||
|
@ -67,7 +71,7 @@ final Map<Pattern, TokenType> _expressionPatterns = {
|
|||
new RegExp(r'0x[A-Fa-f0-9]+'): TokenType.hex,
|
||||
_string1: TokenType.string,
|
||||
_string2: TokenType.string,
|
||||
new RegExp('[A-Za-z_\\\$][A-Za-z0-9_\\\$]*'): TokenType.id,
|
||||
_id: TokenType.id,
|
||||
};
|
||||
|
||||
class _Scanner implements Scanner {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
name: jael
|
||||
version: 1.0.0-beta+1
|
||||
version: 1.0.0-beta+2
|
||||
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
|
||||
|
|
|
@ -9,19 +9,28 @@ main() {
|
|||
<html>
|
||||
<body>
|
||||
<h1>Hello</h1>
|
||||
<img src=profile['avatar'] />
|
||||
<img ready="always" data-img-src=profile['avatar'] />
|
||||
</body>
|
||||
</html>
|
||||
''';
|
||||
|
||||
var buf = new CodeBuffer();
|
||||
var document = jael.parseDocument(template, sourceUrl: 'test.jl');
|
||||
var scope = new SymbolTable(values: {
|
||||
'profile': {
|
||||
'avatar': 'thosakwe.png',
|
||||
}
|
||||
});
|
||||
jael.Document document;
|
||||
SymbolTable scope;
|
||||
|
||||
try {
|
||||
document = jael.parseDocument(template, sourceUrl: 'test.jl');
|
||||
scope = new SymbolTable(values: {
|
||||
'profile': {
|
||||
'avatar': 'thosakwe.png',
|
||||
}
|
||||
});
|
||||
} on jael.JaelError catch(e) {
|
||||
print(e);
|
||||
print(e.stackTrace);
|
||||
}
|
||||
|
||||
expect(document, isNotNull);
|
||||
const jael.Renderer().render(document, buf, scope);
|
||||
print(buf);
|
||||
|
||||
|
@ -33,7 +42,7 @@ main() {
|
|||
<h1>
|
||||
Hello
|
||||
</h1>
|
||||
<img src="thosakwe.png">
|
||||
<img ready="always" data-img-src="thosakwe.png">
|
||||
</body>
|
||||
</html>
|
||||
'''
|
||||
|
|
Loading…
Reference in a new issue