mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-05-20 19:21:30 +00:00
Regexp literal support; Properly resolve statically inherited members
This commit is contained in:
parent
d8fa04f910
commit
7c8670ac35
16
src/ast.ts
16
src/ast.ts
@ -240,10 +240,11 @@ export abstract class Node {
|
|||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static createRegexpLiteralExpression(value: string, range: Range): RegexpLiteralExpression {
|
static createRegexpLiteralExpression(pattern: string, modifiers: string, range: Range): RegexpLiteralExpression {
|
||||||
var expr = new RegexpLiteralExpression();
|
var expr = new RegexpLiteralExpression();
|
||||||
expr.range = range;
|
expr.range = range;
|
||||||
expr.value = value;
|
expr.pattern = pattern;
|
||||||
|
expr.modifiers = modifiers;
|
||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1045,11 +1046,16 @@ export class RegexpLiteralExpression extends LiteralExpression {
|
|||||||
// kind = NodeKind.LITERAL
|
// kind = NodeKind.LITERAL
|
||||||
literalKind = LiteralKind.REGEXP;
|
literalKind = LiteralKind.REGEXP;
|
||||||
|
|
||||||
/** Value of the expression. */
|
/** Regular expression pattern. */
|
||||||
value: string;
|
pattern: string;
|
||||||
|
/** Regular expression modifiers. */
|
||||||
|
modifiers: string;
|
||||||
|
|
||||||
serialize(sb: string[]): void {
|
serialize(sb: string[]): void {
|
||||||
sb.push(this.value);
|
sb.push("/");
|
||||||
|
sb.push(this.pattern);
|
||||||
|
sb.push("/");
|
||||||
|
sb.push(this.modifiers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,6 +212,11 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty
|
|||||||
|
|
||||||
case "isNaN": // isNaN<T>(value: T) -> bool
|
case "isNaN": // isNaN<T>(value: T) -> bool
|
||||||
compiler.currentType = Type.bool;
|
compiler.currentType = Type.bool;
|
||||||
|
// if (operands.length != 1) {
|
||||||
|
// compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "1", operands.length.toString(10));
|
||||||
|
// return module.createUnreachable();
|
||||||
|
// }
|
||||||
|
// TODO: infer type argument if omitted
|
||||||
if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode))
|
if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode))
|
||||||
return module.createUnreachable();
|
return module.createUnreachable();
|
||||||
if ((<Type[]>typeArguments)[0].isAnyInteger)
|
if ((<Type[]>typeArguments)[0].isAnyInteger)
|
||||||
|
@ -30,8 +30,7 @@ export class Decompiler {
|
|||||||
|
|
||||||
private tempI64: I64 = new I64();
|
private tempI64: I64 = new I64();
|
||||||
|
|
||||||
constructor() {
|
constructor() { }
|
||||||
}
|
|
||||||
|
|
||||||
/** Decompiles a module to an AST that can then be serialized. */
|
/** Decompiles a module to an AST that can then be serialized. */
|
||||||
decompile(module: Module) {
|
decompile(module: Module) {
|
||||||
|
@ -63,6 +63,7 @@ export enum DiagnosticCode {
|
|||||||
Generic_type_0_requires_1_type_argument_s = 2314,
|
Generic_type_0_requires_1_type_argument_s = 2314,
|
||||||
Type_0_is_not_generic = 2315,
|
Type_0_is_not_generic = 2315,
|
||||||
Type_0_is_not_assignable_to_type_1 = 2322,
|
Type_0_is_not_assignable_to_type_1 = 2322,
|
||||||
|
Index_signature_is_missing_in_type_0 = 2329,
|
||||||
_this_cannot_be_referenced_in_current_location = 2332,
|
_this_cannot_be_referenced_in_current_location = 2332,
|
||||||
_super_can_only_be_referenced_in_a_derived_class = 2335,
|
_super_can_only_be_referenced_in_a_derived_class = 2335,
|
||||||
Property_0_does_not_exist_on_type_1 = 2339,
|
Property_0_does_not_exist_on_type_1 = 2339,
|
||||||
@ -147,6 +148,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
|
|||||||
case 2314: return "Generic type '{0}' requires {1} type argument(s).";
|
case 2314: return "Generic type '{0}' requires {1} type argument(s).";
|
||||||
case 2315: return "Type '{0}' is not generic.";
|
case 2315: return "Type '{0}' is not generic.";
|
||||||
case 2322: return "Type '{0}' is not assignable to type '{1}'.";
|
case 2322: return "Type '{0}' is not assignable to type '{1}'.";
|
||||||
|
case 2329: return "Index signature is missing in type '{0}'.";
|
||||||
case 2332: return "'this' cannot be referenced in current location.";
|
case 2332: return "'this' cannot be referenced in current location.";
|
||||||
case 2335: return "'super' can only be referenced in a derived class.";
|
case 2335: return "'super' can only be referenced in a derived class.";
|
||||||
case 2339: return "Property '{0}' does not exist on type '{1}'.";
|
case 2339: return "Property '{0}' does not exist on type '{1}'.";
|
||||||
|
@ -63,6 +63,7 @@
|
|||||||
"Generic type '{0}' requires {1} type argument(s).": 2314,
|
"Generic type '{0}' requires {1} type argument(s).": 2314,
|
||||||
"Type '{0}' is not generic.": 2315,
|
"Type '{0}' is not generic.": 2315,
|
||||||
"Type '{0}' is not assignable to type '{1}'.": 2322,
|
"Type '{0}' is not assignable to type '{1}'.": 2322,
|
||||||
|
"Index signature is missing in type '{0}'.": 2329,
|
||||||
"'this' cannot be referenced in current location.": 2332,
|
"'this' cannot be referenced in current location.": 2332,
|
||||||
"'super' can only be referenced in a derived class.": 2335,
|
"'super' can only be referenced in a derived class.": 2335,
|
||||||
"Property '{0}' does not exist on type '{1}'.": 2339,
|
"Property '{0}' does not exist on type '{1}'.": 2339,
|
||||||
|
@ -31,13 +31,14 @@ import {
|
|||||||
NodeKind,
|
NodeKind,
|
||||||
Source,
|
Source,
|
||||||
TypeNode,
|
TypeNode,
|
||||||
// expressions
|
|
||||||
|
Expression,
|
||||||
AssertionKind,
|
AssertionKind,
|
||||||
CallExpression,
|
CallExpression,
|
||||||
Expression,
|
|
||||||
IdentifierExpression,
|
IdentifierExpression,
|
||||||
StringLiteralExpression,
|
StringLiteralExpression,
|
||||||
// statements
|
|
||||||
|
Statement,
|
||||||
BlockStatement,
|
BlockStatement,
|
||||||
BreakStatement,
|
BreakStatement,
|
||||||
ClassDeclaration,
|
ClassDeclaration,
|
||||||
@ -63,7 +64,6 @@ import {
|
|||||||
NamespaceDeclaration,
|
NamespaceDeclaration,
|
||||||
Parameter,
|
Parameter,
|
||||||
ReturnStatement,
|
ReturnStatement,
|
||||||
Statement,
|
|
||||||
SwitchCase,
|
SwitchCase,
|
||||||
SwitchStatement,
|
SwitchStatement,
|
||||||
ThrowStatement,
|
ThrowStatement,
|
||||||
@ -73,7 +73,7 @@ import {
|
|||||||
VariableStatement,
|
VariableStatement,
|
||||||
VariableDeclaration,
|
VariableDeclaration,
|
||||||
WhileStatement,
|
WhileStatement,
|
||||||
// utility
|
|
||||||
addModifier,
|
addModifier,
|
||||||
getModifier,
|
getModifier,
|
||||||
hasModifier,
|
hasModifier,
|
||||||
@ -108,6 +108,7 @@ export class Parser extends DiagnosticEmitter {
|
|||||||
this.program.sources.push(source);
|
this.program.sources.push(source);
|
||||||
|
|
||||||
var tn = new Tokenizer(source, this.program.diagnostics);
|
var tn = new Tokenizer(source, this.program.diagnostics);
|
||||||
|
tn.silentDiagnostics = this.silentDiagnostics;
|
||||||
source.tokenizer = tn;
|
source.tokenizer = tn;
|
||||||
|
|
||||||
while (!tn.skip(Token.ENDOFFILE)) {
|
while (!tn.skip(Token.ENDOFFILE)) {
|
||||||
@ -1512,18 +1513,15 @@ export class Parser extends DiagnosticEmitter {
|
|||||||
return Node.createFloatLiteralExpression(tn.readFloat(), tn.range(startPos, tn.pos));
|
return Node.createFloatLiteralExpression(tn.readFloat(), tn.range(startPos, tn.pos));
|
||||||
|
|
||||||
// RegexpLiteralExpression
|
// RegexpLiteralExpression
|
||||||
/*
|
|
||||||
case Token.SLASH:
|
case Token.SLASH:
|
||||||
var regexpLit = Node.createRegexpLiteral(tn.readRegexp(), tn.range(startPos, tn.pos));
|
var regexpPattern = tn.readRegexpPattern();
|
||||||
|
if (regexpPattern == null)
|
||||||
|
return null;
|
||||||
if (!tn.skip(Token.SLASH)) {
|
if (!tn.skip(Token.SLASH)) {
|
||||||
this.error(DiagnosticCode._0_expected, tn.range(), "/");
|
this.error(DiagnosticCode._0_expected, tn.range(), "/");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// TODO: modifiers, may be move to tokenizer
|
return Node.createRegexpLiteralExpression(regexpPattern, tn.readRegexpModifiers(), tn.range(startPos, tn.pos));
|
||||||
return regexpLit;
|
|
||||||
*/
|
|
||||||
case Token.REGEXPLITERAL: // not yet supported
|
|
||||||
return Node.createRegexpLiteralExpression(tn.readRegexp(), tn.range(startPos, tn.pos));
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
this.error(DiagnosticCode.Expression_expected, tn.range());
|
this.error(DiagnosticCode.Expression_expected, tn.range());
|
||||||
|
118
src/program.ts
118
src/program.ts
@ -146,6 +146,7 @@ export class Program extends DiagnosticEmitter {
|
|||||||
|
|
||||||
var queuedExports = new Map<string,QueuedExport>();
|
var queuedExports = new Map<string,QueuedExport>();
|
||||||
var queuedImports = new Array<QueuedImport>();
|
var queuedImports = new Array<QueuedImport>();
|
||||||
|
var queuedDerivedClasses = new Array<ClassPrototype>();
|
||||||
|
|
||||||
// build initial lookup maps of internal names to declarations
|
// build initial lookup maps of internal names to declarations
|
||||||
for (var i = 0, k = this.sources.length; i < k; ++i) {
|
for (var i = 0, k = this.sources.length; i < k; ++i) {
|
||||||
@ -156,7 +157,7 @@ export class Program extends DiagnosticEmitter {
|
|||||||
switch (statement.kind) {
|
switch (statement.kind) {
|
||||||
|
|
||||||
case NodeKind.CLASS:
|
case NodeKind.CLASS:
|
||||||
this.initializeClass(<ClassDeclaration>statement);
|
this.initializeClass(<ClassDeclaration>statement, queuedDerivedClasses);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NodeKind.ENUM:
|
case NodeKind.ENUM:
|
||||||
@ -180,7 +181,7 @@ export class Program extends DiagnosticEmitter {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case NodeKind.NAMESPACE:
|
case NodeKind.NAMESPACE:
|
||||||
this.initializeNamespace(<NamespaceDeclaration>statement);
|
this.initializeNamespace(<NamespaceDeclaration>statement, queuedDerivedClasses, null);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NodeKind.TYPEDECLARATION:
|
case NodeKind.TYPEDECLARATION:
|
||||||
@ -232,6 +233,22 @@ export class Program extends DiagnosticEmitter {
|
|||||||
}
|
}
|
||||||
} while (currentExport);
|
} while (currentExport);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// resolve base prototypes of derived classes
|
||||||
|
for (i = 0, k = queuedDerivedClasses.length; i < k; ++i) {
|
||||||
|
var derivedDeclaration = queuedDerivedClasses[i].declaration;
|
||||||
|
assert(derivedDeclaration != null);
|
||||||
|
var derivedType = (<ClassDeclaration>derivedDeclaration).extendsType;
|
||||||
|
assert(derivedType != null);
|
||||||
|
var resolved = this.resolveIdentifier((<TypeNode>derivedType).identifier, null); // reports
|
||||||
|
if (resolved) {
|
||||||
|
if (resolved.element.kind != ElementKind.CLASS_PROTOTYPE) {
|
||||||
|
this.error(DiagnosticCode.A_class_may_only_extend_another_class, (<TypeNode>derivedType).range);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
queuedDerivedClasses[i].basePrototype = <ClassPrototype>resolved.element;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Tries to resolve an import by traversing exports and queued exports. */
|
/** Tries to resolve an import by traversing exports and queued exports. */
|
||||||
@ -252,7 +269,7 @@ export class Program extends DiagnosticEmitter {
|
|||||||
} while (true);
|
} while (true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private initializeClass(declaration: ClassDeclaration, namespace: Element | null = null): void {
|
private initializeClass(declaration: ClassDeclaration, queuedDerivedClasses: ClassPrototype[], namespace: Element | null = null): void {
|
||||||
var internalName = declaration.internalName;
|
var internalName = declaration.internalName;
|
||||||
if (this.elements.has(internalName)) {
|
if (this.elements.has(internalName)) {
|
||||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, internalName);
|
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, internalName);
|
||||||
@ -276,6 +293,10 @@ export class Program extends DiagnosticEmitter {
|
|||||||
} else if (declaration.implementsTypes.length)
|
} else if (declaration.implementsTypes.length)
|
||||||
throw new Error("not implemented");
|
throw new Error("not implemented");
|
||||||
|
|
||||||
|
// remember classes that extend another one
|
||||||
|
if (declaration.extendsType)
|
||||||
|
queuedDerivedClasses.push(prototype);
|
||||||
|
|
||||||
// add as namespace member if applicable
|
// add as namespace member if applicable
|
||||||
if (namespace) {
|
if (namespace) {
|
||||||
if (namespace.members) {
|
if (namespace.members) {
|
||||||
@ -762,7 +783,7 @@ export class Program extends DiagnosticEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private initializeNamespace(declaration: NamespaceDeclaration, parentNamespace: Element | null = null): void {
|
private initializeNamespace(declaration: NamespaceDeclaration, queuedExtendingClasses: ClassPrototype[], parentNamespace: Element | null = null): void {
|
||||||
var internalName = declaration.internalName;
|
var internalName = declaration.internalName;
|
||||||
|
|
||||||
var namespace = this.elements.get(internalName);
|
var namespace = this.elements.get(internalName);
|
||||||
@ -794,7 +815,7 @@ export class Program extends DiagnosticEmitter {
|
|||||||
switch (members[i].kind) {
|
switch (members[i].kind) {
|
||||||
|
|
||||||
case NodeKind.CLASS:
|
case NodeKind.CLASS:
|
||||||
this.initializeClass(<ClassDeclaration>members[i], namespace);
|
this.initializeClass(<ClassDeclaration>members[i], queuedExtendingClasses, namespace);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NodeKind.ENUM:
|
case NodeKind.ENUM:
|
||||||
@ -810,7 +831,7 @@ export class Program extends DiagnosticEmitter {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case NodeKind.NAMESPACE:
|
case NodeKind.NAMESPACE:
|
||||||
this.initializeNamespace(<NamespaceDeclaration>members[i], namespace);
|
this.initializeNamespace(<NamespaceDeclaration>members[i], queuedExtendingClasses, namespace);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NodeKind.TYPEDECLARATION:
|
case NodeKind.TYPEDECLARATION:
|
||||||
@ -957,22 +978,26 @@ export class Program extends DiagnosticEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Resolves an identifier to the element it refers to. */
|
/** Resolves an identifier to the element it refers to. */
|
||||||
resolveIdentifier(identifier: IdentifierExpression, contextualFunction: Function): ResolvedElement | null {
|
resolveIdentifier(identifier: IdentifierExpression, contextualFunction: Function | null): ResolvedElement | null {
|
||||||
var name = identifier.name;
|
var name = identifier.name;
|
||||||
var local = contextualFunction.locals.get(name);
|
|
||||||
if (local)
|
|
||||||
return (resolvedElement || (resolvedElement = new ResolvedElement())).set(local);
|
|
||||||
|
|
||||||
var element: Element | null;
|
var element: Element | null;
|
||||||
var namespace: Element | null;
|
var namespace: Element | null;
|
||||||
|
|
||||||
// search contextual parent namespaces if applicable
|
if (contextualFunction) {
|
||||||
if (contextualFunction && (namespace = contextualFunction.prototype.namespace)) {
|
// check locals
|
||||||
do {
|
var local = contextualFunction.locals.get(name);
|
||||||
if (element = this.elements.get(namespace.internalName + STATIC_DELIMITER + name))
|
if (local)
|
||||||
// if ((namespace.members && (element = namespace.members.get(name))) || (element = this.elements.get(namespace.internalName + STATIC_DELIMITER + name)))
|
return (resolvedElement || (resolvedElement = new ResolvedElement())).set(local);
|
||||||
return (resolvedElement || (resolvedElement = new ResolvedElement())).set(element);
|
|
||||||
} while (namespace = namespace.namespace);
|
// search contextual parent namespaces if applicable
|
||||||
|
if (namespace = contextualFunction.prototype.namespace) {
|
||||||
|
do {
|
||||||
|
if (element = this.elements.get(namespace.internalName + STATIC_DELIMITER + name))
|
||||||
|
// if ((namespace.members && (element = namespace.members.get(name))) || (element = this.elements.get(namespace.internalName + STATIC_DELIMITER + name)))
|
||||||
|
return (resolvedElement || (resolvedElement = new ResolvedElement())).set(element);
|
||||||
|
} while (namespace = namespace.namespace);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// search current file
|
// search current file
|
||||||
@ -998,6 +1023,7 @@ export class Program extends DiagnosticEmitter {
|
|||||||
// at this point we know exactly what the target is, so look up the element within
|
// at this point we know exactly what the target is, so look up the element within
|
||||||
var propertyName = propertyAccess.property.name;
|
var propertyName = propertyAccess.property.name;
|
||||||
var targetType: Type;
|
var targetType: Type;
|
||||||
|
var member: Element | null;
|
||||||
switch (target.kind) {
|
switch (target.kind) {
|
||||||
|
|
||||||
case ElementKind.GLOBAL:
|
case ElementKind.GLOBAL:
|
||||||
@ -1008,12 +1034,31 @@ export class Program extends DiagnosticEmitter {
|
|||||||
target = <Class>targetType.classType;
|
target = <Class>targetType.classType;
|
||||||
// fall-through
|
// fall-through
|
||||||
|
|
||||||
default:
|
case ElementKind.CLASS_PROTOTYPE:
|
||||||
if (target.members) {
|
case ElementKind.CLASS:
|
||||||
var member = target.members.get(propertyName);
|
do {
|
||||||
if (member)
|
if (target.members && (member = target.members.get(propertyName)))
|
||||||
return resolvedElement.set(member).withTarget(target, targetExpression);
|
return resolvedElement.set(member).withTarget(target, targetExpression);
|
||||||
}
|
// check inherited static members on the base prototype while target is a class prototype
|
||||||
|
if (target.kind == ElementKind.CLASS_PROTOTYPE) {
|
||||||
|
if ((<ClassPrototype>target).basePrototype)
|
||||||
|
target = <ClassPrototype>(<ClassPrototype>target).basePrototype;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
// or inherited instance members on the cbase class while target is a class instance
|
||||||
|
} else if (target.kind == ElementKind.CLASS) {
|
||||||
|
if ((<Class>target).base)
|
||||||
|
target = <Class>(<Class>target).base;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
} else
|
||||||
|
break;
|
||||||
|
} while (true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: // enums or other namespace-like elements
|
||||||
|
if (target.members && (member = target.members.get(propertyName)))
|
||||||
|
return resolvedElement.set(member).withTarget(target, targetExpression);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
this.error(DiagnosticCode.Property_0_does_not_exist_on_type_1, propertyAccess.property.range, propertyName, target.internalName);
|
this.error(DiagnosticCode.Property_0_does_not_exist_on_type_1, propertyAccess.property.range, propertyName, target.internalName);
|
||||||
@ -1026,16 +1071,19 @@ export class Program extends DiagnosticEmitter {
|
|||||||
if (!(resolvedElement = this.resolveExpression(targetExpression, contextualFunction)))
|
if (!(resolvedElement = this.resolveExpression(targetExpression, contextualFunction)))
|
||||||
return null;
|
return null;
|
||||||
var target = resolvedElement.element;
|
var target = resolvedElement.element;
|
||||||
|
|
||||||
switch (target.kind) {
|
switch (target.kind) {
|
||||||
|
|
||||||
|
// TBD: should indexed access on static classes, like `Heap`, be a supported as well?
|
||||||
case ElementKind.CLASS:
|
case ElementKind.CLASS:
|
||||||
var type = (<Class>target).type;
|
var type = (<Class>target).type;
|
||||||
if (type.classType) {
|
if (type.classType) {
|
||||||
// TODO: check if array etc.
|
var indexedGet: FunctionPrototype | null;
|
||||||
|
if (indexedGet = (target = type.classType).prototype.opIndexedGet)
|
||||||
|
return resolvedElement.set(indexedGet).withTarget(target, targetExpression);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
this.error(DiagnosticCode.Operation_not_supported, elementAccess.range);
|
this.error(DiagnosticCode.Index_signature_is_missing_in_type_0, targetExpression.range, target.internalName);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1820,15 +1868,17 @@ export class ClassPrototype extends Element {
|
|||||||
instances: Map<string,Class> = new Map();
|
instances: Map<string,Class> = new Map();
|
||||||
/** Instance member prototypes. */
|
/** Instance member prototypes. */
|
||||||
instanceMembers: Map<string,Element> | null = null;
|
instanceMembers: Map<string,Element> | null = null;
|
||||||
|
/** Base class prototype, if applicable. */
|
||||||
|
basePrototype: ClassPrototype | null = null; // set in Program#initialize
|
||||||
|
|
||||||
/** Overloaded indexed get method, if any. */
|
/** Overloaded indexed get method, if any. */
|
||||||
opIndexedGet: FunctionPrototype | null;
|
opIndexedGet: FunctionPrototype | null = null; // TODO: indexedGet and indexedSet as an accessor?
|
||||||
/** Overloaded indexed set method, if any. */
|
/** Overloaded indexed set method, if any. */
|
||||||
opIndexedSet: FunctionPrototype | null;
|
opIndexedSet: FunctionPrototype | null = null;
|
||||||
/** Overloaded concatenation method, if any. */
|
/** Overloaded concatenation method, if any. */
|
||||||
opConcat: FunctionPrototype | null;
|
opConcat: FunctionPrototype | null = null;
|
||||||
/** Overloaded equality comparison method, if any. */
|
/** Overloaded equality comparison method, if any. */
|
||||||
opEquals: FunctionPrototype | null;
|
opEquals: FunctionPrototype | null = null;
|
||||||
|
|
||||||
constructor(program: Program, simpleName: string, internalName: string, declaration: ClassDeclaration | null = null) {
|
constructor(program: Program, simpleName: string, internalName: string, declaration: ClassDeclaration | null = null) {
|
||||||
super(program, simpleName, internalName);
|
super(program, simpleName, internalName);
|
||||||
@ -1878,16 +1928,6 @@ export class ClassPrototype extends Element {
|
|||||||
this.program.error(DiagnosticCode.A_class_may_only_extend_another_class, declaration.extendsType.range);
|
this.program.error(DiagnosticCode.A_class_may_only_extend_another_class, declaration.extendsType.range);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if ((this.flags & ElementFlags.HAS_STATIC_BASE_MEMBERS) == 0) { // inherit static base members once
|
|
||||||
this.flags |= ElementFlags.HAS_STATIC_BASE_MEMBERS;
|
|
||||||
if (baseClass.prototype.members) {
|
|
||||||
if (!this.members)
|
|
||||||
this.members = new Map();
|
|
||||||
for (var baseMember of baseClass.prototype.members.values())
|
|
||||||
if (!baseMember.isInstance)
|
|
||||||
this.members.set(baseMember.simpleName, baseMember);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (baseClass.prototype.isStruct != this.isStruct) {
|
if (baseClass.prototype.isStruct != this.isStruct) {
|
||||||
this.program.error(DiagnosticCode.Structs_cannot_extend_classes_and_vice_versa, Range.join(declaration.name.range, declaration.extendsType.range));
|
this.program.error(DiagnosticCode.Structs_cannot_extend_classes_and_vice_versa, Range.join(declaration.name.range, declaration.extendsType.range));
|
||||||
return null;
|
return null;
|
||||||
|
@ -4,18 +4,17 @@
|
|||||||
as much bookkeeping, simply skips over trivia and provides a more general
|
as much bookkeeping, simply skips over trivia and provides a more general
|
||||||
mark/reset mechanism for the parser to utilize on ambiguous tokens.
|
mark/reset mechanism for the parser to utilize on ambiguous tokens.
|
||||||
|
|
||||||
next() advances the token
|
next() advances the token
|
||||||
peek() peeks for the next token
|
peek() peeks for the next token
|
||||||
skip(token) skips over a token if possible
|
skip(token) skips over a token if possible
|
||||||
mark() marks at current token
|
mark() marks at current token
|
||||||
reset() resets to marked state
|
reset() resets to marked state
|
||||||
range() gets the range of the current token
|
range() gets the range of the current token
|
||||||
|
|
||||||
readFloat() on FLOATLITERAL
|
readFloat() on FLOATLITERAL
|
||||||
readIdentifier() on IDENTIFIER
|
readIdentifier() on IDENTIFIER
|
||||||
readInteger() on INTEGERLITERAL
|
readInteger() on INTEGERLITERAL
|
||||||
readRegexp() on REGEXPLITERAL // TODO
|
readString() on STRINGLITERAL
|
||||||
readString() on STRINGLITERAL
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -174,7 +173,6 @@ export enum Token {
|
|||||||
STRINGLITERAL,
|
STRINGLITERAL,
|
||||||
INTEGERLITERAL,
|
INTEGERLITERAL,
|
||||||
FLOATLITERAL,
|
FLOATLITERAL,
|
||||||
REGEXPLITERAL,
|
|
||||||
|
|
||||||
// meta
|
// meta
|
||||||
|
|
||||||
@ -562,8 +560,6 @@ export class Tokenizer extends DiagnosticEmitter {
|
|||||||
return Token.SLASH_EQUALS;
|
return Token.SLASH_EQUALS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.testRegexp())
|
|
||||||
return Token.REGEXPLITERAL; // expects a call to readRegexp
|
|
||||||
return Token.SLASH;
|
return Token.SLASH;
|
||||||
|
|
||||||
case CharCode._0:
|
case CharCode._0:
|
||||||
@ -905,21 +901,14 @@ export class Tokenizer extends DiagnosticEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
testRegexp(): bool {
|
readRegexpPattern(): string | null {
|
||||||
// TODO: this'll require more context
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
readRegexp(): string {
|
|
||||||
var text = this.source.text;
|
var text = this.source.text;
|
||||||
var start = this.pos;
|
var start = this.pos;
|
||||||
var result = "";
|
|
||||||
var escaped = false;
|
var escaped = false;
|
||||||
while (true) {
|
while (true) {
|
||||||
if (this.pos >= this.end) {
|
if (this.pos >= this.end) {
|
||||||
result += text.substring(start, this.pos);
|
|
||||||
this.error(DiagnosticCode.Unterminated_regular_expression_literal, this.range(start, this.end));
|
this.error(DiagnosticCode.Unterminated_regular_expression_literal, this.range(start, this.end));
|
||||||
break;
|
return null;
|
||||||
}
|
}
|
||||||
if (text.charCodeAt(this.pos) == CharCode.BACKSLASH) {
|
if (text.charCodeAt(this.pos) == CharCode.BACKSLASH) {
|
||||||
++this.pos;
|
++this.pos;
|
||||||
@ -927,19 +916,36 @@ export class Tokenizer extends DiagnosticEmitter {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var c = text.charCodeAt(this.pos);
|
var c = text.charCodeAt(this.pos);
|
||||||
if (c == CharCode.SLASH) {
|
if (c == CharCode.SLASH && !escaped)
|
||||||
result += text.substring(start, this.pos);
|
|
||||||
++this.pos;
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
if (isLineBreak(c)) {
|
if (isLineBreak(c)) {
|
||||||
result += text.substring(start, this.pos);
|
|
||||||
this.error(DiagnosticCode.Unterminated_regular_expression_literal, this.range(start, this.pos));
|
this.error(DiagnosticCode.Unterminated_regular_expression_literal, this.range(start, this.pos));
|
||||||
break;
|
return null;
|
||||||
}
|
}
|
||||||
++this.pos;
|
++this.pos;
|
||||||
|
escaped = false;
|
||||||
}
|
}
|
||||||
return result;
|
return text.substring(start, this.pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
readRegexpModifiers(): string {
|
||||||
|
var text = this.source.text;
|
||||||
|
var start = this.pos;
|
||||||
|
/a/
|
||||||
|
while (this.pos < this.end) {
|
||||||
|
switch (text.charCodeAt(this.pos)) {
|
||||||
|
|
||||||
|
case CharCode.g:
|
||||||
|
case CharCode.i:
|
||||||
|
case CharCode.m:
|
||||||
|
++this.pos;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return text.substring(start, this.pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return text.substring(start, this.pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
testInteger(): bool {
|
testInteger(): bool {
|
||||||
|
18
tests/parser/regexp.ts
Normal file
18
tests/parser/regexp.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// with modifiers
|
||||||
|
/(abc)\//ig;
|
||||||
|
|
||||||
|
// without modifiers
|
||||||
|
/(abc)\//;
|
||||||
|
|
||||||
|
// can be assigned
|
||||||
|
var re = /(abc)\//ig;
|
||||||
|
|
||||||
|
// generally behaves like an expression
|
||||||
|
var noRe = !/(abc)\//i;
|
||||||
|
|
||||||
|
// inner line break is unterminated
|
||||||
|
/a
|
||||||
|
b/ig;
|
||||||
|
|
||||||
|
// just a comment
|
||||||
|
//ig;
|
6
tests/parser/regexp.ts.fixture.ts
Normal file
6
tests/parser/regexp.ts.fixture.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
/(abc)\//ig;
|
||||||
|
/(abc)\//;
|
||||||
|
let re = /(abc)\//ig;
|
||||||
|
let noRe = !/(abc)\//i;
|
||||||
|
b / ig;
|
||||||
|
// ERROR 1161: "Unterminated regular expression literal." in regexp.ts @ 75,76
|
Loading…
x
Reference in New Issue
Block a user