Added code for rendering custom element

This commit is contained in:
Tobe O 2018-06-27 20:24:35 -04:00
parent c4b0fa0b4f
commit d387776b9e
10 changed files with 97 additions and 14 deletions

View 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>

View file

@ -1,2 +1,3 @@
analyzer: analyzer:
strong-mode: true strong-mode:
implicit-casts: false

View file

@ -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);
} }
} }

View file

@ -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);
} }

View file

@ -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);
} }
} }

View file

@ -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.');
}
}

View file

@ -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;
} }

View file

@ -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);
} }
} }
} }

View file

@ -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);
} }
} }
} }

View file

@ -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,