Migrated Jael

This commit is contained in:
thomashii@dukefirehawk.com 2021-04-28 08:37:33 +08:00
parent ce510e0394
commit 436f797982
32 changed files with 351 additions and 335 deletions

View file

@ -41,7 +41,7 @@ main(List<String> args) async {
if (argResults.rest.isEmpty) { if (argResults.rest.isEmpty) {
var text = await stdin.transform(utf8.decoder).join(); var text = await stdin.transform(utf8.decoder).join();
var result = var result =
await format(argResults['stdin-name'] as String, text, argResults); await format(argResults['stdin-name'] as String?, text, argResults);
if (result != null) print(result); if (result != null) print(result);
} else { } else {
for (var arg in argResults.rest) { for (var arg in argResults.rest) {
@ -91,14 +91,14 @@ Future<void> formatStat(
Future<void> formatFile(File file, ArgResults argResults) async { Future<void> formatFile(File file, ArgResults argResults) async {
var content = await file.readAsString(); var content = await file.readAsString();
var formatted = await format(file.path, content, argResults); var formatted = format(file.path, content, argResults);
if (formatted == null) return; if (formatted == null) return;
if (argResults['overwrite'] as bool) { if (argResults['overwrite'] as bool) {
if (formatted != content) { if (formatted != content) {
if (argResults['dry-run'] as bool) { if (argResults['dry-run'] as bool) {
print('Would have formatted ${file.path}'); print('Would have formatted ${file.path}');
} else { } else {
await file.writeAsStringSync(formatted); file.writeAsStringSync(formatted);
print('Formatted ${file.path}'); print('Formatted ${file.path}');
} }
} else { } else {
@ -109,7 +109,7 @@ Future<void> formatFile(File file, ArgResults argResults) async {
} }
} }
String format(String filename, String content, ArgResults argResults) { String? format(String? filename, String content, ArgResults argResults) {
var errored = false; var errored = false;
var doc = parseDocument(content, sourceUrl: filename, onError: (e) { var doc = parseDocument(content, sourceUrl: filename, onError: (e) {
stderr.writeln(e); stderr.writeln(e);
@ -118,7 +118,7 @@ String format(String filename, String content, ArgResults argResults) {
if (errored) return null; if (errored) return null;
var fmt = JaelFormatter( var fmt = JaelFormatter(
int.parse(argResults['tab-size'] as String), int.parse(argResults['tab-size'] as String),
argResults['insert-spaces'] as bool, argResults['insert-spaces'] as bool?,
int.parse(argResults['line-length'] as String)); int.parse(argResults['line-length'] as String));
return fmt.apply(doc); return fmt.apply(doc);
} }

View file

@ -3,39 +3,39 @@ import 'expression.dart';
import 'token.dart'; import 'token.dart';
class Array extends Expression { class Array extends Expression {
final Token lBracket, rBracket; final Token? lBracket, rBracket;
final List<Expression> items; final List<Expression> items;
Array(this.lBracket, this.rBracket, this.items); Array(this.lBracket, this.rBracket, this.items);
@override @override
compute(scope) => items.map((e) => e.compute(scope)).toList(); List compute(scope) => items.map((e) => e.compute(scope)).toList();
@override @override
FileSpan get span { FileSpan get span {
return items return items
.fold<FileSpan>(lBracket.span, (out, i) => out.expand(i.span)) .fold<FileSpan?>(lBracket!.span, (out, i) => out!.expand(i.span!))!
.expand(rBracket.span); .expand(rBracket!.span!);
} }
} }
class IndexerExpression extends Expression { class IndexerExpression extends Expression {
final Expression target, indexer; final Expression? target, indexer;
final Token lBracket, rBracket; final Token? lBracket, rBracket;
IndexerExpression(this.target, this.lBracket, this.indexer, this.rBracket); IndexerExpression(this.target, this.lBracket, this.indexer, this.rBracket);
@override @override
FileSpan get span { FileSpan get span {
return target.span return target!.span!
.expand(lBracket.span) .expand(lBracket!.span!)
.expand(indexer.span) .expand(indexer!.span!)
.expand(rBracket.span); .expand(rBracket!.span!);
} }
@override @override
compute(scope) { dynamic compute(scope) {
var a = target.compute(scope), b = indexer.compute(scope); var a = target!.compute(scope), b = indexer!.compute(scope);
return a[b]; return a[b];
} }
} }

View file

@ -1,5 +1,5 @@
import 'package:source_span/source_span.dart'; import 'package:source_span/source_span.dart';
abstract class AstNode { abstract class AstNode {
FileSpan get span; FileSpan? get span;
} }

View file

@ -6,22 +6,22 @@ import 'string.dart';
import 'token.dart'; import 'token.dart';
class Attribute extends AstNode { class Attribute extends AstNode {
final Identifier id; final Identifier? id;
final StringLiteral string; final StringLiteral? string;
final Token equals, nequ; final Token? equals, nequ;
final Expression value; final Expression? value;
Attribute(this.id, this.string, this.equals, this.nequ, this.value); Attribute(this.id, this.string, this.equals, this.nequ, this.value);
bool get isRaw => nequ != null; bool get isRaw => nequ != null;
Expression get nameNode => id ?? string; Expression? get nameNode => id ?? string;
String get name => string?.value ?? id.name; String get name => string?.value ?? id!.name;
@override @override
FileSpan get span { FileSpan? get span {
if (equals == null) return nameNode.span; if (equals == null) return nameNode!.span;
return nameNode.span.expand(equals?.span ?? nequ.span).expand(value.span); return nameNode!.span!.expand(equals?.span ?? nequ!.span!).expand(value!.span!);
} }
} }

View file

@ -3,14 +3,14 @@ import 'expression.dart';
import 'token.dart'; import 'token.dart';
class BinaryExpression extends Expression { class BinaryExpression extends Expression {
final Expression left, right; final Expression? left, right;
final Token operator; final Token? operator;
BinaryExpression(this.left, this.operator, this.right); BinaryExpression(this.left, this.operator, this.right);
@override @override
compute(scope) { compute(scope) {
var l = left.compute(scope), r = right.compute(scope); var l = left!.compute(scope), r = right!.compute(scope);
switch (operator?.type) { switch (operator?.type) {
case TokenType.asterisk: case TokenType.asterisk:
@ -43,5 +43,5 @@ class BinaryExpression extends Expression {
} }
@override @override
FileSpan get span => left.span.expand(operator.span).expand(right.span); FileSpan get span => left!.span!.expand(operator!.span!).expand(right!.span!);
} }

View file

@ -6,8 +6,8 @@ import 'identifier.dart';
import 'token.dart'; import 'token.dart';
class Call extends Expression { class Call extends Expression {
final Expression target; final Expression? target;
final Token lParen, rParen; final Token? lParen, rParen;
final List<Expression> arguments; final List<Expression> arguments;
final List<NamedArgument> namedArguments; final List<NamedArgument> namedArguments;
@ -17,10 +17,10 @@ class Call extends Expression {
@override @override
FileSpan get span { FileSpan get span {
return arguments 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>( .expand(namedArguments.fold<FileSpan?>(
lParen.span, (out, a) => out.expand(a.span))) lParen!.span, (out, a) => out!.expand(a.span))!)
.expand(rParen.span); .expand(rParen!.span!);
} }
List computePositional(SymbolTable scope) => List computePositional(SymbolTable scope) =>
@ -34,7 +34,7 @@ class Call extends Expression {
@override @override
compute(scope) { compute(scope) {
var callee = target.compute(scope); var callee = target!.compute(scope);
var args = computePositional(scope); var args = computePositional(scope);
var named = computeNamed(scope); var named = computeNamed(scope);
@ -44,13 +44,13 @@ class Call extends Expression {
class NamedArgument extends AstNode { class NamedArgument extends AstNode {
final Identifier name; final Identifier name;
final Token colon; final Token? colon;
final Expression value; final Expression value;
NamedArgument(this.name, this.colon, this.value); NamedArgument(this.name, this.colon, this.value);
@override @override
FileSpan get span { FileSpan get span {
return name.span.expand(colon.span).expand(value.span); return name.span!.expand(colon!.span!).expand(value.span!);
} }
} }

View file

@ -3,29 +3,29 @@ import 'expression.dart';
import 'token.dart'; import 'token.dart';
class Conditional extends Expression { class Conditional extends Expression {
final Expression condition, ifTrue, ifFalse; final Expression? condition, ifTrue, ifFalse;
final Token question, colon; final Token? question, colon;
Conditional( Conditional(
this.condition, this.question, this.ifTrue, this.colon, this.ifFalse); this.condition, this.question, this.ifTrue, this.colon, this.ifFalse);
@override @override
FileSpan get span { FileSpan get span {
return condition.span return condition!.span!
.expand(question.span) .expand(question!.span!)
.expand(ifTrue.span) .expand(ifTrue!.span!)
.expand(colon.span) .expand(colon!.span!)
.expand(ifFalse.span); .expand(ifFalse!.span!);
} }
@override @override
compute(scope) { compute(scope) {
var v = condition.compute(scope) as bool; var v = condition!.compute(scope) as bool?;
if (scope.resolve('!strict!')?.value == false) { if (scope.resolve('!strict!')?.value == false) {
v = v == true; v = v == true;
} }
return v ? ifTrue.compute(scope) : ifFalse.compute(scope); return v! ? ifTrue!.compute(scope) : ifFalse!.compute(scope);
} }
} }

View file

@ -6,40 +6,40 @@ import 'string.dart';
import 'token.dart'; import 'token.dart';
class Document extends AstNode { class Document extends AstNode {
final Doctype doctype; final Doctype? doctype;
final Element root; final Element root;
Document(this.doctype, this.root); Document(this.doctype, this.root);
@override @override
FileSpan get span { FileSpan? get span {
if (doctype == null) return root.span; if (doctype == null) return root.span;
return doctype.span.expand(root.span); return doctype!.span.expand(root.span!);
} }
} }
class HtmlComment extends ElementChild { class HtmlComment extends ElementChild {
final Token htmlComment; final Token? htmlComment;
HtmlComment(this.htmlComment); HtmlComment(this.htmlComment);
@override @override
FileSpan get span => htmlComment.span; FileSpan? get span => htmlComment!.span;
} }
class Text extends ElementChild { class Text extends ElementChild {
final Token text; final Token? text;
Text(this.text); Text(this.text);
@override @override
FileSpan get span => text.span; FileSpan? get span => text!.span;
} }
class Doctype extends AstNode { class Doctype extends AstNode {
final Token lt, doctype, gt; final Token? lt, doctype, gt;
final Identifier html, public; final Identifier? html, public;
final StringLiteral name, url; final StringLiteral? name, url;
Doctype(this.lt, this.doctype, this.html, this.public, this.name, this.url, Doctype(this.lt, this.doctype, this.html, this.public, this.name, this.url,
this.gt); this.gt);
@ -47,14 +47,14 @@ class Doctype extends AstNode {
@override @override
FileSpan get span { FileSpan get span {
if (public == null) { if (public == null) {
return lt.span.expand(doctype.span).expand(html.span).expand(gt.span); return lt!.span!.expand(doctype!.span!).expand(html!.span!).expand(gt!.span!);
} }
return lt.span return lt!.span!
.expand(doctype.span) .expand(doctype!.span!)
.expand(html.span) .expand(html!.span!)
.expand(public.span) .expand(public!.span!)
.expand(name.span) .expand(name!.span!)
.expand(url.span) .expand(url!.span!)
.expand(gt.span); .expand(gt!.span!);
} }
} }

View file

@ -1,3 +1,4 @@
import 'package:collection/collection.dart' show IterableExtension;
import 'package:source_span/source_span.dart'; import 'package:source_span/source_span.dart';
import 'ast_node.dart'; import 'ast_node.dart';
import 'attribute.dart'; import 'attribute.dart';
@ -12,7 +13,7 @@ class TextNode extends ElementChild {
TextNode(this.text); TextNode(this.text);
@override @override
FileSpan get span => text.span; FileSpan? get span => text.span;
} }
abstract class Element extends ElementChild { abstract class Element extends ElementChild {
@ -38,12 +39,12 @@ abstract class Element extends ElementChild {
Iterable<ElementChild> get children; Iterable<ElementChild> get children;
Attribute getAttribute(String name) => Attribute? getAttribute(String name) =>
attributes.firstWhere((a) => a.name == name, orElse: () => null); attributes.firstWhereOrNull((a) => a.name == name);
} }
class SelfClosingElement extends Element { class SelfClosingElement extends Element {
final Token lt, slash, gt; final Token? lt, slash, gt;
final Identifier tagName; final Identifier tagName;
@ -58,15 +59,15 @@ class SelfClosingElement extends Element {
@override @override
FileSpan get span { FileSpan get span {
var start = attributes.fold<FileSpan>( 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 return slash != null
? start.expand(slash.span).expand(gt.span) ? start.expand(slash!.span!).expand(gt!.span!)
: start.expand(gt.span); : start.expand(gt!.span!);
} }
} }
class RegularElement extends Element { class RegularElement extends Element {
final Token lt, gt, lt2, slash, gt2; final Token? lt, gt, lt2, slash, gt2;
final Identifier tagName, tagName2; final Identifier tagName, tagName2;
@ -81,16 +82,16 @@ class RegularElement extends Element {
FileSpan get span { FileSpan get span {
var openingTag = attributes var openingTag = attributes
.fold<FileSpan>( .fold<FileSpan>(
lt.span.expand(tagName.span), (out, a) => out.expand(a.span)) lt!.span!.expand(tagName.span!), (out, a) => out.expand(a.span!))
.expand(gt.span); .expand(gt!.span!);
if (gt2 == null) return openingTag; if (gt2 == null) return openingTag;
return children return children
.fold<FileSpan>(openingTag, (out, c) => out.expand(c.span)) .fold<FileSpan>(openingTag, (out, c) => out.expand(c.span!))
.expand(lt2.span) .expand(lt2!.span!)
.expand(slash.span) .expand(slash!.span!)
.expand(tagName2.span) .expand(tagName2.span!)
.expand(gt2.span); .expand(gt2!.span!);
} }
} }

View file

@ -3,15 +3,15 @@ import 'package:source_span/source_span.dart';
class JaelError extends Error { class JaelError extends Error {
final JaelErrorSeverity severity; final JaelErrorSeverity severity;
final String message; final String message;
final FileSpan span; final FileSpan? span;
JaelError(this.severity, this.message, this.span); JaelError(this.severity, this.message, this.span);
@override @override
String toString() { String toString() {
var label = severity == JaelErrorSeverity.warning ? 'warning' : 'error'; var label = severity == JaelErrorSeverity.warning ? 'warning' : 'error';
return '$label: ${span.start.toolString}: $message\n' + return '$label: ${span!.start.toolString}: $message\n' +
span.highlight(color: true); span!.highlight(color: true);
} }
} }

View file

@ -4,30 +4,30 @@ import 'ast_node.dart';
import 'token.dart'; import 'token.dart';
abstract class Expression extends AstNode { abstract class Expression extends AstNode {
compute(SymbolTable scope); dynamic compute(SymbolTable scope);
} }
abstract class Literal extends Expression {} abstract class Literal extends Expression {}
class Negation extends Expression { class Negation extends Expression {
final Token exclamation; final Token? exclamation;
final Expression expression; final Expression? expression;
Negation(this.exclamation, this.expression); Negation(this.exclamation, this.expression);
@override @override
FileSpan get span { FileSpan get span {
return exclamation.span.expand(expression.span); return exclamation!.span!.expand(expression!.span!);
} }
@override @override
compute(SymbolTable scope) { compute(SymbolTable scope) {
var v = expression.compute(scope) as bool; var v = expression!.compute(scope) as bool?;
if (scope.resolve('!strict!')?.value == false) { if (scope.resolve('!strict!')?.value == false) {
v = v == true; v = v == true;
} }
return !v; return !v!;
} }
} }

View file

@ -4,7 +4,7 @@ import 'expression.dart';
import 'token.dart'; import 'token.dart';
class Identifier extends Expression { class Identifier extends Expression {
final Token id; final Token? id;
Identifier(this.id); Identifier(this.id);
@ -23,25 +23,25 @@ class Identifier extends Expression {
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.'); 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 @override
FileSpan get span => id.span; FileSpan? get span => id!.span;
} }
class SyntheticIdentifier extends Identifier { class SyntheticIdentifier extends Identifier {
@override @override
final String name; final String name;
SyntheticIdentifier(this.name, [Token token]) : super(token); SyntheticIdentifier(this.name, [Token? token]) : super(token);
@override @override
FileSpan get span { FileSpan? get span {
if (id != null) return id.span; if (id != null) return id!.span;
throw UnsupportedError('Cannot get the span of a SyntheticIdentifier.'); throw UnsupportedError('Cannot get the span of a SyntheticIdentifier.');
} }
} }

View file

@ -4,15 +4,15 @@ import 'expression.dart';
import 'token.dart'; import 'token.dart';
class Interpolation extends ElementChild { class Interpolation extends ElementChild {
final Token doubleCurlyL, doubleCurlyR; final Token? doubleCurlyL, doubleCurlyR;
final Expression expression; final Expression expression;
Interpolation(this.doubleCurlyL, this.expression, this.doubleCurlyR); Interpolation(this.doubleCurlyL, this.expression, this.doubleCurlyR);
bool get isRaw => doubleCurlyL.span.text.endsWith('-'); bool get isRaw => doubleCurlyL!.span!.text.endsWith('-');
@override @override
FileSpan get span { FileSpan get span {
return doubleCurlyL.span.expand(expression.span).expand(doubleCurlyR.span); return doubleCurlyL!.span!.expand(expression.span!).expand(doubleCurlyR!.span!);
} }
} }

View file

@ -5,7 +5,7 @@ import 'identifier.dart';
import 'token.dart'; import 'token.dart';
class MapLiteral extends Literal { class MapLiteral extends Literal {
final Token lCurly, rCurly; final Token? lCurly, rCurly;
final List<KeyValuePair> pairs; final List<KeyValuePair> pairs;
MapLiteral(this.lCurly, this.pairs, this.rCurly); MapLiteral(this.lCurly, this.pairs, this.rCurly);
@ -17,14 +17,14 @@ class MapLiteral extends Literal {
if (p.colon == null) { if (p.colon == null) {
if (p.key is! Identifier) { if (p.key is! Identifier) {
key = value = p.key.compute(scope); key = value = p.key!.compute(scope);
} else { } else {
key = (p.key as Identifier).name; key = (p.key as Identifier).name;
value = p.key.compute(scope); value = p.key!.compute(scope);
} }
} else { } else {
key = p.key.compute(scope); key = p.key!.compute(scope);
value = p.value.compute(scope); value = p.value!.compute(scope);
} }
return out..[key] = value; return out..[key] = value;
@ -34,20 +34,20 @@ class MapLiteral extends Literal {
@override @override
FileSpan get span { FileSpan get span {
return pairs return pairs
.fold<FileSpan>(lCurly.span, (out, p) => out.expand(p.span)) .fold<FileSpan?>(lCurly!.span, (out, p) => out!.expand(p.span!))!
.expand(rCurly.span); .expand(rCurly!.span!);
} }
} }
class KeyValuePair extends AstNode { class KeyValuePair extends AstNode {
final Expression key, value; final Expression? key, value;
final Token colon; final Token? colon;
KeyValuePair(this.key, this.colon, this.value); KeyValuePair(this.key, this.colon, this.value);
@override @override
FileSpan get span { FileSpan? get span {
if (colon == null) return key.span; if (colon == null) return key!.span;
return colon.span.expand(colon.span).expand(value.span); return colon!.span!.expand(colon!.span!).expand(value!.span!);
} }
} }

View file

@ -6,19 +6,19 @@ import 'identifier.dart';
import 'token.dart'; import 'token.dart';
class MemberExpression extends Expression { class MemberExpression extends Expression {
final Expression expression; final Expression? expression;
final Token op; final Token? op;
final Identifier name; final Identifier name;
MemberExpression(this.expression, this.op, this.name); MemberExpression(this.expression, this.op, this.name);
@override @override
compute(SymbolTable scope) { compute(SymbolTable scope) {
var target = expression.compute(scope); var target = expression!.compute(scope);
if (op.span.text == '?.' && target == null) return null; if (op!.span!.text == '?.' && target == null) return null;
return reflect(target).getField(Symbol(name.name)).reflectee; return reflect(target).getField(Symbol(name.name)).reflectee;
} }
@override @override
FileSpan get span => expression.span.expand(op.span).expand(name.span); FileSpan get span => expression!.span!.expand(op!.span!).expand(name.span!);
} }

View file

@ -6,17 +6,17 @@ import 'member.dart';
import 'token.dart'; import 'token.dart';
class NewExpression extends Expression { class NewExpression extends Expression {
final Token $new; final Token? $new;
final Call call; final Call call;
NewExpression(this.$new, this.call); NewExpression(this.$new, this.call);
@override @override
FileSpan get span => $new.span.expand(call.span); FileSpan get span => $new!.span!.expand(call.span);
@override @override
compute(scope) { compute(scope) {
var targetType = call.target.compute(scope); var targetType = call.target!.compute(scope);
var positional = call.computePositional(scope); var positional = call.computePositional(scope);
var named = call.computeNamed(scope); var named = call.computeNamed(scope);
var name = ''; var name = '';

View file

@ -4,13 +4,13 @@ import 'expression.dart';
import 'token.dart'; import 'token.dart';
class NumberLiteral extends Literal { class NumberLiteral extends Literal {
final Token number; final Token? number;
num _value; num? _value;
NumberLiteral(this.number); NumberLiteral(this.number);
@override @override
FileSpan get span => number.span; FileSpan? get span => number!.span;
static num parse(String value) { static num parse(String value) {
var e = value.indexOf('E'); var e = value.indexOf('E');
@ -25,23 +25,23 @@ class NumberLiteral extends Literal {
@override @override
compute(scope) { compute(scope) {
return _value ??= parse(number.span.text); return _value ??= parse(number!.span!.text);
} }
} }
class HexLiteral extends Literal { class HexLiteral extends Literal {
final Token hex; final Token? hex;
num _value; num? _value;
HexLiteral(this.hex); HexLiteral(this.hex);
@override @override
FileSpan get span => hex.span; FileSpan? get span => hex!.span;
static num parse(String value) => int.parse(value.substring(2), radix: 16); static num parse(String value) => int.parse(value.substring(2), radix: 16);
@override @override
compute(scope) { compute(scope) {
return _value ??= parse(hex.span.text); return _value ??= parse(hex!.span!.text);
} }
} }

View file

@ -6,13 +6,13 @@ import 'expression.dart';
import 'token.dart'; import 'token.dart';
class StringLiteral extends Literal { class StringLiteral extends Literal {
final Token string; final Token? string;
final String value; final String value;
StringLiteral(this.string, this.value); StringLiteral(this.string, this.value);
static String parseValue(Token string) { static String parseValue(Token string) {
var text = string.span.text.substring(1, string.span.text.length - 1); var text = string.span!.text.substring(1, string.span!.text.length - 1);
var codeUnits = text.codeUnits; var codeUnits = text.codeUnits;
var buf = StringBuffer(); var buf = StringBuffer();
@ -71,5 +71,5 @@ class StringLiteral extends Literal {
} }
@override @override
FileSpan get span => string.span; FileSpan? get span => string!.span;
} }

View file

@ -2,14 +2,14 @@ import 'package:source_span/source_span.dart';
class Token { class Token {
final TokenType type; final TokenType type;
final FileSpan span; final FileSpan? span;
final Match match; final Match? match;
Token(this.type, this.span, this.match); Token(this.type, this.span, this.match);
@override @override
String toString() { String toString() {
return '${span.start.toolString}: "${span.text}" => $type'; return '${span!.start.toolString}: "${span!.text}" => $type';
} }
} }

View file

@ -3,11 +3,11 @@ import 'ast/ast.dart';
/// Jael formatter /// Jael formatter
class JaelFormatter { class JaelFormatter {
final num tabSize; final num tabSize;
final bool insertSpaces; final bool? insertSpaces;
final int maxLineLength; final int maxLineLength;
var _buffer = StringBuffer(); var _buffer = StringBuffer();
int _level = 0; int _level = 0;
String _spaces; String? _spaces;
static String _spaceString(int tabSize) { static String _spaceString(int tabSize) {
var b = StringBuffer(); var b = StringBuffer();
@ -18,7 +18,7 @@ class JaelFormatter {
} }
JaelFormatter(this.tabSize, this.insertSpaces, this.maxLineLength) { JaelFormatter(this.tabSize, this.insertSpaces, this.maxLineLength) {
_spaces = insertSpaces ? _spaceString(tabSize.toInt()) : '\t'; _spaces = insertSpaces! ? _spaceString(tabSize.toInt()) : '\t';
} }
void _indent() { void _indent() {
@ -38,20 +38,20 @@ class JaelFormatter {
int get _spaceLength { int get _spaceLength {
var out = 0; var out = 0;
for (int i = 0; i < _level; i++) { for (int i = 0; i < _level; i++) {
out += _spaces.length; out += _spaces!.length;
} }
return out; return out;
} }
String apply(Document document) { String apply(Document? document) {
if (document?.doctype != null) { if (document?.doctype != null) {
_buffer.write('<!doctype'); _buffer.write('<!doctype');
if (document.doctype.html != null) _buffer.write(' html'); if (document!.doctype!.html != null) _buffer.write(' html');
if (document.doctype.public != null) _buffer.write(' public'); if (document.doctype!.public != null) _buffer.write(' public');
if (document.doctype.url != null) { if (document.doctype!.url != null) {
_buffer.write('${document.doctype.url}'); _buffer.write('${document.doctype!.url}');
} }
_buffer.writeln('>'); _buffer.writeln('>');
@ -62,7 +62,7 @@ class JaelFormatter {
return _buffer.toString().trim(); return _buffer.toString().trim();
} }
int _formatChild(ElementChild child, int lineLength, int _formatChild(ElementChild? child, int lineLength,
{bool isFirst = false, bool isLast = false}) { {bool isFirst = false, bool isLast = false}) {
if (child == null) { if (child == null) {
return lineLength; return lineLength;
@ -72,11 +72,11 @@ class JaelFormatter {
var b = StringBuffer('{{'); var b = StringBuffer('{{');
if (child.isRaw) b.write('-'); if (child.isRaw) b.write('-');
b.write(' '); b.write(' ');
b.write(child.expression.span.text.trim()); b.write(child.expression.span!.text.trim());
b.write(' }}'); b.write(' }}');
s = b.toString(); s = b.toString();
} else { } else {
s = child.span.text; s = child.span!.text;
} }
if (isFirst) { if (isFirst) {
s = s.trimLeft(); s = s.trimLeft();
@ -145,7 +145,7 @@ class JaelFormatter {
_indent(); _indent();
var lll = _spaceLength; var lll = _spaceLength;
var i = 1; var i = 1;
ElementChild last; ElementChild? last;
for (var c in element.children) { for (var c in element.children) {
if (lll == _spaceLength && c is! Element) { if (lll == _spaceLength && c is! Element) {
_applySpacing(); _applySpacing();
@ -186,7 +186,7 @@ class JaelFormatter {
} else { } else {
if (attr.nequ != null) b.write('!='); if (attr.nequ != null) b.write('!=');
if (attr.equals != null) b.write('='); if (attr.equals != null) b.write('=');
b.write(attr.value.span.text); b.write(attr.value!.span!.text);
} }
} }
return b.toString(); return b.toString();

View file

@ -1,13 +1,14 @@
import 'dart:convert'; import 'dart:convert';
import 'package:code_buffer/code_buffer.dart'; import 'package:code_buffer/code_buffer.dart';
import 'package:collection/collection.dart' show IterableExtension;
import 'package:symbol_table/symbol_table.dart'; import 'package:symbol_table/symbol_table.dart';
import 'ast/ast.dart'; import 'ast/ast.dart';
import 'text/parser.dart'; import 'text/parser.dart';
import 'text/scanner.dart'; import 'text/scanner.dart';
/// Parses a Jael document. /// Parses a Jael document.
Document parseDocument(String text, Document? parseDocument(String text,
{sourceUrl, bool asDSX = false, void onError(JaelError error)}) { {sourceUrl, bool asDSX = false, void onError(JaelError error)?}) {
var scanner = scan(text, sourceUrl: sourceUrl, asDSX: asDSX); var scanner = scan(text, sourceUrl: sourceUrl, asDSX: asDSX);
//scanner.tokens.forEach(print); //scanner.tokens.forEach(print);
@ -55,12 +56,12 @@ class Renderer {
..writeln('<li>') ..writeln('<li>')
..indent() ..indent()
..writeln( ..writeln(
'<b>$type:</b> ${error.span.start.toolString}: ${error.message}') '<b>$type:</b> ${error.span!.start.toolString}: ${error.message}')
..writeln('<br>') ..writeln('<br>')
..writeln( ..writeln(
'<span style="color: red;">' + '<span style="color: red;">' +
htmlEscape htmlEscape
.convert(error.span.highlight(color: false)) .convert(error.span!.highlight(color: false))
.replaceAll('\n', '<br>') + .replaceAll('\n', '<br>') +
'</span>', '</span>',
) )
@ -83,7 +84,7 @@ class Renderer {
{bool strictResolution = true}) { {bool strictResolution = true}) {
scope.create('!strict!', value: strictResolution != false); scope.create('!strict!', value: strictResolution != false);
if (document.doctype != null) buffer.writeln(document.doctype.span.text); if (document.doctype != null) buffer.writeln(document.doctype!.span.text);
renderElement( renderElement(
document.root, buffer, scope, document.doctype?.public == null); document.root, buffer, scope, document.doctype?.public == null);
} }
@ -177,15 +178,15 @@ class Renderer {
var attribute = element.attributes.singleWhere((a) => a.name == 'for-each'); var attribute = element.attributes.singleWhere((a) => a.name == 'for-each');
if (attribute.value == null) return; if (attribute.value == null) return;
var asAttribute = element.attributes var asAttribute =
.firstWhere((a) => a.name == 'as', orElse: () => null); element.attributes.firstWhereOrNull((a) => a.name == 'as');
var indexAsAttribute = element.attributes var indexAsAttribute =
.firstWhere((a) => a.name == 'index-as', orElse: () => null); element.attributes.firstWhereOrNull((a) => a.name == 'index-as');
var alias = asAttribute?.value?.compute(scope)?.toString() ?? 'item'; var alias = asAttribute?.value?.compute(scope)?.toString() ?? 'item';
var indexAs = indexAsAttribute?.value?.compute(scope)?.toString() ?? 'i'; var indexAs = indexAsAttribute?.value?.compute(scope)?.toString() ?? 'i';
var otherAttributes = element.attributes.where( var otherAttributes = element.attributes.where(
(a) => a.name != 'for-each' && a.name != 'as' && a.name != 'index-as'); (a) => a.name != 'for-each' && a.name != 'as' && a.name != 'index-as');
Element strippedElement; late Element strippedElement;
if (element is SelfClosingElement) { if (element is SelfClosingElement) {
strippedElement = SelfClosingElement(element.lt, element.tagName, strippedElement = SelfClosingElement(element.lt, element.tagName,
@ -204,7 +205,7 @@ class Renderer {
} }
int i = 0; int i = 0;
for (var item in attribute.value.compute(scope)) { for (var item in attribute.value!.compute(scope)) {
var childScope = scope.createChild(values: {alias: item, indexAs: i++}); var childScope = scope.createChild(values: {alias: item, indexAs: i++});
renderElement(strippedElement, buffer, childScope, html5); renderElement(strippedElement, buffer, childScope, html5);
} }
@ -214,7 +215,7 @@ class Renderer {
Element element, CodeBuffer buffer, SymbolTable scope, bool html5) { Element element, CodeBuffer buffer, SymbolTable scope, bool html5) {
var attribute = element.attributes.singleWhere((a) => a.name == 'if'); var attribute = element.attributes.singleWhere((a) => a.name == 'if');
var vv = attribute.value.compute(scope); var vv = attribute.value!.compute(scope);
if (scope.resolve('!strict!')?.value == false) { if (scope.resolve('!strict!')?.value == false) {
vv = vv == true; vv = vv == true;
@ -225,7 +226,7 @@ class Renderer {
if (!v) return; if (!v) return;
var otherAttributes = element.attributes.where((a) => a.name != 'if'); var otherAttributes = element.attributes.where((a) => a.name != 'if');
Element strippedElement; late Element strippedElement;
if (element is SelfClosingElement) { if (element is SelfClosingElement) {
strippedElement = SelfClosingElement(element.lt, element.tagName, strippedElement = SelfClosingElement(element.lt, element.tagName,
@ -263,7 +264,7 @@ class Renderer {
void renderSwitch( void renderSwitch(
Element element, CodeBuffer buffer, SymbolTable scope, bool html5) { Element element, CodeBuffer buffer, SymbolTable scope, bool html5) {
var value = element.attributes var value = element.attributes
.firstWhere((a) => a.name == 'value', orElse: () => null) .firstWhereOrNull((a) => a.name == 'value')
?.value ?.value
?.compute(scope); ?.compute(scope);
@ -273,7 +274,7 @@ class Renderer {
for (var child in cases) { for (var child in cases) {
var comparison = child.attributes var comparison = child.attributes
.firstWhere((a) => a.name == 'value', orElse: () => null) .firstWhereOrNull((a) => a.name == 'value')
?.value ?.value
?.compute(scope); ?.compute(scope);
if (comparison == value) { if (comparison == value) {
@ -287,11 +288,10 @@ class Renderer {
} }
} }
var defaultCase = element.children.firstWhere( var defaultCase = element.children.firstWhereOrNull(
(c) => c is Element && c.tagName.name == 'default', (c) => c is Element && c.tagName.name == 'default') as Element?;
orElse: () => null) as Element;
if (defaultCase != null) { if (defaultCase != null) {
for (int i = 0; i < defaultCase.children.length; i++) { for (var i = 0; i < defaultCase.children.length; i++) {
var child = defaultCase.children.elementAt(i); var child = defaultCase.children.elementAt(i);
renderElementChild(element, child, buffer, scope, html5, i, renderElementChild(element, child, buffer, scope, html5, i,
defaultCase.children.length); defaultCase.children.length);
@ -301,13 +301,13 @@ class Renderer {
void renderElementChild(Element parent, ElementChild child, CodeBuffer buffer, void renderElementChild(Element parent, ElementChild child, CodeBuffer buffer,
SymbolTable scope, bool html5, int index, int total) { SymbolTable scope, bool html5, int index, int total) {
if (child is Text && parent?.tagName?.name != 'textarea') { if (child is Text && parent.tagName.name != 'textarea') {
if (index == 0) { if (index == 0) {
buffer.write(child.span.text.trimLeft()); buffer.write(child.span!.text.trimLeft());
} else if (index == total - 1) { } else if (index == total - 1) {
buffer.write(child.span.text.trimRight()); buffer.write(child.span!.text.trimRight());
} else { } else {
buffer.write(child.span.text); buffer.write(child.span!.text);
} }
} else if (child is Interpolation) { } else if (child is Interpolation) {
var value = child.expression.compute(scope); var value = child.expression.compute(scope);
@ -320,7 +320,7 @@ class Renderer {
} }
} }
} else if (child is Element) { } else if (child is Element) {
if (buffer?.lastLine?.text?.isNotEmpty == true) buffer.writeln(); if (buffer.lastLine?.text.isNotEmpty == true) buffer.writeln();
renderElement(child, buffer, scope, html5); renderElement(child, buffer, scope, html5);
} }
} }
@ -331,7 +331,7 @@ class Renderer {
Element element, CodeBuffer buffer, SymbolTable scope, bool html5) { Element element, CodeBuffer buffer, SymbolTable scope, bool html5) {
if (element is! RegularElement) { if (element is! RegularElement) {
throw JaelError(JaelErrorSeverity.error, throw JaelError(JaelErrorSeverity.error,
"Custom elements cannot be self-closing.", element.span); 'Custom elements cannot be self-closing.', element.span);
} }
var name = element.getAttribute('name')?.value?.compute(scope)?.toString(); var name = element.getAttribute('name')?.value?.compute(scope)?.toString();
@ -344,20 +344,20 @@ class Renderer {
} }
try { try {
var p = scope.isRoot ? scope : scope.parent; var p = scope.isRoot ? scope : scope.parent!;
p.create(customElementName(name), value: element, constant: true); p.create(customElementName(name), value: element, constant: true);
} on StateError { } on StateError {
throw JaelError( throw JaelError(
JaelErrorSeverity.error, JaelErrorSeverity.error,
"Cannot re-define element '$name' in this scope.", "Cannot re-define element '$name' in this scope.",
element.getAttribute('name').span); element.getAttribute('name')!.span);
} }
} }
void renderCustomElement( void renderCustomElement(
Element element, CodeBuffer buffer, SymbolTable scope, bool html5) { Element element, CodeBuffer buffer, SymbolTable scope, bool html5) {
var template = scope.resolve(customElementName(element.tagName.name)).value var template = scope.resolve(customElementName(element.tagName.name))!.value
as RegularElement; as RegularElement?;
var renderAs = element.getAttribute('as')?.value?.compute(scope); var renderAs = element.getAttribute('as')?.value?.compute(scope);
var attrs = element.attributes.where((a) => a.name != 'as'); var attrs = element.attributes.where((a) => a.name != 'as');
@ -369,7 +369,7 @@ class Renderer {
} }
if (renderAs == false) { if (renderAs == false) {
for (int i = 0; i < template.children.length; i++) { for (int i = 0; i < template!.children.length; i++) {
var child = template.children.elementAt(i); var child = template.children.elementAt(i);
renderElementChild( renderElementChild(
element, child, buffer, scope, html5, i, element.children.length); element, child, buffer, scope, html5, i, element.children.length);
@ -378,7 +378,7 @@ class Renderer {
var tagName = renderAs?.toString() ?? 'div'; var tagName = renderAs?.toString() ?? 'div';
var syntheticElement = RegularElement( var syntheticElement = RegularElement(
template.lt, template!.lt,
SyntheticIdentifier(tagName), SyntheticIdentifier(tagName),
element.attributes element.attributes
.where((a) => a.name != 'as' && !a.name.startsWith('@')), .where((a) => a.name != 'as' && !a.name.startsWith('@')),

View file

@ -28,12 +28,12 @@ class ConditionalParselet implements InfixParselet {
const ConditionalParselet(); const ConditionalParselet();
@override @override
Expression parse(Parser parser, Expression left, Token token) { Expression? parse(Parser parser, Expression? left, Token? token) {
var ifTrue = parser.parseExpression(0); var ifTrue = parser.parseExpression(0);
if (ifTrue == null) { if (ifTrue == null) {
parser.errors.add(JaelError(JaelErrorSeverity.error, parser.errors.add(JaelError(JaelErrorSeverity.error,
'Missing expression in conditional expression.', token.span)); 'Missing expression in conditional expression.', token!.span));
return null; return null;
} }
@ -48,7 +48,7 @@ class ConditionalParselet implements InfixParselet {
if (ifFalse == null) { if (ifFalse == null) {
parser.errors.add(JaelError(JaelErrorSeverity.error, parser.errors.add(JaelError(JaelErrorSeverity.error,
'Missing expression in conditional expression.', colon.span)); 'Missing expression in conditional expression.', colon!.span));
return null; return null;
} }
@ -62,14 +62,14 @@ class BinaryParselet implements InfixParselet {
const BinaryParselet(this.precedence); const BinaryParselet(this.precedence);
@override @override
Expression parse(Parser parser, Expression left, Token token) { Expression? parse(Parser parser, Expression? left, Token? token) {
var right = parser.parseExpression(precedence); var right = parser.parseExpression(precedence);
if (right == null) { if (right == null) {
if (token.type != TokenType.gt) { if (token!.type != TokenType.gt) {
parser.errors.add(JaelError( parser.errors.add(JaelError(
JaelErrorSeverity.error, 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)); token.span));
} }
return null; return null;
@ -86,10 +86,10 @@ class CallParselet implements InfixParselet {
int get precedence => 19; int get precedence => 19;
@override @override
Expression parse(Parser parser, Expression left, Token token) { Expression? parse(Parser parser, Expression? left, Token? token) {
List<Expression> arguments = []; List<Expression> arguments = [];
List<NamedArgument> namedArguments = []; List<NamedArgument> namedArguments = [];
Expression argument = parser.parseExpression(0); Expression? argument = parser.parseExpression(0);
while (argument != null) { while (argument != null) {
arguments.add(argument); arguments.add(argument);
@ -98,7 +98,7 @@ class CallParselet implements InfixParselet {
argument = parser.parseExpression(0); argument = parser.parseExpression(0);
} }
NamedArgument namedArgument = parser.parseNamedArgument(); NamedArgument? namedArgument = parser.parseNamedArgument();
while (namedArgument != null) { while (namedArgument != null) {
namedArguments.add(namedArgument); namedArguments.add(namedArgument);
@ -109,7 +109,7 @@ class CallParselet implements InfixParselet {
if (!parser.next(TokenType.rParen)) { if (!parser.next(TokenType.rParen)) {
var lastSpan = arguments.isEmpty ? null : arguments.last.span; var lastSpan = arguments.isEmpty ? null : arguments.last.span;
lastSpan ??= token.span; lastSpan ??= token!.span;
parser.errors.add(JaelError(JaelErrorSeverity.error, parser.errors.add(JaelError(JaelErrorSeverity.error,
'Missing ")" after argument list.', lastSpan)); 'Missing ")" after argument list.', lastSpan));
return null; return null;
@ -126,12 +126,12 @@ class IndexerParselet implements InfixParselet {
int get precedence => 19; int get precedence => 19;
@override @override
Expression parse(Parser parser, Expression left, Token token) { Expression? parse(Parser parser, Expression? left, Token? token) {
var indexer = parser.parseExpression(0); var indexer = parser.parseExpression(0);
if (indexer == null) { if (indexer == null) {
parser.errors.add(JaelError( parser.errors.add(JaelError(
JaelErrorSeverity.error, 'Missing expression after "[".', left.span)); JaelErrorSeverity.error, 'Missing expression after "[".', left!.span));
return null; return null;
} }
@ -152,12 +152,12 @@ class MemberParselet implements InfixParselet {
int get precedence => 19; int get precedence => 19;
@override @override
Expression parse(Parser parser, Expression left, Token token) { Expression? parse(Parser parser, Expression? left, Token? token) {
var name = parser.parseIdentifier(); var name = parser.parseIdentifier();
if (name == null) { if (name == null) {
parser.errors.add(JaelError(JaelErrorSeverity.error, 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; return null;
} }

View file

@ -6,10 +6,10 @@ part 'infix.dart';
part 'prefix.dart'; part 'prefix.dart';
abstract class PrefixParselet { abstract class PrefixParselet {
Expression parse(Parser parser, Token token); Expression? parse(Parser parser, Token? token);
} }
abstract class InfixParselet { abstract class InfixParselet {
int get precedence; int get precedence;
Expression parse(Parser parser, Expression left, Token token); Expression? parse(Parser parser, Expression? left, Token? token);
} }

View file

@ -16,12 +16,12 @@ class NotParselet implements PrefixParselet {
const NotParselet(); const NotParselet();
@override @override
Expression parse(Parser parser, Token token) { Expression parse(Parser parser, Token? token) {
var expression = parser.parseExpression(0); var expression = parser.parseExpression(0);
if (expression == null) { if (expression == null) {
parser.errors.add(JaelError(JaelErrorSeverity.error, 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,14 +32,14 @@ class NewParselet implements PrefixParselet {
const NewParselet(); const NewParselet();
@override @override
Expression parse(Parser parser, Token token) { Expression? parse(Parser parser, Token? token) {
var call = parser.parseExpression(0); var call = parser.parseExpression(0);
if (call == null) { if (call == null) {
parser.errors.add(JaelError( parser.errors.add(JaelError(
JaelErrorSeverity.error, JaelErrorSeverity.error,
'"new" must precede a call expression. Nothing was found.', '"new" must precede a call expression. Nothing was found.',
call.span)); call!.span));
return null; return null;
} else if (call is Call) { } else if (call is Call) {
return NewExpression(token, call); return NewExpression(token, call);
@ -57,31 +57,31 @@ class NumberParselet implements PrefixParselet {
const NumberParselet(); const NumberParselet();
@override @override
Expression parse(Parser parser, Token token) => NumberLiteral(token); Expression parse(Parser parser, Token? token) => NumberLiteral(token);
} }
class HexParselet implements PrefixParselet { class HexParselet implements PrefixParselet {
const HexParselet(); const HexParselet();
@override @override
Expression parse(Parser parser, Token token) => HexLiteral(token); Expression parse(Parser parser, Token? token) => HexLiteral(token);
} }
class StringParselet implements PrefixParselet { class StringParselet implements PrefixParselet {
const StringParselet(); const StringParselet();
@override @override
Expression parse(Parser parser, Token token) => Expression parse(Parser parser, Token? token) =>
StringLiteral(token, StringLiteral.parseValue(token)); StringLiteral(token, StringLiteral.parseValue(token!));
} }
class ArrayParselet implements PrefixParselet { class ArrayParselet implements PrefixParselet {
const ArrayParselet(); const ArrayParselet();
@override @override
Expression parse(Parser parser, Token token) { Expression? parse(Parser parser, Token? token) {
List<Expression> items = []; List<Expression> items = [];
Expression item = parser.parseExpression(0); Expression? item = parser.parseExpression(0);
while (item != null) { while (item != null) {
items.add(item); items.add(item);
@ -92,7 +92,7 @@ class ArrayParselet implements PrefixParselet {
if (!parser.next(TokenType.rBracket)) { if (!parser.next(TokenType.rBracket)) {
var lastSpan = items.isEmpty ? null : items.last.span; var lastSpan = items.isEmpty ? null : items.last.span;
lastSpan ??= token.span; lastSpan ??= token!.span;
parser.errors.add(JaelError(JaelErrorSeverity.error, parser.errors.add(JaelError(JaelErrorSeverity.error,
'Missing "]" to terminate array literal.', lastSpan)); 'Missing "]" to terminate array literal.', lastSpan));
return null; return null;
@ -106,7 +106,7 @@ class MapParselet implements PrefixParselet {
const MapParselet(); const MapParselet();
@override @override
Expression parse(Parser parser, Token token) { Expression? parse(Parser parser, Token? token) {
var pairs = <KeyValuePair>[]; var pairs = <KeyValuePair>[];
var pair = parser.parseKeyValuePair(); var pair = parser.parseKeyValuePair();
@ -118,7 +118,7 @@ class MapParselet implements PrefixParselet {
} }
if (!parser.next(TokenType.rCurly)) { 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( parser.errors.add(JaelError(
JaelErrorSeverity.error, 'Missing "}" in map literal.', lastSpan)); JaelErrorSeverity.error, 'Missing "}" in map literal.', lastSpan));
return null; return null;
@ -132,19 +132,19 @@ class IdentifierParselet implements PrefixParselet {
const IdentifierParselet(); const IdentifierParselet();
@override @override
Expression parse(Parser parser, Token token) => Identifier(token); Expression parse(Parser parser, Token? token) => Identifier(token);
} }
class ParenthesisParselet implements PrefixParselet { class ParenthesisParselet implements PrefixParselet {
const ParenthesisParselet(); const ParenthesisParselet();
@override @override
Expression parse(Parser parser, Token token) { Expression? parse(Parser parser, Token? token) {
var expression = parser.parseExpression(0); var expression = parser.parseExpression(0);
if (expression == null) { if (expression == null) {
parser.errors.add(JaelError(JaelErrorSeverity.error, parser.errors.add(JaelError(JaelErrorSeverity.error,
'Missing expression after "(".', token.span)); 'Missing expression after "(".', token!.span));
return null; return null;
} }

View file

@ -7,12 +7,12 @@ class Parser {
final Scanner scanner; final Scanner scanner;
final bool asDSX; final bool asDSX;
Token _current; Token? _current;
int _index = -1; int _index = -1;
Parser(this.scanner, {this.asDSX = false}); Parser(this.scanner, {this.asDSX = false});
Token get current => _current; Token? get current => _current;
int _nextPrecedence() { int _nextPrecedence() {
var tok = peek(); var tok = peek();
@ -33,12 +33,12 @@ class Parser {
return true; return true;
} }
Token peek() { Token? peek() {
if (_index >= scanner.tokens.length - 1) return null; if (_index >= scanner.tokens.length - 1) return null;
return scanner.tokens[_index + 1]; return scanner.tokens[_index + 1];
} }
Token maybe(TokenType type) => next(type) ? _current : null; Token? maybe(TokenType type) => next(type) ? _current : null;
void skipExtraneous(TokenType type) { void skipExtraneous(TokenType type) {
while (next(type)) { while (next(type)) {
@ -46,7 +46,7 @@ class Parser {
} }
} }
Document parseDocument() { Document? parseDocument() {
var doctype = parseDoctype(); var doctype = parseDoctype();
if (doctype == null) { if (doctype == null) {
@ -66,10 +66,10 @@ class Parser {
return Document(doctype, root); return Document(doctype, root);
} }
StringLiteral implicitString() { StringLiteral? implicitString() {
if (next(TokenType.string)) { if (next(TokenType.string)) {
return prefixParselets[TokenType.string].parse(this, _current) return prefixParselets[TokenType.string]!.parse(this, _current)
as StringLiteral; as StringLiteral?;
} }
/*else if (next(TokenType.text)) { /*else if (next(TokenType.text)) {
@ -78,7 +78,7 @@ class Parser {
return null; return null;
} }
Doctype parseDoctype() { Doctype? parseDoctype() {
if (!next(TokenType.lt)) return null; if (!next(TokenType.lt)) return null;
var lt = _current; var lt = _current;
@ -86,12 +86,12 @@ class Parser {
_index--; _index--;
return null; return null;
} }
var doctype = _current, html = parseIdentifier(); var doctype = _current, html = parseIdentifier() as Token?;
if (html?.span?.text?.toLowerCase() != 'html') { if (html?.span?.text.toLowerCase() != 'html') {
errors.add(JaelError( errors.add(JaelError(
JaelErrorSeverity.error, JaelErrorSeverity.error,
'Expected "html" in doctype declaration.', 'Expected "html" in doctype declaration.',
html?.span ?? doctype.span)); html?.span ?? doctype!.span));
return null; return null;
} }
@ -99,18 +99,19 @@ class Parser {
if (public == null) { if (public == null) {
if (!next(TokenType.gt)) { if (!next(TokenType.gt)) {
errors.add(JaelError(JaelErrorSeverity.error, errors.add(JaelError(JaelErrorSeverity.error,
'Expected ">" in doctype declaration.', html.span)); 'Expected ">" in doctype declaration.', html!.span));
return null; return null;
} }
return Doctype(lt, doctype, html, null, null, null, _current); return Doctype(
lt, doctype, html as Identifier?, null, null, null, _current);
} }
if (public?.span?.text?.toLowerCase() != 'public') { if (public.span?.text.toLowerCase() != 'public') {
errors.add(JaelError( errors.add(JaelError(
JaelErrorSeverity.error, JaelErrorSeverity.error,
'Expected "public" in doctype declaration.', 'Expected "public" in doctype declaration.',
public?.span ?? html.span)); public.span ?? html!.span));
return null; return null;
} }
@ -122,37 +123,38 @@ class Parser {
return null; return null;
} }
var name = stringParser.parse(this, _current) as StringLiteral; var name = stringParser!.parse(this, _current) as StringLiteral?;
if (!next(TokenType.string)) { if (!next(TokenType.string)) {
errors.add(JaelError(JaelErrorSeverity.error, errors.add(JaelError(JaelErrorSeverity.error,
'Expected string in doctype declaration.', name.span)); 'Expected string in doctype declaration.', name!.span));
return null; return null;
} }
var url = stringParser.parse(this, _current) as StringLiteral; var url = stringParser.parse(this, _current) as StringLiteral?;
if (!next(TokenType.gt)) { if (!next(TokenType.gt)) {
errors.add(JaelError(JaelErrorSeverity.error, errors.add(JaelError(JaelErrorSeverity.error,
'Expected ">" in doctype declaration.', url.span)); 'Expected ">" in doctype declaration.', url!.span));
return null; return null;
} }
return Doctype(lt, doctype, html, public, name, url, _current); return Doctype(
lt, doctype, html as Identifier?, public, name, url, _current);
} }
ElementChild parseElementChild() => ElementChild? parseElementChild() =>
parseHtmlComment() ?? parseHtmlComment() ??
parseInterpolation() ?? parseInterpolation() ??
parseText() ?? parseText() ??
parseElement(); parseElement();
HtmlComment parseHtmlComment() => HtmlComment? parseHtmlComment() =>
next(TokenType.htmlComment) ? HtmlComment(_current) : null; next(TokenType.htmlComment) ? HtmlComment(_current) : null;
Text parseText() => next(TokenType.text) ? Text(_current) : null; Text? parseText() => next(TokenType.text) ? Text(_current) : null;
Interpolation parseInterpolation() { Interpolation? parseInterpolation() {
if (!next(asDSX ? TokenType.lCurly : TokenType.lDoubleCurly)) return null; if (!next(asDSX ? TokenType.lCurly : TokenType.lDoubleCurly)) return null;
var doubleCurlyL = _current; var doubleCurlyL = _current;
@ -160,7 +162,7 @@ class Parser {
if (expression == null) { if (expression == null) {
errors.add(JaelError(JaelErrorSeverity.error, errors.add(JaelError(JaelErrorSeverity.error,
'Missing expression in interpolation.', doubleCurlyL.span)); 'Missing expression in interpolation.', doubleCurlyL!.span));
return null; return null;
} }
@ -174,7 +176,7 @@ class Parser {
return Interpolation(doubleCurlyL, expression, _current); return Interpolation(doubleCurlyL, expression, _current);
} }
Element parseElement() { Element? parseElement() {
if (!next(TokenType.lt)) return null; if (!next(TokenType.lt)) return null;
var lt = _current; var lt = _current;
@ -188,11 +190,11 @@ class Parser {
if (tagName == null) { if (tagName == null) {
errors.add( errors.add(
JaelError(JaelErrorSeverity.error, 'Missing tag name.', lt.span)); JaelError(JaelErrorSeverity.error, 'Missing tag name.', lt!.span));
return null; return null;
} }
List<Attribute> attributes = []; var attributes = <Attribute>[];
var attribute = parseAttribute(); var attribute = parseAttribute();
while (attribute != null) { while (attribute != null) {
@ -206,7 +208,7 @@ class Parser {
if (!next(TokenType.gt)) { if (!next(TokenType.gt)) {
errors.add(JaelError(JaelErrorSeverity.error, 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; return null;
} }
@ -228,7 +230,7 @@ class Parser {
return SelfClosingElement(lt, tagName, attributes, null, gt); return SelfClosingElement(lt, tagName, attributes, null, gt);
} }
List<ElementChild> children = []; var children = <ElementChild>[];
var child = parseElementChild(); var child = parseElementChild();
while (child != null) { while (child != null) {
@ -250,25 +252,26 @@ class Parser {
if (!next(TokenType.slash)) { if (!next(TokenType.slash)) {
errors.add(JaelError(JaelErrorSeverity.error, errors.add(JaelError(JaelErrorSeverity.error,
'Missing "/" in "${tagName.name}" closing tag.', lt2.span)); 'Missing "/" in "${tagName.name}" closing tag.', lt2!.span));
return null; return null;
} }
var slash = _current, tagName2 = parseIdentifier(); var slash = _current;
var tagName2 = parseIdentifier();
if (tagName2 == null) { if (tagName2 == null) {
errors.add(JaelError( errors.add(JaelError(
JaelErrorSeverity.error, JaelErrorSeverity.error,
'Missing "${tagName.name}" in "${tagName.name}" closing tag.', 'Missing "${tagName.name}" in "${tagName.name}" closing tag.',
slash.span)); slash!.span));
return null; return null;
} }
if (tagName2.name != tagName.name) { if (tagName2.name != tagName.name) {
errors.add(JaelError( errors.add(JaelError(
JaelErrorSeverity.error, 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)); lt2!.span));
return null; return null;
} }
@ -282,19 +285,19 @@ class Parser {
lt, tagName, attributes, gt, children, lt2, slash, tagName2, _current); lt, tagName, attributes, gt, children, lt2, slash, tagName2, _current);
} }
Attribute parseAttribute() { Attribute? parseAttribute() {
Identifier id; Identifier? id;
StringLiteral string; StringLiteral? string;
if ((id = parseIdentifier()) != null) { if ((id = parseIdentifier()) != null) {
// Nothing // Nothing
} else if (next(TokenType.string)) { } else if (next(TokenType.string)) {
string = StringLiteral(_current, StringLiteral.parseValue(_current)); string = StringLiteral(_current, StringLiteral.parseValue(_current!));
} else { } else {
return null; return null;
} }
Token equals, nequ; Token? equals, nequ;
if (next(TokenType.equals)) { if (next(TokenType.equals)) {
equals = _current; equals = _current;
@ -309,7 +312,7 @@ class Parser {
if (value == null) { if (value == null) {
errors.add(JaelError(JaelErrorSeverity.error, errors.add(JaelError(JaelErrorSeverity.error,
'Missing expression in attribute.', equals?.span ?? nequ.span)); 'Missing expression in attribute.', equals?.span ?? nequ!.span));
return null; return null;
} }
@ -329,22 +332,22 @@ class Parser {
} }
errors.add(JaelError(JaelErrorSeverity.error, errors.add(JaelError(JaelErrorSeverity.error,
'Missing expression in attribute.', equals?.span ?? nequ.span)); 'Missing expression in attribute.', equals?.span ?? nequ!.span));
return null; return null;
} }
} }
Expression parseExpression(int precedence) { Expression? parseExpression(int precedence) {
// Only consume a token if it could potentially be a prefix parselet // Only consume a token if it could potentially be a prefix parselet
for (var type in prefixParselets.keys) { for (var type in prefixParselets.keys) {
if (next(type)) { if (next(type)) {
var left = prefixParselets[type].parse(this, _current); var left = prefixParselets[type]!.parse(this, _current);
while (precedence < _nextPrecedence()) { while (precedence < _nextPrecedence()) {
_current = scanner.tokens[++_index]; _current = scanner.tokens[++_index];
if (_current.type == TokenType.slash && if (_current!.type == TokenType.slash &&
peek()?.type == TokenType.gt) { peek()?.type == TokenType.gt) {
// Handle `/>` // Handle `/>`
// //
@ -354,11 +357,11 @@ class Parser {
return left; return left;
} }
var infix = infixParselets[_current.type]; var infix = infixParselets[_current!.type]!;
var newLeft = infix.parse(this, left, _current); var newLeft = infix.parse(this, left, _current);
if (newLeft == null) { if (newLeft == null) {
if (_current.type == TokenType.gt) _index--; if (_current!.type == TokenType.gt) _index--;
return left; return left;
} }
left = newLeft; left = newLeft;
@ -372,27 +375,27 @@ class Parser {
return null; return null;
} }
Identifier parseIdentifier() => Identifier? parseIdentifier() =>
next(TokenType.id) ? Identifier(_current) : null; next(TokenType.id) ? Identifier(_current) : null;
KeyValuePair parseKeyValuePair() { KeyValuePair? parseKeyValuePair() {
var key = parseExpression(0); var key = parseExpression(0);
if (key == null) return null; if (key == null) return null;
if (!next(TokenType.colon)) return KeyValuePair(key, null, null); if (!next(TokenType.colon)) return KeyValuePair(key, null, null);
var colon = _current, value = parseExpression(0); var colon = _current, value = parseExpression(0) as Token?;
if (value == null) { if (value == null) {
errors.add(JaelError(JaelErrorSeverity.error, errors.add(JaelError(JaelErrorSeverity.error,
'Missing expression in key-value pair.', colon.span)); 'Missing expression in key-value pair.', colon!.span));
return null; return null;
} }
return KeyValuePair(key, colon, value); return KeyValuePair(key, colon, value as Expression?);
} }
NamedArgument parseNamedArgument() { NamedArgument? parseNamedArgument() {
var name = parseIdentifier(); var name = parseIdentifier();
if (name == null) return null; if (name == null) return null;
@ -402,14 +405,14 @@ class Parser {
return null; return null;
} }
var colon = _current, value = parseExpression(0); var colon = _current, value = parseExpression(0) as Token?;
if (value == null) { if (value == null) {
errors.add(JaelError(JaelErrorSeverity.error, errors.add(JaelError(JaelErrorSeverity.error,
'Missing expression in named argument.', colon.span)); 'Missing expression in named argument.', colon!.span));
return null; return null;
} }
return NamedArgument(name, colon, value); return NamedArgument(name, colon, value as Expression);
} }
} }

View file

@ -85,44 +85,44 @@ class _Scanner implements Scanner {
final List<JaelError> errors = []; final List<JaelError> errors = [];
final List<Token> tokens = []; final List<Token> tokens = [];
_ScannerState state = _ScannerState.html; _ScannerState state = _ScannerState.html;
final Queue<String> openTags = Queue(); final Queue<String?> openTags = Queue();
SpanScanner _scanner; SpanScanner? _scanner;
_Scanner(String text, sourceUrl) { _Scanner(String text, sourceUrl) {
_scanner = SpanScanner(text, sourceUrl: sourceUrl); _scanner = SpanScanner(text, sourceUrl: sourceUrl);
} }
void scan({bool asDSX = false}) { void scan({bool asDSX = false}) {
while (!_scanner.isDone) { while (!_scanner!.isDone) {
if (state == _ScannerState.html) { if (state == _ScannerState.html) {
scanHtml(asDSX); scanHtml(asDSX);
} else if (state == _ScannerState.freeText) { } else if (state == _ScannerState.freeText) {
// Just keep parsing until we hit "</" // 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 // Skip through comments
if (_scanner.scan(_htmlComment)) continue; if (_scanner!.scan(_htmlComment)) continue;
// Break on {{ or { // Break on {{ or {
if (_scanner.matches(asDSX ? '{' : '{{')) { if (_scanner!.matches(asDSX ? '{' : '{{')) {
state = _ScannerState.html; state = _ScannerState.html;
//_scanner.position--; //_scanner.position--;
break; break;
} }
var ch = _scanner.readChar(); var ch = _scanner!.readChar();
if (ch == $lt) { if (ch == $lt) {
// && !_scanner.isDone) { // && !_scanner.isDone) {
if (_scanner.matches('/')) { if (_scanner!.matches('/')) {
// If we reached "</", backtrack and break into HTML // If we reached "</", backtrack and break into HTML
openTags.removeFirst(); openTags.removeFirst();
_scanner.position--; _scanner!.position--;
state = _ScannerState.html; state = _ScannerState.html;
break; break;
} else if (_scanner.matches(_id)) { } else if (_scanner!.matches(_id)) {
// Also break when we reach <foo. // Also break when we reach <foo.
// //
// HOWEVER, that is also JavaScript. So we must // HOWEVER, that is also JavaScript. So we must
@ -132,26 +132,26 @@ class _Scanner implements Scanner {
if (!shouldBreak) { if (!shouldBreak) {
// Try to see if we are closing a script tag // Try to see if we are closing a script tag
var replay = _scanner.state; var replay = _scanner!.state;
_scanner _scanner
..readChar() ?..readChar()
..scan(_whitespace); ..scan(_whitespace);
//print(_scanner.emptySpan.highlight()); //print(_scanner.emptySpan.highlight());
if (_scanner.matches(_id)) { if (_scanner!.matches(_id)) {
//print(_scanner.lastMatch[0]); //print(_scanner.lastMatch[0]);
shouldBreak = _scanner.lastMatch[0] == 'script'; shouldBreak = _scanner!.lastMatch![0] == 'script';
_scanner.position--; _scanner!.position--;
} }
if (!shouldBreak) { if (!shouldBreak) {
_scanner.state = replay; _scanner!.state = replay;
} }
} }
if (shouldBreak) { if (shouldBreak) {
openTags.removeFirst(); openTags.removeFirst();
_scanner.position--; _scanner!.position--;
state = _ScannerState.html; state = _ScannerState.html;
break; break;
} }
@ -159,10 +159,10 @@ class _Scanner implements Scanner {
} }
// Otherwise, just add to the "buffer" // 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) {
tokens.add(Token(TokenType.text, span, null)); tokens.add(Token(TokenType.text, span, null));
@ -182,34 +182,35 @@ class _Scanner implements Scanner {
while (true) { while (true) {
// Scan whitespace // Scan whitespace
_scanner.scan(_whitespace); _scanner!.scan(_whitespace);
_expressionPatterns.forEach((pattern, type) { _expressionPatterns.forEach((pattern, type) {
if (_scanner.matches(pattern)) { if (_scanner!.matches(pattern)) {
potential.add(Token(type, _scanner.lastSpan, _scanner.lastMatch)); potential
.add(Token(type, _scanner!.lastSpan, _scanner!.lastMatch));
} }
}); });
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; if (potential.isEmpty) break;
var token = potential.first; var token = potential.first;
tokens.add(token); tokens.add(token);
_scanner.scan(token.span.text); _scanner!.scan(token.span!.text);
if (token.type == TokenType.lt) { if (token.type == TokenType.lt) {
brackets.addFirst(token); brackets.addFirst(token);
// Try to see if we are at a tag. // Try to see if we are at a tag.
var replay = _scanner.state; var replay = _scanner!.state;
_scanner.scan(_whitespace); _scanner!.scan(_whitespace);
if (_scanner.matches(_id)) { if (_scanner!.matches(_id)) {
openTags.addFirst(_scanner.lastMatch[0]); openTags.addFirst(_scanner!.lastMatch![0]);
} else { } else {
_scanner.state = replay; _scanner!.state = replay;
} }
} else if (token.type == TokenType.slash) { } else if (token.type == TokenType.slash) {
// Only push if we're at </foo // Only push if we're at </foo
@ -225,11 +226,11 @@ class _Scanner implements Scanner {
brackets.removeFirst(); brackets.removeFirst();
// Now, ONLY continue parsing HTML if the next character is '<'. // Now, ONLY continue parsing HTML if the next character is '<'.
var replay = _scanner.state; var replay = _scanner!.state;
_scanner.scan(_whitespace); _scanner!.scan(_whitespace);
if (!_scanner.matches('<')) { if (!_scanner!.matches('<')) {
_scanner.state = replay; _scanner!.state = replay;
state = _ScannerState.freeText; state = _ScannerState.freeText;
break; break;
} }
@ -240,11 +241,11 @@ class _Scanner implements Scanner {
// We're at foo>, try to parse text? // We're at foo>, try to parse text?
brackets.removeFirst(); brackets.removeFirst();
var replay = _scanner.state; var replay = _scanner!.state;
_scanner.scan(_whitespace); _scanner!.scan(_whitespace);
if (!_scanner.matches('<')) { if (!_scanner!.matches('<')) {
_scanner.state = replay; _scanner!.state = replay;
state = _ScannerState.freeText; state = _ScannerState.freeText;
break; break;
} }
@ -258,7 +259,7 @@ class _Scanner implements Scanner {
potential.clear(); potential.clear();
} }
} }
} while (brackets.isNotEmpty && !_scanner.isDone); } while (brackets.isNotEmpty && !_scanner!.isDone);
state = _ScannerState.freeText; state = _ScannerState.freeText;
} }

View file

@ -3,15 +3,25 @@ version: 3.0.0
description: A simple server-side HTML templating engine for Dart. Comparable to Blade or Liquid. description: A simple server-side HTML templating engine for Dart. Comparable to Blade or Liquid.
author: Tobe O <thosakwe@gmail.com> author: Tobe O <thosakwe@gmail.com>
homepage: https://docs.angel-dart.dev/packages/front-end/jael homepage: https://docs.angel-dart.dev/packages/front-end/jael
publish_to: none
environment: environment:
sdk: '>=2.10.0 <3.0.0' sdk: '>=2.12.0 <3.0.0'
dependencies: dependencies:
args: ^2.0.0 args: ^2.0.0
charcode: ^1.0.0 charcode: ^1.0.0
code_buffer: ^1.0.0 code_buffer:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x_nnbd
path: packages/code_buffer
source_span: ^1.0.0 source_span: ^1.0.0
string_scanner: ^1.0.0 string_scanner: ^1.0.0
symbol_table: ^2.0.0 symbol_table:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x_nnbd
path: packages/symbol_table
collection: ^1.15.0-nullsafety.4
dev_dependencies: dev_dependencies:
pedantic: ^1.0.0 pedantic: ^1.0.0
test: ^1.0.0 test: ^1.0.0

View file

@ -105,8 +105,8 @@ void main() {
}); });
} }
String render(String template, [Map<String, dynamic> values]) { String render(String template, [Map<String, dynamic> values = const {}]) {
var doc = jael.parseDocument(template, onError: (e) => throw e); var doc = jael.parseDocument(template, onError: (e) => throw e)!;
var buffer = CodeBuffer(); var buffer = CodeBuffer();
const jael.Renderer().render(doc, buffer, SymbolTable(values: values)); const jael.Renderer().render(doc, buffer, SymbolTable(values: values));
return buffer.toString(); return buffer.toString();

View file

@ -6,18 +6,18 @@ void main() {
test('attributes', () { test('attributes', () {
var doc = parseDSX(''' var doc = parseDSX('''
<foo bar="baz" yes={no} /> <foo bar="baz" yes={no} />
'''); ''')!;
var foo = doc.root as SelfClosingElement; var foo = doc.root as SelfClosingElement;
expect(foo.tagName.name, 'foo'); expect(foo.tagName.name, 'foo');
expect(foo.attributes, hasLength(2)); expect(foo.attributes, hasLength(2));
expect(foo.getAttribute('bar'), isNotNull); expect(foo.getAttribute('bar'), isNotNull);
expect(foo.getAttribute('yes'), isNotNull); expect(foo.getAttribute('yes'), isNotNull);
expect(foo.getAttribute('bar').value.compute(null), 'baz'); expect(foo.getAttribute('bar')?.value?.compute(null), 'baz');
expect( expect(
foo foo
.getAttribute('yes') .getAttribute('yes')!
.value .value!
.compute(SymbolTable(values: {'no': 'maybe'})), .compute(SymbolTable(values: {'no': 'maybe'})),
'maybe'); 'maybe');
}); });
@ -27,7 +27,7 @@ void main() {
<foo bar="baz" yes={no}> <foo bar="baz" yes={no}>
<bar>{24 * 3}</bar> <bar>{24 * 3}</bar>
</foo> </foo>
'''); ''')!;
var bar = doc.root.children.first as RegularElement; var bar = doc.root.children.first as RegularElement;
expect(bar.tagName.name, 'bar'); expect(bar.tagName.name, 'bar');
@ -37,7 +37,7 @@ void main() {
}); });
} }
Document parseDSX(String text) { Document? parseDSX(String text) {
return parseDocument(text, return parseDocument(text,
sourceUrl: 'test.dsx', asDSX: true, onError: (e) => throw e); sourceUrl: 'test.dsx', asDSX: true, onError: (e) => throw e);
} }

View file

@ -16,8 +16,8 @@ main() {
'''; ''';
var buf = CodeBuffer(); var buf = CodeBuffer();
jael.Document document; jael.Document? document;
SymbolTable scope; late SymbolTable scope;
try { try {
document = jael.parseDocument(template, sourceUrl: 'test.jael'); document = jael.parseDocument(template, sourceUrl: 'test.jael');
@ -33,7 +33,7 @@ main() {
} }
expect(document, isNotNull); expect(document, isNotNull);
const jael.Renderer().render(document, buf, scope); const jael.Renderer().render(document!, buf, scope);
print(buf); print(buf);
expect( expect(
@ -66,7 +66,7 @@ main() {
var buf = CodeBuffer(); var buf = CodeBuffer();
//jael.scan(template, sourceUrl: 'test.jael').tokens.forEach(print); //jael.scan(template, sourceUrl: 'test.jael').tokens.forEach(print);
var document = jael.parseDocument(template, sourceUrl: 'test.jael'); var document = jael.parseDocument(template, sourceUrl: 'test.jael')!;
var scope = SymbolTable<dynamic>(values: { var scope = SymbolTable<dynamic>(values: {
'pokemon': const _Pokemon('Darkrai', 'Dark'), 'pokemon': const _Pokemon('Darkrai', 'Dark'),
}); });
@ -106,7 +106,7 @@ main() {
'''; ''';
var buf = CodeBuffer(); var buf = CodeBuffer();
var document = jael.parseDocument(template, sourceUrl: 'test.jael'); var document = jael.parseDocument(template, sourceUrl: 'test.jael')!;
var scope = SymbolTable<dynamic>(values: { var scope = SymbolTable<dynamic>(values: {
'starters': starters, 'starters': starters,
}); });
@ -151,7 +151,7 @@ main() {
'''; ''';
var buf = CodeBuffer(); var buf = CodeBuffer();
var document = jael.parseDocument(template, sourceUrl: 'test.jael'); var document = jael.parseDocument(template, sourceUrl: 'test.jael')!;
var scope = SymbolTable<dynamic>(values: { var scope = SymbolTable<dynamic>(values: {
'starters': starters, 'starters': starters,
}); });
@ -197,7 +197,7 @@ main() {
'''; ''';
var buf = CodeBuffer(); var buf = CodeBuffer();
var document = jael.parseDocument(template, sourceUrl: 'test.jael'); var document = jael.parseDocument(template, sourceUrl: 'test.jael')!;
var scope = SymbolTable(); var scope = SymbolTable();
const jael.Renderer().render(document, buf, scope); const jael.Renderer().render(document, buf, scope);
@ -243,7 +243,7 @@ main() {
'''; ''';
var buf = CodeBuffer(); var buf = CodeBuffer();
var document = jael.parseDocument(template, sourceUrl: 'test.jael'); var document = jael.parseDocument(template, sourceUrl: 'test.jael')!;
var scope = SymbolTable(); var scope = SymbolTable();
const jael.Renderer().render(document, buf, scope); const jael.Renderer().render(document, buf, scope);
@ -268,7 +268,7 @@ main() {
'''; ''';
var buf = CodeBuffer(); var buf = CodeBuffer();
var document = jael.parseDocument(template, sourceUrl: 'test.jael'); var document = jael.parseDocument(template, sourceUrl: 'test.jael')!;
var scope = SymbolTable(); var scope = SymbolTable();
const jael.Renderer().render(document, buf, scope); const jael.Renderer().render(document, buf, scope);
@ -299,7 +299,7 @@ main() {
'''; ''';
var buf = CodeBuffer(); var buf = CodeBuffer();
var document = jael.parseDocument(template, sourceUrl: 'test.jael'); var document = jael.parseDocument(template, sourceUrl: 'test.jael')!;
var scope = SymbolTable<dynamic>(values: { var scope = SymbolTable<dynamic>(values: {
'account': _Account(isDisabled: true), 'account': _Account(isDisabled: true),
}); });
@ -326,7 +326,7 @@ main() {
'''; ''';
var buf = CodeBuffer(); var buf = CodeBuffer();
var document = jael.parseDocument(template, sourceUrl: 'test.jael'); var document = jael.parseDocument(template, sourceUrl: 'test.jael')!;
var scope = SymbolTable<dynamic>(values: { var scope = SymbolTable<dynamic>(values: {
'account': _Account(isDisabled: null), 'account': _Account(isDisabled: null),
}); });
@ -351,7 +351,7 @@ class _Pokemon {
} }
class _Account { class _Account {
final bool isDisabled; final bool? isDisabled;
_Account({this.isDisabled}); _Account({this.isDisabled});
} }

View file

@ -1,11 +1,11 @@
import 'package:matcher/matcher.dart'; import 'package:matcher/matcher.dart';
import 'package:jael/src/ast/token.dart'; import 'package:jael/src/ast/token.dart';
Matcher isToken(TokenType type, [String text]) => _IsToken(type, text); Matcher isToken(TokenType type, [String? text]) => _IsToken(type, text);
class _IsToken extends Matcher { class _IsToken extends Matcher {
final TokenType type; final TokenType type;
final String text; final String? text;
_IsToken(this.type, [this.text]); _IsToken(this.type, [this.text]);
@ -19,6 +19,6 @@ class _IsToken extends Matcher {
bool matches(item, Map matchState) { bool matches(item, Map matchState) {
return item is Token && return item is Token &&
item.type == type && item.type == type &&
(text == null || item.span.text == text); (text == null || item.span!.text == text);
} }
} }

View file

@ -1,8 +1,9 @@
name: symbol_table name: symbol_table
version: 2.0.0 version: 3.0.0
description: A generic symbol table implementation in Dart, with support for scopes and constants. description: A generic symbol table implementation in Dart, with support for scopes and constants.
author: Tobe O <thosakwe@gmail.com> author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/thosakwe/symbol_table homepage: https://github.com/thosakwe/symbol_table
publish_to: none
environment: environment:
sdk: '>=2.12.0 <3.0.0' sdk: '>=2.12.0 <3.0.0'
dependencies: dependencies: