Added extra parse node to index expression node to allow intermediate expression type to allow for caching of index expression for both correctness and performance.

This commit is contained in:
Eric Traut 2019-04-11 18:52:45 -07:00
parent dbeafb5298
commit 0fd41aa2ef
6 changed files with 82 additions and 56 deletions

View File

@ -19,11 +19,11 @@ import { TextRange } from '../common/textRange';
import { ArgumentCategory, AssignmentNode, AwaitExpressionNode,
BinaryExpressionNode, CallExpressionNode, ClassNode, ConstantNode,
DecoratorNode, DictionaryNode, EllipsisNode, ExpressionNode,
IndexExpressionNode, LambdaNode, ListComprehensionNode, ListNode,
MemberAccessExpressionNode, NameNode, NumberNode, ParameterCategory, SetNode,
SliceExpressionNode, StarExpressionNode, StatementListNode, StringNode,
TernaryExpressionNode, TupleExpressionNode, TypeAnnotationExpressionNode,
UnaryExpressionNode, YieldExpressionNode } from '../parser/parseNodes';
IndexExpressionNode, IndexItemsNode, LambdaNode, ListComprehensionNode,
ListNode, MemberAccessExpressionNode, NameNode, NumberNode, ParameterCategory,
SetNode, SliceExpressionNode, StarExpressionNode, StatementListNode,
StringNode, TernaryExpressionNode, TupleExpressionNode,
TypeAnnotationExpressionNode, UnaryExpressionNode, YieldExpressionNode } from '../parser/parseNodes';
import { KeywordToken, KeywordType, OperatorType, QuoteTypeFlags,
TokenType } from '../parser/tokenizerTypes';
import { ScopeUtils } from '../scopeUtils';
@ -694,9 +694,9 @@ export class ExpressionEvaluator {
if (subtype.isAny()) {
return subtype;
} else if (subtype instanceof ClassType) {
let typeArgs = this._getTypeArgs(node.indexExpression);
let typeArgs = this._getTypeArgs(node.items);
return this._createSpecializeClassType(subtype, typeArgs,
node.indexExpression, flags);
node.items, flags);
} else if (subtype instanceof FunctionType) {
// TODO - need to implement
return UnknownType.create();
@ -719,24 +719,15 @@ export class ExpressionEvaluator {
}
});
if (this._writeTypeToCache) {
// Cache the type information in the index expression node as well.
this._writeTypeToCache(node.indexExpression, type);
}
return { type, node };
}
private _getTypeArgs(node: ExpressionNode): TypeResult[] {
private _getTypeArgs(node: IndexItemsNode): TypeResult[] {
let typeArgs: TypeResult[] = [];
if (node instanceof TupleExpressionNode) {
node.expressions.forEach(expr => {
typeArgs.push(this._getTypeArg(expr));
});
} else {
typeArgs.push(this._getTypeArg(node));
}
node.items.forEach(expr => {
typeArgs.push(this._getTypeArg(expr));
});
return typeArgs;
}
@ -1915,7 +1906,7 @@ export class ExpressionEvaluator {
if (typeArgs[0].typeList) {
typeArgs[0].typeList.forEach((entry, index) => {
if (entry.type instanceof AnyType && entry.type.isEllipsis()) {
this._addError(`'...' not permitted in this context`, entry.node);
this._addError(`'...' not allowed in this context`, entry.node);
}
functionType.addParameter({
@ -1935,7 +1926,7 @@ export class ExpressionEvaluator {
if (typeArgs && typeArgs.length > 1) {
if (typeArgs[1].type instanceof AnyType && typeArgs[1].type.isEllipsis()) {
this._addError(`'...' not permitted in this context`, typeArgs[1].node);
this._addError(`'...' not allowed in this context`, typeArgs[1].node);
}
functionType.setDeclaredReturnType(typeArgs[1].type);
} else {
@ -1957,7 +1948,7 @@ export class ExpressionEvaluator {
}
if (typeArgs[0].type instanceof AnyType && typeArgs[0].type.isEllipsis()) {
this._addError(`'...' not permitted in this context`, typeArgs[0].node);
this._addError(`'...' not allowed in this context`, typeArgs[0].node);
}
return TypeUtils.combineTypes([typeArgs[0].type, NoneType.create()]);

View File

@ -31,8 +31,9 @@ export class ExpressionUtils {
} else if (node.leftExpression instanceof IndexExpressionNode &&
this._isSysVersionInfoExpression(node.leftExpression.baseExpression) &&
node.leftExpression.indexExpression instanceof NumberNode &&
node.leftExpression.indexExpression.token.value === 0 &&
node.leftExpression.items.items.length === 1 &&
node.leftExpression.items.items[0] instanceof NumberNode &&
(node.leftExpression.items.items[0] as NumberNode).token.value === 0 &&
node.rightExpression instanceof NumberNode) {
// Handle the special case of "sys.version_info[0] >= X"

View File

@ -59,7 +59,8 @@ export class ParseTreeUtils {
')';
} else if (node instanceof IndexExpressionNode) {
return ParseTreeUtils.printExpression(node.baseExpression) + '[' +
this.printExpression(node.indexExpression) + ']';
node.items.items.map(item => this.printExpression(item)).join(', ') +
']';
}
// TODO - need to finish

View File

@ -14,14 +14,14 @@ import { ArgumentNode, AssertNode, AssignmentNode, AugmentedAssignmentExpression
ConstantNode, ContinueNode, DecoratorNode, DelNode, DictionaryExpandEntryNode,
DictionaryKeyEntryNode, DictionaryNode, EllipsisNode, ExceptNode,
ForNode, FunctionNode, GlobalNode, IfNode, ImportAsNode, ImportFromAsNode,
ImportFromNode, ImportNode, IndexExpressionNode, LambdaNode, ListComprehensionForNode,
ListComprehensionIfNode, ListComprehensionNode, ListNode, MemberAccessExpressionNode,
ModuleNameNode, ModuleNode, NameNode, NonlocalNode, NumberNode, ParameterNode,
ParseNode, ParseNodeType, PassNode, RaiseNode, ReturnNode, SetNode,
SliceExpressionNode, StarExpressionNode, StatementListNode, StringNode, SuiteNode,
TernaryExpressionNode, TryNode, TupleExpressionNode, TypeAnnotationExpressionNode,
UnaryExpressionNode, WhileNode, WithItemNode, WithNode,
YieldExpressionNode, YieldFromExpressionNode } from '../parser/parseNodes';
ImportFromNode, ImportNode, IndexExpressionNode, IndexItemsNode, LambdaNode,
ListComprehensionForNode, ListComprehensionIfNode, ListComprehensionNode, ListNode,
MemberAccessExpressionNode, ModuleNameNode, ModuleNode, NameNode, NonlocalNode, NumberNode,
ParameterNode, ParseNode, ParseNodeType, PassNode, RaiseNode, ReturnNode,
SetNode, SliceExpressionNode, StarExpressionNode, StatementListNode, StringNode,
SuiteNode, TernaryExpressionNode, TryNode, TupleExpressionNode,
TypeAnnotationExpressionNode, UnaryExpressionNode, WhileNode, WithItemNode,
WithNode, YieldExpressionNode, YieldFromExpressionNode } from '../parser/parseNodes';
// To use this class, create a subclass and override the
// visitXXX methods that you want to handle.
@ -117,6 +117,9 @@ export class ParseTreeWalker {
case ParseNodeType.Index:
return this.visitIndex(node as IndexExpressionNode);
case ParseNodeType.IndexItems:
return this.visitIndexItems(node as IndexItemsNode);
case ParseNodeType.Ellipsis:
return this.visitEllipsis(node as EllipsisNode);
@ -329,6 +332,10 @@ export class ParseTreeWalker {
return true;
}
visitIndexItems(node: IndexItemsNode) {
return true;
}
visitExcept(node: ExceptNode) {
return true;
}

View File

@ -42,6 +42,7 @@ export enum ParseNodeType {
ImportFrom,
ImportFromAs,
Index,
IndexItems,
Except,
For,
Function,
@ -640,15 +641,14 @@ export class ListComprehensionNode<T extends ParseNode = ExpressionNode> extends
}
}
export class IndexExpressionNode extends ExpressionNode {
readonly nodeType = ParseNodeType.Index;
baseExpression: ExpressionNode;
indexExpression: ExpressionNode;
export class IndexItemsNode extends ParseNode {
readonly nodeType = ParseNodeType.IndexItems;
items: ExpressionNode[];
constructor(baseExpression: ExpressionNode, indexExpression: ExpressionNode) {
super(baseExpression);
this.baseExpression = baseExpression;
this.indexExpression = indexExpression;
constructor(openBracketToken: Token, closeBracketToken: Token, items: ExpressionNode[]) {
super(openBracketToken);
this.items = items;
this.extend(closeBracketToken);
}
getAssignmentError(): string | undefined {
@ -656,7 +656,27 @@ export class IndexExpressionNode extends ExpressionNode {
}
getChildren(): RecursiveParseNodeArray {
return [this.baseExpression, this.indexExpression];
return this.items;
}
}
export class IndexExpressionNode extends ExpressionNode {
readonly nodeType = ParseNodeType.Index;
baseExpression: ExpressionNode;
items: IndexItemsNode;
constructor(baseExpression: ExpressionNode, items: IndexItemsNode) {
super(baseExpression);
this.baseExpression = baseExpression;
this.items = items;
}
getAssignmentError(): string | undefined {
return undefined;
}
getChildren(): RecursiveParseNodeArray {
return [this.baseExpression, this.items];
}
}

View File

@ -30,14 +30,14 @@ import { ArgumentCategory, ArgumentNode, AssertNode,
DictionaryNode, EllipsisNode, ErrorExpressionNode, ExceptNode,
ExpressionNode, ForNode, FunctionNode, GlobalNode, IfNode, ImportAsNode,
ImportFromAsNode, ImportFromNode, ImportNode, IndexExpressionNode,
LambdaNode, ListComprehensionForNode, ListComprehensionIfNode, ListComprehensionIterNode,
ListComprehensionNode, ListNode, MemberAccessExpressionNode, ModuleNameNode,
ModuleNode, NameNode, NonlocalNode, NumberNode, ParameterCategory, ParameterNode,
ParseNode, PassNode, RaiseNode, ReturnNode, SetNode, SliceExpressionNode,
StarExpressionNode, StatementListNode, StatementNode, StringNode,
SuiteNode, TernaryExpressionNode, TryNode, TupleExpressionNode, TypeAnnotationExpression,
TypeAnnotationExpressionNode, UnaryExpressionNode, WhileNode, WithItemNode,
WithNode, YieldExpressionNode, YieldFromExpressionNode } from './parseNodes';
IndexItemsNode, LambdaNode, ListComprehensionForNode, ListComprehensionIfNode,
ListComprehensionIterNode, ListComprehensionNode, ListNode, MemberAccessExpressionNode,
ModuleNameNode, ModuleNode, NameNode, NonlocalNode, NumberNode, ParameterCategory,
ParameterNode, ParseNode, PassNode, RaiseNode, ReturnNode, SetNode,
SliceExpressionNode, StarExpressionNode, StatementListNode, StatementNode,
StringNode, SuiteNode, TernaryExpressionNode, TryNode, TupleExpressionNode,
TypeAnnotationExpression, TypeAnnotationExpressionNode, UnaryExpressionNode, WhileNode,
WithItemNode, WithNode, YieldExpressionNode, YieldFromExpressionNode } from './parseNodes';
import { Tokenizer, TokenizerOutput } from './tokenizer';
import { DedentToken, IdentifierToken, KeywordToken, KeywordType,
NumberToken, OperatorToken, OperatorType, QuoteTypeFlags, StringToken,
@ -1592,6 +1592,8 @@ export class Parser {
// Consume trailers.
while (true) {
let nextToken = this._peekToken();
// Is it a function call?
if (this._consumeTokenIfType(TokenType.OpenParenthesis)) {
let argList = this._parseArgList();
@ -1610,16 +1612,20 @@ export class Parser {
} else if (this._consumeTokenIfType(TokenType.OpenBracket)) {
// Is it an index operator?
let indexExpr = this._parseSubscriptList();
let indexNode = new IndexExpressionNode(atomExpression, indexExpr);
indexNode.extend(indexNode);
let expressions = [indexExpr];
if (indexExpr instanceof TupleExpressionNode) {
expressions = indexExpr.expressions;
}
let nextToken = this._peekToken();
let closingToken = this._peekToken();
if (!this._consumeTokenIfType(TokenType.CloseBracket)) {
return this._handleExpressionParseError('Expected "]"');
} else {
indexNode.extend(nextToken);
}
let indexItemsNode = new IndexItemsNode(nextToken, closingToken, expressions);
let indexNode = new IndexExpressionNode(atomExpression, indexItemsNode);
indexNode.extend(indexNode);
atomExpression = indexNode;
} else if (this._consumeTokenIfType(TokenType.Dot)) {
// Is it a member access?