Parser fixes; Lib updates

This commit is contained in:
dcodeIO
2018-01-29 22:36:07 +01:00
parent 74498852f0
commit 1995bf9eb4
18 changed files with 282 additions and 103 deletions

View File

@ -36,6 +36,7 @@ import {
Expression,
AssertionKind,
CallExpression,
ElementAccessExpression,
IdentifierExpression,
StringLiteralExpression,
@ -1467,7 +1468,7 @@ export class Parser extends DiagnosticEmitter {
var elementExpressions = new Array<Expression | null>();
if (!tn.skip(Token.CLOSEBRACKET)) {
do {
if (tn.peek() == Token.COMMA || tn.peek() == Token.CLOSEBRACKET)
if (tn.peek() == Token.COMMA)
expr = null; // omitted
else {
expr = this.parseExpression(tn, Precedence.COMMA + 1);
@ -1475,6 +1476,8 @@ export class Parser extends DiagnosticEmitter {
return null;
}
elementExpressions.push(expr);
if (tn.peek() == Token.CLOSEBRACKET)
break;
} while (tn.skip(Token.COMMA));
if (!tn.skip(Token.CLOSEBRACKET)) {
this.error(DiagnosticCode._0_expected, tn.range(), "]");
@ -1588,19 +1591,6 @@ export class Parser extends DiagnosticEmitter {
var startPos = expr.range.start;
// ElementAccessExpression
if (tn.skip(Token.OPENBRACKET)) {
next = this.parseExpression(tn); // resets precedence
if (!next)
return null;
if (tn.skip(Token.CLOSEBRACKET))
expr = Node.createElementAccessExpression(<Expression>expr, <Expression>next, tn.range(startPos, tn.pos));
else {
this.error(DiagnosticCode._0_expected, tn.range(), "]");
return null;
}
}
// CallExpression
var typeArguments = this.tryParseTypeArgumentsBeforeArguments(tn); // skips '(' on success
// there might be better ways to distinguish a LESSTHAN from a CALL with type arguments
@ -1617,70 +1607,93 @@ export class Parser extends DiagnosticEmitter {
while ((nextPrecedence = determinePrecedence(token = tn.peek())) >= precedence) { // precedence climbing
tn.next();
// AssertionExpression
if (token == Token.AS) {
var toType = this.parseType(tn);
if (!toType)
return null;
expr = Node.createAssertionExpression(AssertionKind.AS, expr, toType, tn.range(startPos, tn.pos));
switch (token) {
// UnaryPostfixExpression
} else if (token == Token.PLUS_PLUS || token == Token.MINUS_MINUS) {
if (expr.kind != NodeKind.IDENTIFIER && expr.kind != NodeKind.ELEMENTACCESS && expr.kind != NodeKind.PROPERTYACCESS)
this.error(DiagnosticCode.The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access, expr.range);
expr = Node.createUnaryPostfixExpression(token, expr, tn.range(startPos, tn.pos));
// TernaryExpression
} else if (token == Token.QUESTION) {
var ifThen = this.parseExpression(tn);
if (!ifThen)
return null;
if (tn.skip(Token.COLON)) {
var ifElse = this.parseExpression(tn);
if (!ifElse)
// AssertionExpression
case Token.AS:
var toType = this.parseType(tn);
if (!toType)
return null;
expr = Node.createTernaryExpression(<Expression>expr, <Expression>ifThen, <Expression>ifElse, tn.range(startPos, tn.pos));
} else {
this.error(DiagnosticCode._0_expected, tn.range(), ":");
return null;
}
expr = Node.createAssertionExpression(AssertionKind.AS, expr, toType, tn.range(startPos, tn.pos));
break;
// CommaExpression
} else if (token == Token.COMMA) {
var commaExprs = new Array<Expression>(1);
commaExprs[0] = <Expression>expr;
do {
expr = this.parseExpression(tn, Precedence.COMMA + 1);
if (!expr)
// ElementAccessExpression
case Token.OPENBRACKET:
next = this.parseExpression(tn);
if (!next)
return null;
commaExprs.push(expr);
} while (tn.skip(Token.COMMA));
expr = Node.createCommaExpression(commaExprs, tn.range(startPos, tn.pos));
} else {
next = this.parseExpression(tn, isRightAssociative(token) ? nextPrecedence : 1 + nextPrecedence);
if (!next)
return null;
// PropertyAccessExpression
if (token == Token.DOT) {
if (next.kind == NodeKind.IDENTIFIER) {
expr = Node.createPropertyAccessExpression(<Expression>expr, <IdentifierExpression>next, tn.range(startPos, tn.pos));
} else if (next.kind == NodeKind.CALL) { // amend
var propertyCall = <CallExpression>next;
if (propertyCall.expression.kind == NodeKind.IDENTIFIER) {
propertyCall.expression = Node.createPropertyAccessExpression(<Expression>expr, <IdentifierExpression>propertyCall.expression, tn.range(startPos, tn.pos));
} else
throw new Error("unexpected expression kind");
expr = propertyCall;
} else {
this.error(DiagnosticCode.Identifier_expected, next.range);
if (!tn.skip(Token.CLOSEBRACKET)) {
this.error(DiagnosticCode._0_expected, tn.range(), "]");
return null;
}
expr = Node.createElementAccessExpression(<Expression>expr, <Expression>next, tn.range(startPos, tn.pos));
break;
// BinaryExpression
} else
expr = Node.createBinaryExpression(token, <Expression>expr, <Expression>next, tn.range(startPos, tn.pos));
// UnaryPostfixExpression
case Token.PLUS_PLUS:
case Token.MINUS_MINUS:
if (expr.kind != NodeKind.IDENTIFIER && expr.kind != NodeKind.ELEMENTACCESS && expr.kind != NodeKind.PROPERTYACCESS)
this.error(DiagnosticCode.The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access, expr.range);
expr = Node.createUnaryPostfixExpression(token, expr, tn.range(startPos, tn.pos));
break;
// TernaryExpression
case Token.QUESTION:
var ifThen = this.parseExpression(tn);
if (!ifThen)
return null;
if (tn.skip(Token.COLON)) {
var ifElse = this.parseExpression(tn);
if (!ifElse)
return null;
expr = Node.createTernaryExpression(<Expression>expr, <Expression>ifThen, <Expression>ifElse, tn.range(startPos, tn.pos));
} else {
this.error(DiagnosticCode._0_expected, tn.range(), ":");
return null;
}
break;
// CommaExpression
case Token.COMMA:
var commaExprs = new Array<Expression>(1);
commaExprs[0] = <Expression>expr;
do {
expr = this.parseExpression(tn, Precedence.COMMA + 1);
if (!expr)
return null;
commaExprs.push(expr);
} while (tn.skip(Token.COMMA));
expr = Node.createCommaExpression(commaExprs, tn.range(startPos, tn.pos));
break;
default:
next = this.parseExpression(tn, isRightAssociative(token) ? nextPrecedence : 1 + nextPrecedence);
if (!next)
return null;
// PropertyAccessExpression
if (token == Token.DOT) {
if (next.kind == NodeKind.IDENTIFIER)
expr = Node.createPropertyAccessExpression(<Expression>expr, <IdentifierExpression>next, tn.range(startPos, tn.pos));
else if (next.kind == NodeKind.CALL) { // join
var propertyCall = <CallExpression>next;
if (propertyCall.expression.kind == NodeKind.IDENTIFIER) {
propertyCall.expression = Node.createPropertyAccessExpression(<Expression>expr, <IdentifierExpression>propertyCall.expression, tn.range(startPos, tn.pos));
} else {
this.error(DiagnosticCode.Identifier_expected, propertyCall.expression.range);
return null;
}
expr = propertyCall;
} else {
this.error(DiagnosticCode.Identifier_expected, next.range);
return null;
}
// BinaryExpression
} else
expr = Node.createBinaryExpression(token, <Expression>expr, <Expression>next, tn.range(startPos, tn.pos));
break;
}
}
return expr;
@ -1820,6 +1833,7 @@ function determinePrecedence(kind: Token): i32 {
case Token.DOT:
case Token.NEW:
case Token.OPENBRACKET:
return Precedence.MEMBERACCESS;
default:

View File

@ -33,10 +33,12 @@ import {
DecoratorKind,
Expression,
AssertionExpression,
ElementAccessExpression,
IdentifierExpression,
LiteralExpression,
LiteralKind,
ParenthesizedExpression,
PropertyAccessExpression,
StringLiteralExpression,
SuperExpression,
@ -66,7 +68,8 @@ import {
hasDecorator,
hasModifier,
mangleInternalName,
getFirstDecorator
getFirstDecorator,
BinaryExpression
} from "./ast";
import {
@ -1156,8 +1159,22 @@ export class Program extends DiagnosticEmitter {
resolveExpression(expression: Expression, contextualFunction: Function): ResolvedElement | null {
var classType: Class | null;
while (expression.kind == NodeKind.PARENTHESIZED)
expression = (<ParenthesizedExpression>expression).expression;
switch (expression.kind) {
case NodeKind.ASSERTION:
var type = this.resolveType((<AssertionExpression>expression).toType); // reports
if (type && (classType = type.classType))
return (resolvedElement || (resolvedElement = new ResolvedElement())).set(classType);
return null;
case NodeKind.BINARY:
// TODO: string concatenation, mostly
throw new Error("not implemented");
case NodeKind.THIS: // -> Class
if (classType = contextualFunction.instanceMethodOf)
return (resolvedElement || (resolvedElement = new ResolvedElement())).set(classType);