Operator overload preparations

This commit is contained in:
dcodeIO 2018-01-06 10:20:38 +01:00
parent 859a0e05bf
commit d8fa04f910
8 changed files with 142 additions and 45 deletions

View File

@ -351,6 +351,31 @@ export abstract class Node {
if (stmt.arguments = args) if (stmt.arguments = args)
for (var i: i32 = 0, k: i32 = (<Expression[]>args).length; i < k; ++i) for (var i: i32 = 0, k: i32 = (<Expression[]>args).length; i < k; ++i)
(<Expression[]>args)[i].parent = stmt; (<Expression[]>args)[i].parent = stmt;
if (expression.kind == NodeKind.IDENTIFIER) {
switch ((<IdentifierExpression>expression).name) {
case "global":
stmt.decoratorKind = DecoratorKind.GLOBAL;
break;
case "operator":
stmt.decoratorKind = DecoratorKind.OPERATOR;
break;
case "struct":
stmt.decoratorKind = DecoratorKind.STRUCT;
break;
case "size":
stmt.decoratorKind = DecoratorKind.SIZE;
break;
default:
stmt.decoratorKind = DecoratorKind.CUSTOM;
break;
}
} else
stmt.decoratorKind = DecoratorKind.CUSTOM;
return stmt; return stmt;
} }
@ -1333,6 +1358,15 @@ export class ContinueStatement extends Statement {
} }
} }
/** Built-in decorator kinds. */
export const enum DecoratorKind {
CUSTOM,
GLOBAL,
OPERATOR,
STRUCT,
SIZE
}
/** Depresents a decorator. */ /** Depresents a decorator. */
export class Decorator extends Statement { export class Decorator extends Statement {
@ -1342,6 +1376,8 @@ export class Decorator extends Statement {
name: Expression; name: Expression;
/** Argument expressions. */ /** Argument expressions. */
arguments: Expression[] | null; arguments: Expression[] | null;
/** Built-in kind, if applicable. */
decoratorKind: DecoratorKind;
serialize(sb: string[]): void { serialize(sb: string[]): void {
sb.push("@"); sb.push("@");
@ -2153,7 +2189,7 @@ export function hasModifier(kind: ModifierKind, modifiers: Modifier[] | null): b
} }
/** Gets a specific decorator within the specified decorators, if present. */ /** Gets a specific decorator within the specified decorators, if present. */
export function getDecorator(name: string, decorators: Decorator[] | null): Decorator | null { export function getFirstDecorator(name: string, decorators: Decorator[] | null): Decorator | null {
if (decorators) if (decorators)
for (var i = 0, k = decorators.length; i < k; ++i) { for (var i = 0, k = decorators.length; i < k; ++i) {
var decorator = decorators[i]; var decorator = decorators[i];
@ -2166,7 +2202,7 @@ export function getDecorator(name: string, decorators: Decorator[] | null): Deco
/** Tests if a specific decorator is present within the specified decorators. */ /** Tests if a specific decorator is present within the specified decorators. */
export function hasDecorator(name: string, decorators: Decorator[] | null): bool { export function hasDecorator(name: string, decorators: Decorator[] | null): bool {
return getDecorator(name, decorators) != null; return getFirstDecorator(name, decorators) != null;
} }
/** Serializes the specified node to its TypeScript representation. */ /** Serializes the specified node to its TypeScript representation. */

View File

@ -38,11 +38,17 @@ import {
TypeNode, TypeNode,
TypeParameter, TypeParameter,
Decorator, Decorator,
DecoratorKind,
Expression, Expression,
ElementAccessExpression,
IdentifierExpression, IdentifierExpression,
LiteralExpression,
LiteralKind,
PropertyAccessExpression, PropertyAccessExpression,
StringLiteralExpression, StringLiteralExpression,
SuperExpression,
ThisExpression,
CallExpression, CallExpression,
NewExpression, NewExpression,
@ -68,9 +74,7 @@ import {
hasDecorator, hasDecorator,
hasModifier, hasModifier,
mangleInternalName, mangleInternalName,
ElementAccessExpression, getFirstDecorator
ThisExpression,
SuperExpression
} from "./ast"; } from "./ast";
import { import {
@ -354,6 +358,7 @@ export class Program extends DiagnosticEmitter {
private initializeMethod(declaration: MethodDeclaration, classPrototype: ClassPrototype): void { private initializeMethod(declaration: MethodDeclaration, classPrototype: ClassPrototype): void {
var name = declaration.name.name; var name = declaration.name.name;
var internalName = declaration.internalName; var internalName = declaration.internalName;
var instancePrototype: FunctionPrototype | null = null;
// static methods become global functions // static methods become global functions
if (hasModifier(ModifierKind.STATIC, declaration.modifiers)) { if (hasModifier(ModifierKind.STATIC, declaration.modifiers)) {
@ -382,12 +387,56 @@ export class Program extends DiagnosticEmitter {
} }
} else } else
classPrototype.instanceMembers = new Map(); classPrototype.instanceMembers = new Map();
var instancePrototype = new FunctionPrototype(this, name, internalName, declaration, classPrototype); instancePrototype = new FunctionPrototype(this, name, internalName, declaration, classPrototype);
// if (classPrototype.isStruct && instancePrototype.isAbstract) { // if (classPrototype.isStruct && instancePrototype.isAbstract) {
// this.error( Structs cannot declare abstract methods. ); // this.error( Structs cannot declare abstract methods. );
// } // }
classPrototype.instanceMembers.set(name, instancePrototype); classPrototype.instanceMembers.set(name, instancePrototype);
} }
// handle operator annotations. operators are instance methods taking a second argument of the
// instance's type. return values vary depending on the operation.
if (declaration.decorators) {
for (var i = 0, k = declaration.decorators.length; i < k; ++i) {
var decorator = declaration.decorators[i];
if (decorator.decoratorKind == DecoratorKind.OPERATOR) {
if (!instancePrototype) {
this.error(DiagnosticCode.Operation_not_supported, decorator.range);
continue;
}
var numArgs = decorator.arguments && decorator.arguments.length || 0;
if (numArgs == 1) {
var firstArg = (<Expression[]>decorator.arguments)[0];
if (firstArg.kind == NodeKind.LITERAL && (<LiteralExpression>firstArg).literalKind == LiteralKind.STRING) {
switch ((<StringLiteralExpression>firstArg).value) {
case "[]":
classPrototype.opIndexedGet = instancePrototype;
break;
case "[]=":
classPrototype.opIndexedSet = instancePrototype;
break;
case "+":
classPrototype.opConcat = instancePrototype;
break;
case "==":
classPrototype.opEquals = instancePrototype;
break;
default: // TBD: does it make sense to provide more, even though not JS/TS-compatible?
this.error(DiagnosticCode.Operation_not_supported, firstArg.range);
}
} else
this.error(DiagnosticCode.String_literal_expected, firstArg.range);
} else
this.error(DiagnosticCode.Expected_0_arguments_but_got_1, decorator.range, "1", numArgs.toString(0));
} else if (decorator.decoratorKind != DecoratorKind.CUSTOM) // methods support @operator only
this.error(DiagnosticCode.Operation_not_supported, decorator.range);
}
}
} }
private initializeAccessor(declaration: MethodDeclaration, classPrototype: ClassPrototype, isGetter: bool): void { private initializeAccessor(declaration: MethodDeclaration, classPrototype: ClassPrototype, isGetter: bool): void {
@ -1449,6 +1498,7 @@ export class FunctionPrototype extends Element {
} }
// resolve parameters // resolve parameters
// TODO: 'this' type
k = declaration.parameters.length; k = declaration.parameters.length;
var parameters = new Array<Parameter>(k); var parameters = new Array<Parameter>(k);
var parameterTypes = new Array<Type>(k); var parameterTypes = new Array<Type>(k);
@ -1466,6 +1516,7 @@ export class FunctionPrototype extends Element {
} }
// resolve return type // resolve return type
// TODO: 'this' type
var returnType: Type; var returnType: Type;
if (this.isSetter) { if (this.isSetter) {
returnType = Type.void; // not annotated returnType = Type.void; // not annotated
@ -1770,6 +1821,15 @@ export class ClassPrototype extends Element {
/** Instance member prototypes. */ /** Instance member prototypes. */
instanceMembers: Map<string,Element> | null = null; instanceMembers: Map<string,Element> | null = null;
/** Overloaded indexed get method, if any. */
opIndexedGet: FunctionPrototype | null;
/** Overloaded indexed set method, if any. */
opIndexedSet: FunctionPrototype | null;
/** Overloaded concatenation method, if any. */
opConcat: FunctionPrototype | null;
/** Overloaded equality comparison method, if any. */
opEquals: FunctionPrototype | 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);
if (this.declaration = declaration) { if (this.declaration = declaration) {

View File

@ -18,13 +18,13 @@ export class Array<T> {
} }
@operator("[]") @operator("[]")
get(index: i32): T { private __get(index: i32): T {
assert(index > 0 && index < this.capacity); assert(index > 0 && index < this.capacity);
throw new Error("not implemented"); throw new Error("not implemented");
} }
@operator("[]=") @operator("[]=")
set(index: i32, value: T): void { private __set(index: i32, value: T): void {
assert(index > 0 && index < this.capacity); assert(index > 0 && index < this.capacity);
throw new Error("not implemented"); throw new Error("not implemented");
} }
@ -46,12 +46,12 @@ export class CArray<T> {
private constructor() {} private constructor() {}
@operator("[]") @operator("[]")
get(index: usize): T { private __get(index: usize): T {
return load<T>(changetype<usize>(this) + index * sizeof<T>()); return load<T>(changetype<usize>(this) + index * sizeof<T>());
} }
@operator("[]=") @operator("[]=")
set(index: usize, value: T): void { private __set(index: usize, value: T): void {
store<T>(changetype<usize>(this) + index * sizeof<T>(), value); store<T>(changetype<usize>(this) + index * sizeof<T>(), value);
} }
} }

View File

@ -13,6 +13,7 @@ export class String {
this.length = lenght; this.length = lenght;
} }
@operator("[]")
charAt(pos: i32): String { charAt(pos: i32): String {
assert(this != null); assert(this != null);
return pos < 0 || pos >= this.length ? EMPTY return pos < 0 || pos >= this.length ? EMPTY
@ -39,7 +40,7 @@ export class String {
} }
@operator("+") @operator("+")
concat(other: String): String { concat(other: this): String {
assert(this != null); assert(this != null);
assert(other != null); assert(other != null);
var thisLen: isize = this.length; var thisLen: isize = this.length;
@ -59,7 +60,7 @@ export class String {
); );
} }
endsWith(searchString: String, endPosition: i32 = 0x7fffffff): bool { endsWith(searchString: this, endPosition: i32 = 0x7fffffff): bool {
assert(this != null); assert(this != null);
assert(searchString != null); assert(searchString != null);
var end: isize = <isize>min<i32>(max<i32>(endPosition, 0), this.length); var end: isize = <isize>min<i32>(max<i32>(endPosition, 0), this.length);
@ -71,18 +72,18 @@ export class String {
} }
@operator("==") @operator("==")
equals(other: String): bool { private __eq(other: this): bool {
assert(this != null); assert(this != null);
assert(other != null); assert(other != null);
return this.length != other.length ? false return this.length != other.length ? false
: !Heap.compare(this.ptr, other.ptr, <usize>this.length); : !Heap.compare(this.ptr, other.ptr, <usize>this.length);
} }
includes(searchString: String, position: i32 = 0): bool { includes(searchString: this, position: i32 = 0): bool {
return this.indexOf(searchString, position) != -1; return this.indexOf(searchString, position) != -1;
} }
indexOf(searchString: String, position: i32 = 0): i32 { indexOf(searchString: this, position: i32 = 0): i32 {
assert(this != null); assert(this != null);
assert(searchString != null); assert(searchString != null);
var pos: isize = position; var pos: isize = position;
@ -95,7 +96,7 @@ export class String {
return -1; return -1;
} }
startsWith(searchString: String, position: i32 = 0): bool { startsWith(searchString: this, position: i32 = 0): bool {
assert(this != null); assert(this != null);
assert(searchString != null); assert(searchString != null);
var pos: isize = position; var pos: isize = position;

View File

@ -34,8 +34,8 @@
(global $showcase/AnEnum.TWO i32 (i32.const 2)) (global $showcase/AnEnum.TWO i32 (i32.const 2))
(global $showcase/AnEnum.FOUR i32 (i32.const 4)) (global $showcase/AnEnum.FOUR i32 (i32.const 4))
(global $showcase/AnEnum.FIVE i32 (i32.const 5)) (global $showcase/AnEnum.FIVE i32 (i32.const 5))
(global $showcase/AnEnum.FOURTYTWO (mut i32) (i32.const 0)) (global $showcase/AnEnum.FORTYTWO (mut i32) (i32.const 0))
(global $showcase/AnEnum.FOURTYTHREE (mut i32) (i32.const 0)) (global $showcase/AnEnum.FORTYTHREE (mut i32) (i32.const 0))
(global $memcpy/dest (mut i32) (i32.const 0)) (global $memcpy/dest (mut i32) (i32.const 0))
(global $showcase/aClassInstance (mut i32) (i32.const 8)) (global $showcase/aClassInstance (mut i32) (i32.const 8))
(global $showcase/AClass.aStaticField (mut i32) (i32.const 0)) (global $showcase/AClass.aStaticField (mut i32) (i32.const 0))
@ -3944,25 +3944,25 @@
) )
(unreachable) (unreachable)
) )
(set_global $showcase/AnEnum.FOURTYTWO (set_global $showcase/AnEnum.FORTYTWO
(get_global $showcase/aMutableGlobal) (get_global $showcase/aMutableGlobal)
) )
(set_global $showcase/AnEnum.FOURTYTHREE (set_global $showcase/AnEnum.FORTYTHREE
(i32.add (i32.add
(get_global $showcase/AnEnum.FOURTYTWO) (get_global $showcase/AnEnum.FORTYTWO)
(i32.const 1) (i32.const 1)
) )
) )
(if (if
(i32.ne (i32.ne
(get_global $showcase/AnEnum.FOURTYTWO) (get_global $showcase/AnEnum.FORTYTWO)
(i32.const 42) (i32.const 42)
) )
(unreachable) (unreachable)
) )
(if (if
(i32.ne (i32.ne
(get_global $showcase/AnEnum.FOURTYTHREE) (get_global $showcase/AnEnum.FORTYTHREE)
(i32.const 43) (i32.const 43)
) )
(unreachable) (unreachable)

View File

@ -34,8 +34,8 @@
(global $showcase/AnEnum.TWO i32 (i32.const 2)) (global $showcase/AnEnum.TWO i32 (i32.const 2))
(global $showcase/AnEnum.FOUR i32 (i32.const 4)) (global $showcase/AnEnum.FOUR i32 (i32.const 4))
(global $showcase/AnEnum.FIVE i32 (i32.const 5)) (global $showcase/AnEnum.FIVE i32 (i32.const 5))
(global $showcase/AnEnum.FOURTYTWO (mut i32) (i32.const 0)) (global $showcase/AnEnum.FORTYTWO (mut i32) (i32.const 0))
(global $showcase/AnEnum.FOURTYTHREE (mut i32) (i32.const 0)) (global $showcase/AnEnum.FORTYTHREE (mut i32) (i32.const 0))
(global $memcpy/dest (mut i32) (i32.const 0)) (global $memcpy/dest (mut i32) (i32.const 0))
(global $showcase/aClassInstance (mut i32) (i32.const 8)) (global $showcase/aClassInstance (mut i32) (i32.const 8))
(global $showcase/AClass.aStaticField (mut i32) (i32.const 0)) (global $showcase/AClass.aStaticField (mut i32) (i32.const 0))
@ -3961,25 +3961,25 @@
) )
(unreachable) (unreachable)
) )
(set_global $showcase/AnEnum.FOURTYTWO (set_global $showcase/AnEnum.FORTYTWO
(get_global $showcase/aMutableGlobal) (get_global $showcase/aMutableGlobal)
) )
(set_global $showcase/AnEnum.FOURTYTHREE (set_global $showcase/AnEnum.FORTYTHREE
(i32.add (i32.add
(get_global $showcase/AnEnum.FOURTYTWO) (get_global $showcase/AnEnum.FORTYTWO)
(i32.const 1) (i32.const 1)
) )
) )
(if (if
(i32.ne (i32.ne
(get_global $showcase/AnEnum.FOURTYTWO) (get_global $showcase/AnEnum.FORTYTWO)
(i32.const 42) (i32.const 42)
) )
(unreachable) (unreachable)
) )
(if (if
(i32.ne (i32.ne
(get_global $showcase/AnEnum.FOURTYTHREE) (get_global $showcase/AnEnum.FORTYTHREE)
(i32.const 43) (i32.const 43)
) )
(unreachable) (unreachable)

View File

@ -48,15 +48,15 @@ export enum AnEnum {
// or be omitted // or be omitted
FOUR = AnEnum.TWO + 2, // or reference other values (and remain constant through precomputation) FOUR = AnEnum.TWO + 2, // or reference other values (and remain constant through precomputation)
FIVE, // and continue from there FIVE, // and continue from there
FOURTYTWO = aMutableGlobal, // or reference mutable values but then can't be exported FORTYTWO = aMutableGlobal, // or reference mutable values but then can't be exported
FOURTYTHREE // and even continue from there without being exported (tsc doesn't allow this) FORTYTHREE // and even continue from there without being exported (tsc doesn't allow this)
} }
assert(AnEnum.ONE == 1); assert(AnEnum.ONE == 1);
assert(AnEnum.TWO == 2); assert(AnEnum.TWO == 2);
assert(AnEnum.FOUR == 4); assert(AnEnum.FOUR == 4);
assert(AnEnum.FIVE == 5); assert(AnEnum.FIVE == 5);
assert(AnEnum.FOURTYTWO == 42); assert(AnEnum.FORTYTWO == 42);
assert(AnEnum.FOURTYTHREE == 43); assert(AnEnum.FORTYTHREE == 43);
// In fact, there are a couple of things asc just waves through where tsc refuses to // In fact, there are a couple of things asc just waves through where tsc refuses to
1, 2, 3; // for example not-so-useful comma expressions 1, 2, 3; // for example not-so-useful comma expressions
@ -94,13 +94,13 @@ class AClass {
aField: i32; aField: i32;
} }
class ADerivedClass extends AClass { class ADerivedClass extends AClass {
aNotherField: f32; anotherField: f32;
get aWildAccessorAppears(): f32 { return this.aNotherField; } get aWildAccessorAppears(): f32 { return this.anotherField; }
set aWildAccessorAppears(val: f32) { this.aNotherField = val; } set aWildAccessorAppears(val: f32) { this.anotherField = val; }
} }
var aClassInstance = changetype<ADerivedClass>(<usize>8); var aClassInstance = changetype<ADerivedClass>(<usize>8);
aClassInstance.aField = 42; aClassInstance.aField = 42;
aClassInstance.aNotherField = 9000; aClassInstance.anotherField = 9000;
assert(load<i32>(8) == 42); assert(load<i32>(8) == 42);
assert(load<f32>(12) == 9000); assert(load<f32>(12) == 9000);

View File

@ -67,8 +67,8 @@
(global $showcase/AnEnum.TWO i32 (i32.const 2)) (global $showcase/AnEnum.TWO i32 (i32.const 2))
(global $showcase/AnEnum.FOUR i32 (i32.const 4)) (global $showcase/AnEnum.FOUR i32 (i32.const 4))
(global $showcase/AnEnum.FIVE i32 (i32.const 5)) (global $showcase/AnEnum.FIVE i32 (i32.const 5))
(global $showcase/AnEnum.FOURTYTWO (mut i32) (i32.const 0)) (global $showcase/AnEnum.FORTYTWO (mut i32) (i32.const 0))
(global $showcase/AnEnum.FOURTYTHREE (mut i32) (i32.const 0)) (global $showcase/AnEnum.FORTYTHREE (mut i32) (i32.const 0))
(global $memcpy/base i32 (i32.const 8)) (global $memcpy/base i32 (i32.const 8))
(global $memcpy/dest (mut i32) (i32.const 0)) (global $memcpy/dest (mut i32) (i32.const 0))
(global $showcase/aClassInstance (mut i32) (i32.const 8)) (global $showcase/aClassInstance (mut i32) (i32.const 8))
@ -5802,12 +5802,12 @@
) )
(unreachable) (unreachable)
) )
(set_global $showcase/AnEnum.FOURTYTWO (set_global $showcase/AnEnum.FORTYTWO
(get_global $showcase/aMutableGlobal) (get_global $showcase/aMutableGlobal)
) )
(set_global $showcase/AnEnum.FOURTYTHREE (set_global $showcase/AnEnum.FORTYTHREE
(i32.add (i32.add
(get_global $showcase/AnEnum.FOURTYTWO) (get_global $showcase/AnEnum.FORTYTWO)
(i32.const 1) (i32.const 1)
) )
) )
@ -5850,7 +5850,7 @@
(if (if
(i32.eqz (i32.eqz
(i32.eq (i32.eq
(get_global $showcase/AnEnum.FOURTYTWO) (get_global $showcase/AnEnum.FORTYTWO)
(i32.const 42) (i32.const 42)
) )
) )
@ -5859,7 +5859,7 @@
(if (if
(i32.eqz (i32.eqz
(i32.eq (i32.eq
(get_global $showcase/AnEnum.FOURTYTHREE) (get_global $showcase/AnEnum.FORTYTHREE)
(i32.const 43) (i32.const 43)
) )
) )