Added code for rendering custom element
This commit is contained in:
parent
c4b0fa0b4f
commit
d387776b9e
10 changed files with 97 additions and 14 deletions
8
.idea/runConfigurations/tests_in_jael.xml
Normal file
8
.idea/runConfigurations/tests_in_jael.xml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="tests in jael" type="DartTestRunConfigurationType" factoryName="Dart Test" singleton="true" nameIsGenerated="true">
|
||||||
|
<option name="filePath" value="$PROJECT_DIR$/jael" />
|
||||||
|
<option name="scope" value="FOLDER" />
|
||||||
|
<option name="testRunnerOptions" value="-j4" />
|
||||||
|
<method />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
|
@ -1,2 +1,3 @@
|
||||||
analyzer:
|
analyzer:
|
||||||
strong-mode: true
|
strong-mode:
|
||||||
|
implicit-casts: false
|
|
@ -38,7 +38,7 @@ class Call extends Expression {
|
||||||
var args = computePositional(scope);
|
var args = computePositional(scope);
|
||||||
var named = computeNamed(scope);
|
var named = computeNamed(scope);
|
||||||
|
|
||||||
return Function.apply(callee, args, named);
|
return Function.apply(callee as Function, args, named);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ class Conditional extends Expression {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
compute(scope) {
|
compute(scope) {
|
||||||
return condition.compute(scope)
|
return (condition.compute(scope) == true)
|
||||||
? ifTrue.compute(scope)
|
? ifTrue.compute(scope)
|
||||||
: ifFalse.compute(scope);
|
: ifFalse.compute(scope);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,6 @@ class Negation extends Expression {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
compute(SymbolTable scope) {
|
compute(SymbolTable scope) {
|
||||||
return !(expression.compute(scope));
|
return !(expression.compute(scope) == true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,3 +33,16 @@ class Identifier extends Expression {
|
||||||
@override
|
@override
|
||||||
FileSpan get span => id.span;
|
FileSpan get span => id.span;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SyntheticIdentifier extends Identifier {
|
||||||
|
@override
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
SyntheticIdentifier(this.name, [Token token]) : super(token);
|
||||||
|
|
||||||
|
@override
|
||||||
|
FileSpan get span {
|
||||||
|
if (id != null) return id.span;
|
||||||
|
throw new UnsupportedError('Cannot get the span of a SyntheticIdentifier.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ class NewExpression extends Expression {
|
||||||
if (call.target is MemberExpression)
|
if (call.target is MemberExpression)
|
||||||
name = (call.target as MemberExpression).name.name;
|
name = (call.target as MemberExpression).name.name;
|
||||||
|
|
||||||
return reflectClass(targetType)
|
return reflectClass(targetType as Type)
|
||||||
.newInstance(new Symbol(name), positional, named)
|
.newInstance(new Symbol(name), positional, named)
|
||||||
.reflectee;
|
.reflectee;
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,6 +107,10 @@ class Renderer {
|
||||||
} else if (element.tagName.name == 'element') {
|
} else if (element.tagName.name == 'element') {
|
||||||
registerCustomElement(element, buffer, childScope, html5);
|
registerCustomElement(element, buffer, childScope, html5);
|
||||||
return;
|
return;
|
||||||
|
} else if (scope.resolve(customElementName(element.tagName.name))?.value
|
||||||
|
is Element) {
|
||||||
|
renderCustomElement(element, buffer, childScope, html5);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer..write('<')..write(element.tagName.name);
|
buffer..write('<')..write(element.tagName.name);
|
||||||
|
@ -171,7 +175,7 @@ class Renderer {
|
||||||
.firstWhere((a) => a.name == 'as', orElse: () => null);
|
.firstWhere((a) => a.name == 'as', orElse: () => null);
|
||||||
var indexAsAttribute = element.attributes
|
var indexAsAttribute = element.attributes
|
||||||
.firstWhere((a) => a.name == 'index-as', orElse: () => null);
|
.firstWhere((a) => a.name == 'index-as', orElse: () => null);
|
||||||
var alias = asAttribute?.value?.compute(scope) ?? 'item';
|
var alias = asAttribute?.value?.compute(scope)?.toString() ?? 'item';
|
||||||
var indexAs = indexAsAttribute?.value?.compute(scope)?.toString() ?? 'item';
|
var indexAs = indexAsAttribute?.value?.compute(scope)?.toString() ?? 'item';
|
||||||
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');
|
||||||
|
@ -302,12 +306,67 @@ class Renderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static String customElementName(String name) => 'elements@$name';
|
||||||
|
|
||||||
void registerCustomElement(
|
void registerCustomElement(
|
||||||
Element element, CodeBuffer buffer, SymbolTable scope, bool html5) {
|
Element element, CodeBuffer buffer, SymbolTable scope, bool html5) {
|
||||||
|
if (element is! RegularElement) {
|
||||||
|
throw new JaelError(JaelErrorSeverity.error,
|
||||||
|
"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();
|
||||||
|
|
||||||
if (name) {
|
if (name == null) {
|
||||||
|
throw new JaelError(
|
||||||
|
JaelErrorSeverity.error,
|
||||||
|
"Attribute 'name' is required when registering a custom element.",
|
||||||
|
element.tagName.span);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
scope.create(customElementName(name), value: element, constant: true);
|
||||||
|
} on StateError {
|
||||||
|
throw new JaelError(
|
||||||
|
JaelErrorSeverity.error,
|
||||||
|
"Cannot re-define element '$name' in this scope.",
|
||||||
|
element.getAttribute('name').span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderCustomElement(
|
||||||
|
Element element, CodeBuffer buffer, SymbolTable scope, bool html5) {
|
||||||
|
var template = scope.resolve(customElementName(element.tagName.name)).value
|
||||||
|
as RegularElement;
|
||||||
|
var renderAs = element.getAttribute('as')?.value?.compute(scope);
|
||||||
|
var attrs = element.attributes.where((a) => a.name != 'as');
|
||||||
|
|
||||||
|
for (var attribute in attrs) {
|
||||||
|
scope.create(attribute.name,
|
||||||
|
value: attribute.value?.compute(scope), constant: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (renderAs == false) {
|
||||||
|
for (int i = 0; i < template.children.length; i++) {
|
||||||
|
var child = template.children.elementAt(i);
|
||||||
|
renderElementChild(
|
||||||
|
element, child, buffer, scope, html5, i, element.children.length);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var tagName = renderAs?.toString() ?? 'div';
|
||||||
|
|
||||||
|
var syntheticElement = new RegularElement(
|
||||||
|
template.lt,
|
||||||
|
new SyntheticIdentifier(tagName),
|
||||||
|
[],
|
||||||
|
template.gt,
|
||||||
|
template.children,
|
||||||
|
template.lt2,
|
||||||
|
template.slash,
|
||||||
|
new SyntheticIdentifier(tagName),
|
||||||
|
template.gt2);
|
||||||
|
|
||||||
|
renderElement(syntheticElement, buffer, scope, html5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,14 +41,14 @@ class NewParselet implements PrefixParselet {
|
||||||
'"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 new NewExpression(token, call);
|
||||||
|
} else {
|
||||||
parser.errors.add(new JaelError(
|
parser.errors.add(new JaelError(
|
||||||
JaelErrorSeverity.error,
|
JaelErrorSeverity.error,
|
||||||
'"new" must precede a call expression, not a(n) ${call.runtimeType}.',
|
'"new" must precede a call expression, not a(n) ${call.runtimeType}.',
|
||||||
call.span));
|
call.span));
|
||||||
return null;
|
return null;
|
||||||
} else {
|
|
||||||
return new NewExpression(token, call);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,8 +68,10 @@ class Parser {
|
||||||
|
|
||||||
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;
|
||||||
} else if (next(TokenType.text)) {}
|
} /*else if (next(TokenType.text)) {
|
||||||
|
|
||||||
|
}*/
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -118,7 +120,7 @@ class Parser {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var name = stringParser.parse(this, _current);
|
var name = stringParser.parse(this, _current) as StringLiteral;
|
||||||
|
|
||||||
if (!next(TokenType.string)) {
|
if (!next(TokenType.string)) {
|
||||||
errors.add(new JaelError(JaelErrorSeverity.error,
|
errors.add(new JaelError(JaelErrorSeverity.error,
|
||||||
|
@ -126,7 +128,7 @@ class Parser {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var url = stringParser.parse(this, _current);
|
var url = stringParser.parse(this, _current) as StringLiteral;
|
||||||
|
|
||||||
if (!next(TokenType.gt)) {
|
if (!next(TokenType.gt)) {
|
||||||
errors.add(new JaelError(JaelErrorSeverity.error,
|
errors.add(new JaelError(JaelErrorSeverity.error,
|
||||||
|
|
Loading…
Reference in a new issue