Skip to content

Commit 7c8670a

Browse files
committed
Regexp literal support; Properly resolve statically inherited members
1 parent d8fa04f commit 7c8670a

File tree

10 files changed

+170
-89
lines changed

10 files changed

+170
-89
lines changed

src/ast.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -240,10 +240,11 @@ export abstract class Node {
240240
return expr;
241241
}
242242

243-
static createRegexpLiteralExpression(value: string, range: Range): RegexpLiteralExpression {
243+
static createRegexpLiteralExpression(pattern: string, modifiers: string, range: Range): RegexpLiteralExpression {
244244
var expr = new RegexpLiteralExpression();
245245
expr.range = range;
246-
expr.value = value;
246+
expr.pattern = pattern;
247+
expr.modifiers = modifiers;
247248
return expr;
248249
}
249250

@@ -1045,11 +1046,16 @@ export class RegexpLiteralExpression extends LiteralExpression {
10451046
// kind = NodeKind.LITERAL
10461047
literalKind = LiteralKind.REGEXP;
10471048

1048-
/** Value of the expression. */
1049-
value: string;
1049+
/** Regular expression pattern. */
1050+
pattern: string;
1051+
/** Regular expression modifiers. */
1052+
modifiers: string;
10501053

10511054
serialize(sb: string[]): void {
1052-
sb.push(this.value);
1055+
sb.push("/");
1056+
sb.push(this.pattern);
1057+
sb.push("/");
1058+
sb.push(this.modifiers);
10531059
}
10541060
}
10551061

src/builtins.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,11 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty
212212

213213
case "isNaN": // isNaN<T>(value: T) -> bool
214214
compiler.currentType = Type.bool;
215+
// if (operands.length != 1) {
216+
// compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "1", operands.length.toString(10));
217+
// return module.createUnreachable();
218+
// }
219+
// TODO: infer type argument if omitted
215220
if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode))
216221
return module.createUnreachable();
217222
if ((<Type[]>typeArguments)[0].isAnyInteger)

src/decompiler.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,7 @@ export class Decompiler {
3030

3131
private tempI64: I64 = new I64();
3232

33-
constructor() {
34-
}
33+
constructor() { }
3534

3635
/** Decompiles a module to an AST that can then be serialized. */
3736
decompile(module: Module) {

src/diagnosticMessages.generated.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ export enum DiagnosticCode {
6363
Generic_type_0_requires_1_type_argument_s = 2314,
6464
Type_0_is_not_generic = 2315,
6565
Type_0_is_not_assignable_to_type_1 = 2322,
66+
Index_signature_is_missing_in_type_0 = 2329,
6667
_this_cannot_be_referenced_in_current_location = 2332,
6768
_super_can_only_be_referenced_in_a_derived_class = 2335,
6869
Property_0_does_not_exist_on_type_1 = 2339,
@@ -147,6 +148,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
147148
case 2314: return "Generic type '{0}' requires {1} type argument(s).";
148149
case 2315: return "Type '{0}' is not generic.";
149150
case 2322: return "Type '{0}' is not assignable to type '{1}'.";
151+
case 2329: return "Index signature is missing in type '{0}'.";
150152
case 2332: return "'this' cannot be referenced in current location.";
151153
case 2335: return "'super' can only be referenced in a derived class.";
152154
case 2339: return "Property '{0}' does not exist on type '{1}'.";

src/diagnosticMessages.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
"Generic type '{0}' requires {1} type argument(s).": 2314,
6464
"Type '{0}' is not generic.": 2315,
6565
"Type '{0}' is not assignable to type '{1}'.": 2322,
66+
"Index signature is missing in type '{0}'.": 2329,
6667
"'this' cannot be referenced in current location.": 2332,
6768
"'super' can only be referenced in a derived class.": 2335,
6869
"Property '{0}' does not exist on type '{1}'.": 2339,

src/parser.ts

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,14 @@ import {
3131
NodeKind,
3232
Source,
3333
TypeNode,
34-
// expressions
34+
35+
Expression,
3536
AssertionKind,
3637
CallExpression,
37-
Expression,
3838
IdentifierExpression,
3939
StringLiteralExpression,
40-
// statements
40+
41+
Statement,
4142
BlockStatement,
4243
BreakStatement,
4344
ClassDeclaration,
@@ -63,7 +64,6 @@ import {
6364
NamespaceDeclaration,
6465
Parameter,
6566
ReturnStatement,
66-
Statement,
6767
SwitchCase,
6868
SwitchStatement,
6969
ThrowStatement,
@@ -73,7 +73,7 @@ import {
7373
VariableStatement,
7474
VariableDeclaration,
7575
WhileStatement,
76-
// utility
76+
7777
addModifier,
7878
getModifier,
7979
hasModifier,
@@ -108,6 +108,7 @@ export class Parser extends DiagnosticEmitter {
108108
this.program.sources.push(source);
109109

110110
var tn = new Tokenizer(source, this.program.diagnostics);
111+
tn.silentDiagnostics = this.silentDiagnostics;
111112
source.tokenizer = tn;
112113

113114
while (!tn.skip(Token.ENDOFFILE)) {
@@ -1512,18 +1513,15 @@ export class Parser extends DiagnosticEmitter {
15121513
return Node.createFloatLiteralExpression(tn.readFloat(), tn.range(startPos, tn.pos));
15131514

15141515
// RegexpLiteralExpression
1515-
/*
15161516
case Token.SLASH:
1517-
var regexpLit = Node.createRegexpLiteral(tn.readRegexp(), tn.range(startPos, tn.pos));
1517+
var regexpPattern = tn.readRegexpPattern();
1518+
if (regexpPattern == null)
1519+
return null;
15181520
if (!tn.skip(Token.SLASH)) {
15191521
this.error(DiagnosticCode._0_expected, tn.range(), "/");
15201522
return null;
15211523
}
1522-
// TODO: modifiers, may be move to tokenizer
1523-
return regexpLit;
1524-
*/
1525-
case Token.REGEXPLITERAL: // not yet supported
1526-
return Node.createRegexpLiteralExpression(tn.readRegexp(), tn.range(startPos, tn.pos));
1524+
return Node.createRegexpLiteralExpression(regexpPattern, tn.readRegexpModifiers(), tn.range(startPos, tn.pos));
15271525

15281526
default:
15291527
this.error(DiagnosticCode.Expression_expected, tn.range());

src/program.ts

Lines changed: 79 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ export class Program extends DiagnosticEmitter {
146146

147147
var queuedExports = new Map<string,QueuedExport>();
148148
var queuedImports = new Array<QueuedImport>();
149+
var queuedDerivedClasses = new Array<ClassPrototype>();
149150

150151
// build initial lookup maps of internal names to declarations
151152
for (var i = 0, k = this.sources.length; i < k; ++i) {
@@ -156,7 +157,7 @@ export class Program extends DiagnosticEmitter {
156157
switch (statement.kind) {
157158

158159
case NodeKind.CLASS:
159-
this.initializeClass(<ClassDeclaration>statement);
160+
this.initializeClass(<ClassDeclaration>statement, queuedDerivedClasses);
160161
break;
161162

162163
case NodeKind.ENUM:
@@ -180,7 +181,7 @@ export class Program extends DiagnosticEmitter {
180181
break;
181182

182183
case NodeKind.NAMESPACE:
183-
this.initializeNamespace(<NamespaceDeclaration>statement);
184+
this.initializeNamespace(<NamespaceDeclaration>statement, queuedDerivedClasses, null);
184185
break;
185186

186187
case NodeKind.TYPEDECLARATION:
@@ -232,6 +233,22 @@ export class Program extends DiagnosticEmitter {
232233
}
233234
} while (currentExport);
234235
}
236+
237+
// resolve base prototypes of derived classes
238+
for (i = 0, k = queuedDerivedClasses.length; i < k; ++i) {
239+
var derivedDeclaration = queuedDerivedClasses[i].declaration;
240+
assert(derivedDeclaration != null);
241+
var derivedType = (<ClassDeclaration>derivedDeclaration).extendsType;
242+
assert(derivedType != null);
243+
var resolved = this.resolveIdentifier((<TypeNode>derivedType).identifier, null); // reports
244+
if (resolved) {
245+
if (resolved.element.kind != ElementKind.CLASS_PROTOTYPE) {
246+
this.error(DiagnosticCode.A_class_may_only_extend_another_class, (<TypeNode>derivedType).range);
247+
continue;
248+
}
249+
queuedDerivedClasses[i].basePrototype = <ClassPrototype>resolved.element;
250+
}
251+
}
235252
}
236253

237254
/** Tries to resolve an import by traversing exports and queued exports. */
@@ -252,7 +269,7 @@ export class Program extends DiagnosticEmitter {
252269
} while (true);
253270
}
254271

255-
private initializeClass(declaration: ClassDeclaration, namespace: Element | null = null): void {
272+
private initializeClass(declaration: ClassDeclaration, queuedDerivedClasses: ClassPrototype[], namespace: Element | null = null): void {
256273
var internalName = declaration.internalName;
257274
if (this.elements.has(internalName)) {
258275
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, internalName);
@@ -276,6 +293,10 @@ export class Program extends DiagnosticEmitter {
276293
} else if (declaration.implementsTypes.length)
277294
throw new Error("not implemented");
278295

296+
// remember classes that extend another one
297+
if (declaration.extendsType)
298+
queuedDerivedClasses.push(prototype);
299+
279300
// add as namespace member if applicable
280301
if (namespace) {
281302
if (namespace.members) {
@@ -762,7 +783,7 @@ export class Program extends DiagnosticEmitter {
762783
}
763784
}
764785

765-
private initializeNamespace(declaration: NamespaceDeclaration, parentNamespace: Element | null = null): void {
786+
private initializeNamespace(declaration: NamespaceDeclaration, queuedExtendingClasses: ClassPrototype[], parentNamespace: Element | null = null): void {
766787
var internalName = declaration.internalName;
767788

768789
var namespace = this.elements.get(internalName);
@@ -794,7 +815,7 @@ export class Program extends DiagnosticEmitter {
794815
switch (members[i].kind) {
795816

796817
case NodeKind.CLASS:
797-
this.initializeClass(<ClassDeclaration>members[i], namespace);
818+
this.initializeClass(<ClassDeclaration>members[i], queuedExtendingClasses, namespace);
798819
break;
799820

800821
case NodeKind.ENUM:
@@ -810,7 +831,7 @@ export class Program extends DiagnosticEmitter {
810831
break;
811832

812833
case NodeKind.NAMESPACE:
813-
this.initializeNamespace(<NamespaceDeclaration>members[i], namespace);
834+
this.initializeNamespace(<NamespaceDeclaration>members[i], queuedExtendingClasses, namespace);
814835
break;
815836

816837
case NodeKind.TYPEDECLARATION:
@@ -957,22 +978,26 @@ export class Program extends DiagnosticEmitter {
957978
}
958979

959980
/** Resolves an identifier to the element it refers to. */
960-
resolveIdentifier(identifier: IdentifierExpression, contextualFunction: Function): ResolvedElement | null {
981+
resolveIdentifier(identifier: IdentifierExpression, contextualFunction: Function | null): ResolvedElement | null {
961982
var name = identifier.name;
962-
var local = contextualFunction.locals.get(name);
963-
if (local)
964-
return (resolvedElement || (resolvedElement = new ResolvedElement())).set(local);
965983

966984
var element: Element | null;
967985
var namespace: Element | null;
968986

969-
// search contextual parent namespaces if applicable
970-
if (contextualFunction && (namespace = contextualFunction.prototype.namespace)) {
971-
do {
972-
if (element = this.elements.get(namespace.internalName + STATIC_DELIMITER + name))
973-
// if ((namespace.members && (element = namespace.members.get(name))) || (element = this.elements.get(namespace.internalName + STATIC_DELIMITER + name)))
974-
return (resolvedElement || (resolvedElement = new ResolvedElement())).set(element);
975-
} while (namespace = namespace.namespace);
987+
if (contextualFunction) {
988+
// check locals
989+
var local = contextualFunction.locals.get(name);
990+
if (local)
991+
return (resolvedElement || (resolvedElement = new ResolvedElement())).set(local);
992+
993+
// search contextual parent namespaces if applicable
994+
if (namespace = contextualFunction.prototype.namespace) {
995+
do {
996+
if (element = this.elements.get(namespace.internalName + STATIC_DELIMITER + name))
997+
// if ((namespace.members && (element = namespace.members.get(name))) || (element = this.elements.get(namespace.internalName + STATIC_DELIMITER + name)))
998+
return (resolvedElement || (resolvedElement = new ResolvedElement())).set(element);
999+
} while (namespace = namespace.namespace);
1000+
}
9761001
}
9771002

9781003
// search current file
@@ -998,6 +1023,7 @@ export class Program extends DiagnosticEmitter {
9981023
// at this point we know exactly what the target is, so look up the element within
9991024
var propertyName = propertyAccess.property.name;
10001025
var targetType: Type;
1026+
var member: Element | null;
10011027
switch (target.kind) {
10021028

10031029
case ElementKind.GLOBAL:
@@ -1008,12 +1034,31 @@ export class Program extends DiagnosticEmitter {
10081034
target = <Class>targetType.classType;
10091035
// fall-through
10101036

1011-
default:
1012-
if (target.members) {
1013-
var member = target.members.get(propertyName);
1014-
if (member)
1037+
case ElementKind.CLASS_PROTOTYPE:
1038+
case ElementKind.CLASS:
1039+
do {
1040+
if (target.members && (member = target.members.get(propertyName)))
10151041
return resolvedElement.set(member).withTarget(target, targetExpression);
1016-
}
1042+
// check inherited static members on the base prototype while target is a class prototype
1043+
if (target.kind == ElementKind.CLASS_PROTOTYPE) {
1044+
if ((<ClassPrototype>target).basePrototype)
1045+
target = <ClassPrototype>(<ClassPrototype>target).basePrototype;
1046+
else
1047+
break;
1048+
// or inherited instance members on the cbase class while target is a class instance
1049+
} else if (target.kind == ElementKind.CLASS) {
1050+
if ((<Class>target).base)
1051+
target = <Class>(<Class>target).base;
1052+
else
1053+
break;
1054+
} else
1055+
break;
1056+
} while (true);
1057+
break;
1058+
1059+
default: // enums or other namespace-like elements
1060+
if (target.members && (member = target.members.get(propertyName)))
1061+
return resolvedElement.set(member).withTarget(target, targetExpression);
10171062
break;
10181063
}
10191064
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 {
10261071
if (!(resolvedElement = this.resolveExpression(targetExpression, contextualFunction)))
10271072
return null;
10281073
var target = resolvedElement.element;
1029-
10301074
switch (target.kind) {
1075+
1076+
// TBD: should indexed access on static classes, like `Heap`, be a supported as well?
10311077
case ElementKind.CLASS:
10321078
var type = (<Class>target).type;
10331079
if (type.classType) {
1034-
// TODO: check if array etc.
1080+
var indexedGet: FunctionPrototype | null;
1081+
if (indexedGet = (target = type.classType).prototype.opIndexedGet)
1082+
return resolvedElement.set(indexedGet).withTarget(target, targetExpression);
10351083
}
10361084
break;
10371085
}
1038-
this.error(DiagnosticCode.Operation_not_supported, elementAccess.range);
1086+
this.error(DiagnosticCode.Index_signature_is_missing_in_type_0, targetExpression.range, target.internalName);
10391087
return null;
10401088
}
10411089

@@ -1820,15 +1868,17 @@ export class ClassPrototype extends Element {
18201868
instances: Map<string,Class> = new Map();
18211869
/** Instance member prototypes. */
18221870
instanceMembers: Map<string,Element> | null = null;
1871+
/** Base class prototype, if applicable. */
1872+
basePrototype: ClassPrototype | null = null; // set in Program#initialize
18231873

18241874
/** Overloaded indexed get method, if any. */
1825-
opIndexedGet: FunctionPrototype | null;
1875+
opIndexedGet: FunctionPrototype | null = null; // TODO: indexedGet and indexedSet as an accessor?
18261876
/** Overloaded indexed set method, if any. */
1827-
opIndexedSet: FunctionPrototype | null;
1877+
opIndexedSet: FunctionPrototype | null = null;
18281878
/** Overloaded concatenation method, if any. */
1829-
opConcat: FunctionPrototype | null;
1879+
opConcat: FunctionPrototype | null = null;
18301880
/** Overloaded equality comparison method, if any. */
1831-
opEquals: FunctionPrototype | null;
1881+
opEquals: FunctionPrototype | null = null;
18321882

18331883
constructor(program: Program, simpleName: string, internalName: string, declaration: ClassDeclaration | null = null) {
18341884
super(program, simpleName, internalName);
@@ -1878,16 +1928,6 @@ export class ClassPrototype extends Element {
18781928
this.program.error(DiagnosticCode.A_class_may_only_extend_another_class, declaration.extendsType.range);
18791929
return null;
18801930
}
1881-
if ((this.flags & ElementFlags.HAS_STATIC_BASE_MEMBERS) == 0) { // inherit static base members once
1882-
this.flags |= ElementFlags.HAS_STATIC_BASE_MEMBERS;
1883-
if (baseClass.prototype.members) {
1884-
if (!this.members)
1885-
this.members = new Map();
1886-
for (var baseMember of baseClass.prototype.members.values())
1887-
if (!baseMember.isInstance)
1888-
this.members.set(baseMember.simpleName, baseMember);
1889-
}
1890-
}
18911931
if (baseClass.prototype.isStruct != this.isStruct) {
18921932
this.program.error(DiagnosticCode.Structs_cannot_extend_classes_and_vice_versa, Range.join(declaration.name.range, declaration.extendsType.range));
18931933
return null;

0 commit comments

Comments
 (0)