Migrated Jael
This commit is contained in:
parent
ce510e0394
commit
436f797982
32 changed files with 351 additions and 335 deletions
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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!);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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!);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 = '';
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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('@')),
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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:
|
||||||
|
|
Loading…
Reference in a new issue