feat(ExpressionParser): add support for `this`
This commit is contained in:
parent
26c9e1dc70
commit
0ca05eee45
|
@ -14,6 +14,8 @@ import {NgFor, NgIf} from '@angular/common';
|
||||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||||
import {By} from '@angular/platform-browser/src/dom/debug/by';
|
import {By} from '@angular/platform-browser/src/dom/debug/by';
|
||||||
|
|
||||||
|
let thisArg: any;
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('ngFor', () => {
|
describe('ngFor', () => {
|
||||||
const TEMPLATE =
|
const TEMPLATE =
|
||||||
|
@ -460,6 +462,22 @@ export function main() {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('track by', () => {
|
describe('track by', () => {
|
||||||
|
it('should set the context to the component instance',
|
||||||
|
inject(
|
||||||
|
[TestComponentBuilder, AsyncTestCompleter],
|
||||||
|
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||||
|
const template =
|
||||||
|
`<template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackByContext.bind(this)"></template>`;
|
||||||
|
tcb.overrideTemplate(TestComponent, template)
|
||||||
|
.createAsync(TestComponent)
|
||||||
|
.then((fixture) => {
|
||||||
|
thisArg = null;
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(thisArg).toBe(fixture.debugElement.componentInstance);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
it('should not replace tracked items',
|
it('should not replace tracked items',
|
||||||
inject(
|
inject(
|
||||||
[TestComponentBuilder, AsyncTestCompleter],
|
[TestComponentBuilder, AsyncTestCompleter],
|
||||||
|
@ -557,6 +575,7 @@ class TestComponent {
|
||||||
constructor() { this.items = [1, 2]; }
|
constructor() { this.items = [1, 2]; }
|
||||||
trackById(index: number, item: any): string { return item['id']; }
|
trackById(index: number, item: any): string { return item['id']; }
|
||||||
trackByIndex(index: number, item: any): number { return index; }
|
trackByIndex(index: number, item: any): number { return index; }
|
||||||
|
trackByContext(): void { thisArg = this; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({selector: 'outer-cmp', directives: [TestComponent], template: ''})
|
@Component({selector: 'outer-cmp', directives: [TestComponent], template: ''})
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
|
|
||||||
import {Injectable} from '@angular/core';
|
import {Injectable} from '@angular/core';
|
||||||
import * as chars from '../chars';
|
import * as chars from '../chars';
|
||||||
import {BaseException} from '../facade/exceptions';
|
|
||||||
import {NumberWrapper, StringJoiner, StringWrapper, isPresent} from '../facade/lang';
|
import {NumberWrapper, StringJoiner, StringWrapper, isPresent} from '../facade/lang';
|
||||||
|
|
||||||
export enum TokenType {
|
export enum TokenType {
|
||||||
|
@ -21,7 +20,7 @@ export enum TokenType {
|
||||||
Error
|
Error
|
||||||
}
|
}
|
||||||
|
|
||||||
const KEYWORDS = ['var', 'let', 'null', 'undefined', 'true', 'false', 'if', 'else'];
|
const KEYWORDS = ['var', 'let', 'null', 'undefined', 'true', 'false', 'if', 'else', 'this'];
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class Lexer {
|
export class Lexer {
|
||||||
|
@ -74,6 +73,8 @@ export class Token {
|
||||||
|
|
||||||
isKeywordFalse(): boolean { return this.type == TokenType.Keyword && this.strValue == 'false'; }
|
isKeywordFalse(): boolean { return this.type == TokenType.Keyword && this.strValue == 'false'; }
|
||||||
|
|
||||||
|
isKeywordThis(): boolean { return this.type == TokenType.Keyword && this.strValue == 'this'; }
|
||||||
|
|
||||||
isError(): boolean { return this.type == TokenType.Error; }
|
isError(): boolean { return this.type == TokenType.Error; }
|
||||||
|
|
||||||
toNumber(): number { return this.type == TokenType.Number ? this.numValue : -1; }
|
toNumber(): number { return this.type == TokenType.Number ? this.numValue : -1; }
|
||||||
|
|
|
@ -523,6 +523,10 @@ export class _ParseAST {
|
||||||
this.advance();
|
this.advance();
|
||||||
return new LiteralPrimitive(this.span(start), false);
|
return new LiteralPrimitive(this.span(start), false);
|
||||||
|
|
||||||
|
} else if (this.next.isKeywordThis()) {
|
||||||
|
this.advance();
|
||||||
|
return new ImplicitReceiver(this.span(start));
|
||||||
|
|
||||||
} else if (this.optionalCharacter(chars.$LBRACKET)) {
|
} else if (this.optionalCharacter(chars.$LBRACKET)) {
|
||||||
this.rbracketsExpected++;
|
this.rbracketsExpected++;
|
||||||
const elements = this.parseExpressionList(chars.$RBRACKET);
|
const elements = this.parseExpressionList(chars.$RBRACKET);
|
||||||
|
@ -780,13 +784,7 @@ class SimpleExpressionChecker implements AstVisitor {
|
||||||
|
|
||||||
visitKeyedWrite(ast: KeyedWrite, context: any) { this.simple = false; }
|
visitKeyedWrite(ast: KeyedWrite, context: any) { this.simple = false; }
|
||||||
|
|
||||||
visitAll(asts: any[]): any[] {
|
visitAll(asts: any[]): any[] { return asts.map(node => node.visit(this)); }
|
||||||
var res = ListWrapper.createFixedSize(asts.length);
|
|
||||||
for (var i = 0; i < asts.length; ++i) {
|
|
||||||
res[i] = asts[i].visit(this);
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
visitChain(ast: Chain, context: any) { this.simple = false; }
|
visitChain(ast: Chain, context: any) { this.simple = false; }
|
||||||
|
|
||||||
|
|
|
@ -283,7 +283,7 @@ export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.Ex
|
||||||
abstract visitDeclareFunctionStmt(stmt: o.DeclareFunctionStmt, context: any): any;
|
abstract visitDeclareFunctionStmt(stmt: o.DeclareFunctionStmt, context: any): any;
|
||||||
|
|
||||||
visitBinaryOperatorExpr(ast: o.BinaryOperatorExpr, ctx: EmitterVisitorContext): any {
|
visitBinaryOperatorExpr(ast: o.BinaryOperatorExpr, ctx: EmitterVisitorContext): any {
|
||||||
var opStr: any /** TODO #9100 */;
|
var opStr: string;
|
||||||
switch (ast.operator) {
|
switch (ast.operator) {
|
||||||
case o.BinaryOperator.Equals:
|
case o.BinaryOperator.Equals:
|
||||||
opStr = '==';
|
opStr = '==';
|
||||||
|
|
|
@ -70,10 +70,12 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
|
||||||
ctx.print(defaultType);
|
ctx.print(defaultType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
visitExternalExpr(ast: o.ExternalExpr, ctx: EmitterVisitorContext): any {
|
visitExternalExpr(ast: o.ExternalExpr, ctx: EmitterVisitorContext): any {
|
||||||
this._visitIdentifier(ast.value, ast.typeParams, ctx);
|
this._visitIdentifier(ast.value, ast.typeParams, ctx);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: EmitterVisitorContext): any {
|
visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: EmitterVisitorContext): any {
|
||||||
if (ctx.isExportedVar(stmt.name)) {
|
if (ctx.isExportedVar(stmt.name)) {
|
||||||
ctx.print(`export `);
|
ctx.print(`export `);
|
||||||
|
@ -90,6 +92,7 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
|
||||||
ctx.println(`;`);
|
ctx.println(`;`);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
visitCastExpr(ast: o.CastExpr, ctx: EmitterVisitorContext): any {
|
visitCastExpr(ast: o.CastExpr, ctx: EmitterVisitorContext): any {
|
||||||
ctx.print(`(<`);
|
ctx.print(`(<`);
|
||||||
ast.type.visitType(this, ctx);
|
ast.type.visitType(this, ctx);
|
||||||
|
@ -98,6 +101,7 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
|
||||||
ctx.print(`)`);
|
ctx.print(`)`);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
visitDeclareClassStmt(stmt: o.ClassStmt, ctx: EmitterVisitorContext): any {
|
visitDeclareClassStmt(stmt: o.ClassStmt, ctx: EmitterVisitorContext): any {
|
||||||
ctx.pushClass(stmt);
|
ctx.pushClass(stmt);
|
||||||
if (ctx.isExportedVar(stmt.name)) {
|
if (ctx.isExportedVar(stmt.name)) {
|
||||||
|
@ -121,6 +125,7 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
|
||||||
ctx.popClass();
|
ctx.popClass();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _visitClassField(field: o.ClassField, ctx: EmitterVisitorContext) {
|
private _visitClassField(field: o.ClassField, ctx: EmitterVisitorContext) {
|
||||||
if (field.hasModifier(o.StmtModifier.Private)) {
|
if (field.hasModifier(o.StmtModifier.Private)) {
|
||||||
ctx.print(`private `);
|
ctx.print(`private `);
|
||||||
|
@ -130,6 +135,7 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
|
||||||
this.visitType(field.type, ctx);
|
this.visitType(field.type, ctx);
|
||||||
ctx.println(`;`);
|
ctx.println(`;`);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _visitClassGetter(getter: o.ClassGetter, ctx: EmitterVisitorContext) {
|
private _visitClassGetter(getter: o.ClassGetter, ctx: EmitterVisitorContext) {
|
||||||
if (getter.hasModifier(o.StmtModifier.Private)) {
|
if (getter.hasModifier(o.StmtModifier.Private)) {
|
||||||
ctx.print(`private `);
|
ctx.print(`private `);
|
||||||
|
@ -143,6 +149,7 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
|
||||||
ctx.decIndent();
|
ctx.decIndent();
|
||||||
ctx.println(`}`);
|
ctx.println(`}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _visitClassConstructor(stmt: o.ClassStmt, ctx: EmitterVisitorContext) {
|
private _visitClassConstructor(stmt: o.ClassStmt, ctx: EmitterVisitorContext) {
|
||||||
ctx.print(`constructor(`);
|
ctx.print(`constructor(`);
|
||||||
this._visitParams(stmt.constructorMethod.params, ctx);
|
this._visitParams(stmt.constructorMethod.params, ctx);
|
||||||
|
@ -152,6 +159,7 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
|
||||||
ctx.decIndent();
|
ctx.decIndent();
|
||||||
ctx.println(`}`);
|
ctx.println(`}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _visitClassMethod(method: o.ClassMethod, ctx: EmitterVisitorContext) {
|
private _visitClassMethod(method: o.ClassMethod, ctx: EmitterVisitorContext) {
|
||||||
if (method.hasModifier(o.StmtModifier.Private)) {
|
if (method.hasModifier(o.StmtModifier.Private)) {
|
||||||
ctx.print(`private `);
|
ctx.print(`private `);
|
||||||
|
@ -166,6 +174,7 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
|
||||||
ctx.decIndent();
|
ctx.decIndent();
|
||||||
ctx.println(`}`);
|
ctx.println(`}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitFunctionExpr(ast: o.FunctionExpr, ctx: EmitterVisitorContext): any {
|
visitFunctionExpr(ast: o.FunctionExpr, ctx: EmitterVisitorContext): any {
|
||||||
ctx.print(`(`);
|
ctx.print(`(`);
|
||||||
this._visitParams(ast.params, ctx);
|
this._visitParams(ast.params, ctx);
|
||||||
|
@ -178,6 +187,7 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
|
||||||
ctx.print(`}`);
|
ctx.print(`}`);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
visitDeclareFunctionStmt(stmt: o.DeclareFunctionStmt, ctx: EmitterVisitorContext): any {
|
visitDeclareFunctionStmt(stmt: o.DeclareFunctionStmt, ctx: EmitterVisitorContext): any {
|
||||||
if (ctx.isExportedVar(stmt.name)) {
|
if (ctx.isExportedVar(stmt.name)) {
|
||||||
ctx.print(`export `);
|
ctx.print(`export `);
|
||||||
|
@ -193,6 +203,7 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
|
||||||
ctx.println(`}`);
|
ctx.println(`}`);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
visitTryCatchStmt(stmt: o.TryCatchStmt, ctx: EmitterVisitorContext): any {
|
visitTryCatchStmt(stmt: o.TryCatchStmt, ctx: EmitterVisitorContext): any {
|
||||||
ctx.println(`try {`);
|
ctx.println(`try {`);
|
||||||
ctx.incIndent();
|
ctx.incIndent();
|
||||||
|
@ -237,15 +248,18 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
|
||||||
ctx.print(typeStr);
|
ctx.print(typeStr);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
visitExternalType(ast: o.ExternalType, ctx: EmitterVisitorContext): any {
|
visitExternalType(ast: o.ExternalType, ctx: EmitterVisitorContext): any {
|
||||||
this._visitIdentifier(ast.value, ast.typeParams, ctx);
|
this._visitIdentifier(ast.value, ast.typeParams, ctx);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
visitArrayType(type: o.ArrayType, ctx: EmitterVisitorContext): any {
|
visitArrayType(type: o.ArrayType, ctx: EmitterVisitorContext): any {
|
||||||
this.visitType(type.of, ctx);
|
this.visitType(type.of, ctx);
|
||||||
ctx.print(`[]`);
|
ctx.print(`[]`);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
visitMapType(type: o.MapType, ctx: EmitterVisitorContext): any {
|
visitMapType(type: o.MapType, ctx: EmitterVisitorContext): any {
|
||||||
ctx.print(`{[key: string]:`);
|
ctx.print(`{[key: string]:`);
|
||||||
this.visitType(type.valueType, ctx);
|
this.visitType(type.valueType, ctx);
|
||||||
|
@ -271,7 +285,6 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private _visitParams(params: o.FnParam[], ctx: EmitterVisitorContext): void {
|
private _visitParams(params: o.FnParam[], ctx: EmitterVisitorContext): void {
|
||||||
this.visitAllObjects((param: any /** TODO #9100 */) => {
|
this.visitAllObjects((param: any /** TODO #9100 */) => {
|
||||||
ctx.print(param.name);
|
ctx.print(param.name);
|
||||||
|
|
|
@ -21,6 +21,7 @@ class _ValueOutputAstTransformer implements ValueTransformer {
|
||||||
visitArray(arr: any[], type: o.Type): o.Expression {
|
visitArray(arr: any[], type: o.Type): o.Expression {
|
||||||
return o.literalArr(arr.map(value => visitValue(value, this, null)), type);
|
return o.literalArr(arr.map(value => visitValue(value, this, null)), type);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitStringMap(map: {[key: string]: any}, type: o.MapType): o.Expression {
|
visitStringMap(map: {[key: string]: any}, type: o.MapType): o.Expression {
|
||||||
var entries: Array<string|o.Expression>[] = [];
|
var entries: Array<string|o.Expression>[] = [];
|
||||||
StringMapWrapper.forEach(map, (value: any, key: string) => {
|
StringMapWrapper.forEach(map, (value: any, key: string) => {
|
||||||
|
@ -28,7 +29,9 @@ class _ValueOutputAstTransformer implements ValueTransformer {
|
||||||
});
|
});
|
||||||
return o.literalMap(entries, type);
|
return o.literalMap(entries, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitPrimitive(value: any, type: o.Type): o.Expression { return o.literal(value, type); }
|
visitPrimitive(value: any, type: o.Type): o.Expression { return o.literal(value, type); }
|
||||||
|
|
||||||
visitOther(value: any, type: o.Type): o.Expression {
|
visitOther(value: any, type: o.Type): o.Expression {
|
||||||
if (value instanceof CompileIdentifierMetadata) {
|
if (value instanceof CompileIdentifierMetadata) {
|
||||||
return o.importExpr(value);
|
return o.importExpr(value);
|
||||||
|
|
|
@ -12,8 +12,6 @@ import {isArray, isBlank, isPresent} from '../facade/lang';
|
||||||
import {Identifiers} from '../identifiers';
|
import {Identifiers} from '../identifiers';
|
||||||
import * as o from '../output/output_ast';
|
import * as o from '../output/output_ast';
|
||||||
|
|
||||||
var IMPLICIT_RECEIVER = o.variable('#implicit');
|
|
||||||
|
|
||||||
export interface NameResolver {
|
export interface NameResolver {
|
||||||
callPipe(name: string, input: o.Expression, args: o.Expression[]): o.Expression;
|
callPipe(name: string, input: o.Expression, args: o.Expression[]): o.Expression;
|
||||||
getLocal(name: string): o.Expression;
|
getLocal(name: string): o.Expression;
|
||||||
|
@ -132,10 +130,12 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
|
||||||
new o.BinaryOperatorExpr(
|
new o.BinaryOperatorExpr(
|
||||||
op, this.visit(ast.left, _Mode.Expression), this.visit(ast.right, _Mode.Expression)));
|
op, this.visit(ast.left, _Mode.Expression), this.visit(ast.right, _Mode.Expression)));
|
||||||
}
|
}
|
||||||
|
|
||||||
visitChain(ast: cdAst.Chain, mode: _Mode): any {
|
visitChain(ast: cdAst.Chain, mode: _Mode): any {
|
||||||
ensureStatementMode(mode, ast);
|
ensureStatementMode(mode, ast);
|
||||||
return this.visitAll(ast.expressions, mode);
|
return this.visitAll(ast.expressions, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitConditional(ast: cdAst.Conditional, mode: _Mode): any {
|
visitConditional(ast: cdAst.Conditional, mode: _Mode): any {
|
||||||
const value: o.Expression = this.visit(ast.condition, _Mode.Expression);
|
const value: o.Expression = this.visit(ast.condition, _Mode.Expression);
|
||||||
return convertToStatementIfNeeded(
|
return convertToStatementIfNeeded(
|
||||||
|
@ -143,6 +143,7 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
|
||||||
value.conditional(
|
value.conditional(
|
||||||
this.visit(ast.trueExp, _Mode.Expression), this.visit(ast.falseExp, _Mode.Expression)));
|
this.visit(ast.trueExp, _Mode.Expression), this.visit(ast.falseExp, _Mode.Expression)));
|
||||||
}
|
}
|
||||||
|
|
||||||
visitPipe(ast: cdAst.BindingPipe, mode: _Mode): any {
|
visitPipe(ast: cdAst.BindingPipe, mode: _Mode): any {
|
||||||
const input = this.visit(ast.exp, _Mode.Expression);
|
const input = this.visit(ast.exp, _Mode.Expression);
|
||||||
const args = this.visitAll(ast.args, _Mode.Expression);
|
const args = this.visitAll(ast.args, _Mode.Expression);
|
||||||
|
@ -150,15 +151,18 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
|
||||||
this.needsValueUnwrapper = true;
|
this.needsValueUnwrapper = true;
|
||||||
return convertToStatementIfNeeded(mode, this._valueUnwrapper.callMethod('unwrap', [value]));
|
return convertToStatementIfNeeded(mode, this._valueUnwrapper.callMethod('unwrap', [value]));
|
||||||
}
|
}
|
||||||
|
|
||||||
visitFunctionCall(ast: cdAst.FunctionCall, mode: _Mode): any {
|
visitFunctionCall(ast: cdAst.FunctionCall, mode: _Mode): any {
|
||||||
return convertToStatementIfNeeded(
|
return convertToStatementIfNeeded(
|
||||||
mode,
|
mode,
|
||||||
this.visit(ast.target, _Mode.Expression).callFn(this.visitAll(ast.args, _Mode.Expression)));
|
this.visit(ast.target, _Mode.Expression).callFn(this.visitAll(ast.args, _Mode.Expression)));
|
||||||
}
|
}
|
||||||
|
|
||||||
visitImplicitReceiver(ast: cdAst.ImplicitReceiver, mode: _Mode): any {
|
visitImplicitReceiver(ast: cdAst.ImplicitReceiver, mode: _Mode): any {
|
||||||
ensureExpressionMode(mode, ast);
|
ensureExpressionMode(mode, ast);
|
||||||
return IMPLICIT_RECEIVER;
|
return this._implicitReceiver;
|
||||||
}
|
}
|
||||||
|
|
||||||
visitInterpolation(ast: cdAst.Interpolation, mode: _Mode): any {
|
visitInterpolation(ast: cdAst.Interpolation, mode: _Mode): any {
|
||||||
ensureExpressionMode(mode, ast);
|
ensureExpressionMode(mode, ast);
|
||||||
const args = [o.literal(ast.expressions.length)];
|
const args = [o.literal(ast.expressions.length)];
|
||||||
|
@ -169,20 +173,24 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
|
||||||
args.push(o.literal(ast.strings[ast.strings.length - 1]));
|
args.push(o.literal(ast.strings[ast.strings.length - 1]));
|
||||||
return o.importExpr(Identifiers.interpolate).callFn(args);
|
return o.importExpr(Identifiers.interpolate).callFn(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitKeyedRead(ast: cdAst.KeyedRead, mode: _Mode): any {
|
visitKeyedRead(ast: cdAst.KeyedRead, mode: _Mode): any {
|
||||||
return convertToStatementIfNeeded(
|
return convertToStatementIfNeeded(
|
||||||
mode, this.visit(ast.obj, _Mode.Expression).key(this.visit(ast.key, _Mode.Expression)));
|
mode, this.visit(ast.obj, _Mode.Expression).key(this.visit(ast.key, _Mode.Expression)));
|
||||||
}
|
}
|
||||||
|
|
||||||
visitKeyedWrite(ast: cdAst.KeyedWrite, mode: _Mode): any {
|
visitKeyedWrite(ast: cdAst.KeyedWrite, mode: _Mode): any {
|
||||||
const obj: o.Expression = this.visit(ast.obj, _Mode.Expression);
|
const obj: o.Expression = this.visit(ast.obj, _Mode.Expression);
|
||||||
const key: o.Expression = this.visit(ast.key, _Mode.Expression);
|
const key: o.Expression = this.visit(ast.key, _Mode.Expression);
|
||||||
const value: o.Expression = this.visit(ast.value, _Mode.Expression);
|
const value: o.Expression = this.visit(ast.value, _Mode.Expression);
|
||||||
return convertToStatementIfNeeded(mode, obj.key(key).set(value));
|
return convertToStatementIfNeeded(mode, obj.key(key).set(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
visitLiteralArray(ast: cdAst.LiteralArray, mode: _Mode): any {
|
visitLiteralArray(ast: cdAst.LiteralArray, mode: _Mode): any {
|
||||||
return convertToStatementIfNeeded(
|
return convertToStatementIfNeeded(
|
||||||
mode, this._nameResolver.createLiteralArray(this.visitAll(ast.expressions, mode)));
|
mode, this._nameResolver.createLiteralArray(this.visitAll(ast.expressions, mode)));
|
||||||
}
|
}
|
||||||
|
|
||||||
visitLiteralMap(ast: cdAst.LiteralMap, mode: _Mode): any {
|
visitLiteralMap(ast: cdAst.LiteralMap, mode: _Mode): any {
|
||||||
let parts: any[] = [];
|
let parts: any[] = [];
|
||||||
for (let i = 0; i < ast.keys.length; i++) {
|
for (let i = 0; i < ast.keys.length; i++) {
|
||||||
|
@ -190,9 +198,11 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
|
||||||
}
|
}
|
||||||
return convertToStatementIfNeeded(mode, this._nameResolver.createLiteralMap(parts));
|
return convertToStatementIfNeeded(mode, this._nameResolver.createLiteralMap(parts));
|
||||||
}
|
}
|
||||||
|
|
||||||
visitLiteralPrimitive(ast: cdAst.LiteralPrimitive, mode: _Mode): any {
|
visitLiteralPrimitive(ast: cdAst.LiteralPrimitive, mode: _Mode): any {
|
||||||
return convertToStatementIfNeeded(mode, o.literal(ast.value));
|
return convertToStatementIfNeeded(mode, o.literal(ast.value));
|
||||||
}
|
}
|
||||||
|
|
||||||
visitMethodCall(ast: cdAst.MethodCall, mode: _Mode): any {
|
visitMethodCall(ast: cdAst.MethodCall, mode: _Mode): any {
|
||||||
const leftMostSafe = this.leftMostSafeNode(ast);
|
const leftMostSafe = this.leftMostSafeNode(ast);
|
||||||
if (leftMostSafe) {
|
if (leftMostSafe) {
|
||||||
|
@ -201,12 +211,10 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
|
||||||
const args = this.visitAll(ast.args, _Mode.Expression);
|
const args = this.visitAll(ast.args, _Mode.Expression);
|
||||||
let result: any = null;
|
let result: any = null;
|
||||||
let receiver = this.visit(ast.receiver, _Mode.Expression);
|
let receiver = this.visit(ast.receiver, _Mode.Expression);
|
||||||
if (receiver === IMPLICIT_RECEIVER) {
|
if (receiver === this._implicitReceiver) {
|
||||||
var varExpr = this._nameResolver.getLocal(ast.name);
|
var varExpr = this._nameResolver.getLocal(ast.name);
|
||||||
if (isPresent(varExpr)) {
|
if (isPresent(varExpr)) {
|
||||||
result = varExpr.callFn(args);
|
result = varExpr.callFn(args);
|
||||||
} else {
|
|
||||||
receiver = this._implicitReceiver;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isBlank(result)) {
|
if (isBlank(result)) {
|
||||||
|
@ -215,9 +223,11 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
|
||||||
return convertToStatementIfNeeded(mode, result);
|
return convertToStatementIfNeeded(mode, result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
visitPrefixNot(ast: cdAst.PrefixNot, mode: _Mode): any {
|
visitPrefixNot(ast: cdAst.PrefixNot, mode: _Mode): any {
|
||||||
return convertToStatementIfNeeded(mode, o.not(this.visit(ast.expression, _Mode.Expression)));
|
return convertToStatementIfNeeded(mode, o.not(this.visit(ast.expression, _Mode.Expression)));
|
||||||
}
|
}
|
||||||
|
|
||||||
visitPropertyRead(ast: cdAst.PropertyRead, mode: _Mode): any {
|
visitPropertyRead(ast: cdAst.PropertyRead, mode: _Mode): any {
|
||||||
const leftMostSafe = this.leftMostSafeNode(ast);
|
const leftMostSafe = this.leftMostSafeNode(ast);
|
||||||
if (leftMostSafe) {
|
if (leftMostSafe) {
|
||||||
|
@ -225,11 +235,8 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
|
||||||
} else {
|
} else {
|
||||||
let result: any = null;
|
let result: any = null;
|
||||||
var receiver = this.visit(ast.receiver, _Mode.Expression);
|
var receiver = this.visit(ast.receiver, _Mode.Expression);
|
||||||
if (receiver === IMPLICIT_RECEIVER) {
|
if (receiver === this._implicitReceiver) {
|
||||||
result = this._nameResolver.getLocal(ast.name);
|
result = this._nameResolver.getLocal(ast.name);
|
||||||
if (isBlank(result)) {
|
|
||||||
receiver = this._implicitReceiver;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (isBlank(result)) {
|
if (isBlank(result)) {
|
||||||
result = receiver.prop(ast.name);
|
result = receiver.prop(ast.name);
|
||||||
|
@ -237,25 +244,29 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
|
||||||
return convertToStatementIfNeeded(mode, result);
|
return convertToStatementIfNeeded(mode, result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
visitPropertyWrite(ast: cdAst.PropertyWrite, mode: _Mode): any {
|
visitPropertyWrite(ast: cdAst.PropertyWrite, mode: _Mode): any {
|
||||||
let receiver: o.Expression = this.visit(ast.receiver, _Mode.Expression);
|
let receiver: o.Expression = this.visit(ast.receiver, _Mode.Expression);
|
||||||
if (receiver === IMPLICIT_RECEIVER) {
|
if (receiver === this._implicitReceiver) {
|
||||||
var varExpr = this._nameResolver.getLocal(ast.name);
|
var varExpr = this._nameResolver.getLocal(ast.name);
|
||||||
if (isPresent(varExpr)) {
|
if (isPresent(varExpr)) {
|
||||||
throw new BaseException('Cannot assign to a reference or variable!');
|
throw new BaseException('Cannot assign to a reference or variable!');
|
||||||
}
|
}
|
||||||
receiver = this._implicitReceiver;
|
|
||||||
}
|
}
|
||||||
return convertToStatementIfNeeded(
|
return convertToStatementIfNeeded(
|
||||||
mode, receiver.prop(ast.name).set(this.visit(ast.value, _Mode.Expression)));
|
mode, receiver.prop(ast.name).set(this.visit(ast.value, _Mode.Expression)));
|
||||||
}
|
}
|
||||||
|
|
||||||
visitSafePropertyRead(ast: cdAst.SafePropertyRead, mode: _Mode): any {
|
visitSafePropertyRead(ast: cdAst.SafePropertyRead, mode: _Mode): any {
|
||||||
return this.convertSafeAccess(ast, this.leftMostSafeNode(ast), mode);
|
return this.convertSafeAccess(ast, this.leftMostSafeNode(ast), mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitSafeMethodCall(ast: cdAst.SafeMethodCall, mode: _Mode): any {
|
visitSafeMethodCall(ast: cdAst.SafeMethodCall, mode: _Mode): any {
|
||||||
return this.convertSafeAccess(ast, this.leftMostSafeNode(ast), mode);
|
return this.convertSafeAccess(ast, this.leftMostSafeNode(ast), mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitAll(asts: cdAst.AST[], mode: _Mode): any { return asts.map(ast => this.visit(ast, mode)); }
|
visitAll(asts: cdAst.AST[], mode: _Mode): any { return asts.map(ast => this.visit(ast, mode)); }
|
||||||
|
|
||||||
visitQuote(ast: cdAst.Quote, mode: _Mode): any {
|
visitQuote(ast: cdAst.Quote, mode: _Mode): any {
|
||||||
throw new BaseException('Quotes are not supported for evaluation!');
|
throw new BaseException('Quotes are not supported for evaluation!');
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,6 +67,12 @@ export function main() {
|
||||||
expectIdentifierToken(tokens[0], 0, 'j');
|
expectIdentifierToken(tokens[0], 0, 'j');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should tokenize "this"', () => {
|
||||||
|
var tokens: number[] = lex('this');
|
||||||
|
expect(tokens.length).toEqual(1);
|
||||||
|
expectKeywordToken(tokens[0], 0, 'this');
|
||||||
|
});
|
||||||
|
|
||||||
it('should tokenize a dotted identifier', () => {
|
it('should tokenize a dotted identifier', () => {
|
||||||
var tokens: number[] = lex('j.k');
|
var tokens: number[] = lex('j.k');
|
||||||
expect(tokens.length).toEqual(3);
|
expect(tokens.length).toEqual(3);
|
||||||
|
|
|
@ -163,6 +163,7 @@ export function main() {
|
||||||
describe('member access', () => {
|
describe('member access', () => {
|
||||||
it('should parse field access', () => {
|
it('should parse field access', () => {
|
||||||
checkAction('a');
|
checkAction('a');
|
||||||
|
checkAction('this.a', 'a');
|
||||||
checkAction('a.a');
|
checkAction('a.a');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue