mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-04-25 15:12:12 +00:00
Add @operator.binary
, @operator.prefix
, @operator.postfix
decorators for #124
This commit is contained in:
parent
9d25f78fc1
commit
f69bccfe09
2
dist/asc.js
vendored
2
dist/asc.js
vendored
File diff suppressed because one or more lines are too long
2
dist/asc.js.map
vendored
2
dist/asc.js.map
vendored
File diff suppressed because one or more lines are too long
2
dist/assemblyscript.js
vendored
2
dist/assemblyscript.js
vendored
File diff suppressed because one or more lines are too long
2
dist/assemblyscript.js.map
vendored
2
dist/assemblyscript.js.map
vendored
File diff suppressed because one or more lines are too long
83
src/ast.ts
83
src/ast.ts
@ -191,17 +191,15 @@ export abstract class Node {
|
||||
// special
|
||||
|
||||
static createDecorator(
|
||||
expression: Expression,
|
||||
name: Expression,
|
||||
args: Expression[] | null,
|
||||
range: Range
|
||||
): DecoratorNode {
|
||||
var stmt = new DecoratorNode();
|
||||
stmt.range = range;
|
||||
stmt.name = expression; expression.parent = stmt;
|
||||
stmt.name = name; name.parent = stmt;
|
||||
stmt.arguments = args; if (args) setParent(args, stmt);
|
||||
stmt.decoratorKind = expression.kind == NodeKind.IDENTIFIER
|
||||
? stringToDecoratorKind((<IdentifierExpression>expression).text)
|
||||
: DecoratorKind.CUSTOM;
|
||||
stmt.decoratorKind = decoratorNameToKind(name);
|
||||
return stmt;
|
||||
}
|
||||
|
||||
@ -1052,34 +1050,65 @@ export enum DecoratorKind {
|
||||
CUSTOM,
|
||||
GLOBAL,
|
||||
OPERATOR,
|
||||
OPERATOR_BINARY,
|
||||
OPERATOR_PREFIX,
|
||||
OPERATOR_POSTFIX,
|
||||
UNMANAGED,
|
||||
SEALED,
|
||||
INLINE
|
||||
}
|
||||
|
||||
/** Returns the decorator kind represented by the specified string. */
|
||||
export function stringToDecoratorKind(str: string): DecoratorKind {
|
||||
assert(str.length);
|
||||
switch (str.charCodeAt(0)) {
|
||||
case CharCode.g: {
|
||||
if (str == "global") return DecoratorKind.GLOBAL;
|
||||
break;
|
||||
/** Returns the kind of the specified decorator. Defaults to {@link DecoratorKind.CUSTOM}. */
|
||||
export function decoratorNameToKind(name: Expression): DecoratorKind {
|
||||
// @global, @inline, @operator, @sealed, @unmanaged
|
||||
if (name.kind == NodeKind.IDENTIFIER) {
|
||||
let nameStr = (<IdentifierExpression>name).text;
|
||||
assert(nameStr.length);
|
||||
switch (nameStr.charCodeAt(0)) {
|
||||
case CharCode.g: {
|
||||
if (nameStr == "global") return DecoratorKind.GLOBAL;
|
||||
break;
|
||||
}
|
||||
case CharCode.i: {
|
||||
if (nameStr == "inline") return DecoratorKind.INLINE;
|
||||
break;
|
||||
}
|
||||
case CharCode.o: {
|
||||
if (nameStr == "operator") return DecoratorKind.OPERATOR;
|
||||
break;
|
||||
}
|
||||
case CharCode.s: {
|
||||
if (nameStr == "sealed") return DecoratorKind.SEALED;
|
||||
break;
|
||||
}
|
||||
case CharCode.u: {
|
||||
if (nameStr == "unmanaged") return DecoratorKind.UNMANAGED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
case CharCode.i: {
|
||||
if (str == "inline") return DecoratorKind.INLINE;
|
||||
break;
|
||||
}
|
||||
case CharCode.o: {
|
||||
if (str == "operator") return DecoratorKind.OPERATOR;
|
||||
break;
|
||||
}
|
||||
case CharCode.s: {
|
||||
if (str == "sealed") return DecoratorKind.SEALED;
|
||||
break;
|
||||
}
|
||||
case CharCode.u: {
|
||||
if (str == "unmanaged") return DecoratorKind.UNMANAGED;
|
||||
break;
|
||||
} else if (
|
||||
name.kind == NodeKind.PROPERTYACCESS &&
|
||||
(<PropertyAccessExpression>name).expression.kind == NodeKind.IDENTIFIER
|
||||
) {
|
||||
let nameStr = (<IdentifierExpression>(<PropertyAccessExpression>name).expression).text;
|
||||
assert(nameStr.length);
|
||||
let propStr = (<PropertyAccessExpression>name).property.text;
|
||||
assert(propStr.length);
|
||||
// @operator.binary, @operator.prefix, @operator.postfix
|
||||
if (nameStr == "operator") {
|
||||
switch (propStr.charCodeAt(0)) {
|
||||
case CharCode.b: {
|
||||
if (propStr == "binary") return DecoratorKind.OPERATOR_BINARY;
|
||||
break;
|
||||
}
|
||||
case CharCode.p: {
|
||||
switch (propStr) {
|
||||
case "prefix": return DecoratorKind.OPERATOR_PREFIX;
|
||||
case "postfix": return DecoratorKind.OPERATOR_POSTFIX;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return DecoratorKind.CUSTOM;
|
||||
|
@ -3940,7 +3940,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
// check operator overloadd
|
||||
let classReference = leftType.classReference;
|
||||
if (classReference) {
|
||||
let overload = classReference.lookupOverload(OperatorKind.AND);
|
||||
let overload = classReference.lookupOverload(OperatorKind.BITWISE_AND);
|
||||
if (overload) {
|
||||
expr = this.compileBinaryOverload(overload, left, right, expression);
|
||||
break;
|
||||
@ -4029,7 +4029,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
// check operator overload
|
||||
let classReference = leftType.classReference;
|
||||
if (classReference) {
|
||||
let overload = classReference.lookupOverload(OperatorKind.OR);
|
||||
let overload = classReference.lookupOverload(OperatorKind.BITWISE_OR);
|
||||
if (overload) {
|
||||
expr = this.compileBinaryOverload(overload, left, right, expression);
|
||||
break;
|
||||
@ -4121,7 +4121,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
// check operator overload
|
||||
let classReference = leftType.classReference;
|
||||
if (classReference) {
|
||||
let overload = classReference.lookupOverload(OperatorKind.XOR);
|
||||
let overload = classReference.lookupOverload(OperatorKind.BITWISE_XOR);
|
||||
if (overload) {
|
||||
expr = this.compileBinaryOverload(overload, left, right, expression);
|
||||
break;
|
||||
|
218
src/program.ts
218
src/program.ts
@ -64,7 +64,7 @@ import {
|
||||
VariableLikeDeclarationStatement,
|
||||
VariableStatement,
|
||||
|
||||
stringToDecoratorKind
|
||||
decoratorNameToKind
|
||||
} from "./ast";
|
||||
|
||||
import {
|
||||
@ -145,96 +145,122 @@ class TypeAlias {
|
||||
/** Represents the kind of an operator overload. */
|
||||
export enum OperatorKind {
|
||||
INVALID,
|
||||
INDEXED_GET,
|
||||
INDEXED_SET,
|
||||
UNCHECKED_INDEXED_GET,
|
||||
UNCHECKED_INDEXED_SET,
|
||||
ADD,
|
||||
SUB,
|
||||
MUL,
|
||||
DIV,
|
||||
REM,
|
||||
POW,
|
||||
AND,
|
||||
OR,
|
||||
XOR,
|
||||
EQ,
|
||||
NE,
|
||||
GT,
|
||||
GE,
|
||||
LT,
|
||||
LE
|
||||
|
||||
// indexed access
|
||||
INDEXED_GET, // a[]
|
||||
INDEXED_SET, // a[]=b
|
||||
UNCHECKED_INDEXED_GET, // unchecked(a[])
|
||||
UNCHECKED_INDEXED_SET, // unchecked(a[]=b)
|
||||
|
||||
// binary
|
||||
ADD, // a + b
|
||||
SUB, // a - b
|
||||
MUL, // a * b
|
||||
DIV, // a / b
|
||||
REM, // a % b
|
||||
POW, // a ** b
|
||||
BITWISE_AND, // a & b
|
||||
BITWISE_OR, // a | b
|
||||
BITWISE_XOR, // a ^ b
|
||||
BITWISE_SHL, // a << b
|
||||
BITWISE_SHR, // a >> b
|
||||
BITWISE_SHR_U, // a >>> b
|
||||
EQ, // a == b
|
||||
NE, // a != b
|
||||
GT, // a > b
|
||||
GE, // a >= b
|
||||
LT, // a < b
|
||||
LE, // a <= b
|
||||
|
||||
// unary prefix
|
||||
PLUS, // +a
|
||||
MINUS, // -a
|
||||
NOT, // !a
|
||||
BITWISE_NOT, // ~a
|
||||
PREFIX_INC, // ++a
|
||||
PREFIX_DEC, // --a
|
||||
|
||||
// unary postfix
|
||||
POSTFIX_INC, // a++
|
||||
POSTFIX_DEC // a--
|
||||
|
||||
// not overridable:
|
||||
// IDENTITY // a === b
|
||||
// LOGICAL_AND // a && b
|
||||
// LOGICAL_OR // a || b
|
||||
}
|
||||
|
||||
function operatorKindFromString(str: string): OperatorKind {
|
||||
assert(str.length);
|
||||
switch (str.charCodeAt(0)) {
|
||||
/** Returns the operator kind represented by the specified decorator and string argument. */
|
||||
function operatorKindFromDecorator(decoratorKind: DecoratorKind, arg: string): OperatorKind {
|
||||
// TODO: currently handles binary only but some differ if unary prefix or postfix
|
||||
assert(arg.length);
|
||||
switch (arg.charCodeAt(0)) {
|
||||
case CharCode.OPENBRACKET: {
|
||||
switch (str) {
|
||||
switch (arg) {
|
||||
case "[]" : return OperatorKind.INDEXED_GET;
|
||||
case "[]=": return OperatorKind.INDEXED_SET;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CharCode.OPENBRACE: {
|
||||
switch (str) {
|
||||
switch (arg) {
|
||||
case "{}" : return OperatorKind.UNCHECKED_INDEXED_GET;
|
||||
case "{}=": return OperatorKind.UNCHECKED_INDEXED_SET;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CharCode.PLUS: {
|
||||
if (str.length == 1) return OperatorKind.ADD;
|
||||
if (arg.length == 1) return OperatorKind.ADD;
|
||||
break;
|
||||
}
|
||||
case CharCode.MINUS: {
|
||||
if (str.length == 1) return OperatorKind.SUB;
|
||||
if (arg.length == 1) return OperatorKind.SUB;
|
||||
break;
|
||||
}
|
||||
case CharCode.ASTERISK: {
|
||||
switch (str) {
|
||||
switch (arg) {
|
||||
case "*" : return OperatorKind.MUL;
|
||||
case "**": return OperatorKind.POW;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CharCode.SLASH: {
|
||||
if (str.length == 1) return OperatorKind.DIV;
|
||||
if (arg.length == 1) return OperatorKind.DIV;
|
||||
break;
|
||||
}
|
||||
case CharCode.PERCENT: {
|
||||
if (str.length == 1) return OperatorKind.REM;
|
||||
if (arg.length == 1) return OperatorKind.REM;
|
||||
break;
|
||||
}
|
||||
case CharCode.AMPERSAND: {
|
||||
if (str.length == 1) return OperatorKind.AND;
|
||||
if (arg.length == 1) return OperatorKind.BITWISE_AND;
|
||||
break;
|
||||
}
|
||||
case CharCode.BAR: {
|
||||
if (str.length == 1) return OperatorKind.OR;
|
||||
if (arg.length == 1) return OperatorKind.BITWISE_OR;
|
||||
break;
|
||||
}
|
||||
case CharCode.CARET: {
|
||||
if (str.length == 1) return OperatorKind.XOR;
|
||||
if (arg.length == 1) return OperatorKind.BITWISE_XOR;
|
||||
break;
|
||||
}
|
||||
case CharCode.EQUALS: {
|
||||
if (str == "==") return OperatorKind.EQ;
|
||||
if (arg == "==") return OperatorKind.EQ;
|
||||
break;
|
||||
}
|
||||
case CharCode.EXCLAMATION: {
|
||||
if (str == "!=") return OperatorKind.NE;
|
||||
if (arg == "!=") return OperatorKind.NE;
|
||||
break;
|
||||
}
|
||||
case CharCode.GREATERTHAN: {
|
||||
switch (str) {
|
||||
switch (arg) {
|
||||
case ">" : return OperatorKind.GT;
|
||||
case ">=": return OperatorKind.GE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CharCode.LESSTHAN: {
|
||||
switch (str) {
|
||||
switch (arg) {
|
||||
case "<" : return OperatorKind.LT;
|
||||
case "<=": return OperatorKind.LE;
|
||||
}
|
||||
@ -534,24 +560,21 @@ export class Program extends DiagnosticEmitter {
|
||||
var presentFlags = DecoratorFlags.NONE;
|
||||
for (let i = 0, k = decorators.length; i < k; ++i) {
|
||||
let decorator = decorators[i];
|
||||
if (decorator.name.kind == NodeKind.IDENTIFIER) {
|
||||
let name = (<IdentifierExpression>decorator.name).text;
|
||||
let kind = stringToDecoratorKind(name);
|
||||
let flag = decoratorKindToFlag(kind);
|
||||
if (flag) {
|
||||
if (!(acceptedFlags & flag)) {
|
||||
this.error(
|
||||
DiagnosticCode.Decorator_0_is_not_valid_here,
|
||||
decorator.range, name
|
||||
);
|
||||
} else if (presentFlags & flag) {
|
||||
this.error(
|
||||
DiagnosticCode.Duplicate_decorator,
|
||||
decorator.range, name
|
||||
);
|
||||
} else {
|
||||
presentFlags |= flag;
|
||||
}
|
||||
let kind = decoratorNameToKind(decorator.name);
|
||||
let flag = decoratorKindToFlag(kind);
|
||||
if (flag) {
|
||||
if (!(acceptedFlags & flag)) {
|
||||
this.error(
|
||||
DiagnosticCode.Decorator_0_is_not_valid_here,
|
||||
decorator.range, decorator.name.range.toString()
|
||||
);
|
||||
} else if (presentFlags & flag) {
|
||||
this.error(
|
||||
DiagnosticCode.Duplicate_decorator,
|
||||
decorator.range, decorator.name.range.toString()
|
||||
);
|
||||
} else {
|
||||
presentFlags |= flag;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -796,7 +819,7 @@ export class Program extends DiagnosticEmitter {
|
||||
var decoratorFlags = DecoratorFlags.NONE;
|
||||
if (decorators) {
|
||||
decoratorFlags = this.filterDecorators(decorators,
|
||||
DecoratorFlags.OPERATOR |
|
||||
DecoratorFlags.OPERATOR_BINARY |
|
||||
DecoratorFlags.INLINE
|
||||
);
|
||||
}
|
||||
@ -887,50 +910,54 @@ export class Program extends DiagnosticEmitter {
|
||||
prototype: FunctionPrototype,
|
||||
classPrototype: ClassPrototype
|
||||
): void {
|
||||
// handle operator annotations. operators are either instance methods taking
|
||||
// a second argument of the instance's type or static methods taking two
|
||||
// arguments of the instance's type. return values vary depending on the
|
||||
// operation.
|
||||
if (decorators) {
|
||||
for (let i = 0, k = decorators.length; i < k; ++i) {
|
||||
let decorator = decorators[i];
|
||||
if (decorator.decoratorKind == DecoratorKind.OPERATOR) {
|
||||
let numArgs = decorator.arguments && decorator.arguments.length || 0;
|
||||
if (numArgs == 1) {
|
||||
let firstArg = (<Expression[]>decorator.arguments)[0];
|
||||
if (
|
||||
firstArg.kind == NodeKind.LITERAL &&
|
||||
(<LiteralExpression>firstArg).literalKind == LiteralKind.STRING
|
||||
) {
|
||||
let kind = operatorKindFromString((<StringLiteralExpression>firstArg).value);
|
||||
if (kind == OperatorKind.INVALID) {
|
||||
this.error(
|
||||
DiagnosticCode.Operation_not_supported,
|
||||
firstArg.range
|
||||
switch (decorator.decoratorKind) {
|
||||
case DecoratorKind.OPERATOR:
|
||||
case DecoratorKind.OPERATOR_BINARY:
|
||||
case DecoratorKind.OPERATOR_PREFIX:
|
||||
case DecoratorKind.OPERATOR_POSTFIX: {
|
||||
let numArgs = decorator.arguments && decorator.arguments.length || 0;
|
||||
if (numArgs == 1) {
|
||||
let firstArg = (<Expression[]>decorator.arguments)[0];
|
||||
if (
|
||||
firstArg.kind == NodeKind.LITERAL &&
|
||||
(<LiteralExpression>firstArg).literalKind == LiteralKind.STRING
|
||||
) {
|
||||
let kind = operatorKindFromDecorator(
|
||||
decorator.decoratorKind,
|
||||
(<StringLiteralExpression>firstArg).value
|
||||
);
|
||||
} else {
|
||||
let overloads = classPrototype.overloadPrototypes;
|
||||
if (overloads.has(kind)) {
|
||||
if (kind == OperatorKind.INVALID) {
|
||||
this.error(
|
||||
DiagnosticCode.Duplicate_function_implementation,
|
||||
DiagnosticCode.Operation_not_supported,
|
||||
firstArg.range
|
||||
);
|
||||
} else {
|
||||
prototype.operatorKind = kind;
|
||||
overloads.set(kind, prototype);
|
||||
let overloads = classPrototype.overloadPrototypes;
|
||||
if (overloads.has(kind)) {
|
||||
this.error(
|
||||
DiagnosticCode.Duplicate_function_implementation,
|
||||
firstArg.range
|
||||
);
|
||||
} else {
|
||||
prototype.operatorKind = kind;
|
||||
overloads.set(kind, prototype);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.error(
|
||||
DiagnosticCode.String_literal_expected,
|
||||
firstArg.range
|
||||
);
|
||||
}
|
||||
} else {
|
||||
this.error(
|
||||
DiagnosticCode.String_literal_expected,
|
||||
firstArg.range
|
||||
DiagnosticCode.Expected_0_arguments_but_got_1,
|
||||
decorator.range, "1", numArgs.toString(0)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
this.error(
|
||||
DiagnosticCode.Expected_0_arguments_but_got_1,
|
||||
decorator.range, "1", numArgs.toString(0)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2382,20 +2409,27 @@ export enum DecoratorFlags {
|
||||
NONE = 0,
|
||||
/** Is a program global. */
|
||||
GLOBAL = 1 << 0,
|
||||
/** Is an operator overload. */
|
||||
OPERATOR = 1 << 1,
|
||||
/** Is a binary operator overload. */
|
||||
OPERATOR_BINARY = 1 << 1,
|
||||
/** Is a unary prefix operator overload. */
|
||||
OPERATOR_PREFIX = 1 << 2,
|
||||
/** Is a unary postfix operator overload. */
|
||||
OPERATOR_POSTFIX = 1 << 3,
|
||||
/** Is an unmanaged class. */
|
||||
UNMANAGED = 1 << 2,
|
||||
UNMANAGED = 1 << 4,
|
||||
/** Is a sealed class. */
|
||||
SEALED = 1 << 3,
|
||||
SEALED = 1 << 5,
|
||||
/** Is always inlined. */
|
||||
INLINE = 1 << 4
|
||||
INLINE = 1 << 6
|
||||
}
|
||||
|
||||
export function decoratorKindToFlag(kind: DecoratorKind): DecoratorFlags {
|
||||
switch (kind) {
|
||||
case DecoratorKind.GLOBAL: return DecoratorFlags.GLOBAL;
|
||||
case DecoratorKind.OPERATOR: return DecoratorFlags.OPERATOR;
|
||||
case DecoratorKind.OPERATOR:
|
||||
case DecoratorKind.OPERATOR_BINARY: return DecoratorFlags.OPERATOR_BINARY;
|
||||
case DecoratorKind.OPERATOR_PREFIX: return DecoratorFlags.OPERATOR_PREFIX;
|
||||
case DecoratorKind.OPERATOR_POSTFIX: return DecoratorFlags.OPERATOR_POSTFIX;
|
||||
case DecoratorKind.UNMANAGED: return DecoratorFlags.UNMANAGED;
|
||||
case DecoratorKind.SEALED: return DecoratorFlags.SEALED;
|
||||
case DecoratorKind.INLINE: return DecoratorFlags.INLINE;
|
||||
|
10
std/assembly.d.ts
vendored
10
std/assembly.d.ts
vendored
@ -573,8 +573,16 @@ declare const Mathf: IMath<f32>;
|
||||
/** Annotates an element as a program global. */
|
||||
declare function global(target: Function, propertyKey: string, descriptor: any): void;
|
||||
|
||||
/** Annotates a method as an operator overload for the specified `token`. */
|
||||
/** Annotates a method as a binary operator overload for the specified `token`. */
|
||||
declare function operator(token: string): (target: any, propertyKey: string, descriptor: any) => void;
|
||||
declare namespace operator {
|
||||
/** Annotates a method as a binary operator overload for the specified `token`. */
|
||||
export function binary(token: string): (target: any, propertyKey: string, descriptor: any) => void;
|
||||
/** Annotates a method as an unary prefix operator overload for the specified `token`. */
|
||||
export function prefix(token: string): (target: any, propertyKey: string, descriptor: any) => void;
|
||||
/** Annotates a method as an unary postfix operator overload for the specified `token`. */
|
||||
export function postfix(token: string): (target: any, propertyKey: string, descriptor: any) => void;
|
||||
}
|
||||
|
||||
/** Annotates a class as being unmanaged with limited capabilities. */
|
||||
declare function unmanaged(target: Function): any;
|
||||
|
10
tests/parser/decorators.ts
Normal file
10
tests/parser/decorators.ts
Normal file
@ -0,0 +1,10 @@
|
||||
@global
|
||||
@operator("+")
|
||||
@operator.binary("-")
|
||||
@operator.prefix("~")
|
||||
@operator.postfix("++")
|
||||
@unmanaged
|
||||
@sealed
|
||||
@inline
|
||||
@custom
|
||||
function a(): void {}
|
10
tests/parser/decorators.ts.fixture.ts
Normal file
10
tests/parser/decorators.ts.fixture.ts
Normal file
@ -0,0 +1,10 @@
|
||||
@global
|
||||
@operator("+")
|
||||
@operator.binary("-")
|
||||
@operator.prefix("~")
|
||||
@operator.postfix("++")
|
||||
@unmanaged
|
||||
@sealed
|
||||
@inline
|
||||
@custom
|
||||
function a(): void {}
|
Loading…
x
Reference in New Issue
Block a user