Migrated Jael
This commit is contained in:
parent
436f797982
commit
ea6d566607
32 changed files with 352 additions and 312 deletions
52
CHANGELOG.md
52
CHANGELOG.md
|
@ -1,12 +1,12 @@
|
|||
# 4.0.0 (NNBD)
|
||||
* Changed Dart SDK requirements for all packages to ">=2.12.0 <3.0.0" to support NNBD.
|
||||
* Migrated pretty_logging to 3.0.0 (0/0 tests)
|
||||
* Migrated angel_http_exception to 3.0.0 (0/0 tests)
|
||||
* Migrated pretty_logging to 3.0.0 (0/0 tests passed)
|
||||
* Migrated angel_http_exception to 3.0.0 (0/0 tests passed)
|
||||
* Moved angel_cli to https://github.com/dukefirehawk/cli (Not migrated)
|
||||
* Added code_buffer and migrated to 2.0.0 (16/16 tests)
|
||||
* Added combinator and migrated to 2.0.0 (16/16 tests)
|
||||
* Added code_buffer and migrated to 2.0.0 (16/16 tests passed)
|
||||
* Added combinator and migrated to 2.0.0 (16/16 tests passed)
|
||||
* Migrated angel_route to 5.0.0 (35/35 tests passed)
|
||||
* Migrated angel_model to 3.0.0 (0/0 tests)
|
||||
* Migrated angel_model to 3.0.0 (0/0 tests passed)
|
||||
* Migrated angel_container to 3.0.0 (55/55 tests passed)
|
||||
* Added merge_map and migrated to 2.0.0 (6/6 tests passed)
|
||||
* Added mock_request and migrated to 2.0.0 (0/0 tests)
|
||||
|
@ -19,7 +19,7 @@
|
|||
* Migrated angel_websocket to 4.0.0 (2/3 tests passed)
|
||||
* Updated test to 4.0.0 (1/1 test passed)
|
||||
* Added symbol_table and migrated to 2.0.0 (16/16 tests passed)
|
||||
* Updated jael to 3.0.0 (in progress)
|
||||
* Migrated jael to 4.0.0 (17/20 tests passed)
|
||||
* Updated jael_preprocessor to 3.0.0 (in progress)
|
||||
* Updated angel_jael to 3.0.0 (in progress)
|
||||
* Updated pub_sub to 3.0.0 (in progress)
|
||||
|
@ -30,27 +30,27 @@
|
|||
|
||||
# 3.0.0 (Non NNBD)
|
||||
* Changed Dart SDK requirements for all packages to ">=2.10.0 <3.0.0"
|
||||
* Updated pretty_logging to 2.0.0
|
||||
* Updated angel_http_exception to 2.0.0
|
||||
* Updated pretty_logging to 2.0.0 (0/0 tests passed)
|
||||
* Updated angel_http_exception to 2.0.0 (0/0 tests passed)
|
||||
* Updated angel_cli to 3.0.0. (Rename not working)
|
||||
* Updated angel_route to 4.0.0
|
||||
* Updated angel_model to 2.0.0
|
||||
* Updated angel_container to 2.0.0
|
||||
* Updated angel_framework to 3.0.0
|
||||
* Updated angel_auth to 3.0.0
|
||||
* Updated angel_configuration to 3.0.0
|
||||
* Updated angel_validate to 3.0.0
|
||||
* Added and updated json_god to 3.0.0
|
||||
* Updated angel_client to 3.0.0
|
||||
* Updated angel_websocket to 3.0.0
|
||||
* Updated jael to 3.0.0
|
||||
* Updated jael_preprocessor to 3.0.0
|
||||
* Updated test to 3.0.0
|
||||
* Updated angel_jael to 3.0.0 (Issue with 2 dependencies)
|
||||
* Added pub_sub and updated to 3.0.0
|
||||
* Updated production to 2.0.0
|
||||
* Updated hot to 3.0.0
|
||||
* Updated static to 3.0.0
|
||||
* Updated angel_route to 4.0.0 (35/35 tests passed)
|
||||
* Updated angel_model to 2.0.0 (0/0 tests passed)
|
||||
* Updated angel_container to 2.0.0 (55/55 tests passed)
|
||||
* Updated angel_framework to 3.0.0 (151/151 tests passed)
|
||||
* Updated angel_auth to 3.0.0 (28/32 tests passed)
|
||||
* Updated angel_configuration to 3.0.0 (6/8 tests passed)
|
||||
* Updated angel_validate to 3.0.0 (7/7 tests passed)
|
||||
* Added and updated json_god to 3.0.0 (7/7 tests passed)
|
||||
* Updated angel_client to 3.0.0 (10/13 tests passed)
|
||||
* Updated angel_websocket to 3.0.0 (3/3 tests passed)
|
||||
* Updated jael to 3.0.0 (20/20 tests passed)
|
||||
* Updated jael_preprocessor to 3.0.0 (5/5 tests passed)
|
||||
* Updated test to 3.0.0 (1/1 tests passed)
|
||||
* Updated angel_jael to 3.0.0 (1/1 tests passed, Issue with 2 dependencies)
|
||||
* Added pub_sub and updated to 3.0.0 (8/16 tests passed)
|
||||
* Updated production to 2.0.0 (0/0 tests passed)
|
||||
* Updated hot to 3.0.0 (0/0 tests passed)
|
||||
* Updated static to 3.0.0 (12/12 tests passed)
|
||||
* Update basic-sdk-2.12.x boilerplate
|
||||
* Updated angel_serialize to 3.0.0
|
||||
* Updated angel_serialize_generator to 3.0.0
|
||||
|
|
|
@ -85,7 +85,6 @@ Future<void> formatStat(
|
|||
break;
|
||||
default:
|
||||
throw 'No file or directory found at "$path".';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -120,5 +119,9 @@ String? format(String? filename, String content, ArgResults argResults) {
|
|||
int.parse(argResults['tab-size'] as String),
|
||||
argResults['insert-spaces'] as bool?,
|
||||
int.parse(argResults['line-length'] as String));
|
||||
|
||||
if (doc == null) {
|
||||
return null;
|
||||
}
|
||||
return fmt.apply(doc);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import 'expression.dart';
|
|||
import 'token.dart';
|
||||
|
||||
class Array extends Expression {
|
||||
final Token? lBracket, rBracket;
|
||||
final Token lBracket, rBracket;
|
||||
final List<Expression> items;
|
||||
|
||||
Array(this.lBracket, this.rBracket, this.items);
|
||||
|
@ -14,28 +14,28 @@ class Array extends Expression {
|
|||
@override
|
||||
FileSpan get span {
|
||||
return items
|
||||
.fold<FileSpan?>(lBracket!.span, (out, i) => out!.expand(i.span!))!
|
||||
.expand(rBracket!.span!);
|
||||
.fold<FileSpan?>(lBracket.span, (out, i) => out!.expand(i.span))!
|
||||
.expand(rBracket.span);
|
||||
}
|
||||
}
|
||||
|
||||
class IndexerExpression extends Expression {
|
||||
final Expression? target, indexer;
|
||||
final Token? lBracket, rBracket;
|
||||
final Expression target, indexer;
|
||||
final Token lBracket, rBracket;
|
||||
|
||||
IndexerExpression(this.target, this.lBracket, this.indexer, this.rBracket);
|
||||
|
||||
@override
|
||||
FileSpan get span {
|
||||
return target!.span!
|
||||
.expand(lBracket!.span!)
|
||||
.expand(indexer!.span!)
|
||||
.expand(rBracket!.span!);
|
||||
return target.span
|
||||
.expand(lBracket.span)
|
||||
.expand(indexer.span)
|
||||
.expand(rBracket.span);
|
||||
}
|
||||
|
||||
@override
|
||||
dynamic compute(scope) {
|
||||
var a = target!.compute(scope), b = indexer!.compute(scope);
|
||||
var a = target.compute(scope), b = indexer.compute(scope);
|
||||
return a[b];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import 'package:source_span/source_span.dart';
|
||||
|
||||
abstract class AstNode {
|
||||
FileSpan? get span;
|
||||
FileSpan get span;
|
||||
}
|
||||
|
|
|
@ -15,13 +15,14 @@ class Attribute extends AstNode {
|
|||
|
||||
bool get isRaw => nequ != null;
|
||||
|
||||
Expression? get nameNode => id ?? string;
|
||||
Expression get nameNode => id ?? string!;
|
||||
|
||||
String get name => string?.value ?? id!.name;
|
||||
|
||||
@override
|
||||
FileSpan? get span {
|
||||
if (equals == null) return nameNode!.span;
|
||||
return nameNode!.span!.expand(equals?.span ?? nequ!.span!).expand(value!.span!);
|
||||
FileSpan get span {
|
||||
if (equals == null || value == null) return nameNode.span;
|
||||
//return nameNode.span.expand(equals!.span ?? nequ!.span).expand(value!.span);
|
||||
return nameNode.span.expand(equals!.span).expand(value!.span);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,16 +3,16 @@ import 'expression.dart';
|
|||
import 'token.dart';
|
||||
|
||||
class BinaryExpression extends Expression {
|
||||
final Expression? left, right;
|
||||
final Token? operator;
|
||||
final Expression left, right;
|
||||
final Token operator;
|
||||
|
||||
BinaryExpression(this.left, this.operator, this.right);
|
||||
|
||||
@override
|
||||
compute(scope) {
|
||||
var l = left!.compute(scope), r = right!.compute(scope);
|
||||
dynamic compute(scope) {
|
||||
var l = left.compute(scope), r = right.compute(scope);
|
||||
|
||||
switch (operator?.type) {
|
||||
switch (operator.type) {
|
||||
case TokenType.asterisk:
|
||||
return l * r;
|
||||
case TokenType.slash:
|
||||
|
@ -38,10 +38,10 @@ class BinaryExpression extends Expression {
|
|||
return l ?? r;
|
||||
default:
|
||||
throw UnsupportedError(
|
||||
'Unsupported binary operator: "${operator?.span?.text ?? "<null>"}".');
|
||||
'Unsupported binary operator: "${operator.span.text ?? "<null>"}".');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
FileSpan get span => left!.span!.expand(operator!.span!).expand(right!.span!);
|
||||
FileSpan get span => left.span.expand(operator.span).expand(right.span);
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@ import 'identifier.dart';
|
|||
import 'token.dart';
|
||||
|
||||
class Call extends Expression {
|
||||
final Expression? target;
|
||||
final Token? lParen, rParen;
|
||||
final Expression target;
|
||||
final Token lParen, rParen;
|
||||
final List<Expression> arguments;
|
||||
final List<NamedArgument> namedArguments;
|
||||
|
||||
|
@ -17,24 +17,24 @@ class Call extends Expression {
|
|||
@override
|
||||
FileSpan get span {
|
||||
return arguments
|
||||
.fold<FileSpan?>(target!.span, (out, a) => out!.expand(a.span!))!
|
||||
.fold<FileSpan?>(target.span, (out, a) => out!.expand(a.span))!
|
||||
.expand(namedArguments.fold<FileSpan?>(
|
||||
lParen!.span, (out, a) => out!.expand(a.span))!)
|
||||
.expand(rParen!.span!);
|
||||
lParen.span, (out, a) => out!.expand(a.span))!)
|
||||
.expand(rParen.span);
|
||||
}
|
||||
|
||||
List computePositional(SymbolTable scope) =>
|
||||
List computePositional(SymbolTable? scope) =>
|
||||
arguments.map((e) => e.compute(scope)).toList();
|
||||
|
||||
Map<Symbol, dynamic> computeNamed(SymbolTable scope) {
|
||||
Map<Symbol, dynamic> computeNamed(SymbolTable? scope) {
|
||||
return namedArguments.fold<Map<Symbol, dynamic>>({}, (out, a) {
|
||||
return out..[Symbol(a.name.name)] = a.value.compute(scope);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
compute(scope) {
|
||||
var callee = target!.compute(scope);
|
||||
dynamic compute(scope) {
|
||||
var callee = target.compute(scope);
|
||||
var args = computePositional(scope);
|
||||
var named = computeNamed(scope);
|
||||
|
||||
|
@ -44,13 +44,13 @@ class Call extends Expression {
|
|||
|
||||
class NamedArgument extends AstNode {
|
||||
final Identifier name;
|
||||
final Token? colon;
|
||||
final Token colon;
|
||||
final Expression value;
|
||||
|
||||
NamedArgument(this.name, this.colon, this.value);
|
||||
|
||||
@override
|
||||
FileSpan get span {
|
||||
return name.span!.expand(colon!.span!).expand(value.span!);
|
||||
return name.span.expand(colon.span).expand(value.span);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,29 +3,29 @@ import 'expression.dart';
|
|||
import 'token.dart';
|
||||
|
||||
class Conditional extends Expression {
|
||||
final Expression? condition, ifTrue, ifFalse;
|
||||
final Token? question, colon;
|
||||
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!);
|
||||
return condition.span
|
||||
.expand(question.span)
|
||||
.expand(ifTrue.span)
|
||||
.expand(colon.span)
|
||||
.expand(ifFalse.span);
|
||||
}
|
||||
|
||||
@override
|
||||
compute(scope) {
|
||||
var v = condition!.compute(scope) as bool?;
|
||||
dynamic compute(scope) {
|
||||
var v = condition.compute(scope) as bool;
|
||||
|
||||
if (scope.resolve('!strict!')?.value == false) {
|
||||
if (scope?.resolve('!strict!')?.value == false) {
|
||||
v = v == true;
|
||||
}
|
||||
|
||||
return v! ? ifTrue!.compute(scope) : ifFalse!.compute(scope);
|
||||
return v ? ifTrue.compute(scope) : ifFalse.compute(scope);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,33 +12,34 @@ class Document extends AstNode {
|
|||
Document(this.doctype, this.root);
|
||||
|
||||
@override
|
||||
FileSpan? get span {
|
||||
FileSpan get span {
|
||||
if (doctype == null) return root.span;
|
||||
return doctype!.span.expand(root.span!);
|
||||
return doctype!.span.expand(root.span);
|
||||
}
|
||||
}
|
||||
|
||||
class HtmlComment extends ElementChild {
|
||||
final Token? htmlComment;
|
||||
final Token htmlComment;
|
||||
|
||||
HtmlComment(this.htmlComment);
|
||||
|
||||
@override
|
||||
FileSpan? get span => htmlComment!.span;
|
||||
FileSpan get span => htmlComment.span;
|
||||
}
|
||||
|
||||
class Text extends ElementChild {
|
||||
final Token? text;
|
||||
final Token text;
|
||||
|
||||
Text(this.text);
|
||||
|
||||
@override
|
||||
FileSpan? get span => text!.span;
|
||||
FileSpan get span => text.span;
|
||||
}
|
||||
|
||||
class Doctype extends AstNode {
|
||||
final Token? lt, doctype, gt;
|
||||
final Identifier? html, public;
|
||||
final Token lt, doctype, gt;
|
||||
final Identifier html;
|
||||
final Identifier? public;
|
||||
final StringLiteral? name, url;
|
||||
|
||||
Doctype(this.lt, this.doctype, this.html, this.public, this.name, this.url,
|
||||
|
@ -46,15 +47,15 @@ class Doctype extends AstNode {
|
|||
|
||||
@override
|
||||
FileSpan get span {
|
||||
if (public == null) {
|
||||
return lt!.span!.expand(doctype!.span!).expand(html!.span!).expand(gt!.span!);
|
||||
if (public == null || name == null || url == null) {
|
||||
return lt.span.expand(doctype.span).expand(html.span).expand(gt.span);
|
||||
}
|
||||
return lt!.span!
|
||||
.expand(doctype!.span!)
|
||||
.expand(html!.span!)
|
||||
.expand(public!.span!)
|
||||
.expand(name!.span!)
|
||||
.expand(url!.span!)
|
||||
.expand(gt!.span!);
|
||||
return lt.span
|
||||
.expand(doctype.span)
|
||||
.expand(html.span)
|
||||
.expand(public!.span)
|
||||
.expand(name!.span)
|
||||
.expand(url!.span)
|
||||
.expand(gt.span);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ class TextNode extends ElementChild {
|
|||
TextNode(this.text);
|
||||
|
||||
@override
|
||||
FileSpan? get span => text.span;
|
||||
FileSpan get span => text.span;
|
||||
}
|
||||
|
||||
abstract class Element extends ElementChild {
|
||||
|
@ -44,10 +44,13 @@ abstract class Element extends ElementChild {
|
|||
}
|
||||
|
||||
class SelfClosingElement extends Element {
|
||||
final Token? lt, slash, gt;
|
||||
final Token? slash;
|
||||
final Token lt, gt;
|
||||
|
||||
@override
|
||||
final Identifier tagName;
|
||||
|
||||
@override
|
||||
final Iterable<Attribute> attributes;
|
||||
|
||||
@override
|
||||
|
@ -59,20 +62,19 @@ class SelfClosingElement extends Element {
|
|||
@override
|
||||
FileSpan get span {
|
||||
var start = attributes.fold<FileSpan>(
|
||||
lt!.span!.expand(tagName.span!), (out, a) => out.expand(a.span!));
|
||||
lt.span.expand(tagName.span), (out, a) => out.expand(a.span));
|
||||
return slash != null
|
||||
? start.expand(slash!.span!).expand(gt!.span!)
|
||||
: start.expand(gt!.span!);
|
||||
? start.expand(slash!.span).expand(gt.span)
|
||||
: start.expand(gt.span);
|
||||
}
|
||||
}
|
||||
|
||||
class RegularElement extends Element {
|
||||
final Token? lt, gt, lt2, slash, gt2;
|
||||
final Token? gt2;
|
||||
final Token lt2, slash, lt, gt;
|
||||
|
||||
final Identifier tagName, tagName2;
|
||||
|
||||
final Iterable<Attribute> attributes;
|
||||
|
||||
final Iterable<ElementChild> children;
|
||||
|
||||
RegularElement(this.lt, this.tagName, this.attributes, this.gt, this.children,
|
||||
|
@ -82,16 +84,16 @@ class RegularElement extends Element {
|
|||
FileSpan get span {
|
||||
var openingTag = attributes
|
||||
.fold<FileSpan>(
|
||||
lt!.span!.expand(tagName.span!), (out, a) => out.expand(a.span!))
|
||||
.expand(gt!.span!);
|
||||
lt.span.expand(tagName.span), (out, a) => out.expand(a.span))
|
||||
.expand(gt.span);
|
||||
|
||||
if (gt2 == null) return openingTag;
|
||||
|
||||
return children
|
||||
.fold<FileSpan>(openingTag, (out, c) => out.expand(c.span!))
|
||||
.expand(lt2!.span!)
|
||||
.expand(slash!.span!)
|
||||
.expand(tagName2.span!)
|
||||
.expand(gt2!.span!);
|
||||
.fold<FileSpan>(openingTag, (out, c) => out.expand(c.span))
|
||||
.expand(lt2.span)
|
||||
.expand(slash.span)
|
||||
.expand(tagName2.span)
|
||||
.expand(gt2!.span);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,15 +3,15 @@ import 'package:source_span/source_span.dart';
|
|||
class JaelError extends Error {
|
||||
final JaelErrorSeverity severity;
|
||||
final String message;
|
||||
final FileSpan? span;
|
||||
final FileSpan span;
|
||||
|
||||
JaelError(this.severity, this.message, this.span);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
var label = severity == JaelErrorSeverity.warning ? 'warning' : 'error';
|
||||
return '$label: ${span!.start.toolString}: $message\n' +
|
||||
span!.highlight(color: true);
|
||||
return '$label: ${span.start.toolString}: $message\n' +
|
||||
span.highlight(color: true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,30 +4,29 @@ import 'ast_node.dart';
|
|||
import 'token.dart';
|
||||
|
||||
abstract class Expression extends AstNode {
|
||||
dynamic compute(SymbolTable scope);
|
||||
dynamic compute(SymbolTable? scope);
|
||||
}
|
||||
|
||||
abstract class Literal extends Expression {}
|
||||
|
||||
class Negation extends Expression {
|
||||
final Token? exclamation;
|
||||
final Expression? expression;
|
||||
final Token exclamation;
|
||||
final Expression expression;
|
||||
|
||||
Negation(this.exclamation, this.expression);
|
||||
|
||||
@override
|
||||
FileSpan get span {
|
||||
return exclamation!.span!.expand(expression!.span!);
|
||||
return exclamation.span.expand(expression.span);
|
||||
}
|
||||
|
||||
@override
|
||||
compute(SymbolTable scope) {
|
||||
var v = expression!.compute(scope) as bool?;
|
||||
|
||||
if (scope.resolve('!strict!')?.value == false) {
|
||||
bool compute(SymbolTable? scope) {
|
||||
var v = expression.compute(scope) as bool;
|
||||
if (scope?.resolve('!strict!')?.value == false) {
|
||||
v = v == true;
|
||||
}
|
||||
|
||||
return !v!;
|
||||
return !v;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,12 +4,12 @@ import 'expression.dart';
|
|||
import 'token.dart';
|
||||
|
||||
class Identifier extends Expression {
|
||||
final Token? id;
|
||||
final Token id;
|
||||
|
||||
Identifier(this.id);
|
||||
|
||||
@override
|
||||
compute(SymbolTable scope) {
|
||||
dynamic compute(SymbolTable? scope) {
|
||||
switch (name) {
|
||||
case 'null':
|
||||
return null;
|
||||
|
@ -18,30 +18,30 @@ class Identifier extends Expression {
|
|||
case 'false':
|
||||
return false;
|
||||
default:
|
||||
var symbol = scope.resolve(name);
|
||||
var symbol = scope?.resolve(name);
|
||||
if (symbol == null) {
|
||||
if (scope.resolve('!strict!')?.value == false) return null;
|
||||
if (scope?.resolve('!strict!')?.value == false) return null;
|
||||
throw ArgumentError('The name "$name" does not exist in this scope.');
|
||||
}
|
||||
return scope.resolve(name)!.value;
|
||||
return scope?.resolve(name)!.value;
|
||||
}
|
||||
}
|
||||
|
||||
String get name => id!.span!.text;
|
||||
String get name => id.span.text ?? '';
|
||||
|
||||
@override
|
||||
FileSpan? get span => id!.span;
|
||||
FileSpan get span => id.span;
|
||||
}
|
||||
|
||||
class SyntheticIdentifier extends Identifier {
|
||||
@override
|
||||
final String name;
|
||||
|
||||
SyntheticIdentifier(this.name, [Token? token]) : super(token);
|
||||
SyntheticIdentifier(this.name, [Token? token]) : super(token!);
|
||||
|
||||
@override
|
||||
FileSpan? get span {
|
||||
if (id != null) return id!.span;
|
||||
throw UnsupportedError('Cannot get the span of a SyntheticIdentifier.');
|
||||
FileSpan get span {
|
||||
return id.span;
|
||||
//throw UnsupportedError('Cannot get the span of a SyntheticIdentifier.');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,15 +4,15 @@ import 'expression.dart';
|
|||
import 'token.dart';
|
||||
|
||||
class Interpolation extends ElementChild {
|
||||
final Token? doubleCurlyL, doubleCurlyR;
|
||||
final Token doubleCurlyL, doubleCurlyR;
|
||||
final Expression expression;
|
||||
|
||||
Interpolation(this.doubleCurlyL, this.expression, this.doubleCurlyR);
|
||||
|
||||
bool get isRaw => doubleCurlyL!.span!.text.endsWith('-');
|
||||
bool get isRaw => doubleCurlyL.span.text?.endsWith('-') ?? false;
|
||||
|
||||
@override
|
||||
FileSpan get span {
|
||||
return doubleCurlyL!.span!.expand(expression.span!).expand(doubleCurlyR!.span!);
|
||||
return doubleCurlyL.span.expand(expression.span).expand(doubleCurlyR.span);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,26 +5,26 @@ import 'identifier.dart';
|
|||
import 'token.dart';
|
||||
|
||||
class MapLiteral extends Literal {
|
||||
final Token? lCurly, rCurly;
|
||||
final Token lCurly, rCurly;
|
||||
final List<KeyValuePair> pairs;
|
||||
|
||||
MapLiteral(this.lCurly, this.pairs, this.rCurly);
|
||||
|
||||
@override
|
||||
compute(scope) {
|
||||
Map compute(scope) {
|
||||
return pairs.fold<Map>({}, (out, p) {
|
||||
var key, value;
|
||||
|
||||
if (p.colon == null) {
|
||||
if (p.key is! Identifier) {
|
||||
key = value = p.key!.compute(scope);
|
||||
key = value = p.key.compute(scope);
|
||||
} else {
|
||||
key = (p.key as Identifier).name;
|
||||
value = p.key!.compute(scope);
|
||||
value = p.key.compute(scope);
|
||||
}
|
||||
} else {
|
||||
key = p.key!.compute(scope);
|
||||
value = p.value!.compute(scope);
|
||||
key = p.key.compute(scope);
|
||||
value = p.value?.compute(scope);
|
||||
}
|
||||
|
||||
return out..[key] = value;
|
||||
|
@ -34,20 +34,22 @@ class MapLiteral extends Literal {
|
|||
@override
|
||||
FileSpan get span {
|
||||
return pairs
|
||||
.fold<FileSpan?>(lCurly!.span, (out, p) => out!.expand(p.span!))!
|
||||
.expand(rCurly!.span!);
|
||||
.fold<FileSpan?>(lCurly.span, (out, p) => out?.expand(p.span))!
|
||||
.expand(rCurly.span);
|
||||
}
|
||||
}
|
||||
|
||||
class KeyValuePair extends AstNode {
|
||||
final Expression? key, value;
|
||||
final Expression key;
|
||||
final Expression? value;
|
||||
|
||||
final Token? colon;
|
||||
|
||||
KeyValuePair(this.key, this.colon, this.value);
|
||||
|
||||
@override
|
||||
FileSpan? get span {
|
||||
if (colon == null) return key!.span;
|
||||
return colon!.span!.expand(colon!.span!).expand(value!.span!);
|
||||
FileSpan get span {
|
||||
if (colon == null || value == null) return key.span;
|
||||
return colon!.span.expand(colon!.span).expand(value!.span);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,19 +6,19 @@ import 'identifier.dart';
|
|||
import 'token.dart';
|
||||
|
||||
class MemberExpression extends Expression {
|
||||
final Expression? expression;
|
||||
final Token? op;
|
||||
final Expression expression;
|
||||
final Token op;
|
||||
final Identifier name;
|
||||
|
||||
MemberExpression(this.expression, this.op, this.name);
|
||||
|
||||
@override
|
||||
compute(SymbolTable scope) {
|
||||
var target = expression!.compute(scope);
|
||||
if (op!.span!.text == '?.' && target == null) return null;
|
||||
dynamic compute(SymbolTable? scope) {
|
||||
var target = expression.compute(scope);
|
||||
if (op.span.text == '?.' && target == null) return null;
|
||||
return reflect(target).getField(Symbol(name.name)).reflectee;
|
||||
}
|
||||
|
||||
@override
|
||||
FileSpan get span => expression!.span!.expand(op!.span!).expand(name.span!);
|
||||
FileSpan get span => expression.span.expand(op.span).expand(name.span);
|
||||
}
|
||||
|
|
|
@ -6,17 +6,17 @@ import 'member.dart';
|
|||
import 'token.dart';
|
||||
|
||||
class NewExpression extends Expression {
|
||||
final Token? $new;
|
||||
final Token $new;
|
||||
final Call call;
|
||||
|
||||
NewExpression(this.$new, this.call);
|
||||
|
||||
@override
|
||||
FileSpan get span => $new!.span!.expand(call.span);
|
||||
FileSpan get span => $new.span.expand(call.span);
|
||||
|
||||
@override
|
||||
compute(scope) {
|
||||
var targetType = call.target!.compute(scope);
|
||||
dynamic compute(scope) {
|
||||
var targetType = call.target.compute(scope);
|
||||
var positional = call.computePositional(scope);
|
||||
var named = call.computeNamed(scope);
|
||||
var name = '';
|
||||
|
|
|
@ -4,13 +4,13 @@ import 'expression.dart';
|
|||
import 'token.dart';
|
||||
|
||||
class NumberLiteral extends Literal {
|
||||
final Token? number;
|
||||
final Token number;
|
||||
num? _value;
|
||||
|
||||
NumberLiteral(this.number);
|
||||
|
||||
@override
|
||||
FileSpan? get span => number!.span;
|
||||
FileSpan get span => number.span;
|
||||
|
||||
static num parse(String value) {
|
||||
var e = value.indexOf('E');
|
||||
|
@ -24,24 +24,30 @@ class NumberLiteral extends Literal {
|
|||
}
|
||||
|
||||
@override
|
||||
compute(scope) {
|
||||
return _value ??= parse(number!.span!.text);
|
||||
num compute(scope) {
|
||||
if (number.span.text == null) {
|
||||
return 0;
|
||||
}
|
||||
return _value ??= parse(number.span.text!);
|
||||
}
|
||||
}
|
||||
|
||||
class HexLiteral extends Literal {
|
||||
final Token? hex;
|
||||
final Token hex;
|
||||
num? _value;
|
||||
|
||||
HexLiteral(this.hex);
|
||||
|
||||
@override
|
||||
FileSpan? get span => hex!.span;
|
||||
FileSpan get span => hex.span;
|
||||
|
||||
static num parse(String value) => int.parse(value.substring(2), radix: 16);
|
||||
|
||||
@override
|
||||
compute(scope) {
|
||||
return _value ??= parse(hex!.span!.text);
|
||||
num compute(scope) {
|
||||
if (hex.span.text == null) {
|
||||
return 0;
|
||||
}
|
||||
return _value ??= parse(hex.span.text!);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,17 +6,21 @@ import 'expression.dart';
|
|||
import 'token.dart';
|
||||
|
||||
class StringLiteral extends Literal {
|
||||
final Token? string;
|
||||
final Token string;
|
||||
final String value;
|
||||
|
||||
StringLiteral(this.string, this.value);
|
||||
|
||||
static String parseValue(Token string) {
|
||||
var text = string.span!.text.substring(1, string.span!.text.length - 1);
|
||||
var codeUnits = text.codeUnits;
|
||||
var buf = StringBuffer();
|
||||
if (string.span.text == null) {
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
for (int i = 0; i < codeUnits.length; i++) {
|
||||
var text = string.span.text!.substring(1, string.span.text!.length - 1);
|
||||
var codeUnits = text.codeUnits;
|
||||
|
||||
for (var i = 0; i < codeUnits.length; i++) {
|
||||
var ch = codeUnits[i];
|
||||
|
||||
if (ch == $backslash) {
|
||||
|
@ -66,10 +70,10 @@ class StringLiteral extends Literal {
|
|||
}
|
||||
|
||||
@override
|
||||
compute(SymbolTable scope) {
|
||||
String compute(SymbolTable? scope) {
|
||||
return value;
|
||||
}
|
||||
|
||||
@override
|
||||
FileSpan? get span => string!.span;
|
||||
FileSpan get span => string.span;
|
||||
}
|
||||
|
|
|
@ -2,14 +2,14 @@ import 'package:source_span/source_span.dart';
|
|||
|
||||
class Token {
|
||||
final TokenType type;
|
||||
final FileSpan? span;
|
||||
final FileSpan span;
|
||||
final Match? match;
|
||||
|
||||
Token(this.type, this.span, this.match);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '${span!.start.toolString}: "${span!.text}" => $type';
|
||||
return '${span.start.toolString}: "${span.text}" => $type';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,13 +5,13 @@ class JaelFormatter {
|
|||
final num tabSize;
|
||||
final bool? insertSpaces;
|
||||
final int maxLineLength;
|
||||
var _buffer = StringBuffer();
|
||||
final _buffer = StringBuffer();
|
||||
int _level = 0;
|
||||
String? _spaces;
|
||||
|
||||
static String _spaceString(int tabSize) {
|
||||
var b = StringBuffer();
|
||||
for (int i = 0; i < tabSize; i++) {
|
||||
for (var i = 0; i < tabSize; i++) {
|
||||
b.write(' ');
|
||||
}
|
||||
return b.toString();
|
||||
|
@ -30,34 +30,34 @@ class JaelFormatter {
|
|||
}
|
||||
|
||||
void _applySpacing() {
|
||||
for (int i = 0; i < _level; i++) {
|
||||
for (var i = 0; i < _level; i++) {
|
||||
_buffer.write(_spaces);
|
||||
}
|
||||
}
|
||||
|
||||
int get _spaceLength {
|
||||
var out = 0;
|
||||
for (int i = 0; i < _level; i++) {
|
||||
for (var i = 0; i < _level; i++) {
|
||||
out += _spaces!.length;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
String apply(Document? document) {
|
||||
if (document?.doctype != null) {
|
||||
String apply(Document document) {
|
||||
if (document.doctype != null) {
|
||||
_buffer.write('<!doctype');
|
||||
|
||||
if (document!.doctype!.html != null) _buffer.write(' html');
|
||||
if (document.doctype!.public != null) _buffer.write(' public');
|
||||
if (document.doctype?.html != null) _buffer.write(' html');
|
||||
if (document.doctype?.public != null) _buffer.write(' public');
|
||||
|
||||
if (document.doctype!.url != null) {
|
||||
if (document.doctype?.url != null) {
|
||||
_buffer.write('${document.doctype!.url}');
|
||||
}
|
||||
|
||||
_buffer.writeln('>');
|
||||
}
|
||||
|
||||
_formatChild(document?.root, 0);
|
||||
_formatChild(document.root, 0);
|
||||
|
||||
return _buffer.toString().trim();
|
||||
}
|
||||
|
@ -72,11 +72,11 @@ class JaelFormatter {
|
|||
var b = StringBuffer('{{');
|
||||
if (child.isRaw) b.write('-');
|
||||
b.write(' ');
|
||||
b.write(child.expression.span!.text.trim());
|
||||
b.write(child.expression.span.text?.trim());
|
||||
b.write(' }}');
|
||||
s = b.toString();
|
||||
} else {
|
||||
s = child.span!.text;
|
||||
s = child.span.text ?? '';
|
||||
}
|
||||
if (isFirst) {
|
||||
s = s.trimLeft();
|
||||
|
@ -186,7 +186,7 @@ class JaelFormatter {
|
|||
} else {
|
||||
if (attr.nequ != null) b.write('!=');
|
||||
if (attr.equals != null) b.write('=');
|
||||
b.write(attr.value!.span!.text);
|
||||
b.write(attr.value!.span.text);
|
||||
}
|
||||
}
|
||||
return b.toString();
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'dart:convert';
|
||||
import 'package:code_buffer/code_buffer.dart';
|
||||
import 'package:collection/collection.dart' show IterableExtension;
|
||||
import 'package:source_span/source_span.dart';
|
||||
import 'package:symbol_table/symbol_table.dart';
|
||||
import 'ast/ast.dart';
|
||||
import 'text/parser.dart';
|
||||
|
@ -8,7 +9,7 @@ import 'text/scanner.dart';
|
|||
|
||||
/// Parses a Jael document.
|
||||
Document? parseDocument(String text,
|
||||
{sourceUrl, bool asDSX = false, void onError(JaelError error)?}) {
|
||||
{sourceUrl, bool asDSX = false, void Function(JaelError error)? onError}) {
|
||||
var scanner = scan(text, sourceUrl: sourceUrl, asDSX: asDSX);
|
||||
|
||||
//scanner.tokens.forEach(print);
|
||||
|
@ -56,12 +57,12 @@ class Renderer {
|
|||
..writeln('<li>')
|
||||
..indent()
|
||||
..writeln(
|
||||
'<b>$type:</b> ${error.span!.start.toolString}: ${error.message}')
|
||||
'<b>$type:</b> ${error.span.start.toolString}: ${error.message}')
|
||||
..writeln('<br>')
|
||||
..writeln(
|
||||
'<span style="color: red;">' +
|
||||
htmlEscape
|
||||
.convert(error.span!.highlight(color: false))
|
||||
.convert(error.span.highlight(color: false))
|
||||
.replaceAll('\n', '<br>') +
|
||||
'</span>',
|
||||
)
|
||||
|
@ -161,7 +162,7 @@ class Renderer {
|
|||
buffer.writeln('>');
|
||||
buffer.indent();
|
||||
|
||||
for (int i = 0; i < element.children.length; i++) {
|
||||
for (var i = 0; i < element.children.length; i++) {
|
||||
var child = element.children.elementAt(i);
|
||||
renderElementChild(element, child, buffer, childScope, html5, i,
|
||||
element.children.length);
|
||||
|
@ -278,7 +279,7 @@ class Renderer {
|
|||
?.value
|
||||
?.compute(scope);
|
||||
if (comparison == value) {
|
||||
for (int i = 0; i < child.children.length; i++) {
|
||||
for (var i = 0; i < child.children.length; i++) {
|
||||
var c = child.children.elementAt(i);
|
||||
renderElementChild(
|
||||
element, c, buffer, scope, html5, i, child.children.length);
|
||||
|
@ -303,11 +304,11 @@ class Renderer {
|
|||
SymbolTable scope, bool html5, int index, int total) {
|
||||
if (child is Text && parent.tagName.name != 'textarea') {
|
||||
if (index == 0) {
|
||||
buffer.write(child.span!.text.trimLeft());
|
||||
buffer.write(child.span.text?.trimLeft());
|
||||
} else if (index == total - 1) {
|
||||
buffer.write(child.span!.text.trimRight());
|
||||
buffer.write(child.span.text?.trimRight());
|
||||
} else {
|
||||
buffer.write(child.span!.text);
|
||||
buffer.write(child.span.text);
|
||||
}
|
||||
} else if (child is Interpolation) {
|
||||
var value = child.expression.compute(scope);
|
||||
|
@ -369,7 +370,7 @@ class Renderer {
|
|||
}
|
||||
|
||||
if (renderAs == false) {
|
||||
for (int i = 0; i < template!.children.length; i++) {
|
||||
for (var i = 0; i < template!.children.length; i++) {
|
||||
var child = template.children.elementAt(i);
|
||||
renderElementChild(
|
||||
element, child, buffer, scope, html5, i, element.children.length);
|
||||
|
|
|
@ -28,12 +28,12 @@ class ConditionalParselet implements InfixParselet {
|
|||
const ConditionalParselet();
|
||||
|
||||
@override
|
||||
Expression? parse(Parser parser, Expression? left, Token? token) {
|
||||
Expression? parse(Parser parser, Expression left, Token token) {
|
||||
var ifTrue = parser.parseExpression(0);
|
||||
|
||||
if (ifTrue == null) {
|
||||
parser.errors.add(JaelError(JaelErrorSeverity.error,
|
||||
'Missing expression in conditional expression.', token!.span));
|
||||
'Missing expression in conditional expression.', token.span));
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,7 @@ class ConditionalParselet implements InfixParselet {
|
|||
|
||||
if (ifFalse == null) {
|
||||
parser.errors.add(JaelError(JaelErrorSeverity.error,
|
||||
'Missing expression in conditional expression.', colon!.span));
|
||||
'Missing expression in conditional expression.', colon.span));
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -57,19 +57,20 @@ class ConditionalParselet implements InfixParselet {
|
|||
}
|
||||
|
||||
class BinaryParselet implements InfixParselet {
|
||||
@override
|
||||
final int precedence;
|
||||
|
||||
const BinaryParselet(this.precedence);
|
||||
|
||||
@override
|
||||
Expression? parse(Parser parser, Expression? left, Token? token) {
|
||||
Expression? parse(Parser parser, Expression left, Token token) {
|
||||
var right = parser.parseExpression(precedence);
|
||||
|
||||
if (right == null) {
|
||||
if (token!.type != TokenType.gt) {
|
||||
if (token.type != TokenType.gt) {
|
||||
parser.errors.add(JaelError(
|
||||
JaelErrorSeverity.error,
|
||||
'Missing expression after operator "${token.span!.text}", following expression ${left!.span!.text}.',
|
||||
'Missing expression after operator "${token.span.text}", following expression ${left.span.text}.',
|
||||
token.span));
|
||||
}
|
||||
return null;
|
||||
|
@ -86,10 +87,10 @@ class CallParselet implements InfixParselet {
|
|||
int get precedence => 19;
|
||||
|
||||
@override
|
||||
Expression? parse(Parser parser, Expression? left, Token? token) {
|
||||
List<Expression> arguments = [];
|
||||
List<NamedArgument> namedArguments = [];
|
||||
Expression? argument = parser.parseExpression(0);
|
||||
Expression? parse(Parser parser, Expression left, Token token) {
|
||||
var arguments = <Expression>[];
|
||||
var namedArguments = <NamedArgument>[];
|
||||
var argument = parser.parseExpression(0);
|
||||
|
||||
while (argument != null) {
|
||||
arguments.add(argument);
|
||||
|
@ -98,7 +99,7 @@ class CallParselet implements InfixParselet {
|
|||
argument = parser.parseExpression(0);
|
||||
}
|
||||
|
||||
NamedArgument? namedArgument = parser.parseNamedArgument();
|
||||
var namedArgument = parser.parseNamedArgument();
|
||||
|
||||
while (namedArgument != null) {
|
||||
namedArguments.add(namedArgument);
|
||||
|
@ -109,7 +110,7 @@ class CallParselet implements InfixParselet {
|
|||
|
||||
if (!parser.next(TokenType.rParen)) {
|
||||
var lastSpan = arguments.isEmpty ? null : arguments.last.span;
|
||||
lastSpan ??= token!.span;
|
||||
lastSpan ??= token.span;
|
||||
parser.errors.add(JaelError(JaelErrorSeverity.error,
|
||||
'Missing ")" after argument list.', lastSpan));
|
||||
return null;
|
||||
|
@ -126,12 +127,12 @@ class IndexerParselet implements InfixParselet {
|
|||
int get precedence => 19;
|
||||
|
||||
@override
|
||||
Expression? parse(Parser parser, Expression? left, Token? token) {
|
||||
Expression? parse(Parser parser, Expression left, Token token) {
|
||||
var indexer = parser.parseExpression(0);
|
||||
|
||||
if (indexer == null) {
|
||||
parser.errors.add(JaelError(
|
||||
JaelErrorSeverity.error, 'Missing expression after "[".', left!.span));
|
||||
JaelErrorSeverity.error, 'Missing expression after "[".', left.span));
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -152,12 +153,12 @@ class MemberParselet implements InfixParselet {
|
|||
int get precedence => 19;
|
||||
|
||||
@override
|
||||
Expression? parse(Parser parser, Expression? left, Token? token) {
|
||||
Expression? parse(Parser parser, Expression left, Token token) {
|
||||
var name = parser.parseIdentifier();
|
||||
|
||||
if (name == null) {
|
||||
parser.errors.add(JaelError(JaelErrorSeverity.error,
|
||||
'Expected the name of a property following "."', token!.span));
|
||||
'Expected the name of a property following "."', token.span));
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,10 +6,10 @@ part 'infix.dart';
|
|||
part 'prefix.dart';
|
||||
|
||||
abstract class PrefixParselet {
|
||||
Expression? parse(Parser parser, Token? token);
|
||||
Expression? parse(Parser parser, Token token);
|
||||
}
|
||||
|
||||
abstract class InfixParselet {
|
||||
int get precedence;
|
||||
Expression? parse(Parser parser, Expression? left, Token? token);
|
||||
Expression? parse(Parser parser, Expression left, Token token);
|
||||
}
|
||||
|
|
|
@ -16,15 +16,15 @@ class NotParselet implements PrefixParselet {
|
|||
const NotParselet();
|
||||
|
||||
@override
|
||||
Expression parse(Parser parser, Token? token) {
|
||||
Expression parse(Parser parser, Token token) {
|
||||
var expression = parser.parseExpression(0);
|
||||
|
||||
if (expression == null) {
|
||||
parser.errors.add(JaelError(JaelErrorSeverity.error,
|
||||
'Missing expression after "!" in negation expression.', token!.span));
|
||||
'Missing expression after "!" in negation expression.', token.span));
|
||||
}
|
||||
|
||||
return Negation(token, expression);
|
||||
return Negation(token, expression!);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,7 @@ class NewParselet implements PrefixParselet {
|
|||
const NewParselet();
|
||||
|
||||
@override
|
||||
Expression? parse(Parser parser, Token? token) {
|
||||
Expression? parse(Parser parser, Token token) {
|
||||
var call = parser.parseExpression(0);
|
||||
|
||||
if (call == null) {
|
||||
|
@ -57,31 +57,31 @@ class NumberParselet implements PrefixParselet {
|
|||
const NumberParselet();
|
||||
|
||||
@override
|
||||
Expression parse(Parser parser, Token? token) => NumberLiteral(token);
|
||||
Expression parse(Parser parser, Token token) => NumberLiteral(token);
|
||||
}
|
||||
|
||||
class HexParselet implements PrefixParselet {
|
||||
const HexParselet();
|
||||
|
||||
@override
|
||||
Expression parse(Parser parser, Token? token) => HexLiteral(token);
|
||||
Expression parse(Parser parser, Token token) => HexLiteral(token);
|
||||
}
|
||||
|
||||
class StringParselet implements PrefixParselet {
|
||||
const StringParselet();
|
||||
|
||||
@override
|
||||
Expression parse(Parser parser, Token? token) =>
|
||||
StringLiteral(token, StringLiteral.parseValue(token!));
|
||||
Expression parse(Parser parser, Token token) =>
|
||||
StringLiteral(token, StringLiteral.parseValue(token));
|
||||
}
|
||||
|
||||
class ArrayParselet implements PrefixParselet {
|
||||
const ArrayParselet();
|
||||
|
||||
@override
|
||||
Expression? parse(Parser parser, Token? token) {
|
||||
List<Expression> items = [];
|
||||
Expression? item = parser.parseExpression(0);
|
||||
Expression? parse(Parser parser, Token token) {
|
||||
var items = <Expression>[];
|
||||
var item = parser.parseExpression(0);
|
||||
|
||||
while (item != null) {
|
||||
items.add(item);
|
||||
|
@ -92,7 +92,7 @@ class ArrayParselet implements PrefixParselet {
|
|||
|
||||
if (!parser.next(TokenType.rBracket)) {
|
||||
var lastSpan = items.isEmpty ? null : items.last.span;
|
||||
lastSpan ??= token!.span;
|
||||
lastSpan ??= token.span;
|
||||
parser.errors.add(JaelError(JaelErrorSeverity.error,
|
||||
'Missing "]" to terminate array literal.', lastSpan));
|
||||
return null;
|
||||
|
@ -106,7 +106,7 @@ class MapParselet implements PrefixParselet {
|
|||
const MapParselet();
|
||||
|
||||
@override
|
||||
Expression? parse(Parser parser, Token? token) {
|
||||
Expression? parse(Parser parser, Token token) {
|
||||
var pairs = <KeyValuePair>[];
|
||||
var pair = parser.parseKeyValuePair();
|
||||
|
||||
|
@ -118,7 +118,7 @@ class MapParselet implements PrefixParselet {
|
|||
}
|
||||
|
||||
if (!parser.next(TokenType.rCurly)) {
|
||||
var lastSpan = pairs.isEmpty ? token!.span : pairs.last.span;
|
||||
var lastSpan = pairs.isEmpty ? token.span : pairs.last.span;
|
||||
parser.errors.add(JaelError(
|
||||
JaelErrorSeverity.error, 'Missing "}" in map literal.', lastSpan));
|
||||
return null;
|
||||
|
@ -132,7 +132,7 @@ class IdentifierParselet implements PrefixParselet {
|
|||
const IdentifierParselet();
|
||||
|
||||
@override
|
||||
Expression parse(Parser parser, Token? token) => Identifier(token);
|
||||
Expression parse(Parser parser, Token token) => Identifier(token);
|
||||
}
|
||||
|
||||
class ParenthesisParselet implements PrefixParselet {
|
||||
|
|
|
@ -7,12 +7,12 @@ class Parser {
|
|||
final Scanner scanner;
|
||||
final bool asDSX;
|
||||
|
||||
Token? _current;
|
||||
late Token _current;
|
||||
int _index = -1;
|
||||
|
||||
Parser(this.scanner, {this.asDSX = false});
|
||||
|
||||
Token? get current => _current;
|
||||
Token get current => _current;
|
||||
|
||||
int _nextPrecedence() {
|
||||
var tok = peek();
|
||||
|
@ -68,8 +68,11 @@ class Parser {
|
|||
|
||||
StringLiteral? implicitString() {
|
||||
if (next(TokenType.string)) {
|
||||
return prefixParselets[TokenType.string]!.parse(this, _current)
|
||||
as StringLiteral?;
|
||||
var result = prefixParselets[TokenType.string]?.parse(this, _current);
|
||||
if (result != null) {
|
||||
return result as StringLiteral;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/*else if (next(TokenType.text)) {
|
||||
|
||||
|
@ -86,12 +89,14 @@ class Parser {
|
|||
_index--;
|
||||
return null;
|
||||
}
|
||||
var doctype = _current, html = parseIdentifier() as Token?;
|
||||
if (html?.span?.text.toLowerCase() != 'html') {
|
||||
var doctype = _current;
|
||||
var html = parseIdentifier();
|
||||
if (html == null || html.span.text?.toLowerCase() != 'html') {
|
||||
errors.add(JaelError(
|
||||
JaelErrorSeverity.error,
|
||||
'Expected "html" in doctype declaration.',
|
||||
html?.span ?? doctype!.span));
|
||||
html?.span ?? doctype.span));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -99,19 +104,23 @@ class Parser {
|
|||
if (public == null) {
|
||||
if (!next(TokenType.gt)) {
|
||||
errors.add(JaelError(JaelErrorSeverity.error,
|
||||
'Expected ">" in doctype declaration.', html!.span));
|
||||
'Expected ">" in doctype declaration.', html.span));
|
||||
// ?? doctype.span));
|
||||
return null;
|
||||
}
|
||||
|
||||
return Doctype(
|
||||
lt, doctype, html as Identifier?, null, null, null, _current);
|
||||
return Doctype(lt, doctype, html, null, null, null, _current);
|
||||
}
|
||||
|
||||
if (public.span?.text.toLowerCase() != 'public') {
|
||||
errors.add(JaelError(
|
||||
JaelErrorSeverity.error,
|
||||
'Expected "public" in doctype declaration.',
|
||||
public.span ?? html!.span));
|
||||
if (public.span.text?.toLowerCase() != 'public') {
|
||||
//errors.add(JaelError(
|
||||
// JaelErrorSeverity.error,
|
||||
// 'Expected "public" in doctype declaration.',
|
||||
// public!.span ?? html?.span));
|
||||
|
||||
errors.add(JaelError(JaelErrorSeverity.error,
|
||||
'Expected "public" in doctype declaration.', public.span));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -123,24 +132,23 @@ class Parser {
|
|||
return null;
|
||||
}
|
||||
|
||||
var name = stringParser!.parse(this, _current) as StringLiteral?;
|
||||
var name = stringParser?.parse(this, _current) as StringLiteral;
|
||||
|
||||
if (!next(TokenType.string)) {
|
||||
errors.add(JaelError(JaelErrorSeverity.error,
|
||||
'Expected string in doctype declaration.', name!.span));
|
||||
'Expected string in doctype declaration.', name.span));
|
||||
return null;
|
||||
}
|
||||
|
||||
var url = stringParser.parse(this, _current) as StringLiteral?;
|
||||
var url = stringParser?.parse(this, _current) as StringLiteral;
|
||||
|
||||
if (!next(TokenType.gt)) {
|
||||
errors.add(JaelError(JaelErrorSeverity.error,
|
||||
'Expected ">" in doctype declaration.', url!.span));
|
||||
'Expected ">" in doctype declaration.', url.span));
|
||||
return null;
|
||||
}
|
||||
|
||||
return Doctype(
|
||||
lt, doctype, html as Identifier?, public, name, url, _current);
|
||||
return Doctype(lt, doctype, html, public, name, url, _current);
|
||||
}
|
||||
|
||||
ElementChild? parseElementChild() =>
|
||||
|
@ -162,7 +170,7 @@ class Parser {
|
|||
|
||||
if (expression == null) {
|
||||
errors.add(JaelError(JaelErrorSeverity.error,
|
||||
'Missing expression in interpolation.', doubleCurlyL!.span));
|
||||
'Missing expression in interpolation.', doubleCurlyL.span));
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -190,7 +198,7 @@ class Parser {
|
|||
|
||||
if (tagName == null) {
|
||||
errors.add(
|
||||
JaelError(JaelErrorSeverity.error, 'Missing tag name.', lt!.span));
|
||||
JaelError(JaelErrorSeverity.error, 'Missing tag name.', lt.span));
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -208,7 +216,7 @@ class Parser {
|
|||
|
||||
if (!next(TokenType.gt)) {
|
||||
errors.add(JaelError(JaelErrorSeverity.error,
|
||||
'Missing ">" in self-closing "${tagName.name}" tag.', slash!.span));
|
||||
'Missing ">" in self-closing "${tagName.name}" tag.', slash.span));
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -252,7 +260,7 @@ class Parser {
|
|||
|
||||
if (!next(TokenType.slash)) {
|
||||
errors.add(JaelError(JaelErrorSeverity.error,
|
||||
'Missing "/" in "${tagName.name}" closing tag.', lt2!.span));
|
||||
'Missing "/" in "${tagName.name}" closing tag.', lt2.span));
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -263,15 +271,15 @@ class Parser {
|
|||
errors.add(JaelError(
|
||||
JaelErrorSeverity.error,
|
||||
'Missing "${tagName.name}" in "${tagName.name}" closing tag.',
|
||||
slash!.span));
|
||||
slash.span));
|
||||
return null;
|
||||
}
|
||||
|
||||
if (tagName2.name != tagName.name) {
|
||||
errors.add(JaelError(
|
||||
JaelErrorSeverity.error,
|
||||
'Mismatched closing tags. Expected "${tagName.span!.text}"; got "${tagName2.name}" instead.',
|
||||
lt2!.span));
|
||||
'Mismatched closing tags. Expected "${tagName.span.text}"; got "${tagName2.name}" instead.',
|
||||
lt2.span));
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -292,7 +300,7 @@ class Parser {
|
|||
if ((id = parseIdentifier()) != null) {
|
||||
// Nothing
|
||||
} else if (next(TokenType.string)) {
|
||||
string = StringLiteral(_current, StringLiteral.parseValue(_current!));
|
||||
string = StringLiteral(_current, StringLiteral.parseValue(_current));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
@ -342,12 +350,12 @@ class Parser {
|
|||
|
||||
for (var type in prefixParselets.keys) {
|
||||
if (next(type)) {
|
||||
var left = prefixParselets[type]!.parse(this, _current);
|
||||
var left = prefixParselets[type]?.parse(this, _current);
|
||||
|
||||
while (precedence < _nextPrecedence()) {
|
||||
_current = scanner.tokens[++_index];
|
||||
|
||||
if (_current!.type == TokenType.slash &&
|
||||
if (_current.type == TokenType.slash &&
|
||||
peek()?.type == TokenType.gt) {
|
||||
// Handle `/>`
|
||||
//
|
||||
|
@ -357,11 +365,14 @@ class Parser {
|
|||
return left;
|
||||
}
|
||||
|
||||
var infix = infixParselets[_current!.type]!;
|
||||
var newLeft = infix.parse(this, left, _current);
|
||||
var infix = infixParselets[_current.type]!;
|
||||
Expression? newLeft;
|
||||
if (left != null) {
|
||||
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;
|
||||
|
@ -388,11 +399,11 @@ class Parser {
|
|||
|
||||
if (value == null) {
|
||||
errors.add(JaelError(JaelErrorSeverity.error,
|
||||
'Missing expression in key-value pair.', colon!.span));
|
||||
'Missing expression in key-value pair.', colon.span));
|
||||
return null;
|
||||
}
|
||||
|
||||
return KeyValuePair(key, colon, value as Expression?);
|
||||
return KeyValuePair(key, colon, value as Expression);
|
||||
}
|
||||
|
||||
NamedArgument? parseNamedArgument() {
|
||||
|
@ -409,7 +420,7 @@ class Parser {
|
|||
|
||||
if (value == null) {
|
||||
errors.add(JaelError(JaelErrorSeverity.error,
|
||||
'Missing expression in named argument.', colon!.span));
|
||||
'Missing expression in named argument.', colon.span));
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ final Map<Pattern, TokenType> _expressionPatterns = {
|
|||
'.': TokenType.dot,
|
||||
'??': TokenType.elvis,
|
||||
'?.': TokenType.elvis_dot,
|
||||
'=': TokenType.equals,
|
||||
//'=': TokenType.equals,
|
||||
'!': TokenType.exclamation,
|
||||
'-': TokenType.minus,
|
||||
'%': TokenType.percent,
|
||||
|
@ -66,14 +66,14 @@ final Map<Pattern, TokenType> _expressionPatterns = {
|
|||
'}': TokenType.rCurly,
|
||||
'(': TokenType.lParen,
|
||||
')': TokenType.rParen,
|
||||
'/': TokenType.slash,
|
||||
'<': TokenType.lt,
|
||||
//'/': TokenType.slash,
|
||||
//'<': TokenType.lt,
|
||||
'<=': TokenType.lte,
|
||||
'>': TokenType.gt,
|
||||
//'>': TokenType.gt,
|
||||
'>=': TokenType.gte,
|
||||
'==': TokenType.equ,
|
||||
'!=': TokenType.nequ,
|
||||
'=': TokenType.equals,
|
||||
//'!=': TokenType.nequ,
|
||||
//'=': TokenType.equals,
|
||||
RegExp(r'-?[0-9]+(\.[0-9]+)?([Ee][0-9]+)?'): TokenType.number,
|
||||
RegExp(r'0x[A-Fa-f0-9]+'): TokenType.hex,
|
||||
_string1: TokenType.string,
|
||||
|
@ -82,47 +82,50 @@ final Map<Pattern, TokenType> _expressionPatterns = {
|
|||
};
|
||||
|
||||
class _Scanner implements Scanner {
|
||||
@override
|
||||
final List<JaelError> errors = [];
|
||||
|
||||
@override
|
||||
final List<Token> tokens = [];
|
||||
_ScannerState state = _ScannerState.html;
|
||||
final Queue<String?> openTags = Queue();
|
||||
|
||||
SpanScanner? _scanner;
|
||||
late SpanScanner _scanner;
|
||||
|
||||
_Scanner(String text, sourceUrl) {
|
||||
_scanner = SpanScanner(text, sourceUrl: sourceUrl);
|
||||
}
|
||||
|
||||
void scan({bool asDSX = false}) {
|
||||
while (!_scanner!.isDone) {
|
||||
while (!_scanner.isDone) {
|
||||
if (state == _ScannerState.html) {
|
||||
scanHtml(asDSX);
|
||||
} else if (state == _ScannerState.freeText) {
|
||||
// Just keep parsing until we hit "</"
|
||||
var start = _scanner!.state, end = start;
|
||||
var start = _scanner.state, end = start;
|
||||
|
||||
while (!_scanner!.isDone) {
|
||||
while (!_scanner.isDone) {
|
||||
// Skip through comments
|
||||
if (_scanner!.scan(_htmlComment)) continue;
|
||||
if (_scanner.scan(_htmlComment)) continue;
|
||||
|
||||
// Break on {{ or {
|
||||
if (_scanner!.matches(asDSX ? '{' : '{{')) {
|
||||
if (_scanner.matches(asDSX ? '{' : '{{')) {
|
||||
state = _ScannerState.html;
|
||||
//_scanner.position--;
|
||||
break;
|
||||
}
|
||||
|
||||
var ch = _scanner!.readChar();
|
||||
var ch = _scanner.readChar();
|
||||
|
||||
if (ch == $lt) {
|
||||
// && !_scanner.isDone) {
|
||||
if (_scanner!.matches('/')) {
|
||||
if (_scanner.matches('/')) {
|
||||
// If we reached "</", backtrack and break into HTML
|
||||
openTags.removeFirst();
|
||||
_scanner!.position--;
|
||||
_scanner.position--;
|
||||
state = _ScannerState.html;
|
||||
break;
|
||||
} else if (_scanner!.matches(_id)) {
|
||||
} else if (_scanner.matches(_id)) {
|
||||
// Also break when we reach <foo.
|
||||
//
|
||||
// HOWEVER, that is also JavaScript. So we must
|
||||
|
@ -132,26 +135,26 @@ class _Scanner implements Scanner {
|
|||
|
||||
if (!shouldBreak) {
|
||||
// Try to see if we are closing a script tag
|
||||
var replay = _scanner!.state;
|
||||
var replay = _scanner.state;
|
||||
_scanner
|
||||
?..readChar()
|
||||
..readChar()
|
||||
..scan(_whitespace);
|
||||
//print(_scanner.emptySpan.highlight());
|
||||
|
||||
if (_scanner!.matches(_id)) {
|
||||
if (_scanner.matches(_id)) {
|
||||
//print(_scanner.lastMatch[0]);
|
||||
shouldBreak = _scanner!.lastMatch![0] == 'script';
|
||||
_scanner!.position--;
|
||||
shouldBreak = _scanner.lastMatch?[0] == 'script';
|
||||
_scanner.position--;
|
||||
}
|
||||
|
||||
if (!shouldBreak) {
|
||||
_scanner!.state = replay;
|
||||
_scanner.state = replay;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldBreak) {
|
||||
openTags.removeFirst();
|
||||
_scanner!.position--;
|
||||
_scanner.position--;
|
||||
state = _ScannerState.html;
|
||||
break;
|
||||
}
|
||||
|
@ -159,12 +162,12 @@ class _Scanner implements Scanner {
|
|||
}
|
||||
|
||||
// Otherwise, just add to the "buffer"
|
||||
end = _scanner!.state;
|
||||
end = _scanner.state;
|
||||
}
|
||||
|
||||
var span = _scanner!.spanFrom(start, end);
|
||||
var span = _scanner.spanFrom(start, end);
|
||||
|
||||
if (span.text.isNotEmpty) {
|
||||
if (span.text?.isNotEmpty == true) {
|
||||
tokens.add(Token(TokenType.text, span, null));
|
||||
}
|
||||
}
|
||||
|
@ -182,35 +185,41 @@ class _Scanner implements Scanner {
|
|||
|
||||
while (true) {
|
||||
// Scan whitespace
|
||||
_scanner!.scan(_whitespace);
|
||||
_scanner.scan(_whitespace);
|
||||
|
||||
_expressionPatterns.forEach((pattern, type) {
|
||||
if (_scanner!.matches(pattern)) {
|
||||
potential
|
||||
.add(Token(type, _scanner!.lastSpan, _scanner!.lastMatch));
|
||||
if (_scanner.matches(pattern)) {
|
||||
if (_scanner.lastSpan != null) {
|
||||
potential
|
||||
.add(Token(type, _scanner.lastSpan!, _scanner.lastMatch));
|
||||
} else {
|
||||
// TODO: To be relooked
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
potential.sort((a, b) => b.span!.length.compareTo(a.span!.length));
|
||||
potential.sort((a, b) => b.span.length.compareTo(a.span.length));
|
||||
|
||||
if (potential.isEmpty) break;
|
||||
|
||||
var token = potential.first;
|
||||
tokens.add(token);
|
||||
|
||||
_scanner!.scan(token.span!.text);
|
||||
if (token.span.text != null) {
|
||||
_scanner.scan(token.span.text!);
|
||||
}
|
||||
|
||||
if (token.type == TokenType.lt) {
|
||||
brackets.addFirst(token);
|
||||
|
||||
// Try to see if we are at a tag.
|
||||
var replay = _scanner!.state;
|
||||
_scanner!.scan(_whitespace);
|
||||
var replay = _scanner.state;
|
||||
_scanner.scan(_whitespace);
|
||||
|
||||
if (_scanner!.matches(_id)) {
|
||||
openTags.addFirst(_scanner!.lastMatch![0]);
|
||||
if (_scanner.matches(_id)) {
|
||||
openTags.addFirst(_scanner.lastMatch![0]);
|
||||
} else {
|
||||
_scanner!.state = replay;
|
||||
_scanner.state = replay;
|
||||
}
|
||||
} else if (token.type == TokenType.slash) {
|
||||
// Only push if we're at </foo
|
||||
|
@ -226,11 +235,11 @@ class _Scanner implements Scanner {
|
|||
brackets.removeFirst();
|
||||
|
||||
// Now, ONLY continue parsing HTML if the next character is '<'.
|
||||
var replay = _scanner!.state;
|
||||
_scanner!.scan(_whitespace);
|
||||
var replay = _scanner.state;
|
||||
_scanner.scan(_whitespace);
|
||||
|
||||
if (!_scanner!.matches('<')) {
|
||||
_scanner!.state = replay;
|
||||
if (!_scanner.matches('<')) {
|
||||
_scanner.state = replay;
|
||||
state = _ScannerState.freeText;
|
||||
break;
|
||||
}
|
||||
|
@ -241,11 +250,11 @@ class _Scanner implements Scanner {
|
|||
// We're at foo>, try to parse text?
|
||||
brackets.removeFirst();
|
||||
|
||||
var replay = _scanner!.state;
|
||||
_scanner!.scan(_whitespace);
|
||||
var replay = _scanner.state;
|
||||
_scanner.scan(_whitespace);
|
||||
|
||||
if (!_scanner!.matches('<')) {
|
||||
_scanner!.state = replay;
|
||||
if (!_scanner.matches('<')) {
|
||||
_scanner.state = replay;
|
||||
state = _ScannerState.freeText;
|
||||
break;
|
||||
}
|
||||
|
@ -259,7 +268,7 @@ class _Scanner implements Scanner {
|
|||
potential.clear();
|
||||
}
|
||||
}
|
||||
} while (brackets.isNotEmpty && !_scanner!.isDone);
|
||||
} while (brackets.isNotEmpty && !_scanner.isDone);
|
||||
|
||||
state = _ScannerState.freeText;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
name: jael
|
||||
version: 3.0.0
|
||||
version: 4.0.0
|
||||
description: A simple server-side HTML templating engine for Dart. Comparable to Blade or Liquid.
|
||||
author: Tobe O <thosakwe@gmail.com>
|
||||
homepage: https://docs.angel-dart.dev/packages/front-end/jael
|
||||
|
|
|
@ -16,9 +16,9 @@ void main() {
|
|||
expect(foo.getAttribute('bar')?.value?.compute(null), 'baz');
|
||||
expect(
|
||||
foo
|
||||
.getAttribute('yes')!
|
||||
.value!
|
||||
.compute(SymbolTable(values: {'no': 'maybe'})),
|
||||
.getAttribute('yes')
|
||||
?.value
|
||||
?.compute(SymbolTable(values: {'no': 'maybe'})),
|
||||
'maybe');
|
||||
});
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ import 'package:jael/jael.dart' as jael;
|
|||
import 'package:symbol_table/symbol_table.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
main() {
|
||||
void main() {
|
||||
test('attribute binding', () {
|
||||
const template = '''
|
||||
<html>
|
||||
|
|
|
@ -19,6 +19,6 @@ class _IsToken extends Matcher {
|
|||
bool matches(item, Map matchState) {
|
||||
return item is Token &&
|
||||
item.type == type &&
|
||||
(text == null || item.span!.text == text);
|
||||
(text == null || item.span.text == text);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import 'package:jael/src/text/scanner.dart';
|
|||
import 'package:test/test.dart';
|
||||
import 'common.dart';
|
||||
|
||||
main() {
|
||||
void main() {
|
||||
test('plain html', () {
|
||||
var tokens = scan('<img src="foo.png" />', sourceUrl: 'test.jael').tokens;
|
||||
tokens.forEach(print);
|
||||
|
|
Loading…
Reference in a new issue