Add @operator.binary, @operator.prefix, @operator.postfix decorators for #124

This commit is contained in:
dcodeIO 2018-06-01 14:17:27 +02:00
parent 9d25f78fc1
commit f69bccfe09
10 changed files with 218 additions and 127 deletions

2
dist/asc.js vendored

File diff suppressed because one or more lines are too long

2
dist/asc.js.map vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -191,17 +191,15 @@ export abstract class Node {
// special // special
static createDecorator( static createDecorator(
expression: Expression, name: Expression,
args: Expression[] | null, args: Expression[] | null,
range: Range range: Range
): DecoratorNode { ): DecoratorNode {
var stmt = new DecoratorNode(); var stmt = new DecoratorNode();
stmt.range = range; stmt.range = range;
stmt.name = expression; expression.parent = stmt; stmt.name = name; name.parent = stmt;
stmt.arguments = args; if (args) setParent(args, stmt); stmt.arguments = args; if (args) setParent(args, stmt);
stmt.decoratorKind = expression.kind == NodeKind.IDENTIFIER stmt.decoratorKind = decoratorNameToKind(name);
? stringToDecoratorKind((<IdentifierExpression>expression).text)
: DecoratorKind.CUSTOM;
return stmt; return stmt;
} }
@ -1052,34 +1050,65 @@ export enum DecoratorKind {
CUSTOM, CUSTOM,
GLOBAL, GLOBAL,
OPERATOR, OPERATOR,
OPERATOR_BINARY,
OPERATOR_PREFIX,
OPERATOR_POSTFIX,
UNMANAGED, UNMANAGED,
SEALED, SEALED,
INLINE INLINE
} }
/** Returns the decorator kind represented by the specified string. */ /** Returns the kind of the specified decorator. Defaults to {@link DecoratorKind.CUSTOM}. */
export function stringToDecoratorKind(str: string): DecoratorKind { export function decoratorNameToKind(name: Expression): DecoratorKind {
assert(str.length); // @global, @inline, @operator, @sealed, @unmanaged
switch (str.charCodeAt(0)) { if (name.kind == NodeKind.IDENTIFIER) {
case CharCode.g: { let nameStr = (<IdentifierExpression>name).text;
if (str == "global") return DecoratorKind.GLOBAL; assert(nameStr.length);
break; 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: { } else if (
if (str == "inline") return DecoratorKind.INLINE; name.kind == NodeKind.PROPERTYACCESS &&
break; (<PropertyAccessExpression>name).expression.kind == NodeKind.IDENTIFIER
} ) {
case CharCode.o: { let nameStr = (<IdentifierExpression>(<PropertyAccessExpression>name).expression).text;
if (str == "operator") return DecoratorKind.OPERATOR; assert(nameStr.length);
break; let propStr = (<PropertyAccessExpression>name).property.text;
} assert(propStr.length);
case CharCode.s: { // @operator.binary, @operator.prefix, @operator.postfix
if (str == "sealed") return DecoratorKind.SEALED; if (nameStr == "operator") {
break; switch (propStr.charCodeAt(0)) {
} case CharCode.b: {
case CharCode.u: { if (propStr == "binary") return DecoratorKind.OPERATOR_BINARY;
if (str == "unmanaged") return DecoratorKind.UNMANAGED; break;
break; }
case CharCode.p: {
switch (propStr) {
case "prefix": return DecoratorKind.OPERATOR_PREFIX;
case "postfix": return DecoratorKind.OPERATOR_POSTFIX;
}
break;
}
}
} }
} }
return DecoratorKind.CUSTOM; return DecoratorKind.CUSTOM;

View File

@ -3940,7 +3940,7 @@ export class Compiler extends DiagnosticEmitter {
// check operator overloadd // check operator overloadd
let classReference = leftType.classReference; let classReference = leftType.classReference;
if (classReference) { if (classReference) {
let overload = classReference.lookupOverload(OperatorKind.AND); let overload = classReference.lookupOverload(OperatorKind.BITWISE_AND);
if (overload) { if (overload) {
expr = this.compileBinaryOverload(overload, left, right, expression); expr = this.compileBinaryOverload(overload, left, right, expression);
break; break;
@ -4029,7 +4029,7 @@ export class Compiler extends DiagnosticEmitter {
// check operator overload // check operator overload
let classReference = leftType.classReference; let classReference = leftType.classReference;
if (classReference) { if (classReference) {
let overload = classReference.lookupOverload(OperatorKind.OR); let overload = classReference.lookupOverload(OperatorKind.BITWISE_OR);
if (overload) { if (overload) {
expr = this.compileBinaryOverload(overload, left, right, expression); expr = this.compileBinaryOverload(overload, left, right, expression);
break; break;
@ -4121,7 +4121,7 @@ export class Compiler extends DiagnosticEmitter {
// check operator overload // check operator overload
let classReference = leftType.classReference; let classReference = leftType.classReference;
if (classReference) { if (classReference) {
let overload = classReference.lookupOverload(OperatorKind.XOR); let overload = classReference.lookupOverload(OperatorKind.BITWISE_XOR);
if (overload) { if (overload) {
expr = this.compileBinaryOverload(overload, left, right, expression); expr = this.compileBinaryOverload(overload, left, right, expression);
break; break;

View File

@ -64,7 +64,7 @@ import {
VariableLikeDeclarationStatement, VariableLikeDeclarationStatement,
VariableStatement, VariableStatement,
stringToDecoratorKind decoratorNameToKind
} from "./ast"; } from "./ast";
import { import {
@ -145,96 +145,122 @@ class TypeAlias {
/** Represents the kind of an operator overload. */ /** Represents the kind of an operator overload. */
export enum OperatorKind { export enum OperatorKind {
INVALID, INVALID,
INDEXED_GET,
INDEXED_SET, // indexed access
UNCHECKED_INDEXED_GET, INDEXED_GET, // a[]
UNCHECKED_INDEXED_SET, INDEXED_SET, // a[]=b
ADD, UNCHECKED_INDEXED_GET, // unchecked(a[])
SUB, UNCHECKED_INDEXED_SET, // unchecked(a[]=b)
MUL,
DIV, // binary
REM, ADD, // a + b
POW, SUB, // a - b
AND, MUL, // a * b
OR, DIV, // a / b
XOR, REM, // a % b
EQ, POW, // a ** b
NE, BITWISE_AND, // a & b
GT, BITWISE_OR, // a | b
GE, BITWISE_XOR, // a ^ b
LT, BITWISE_SHL, // a << b
LE 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 { /** Returns the operator kind represented by the specified decorator and string argument. */
assert(str.length); function operatorKindFromDecorator(decoratorKind: DecoratorKind, arg: string): OperatorKind {
switch (str.charCodeAt(0)) { // TODO: currently handles binary only but some differ if unary prefix or postfix
assert(arg.length);
switch (arg.charCodeAt(0)) {
case CharCode.OPENBRACKET: { case CharCode.OPENBRACKET: {
switch (str) { switch (arg) {
case "[]" : return OperatorKind.INDEXED_GET; case "[]" : return OperatorKind.INDEXED_GET;
case "[]=": return OperatorKind.INDEXED_SET; case "[]=": return OperatorKind.INDEXED_SET;
} }
break; break;
} }
case CharCode.OPENBRACE: { case CharCode.OPENBRACE: {
switch (str) { switch (arg) {
case "{}" : return OperatorKind.UNCHECKED_INDEXED_GET; case "{}" : return OperatorKind.UNCHECKED_INDEXED_GET;
case "{}=": return OperatorKind.UNCHECKED_INDEXED_SET; case "{}=": return OperatorKind.UNCHECKED_INDEXED_SET;
} }
break; break;
} }
case CharCode.PLUS: { case CharCode.PLUS: {
if (str.length == 1) return OperatorKind.ADD; if (arg.length == 1) return OperatorKind.ADD;
break; break;
} }
case CharCode.MINUS: { case CharCode.MINUS: {
if (str.length == 1) return OperatorKind.SUB; if (arg.length == 1) return OperatorKind.SUB;
break; break;
} }
case CharCode.ASTERISK: { case CharCode.ASTERISK: {
switch (str) { switch (arg) {
case "*" : return OperatorKind.MUL; case "*" : return OperatorKind.MUL;
case "**": return OperatorKind.POW; case "**": return OperatorKind.POW;
} }
break; break;
} }
case CharCode.SLASH: { case CharCode.SLASH: {
if (str.length == 1) return OperatorKind.DIV; if (arg.length == 1) return OperatorKind.DIV;
break; break;
} }
case CharCode.PERCENT: { case CharCode.PERCENT: {
if (str.length == 1) return OperatorKind.REM; if (arg.length == 1) return OperatorKind.REM;
break; break;
} }
case CharCode.AMPERSAND: { case CharCode.AMPERSAND: {
if (str.length == 1) return OperatorKind.AND; if (arg.length == 1) return OperatorKind.BITWISE_AND;
break; break;
} }
case CharCode.BAR: { case CharCode.BAR: {
if (str.length == 1) return OperatorKind.OR; if (arg.length == 1) return OperatorKind.BITWISE_OR;
break; break;
} }
case CharCode.CARET: { case CharCode.CARET: {
if (str.length == 1) return OperatorKind.XOR; if (arg.length == 1) return OperatorKind.BITWISE_XOR;
break; break;
} }
case CharCode.EQUALS: { case CharCode.EQUALS: {
if (str == "==") return OperatorKind.EQ; if (arg == "==") return OperatorKind.EQ;
break; break;
} }
case CharCode.EXCLAMATION: { case CharCode.EXCLAMATION: {
if (str == "!=") return OperatorKind.NE; if (arg == "!=") return OperatorKind.NE;
break; break;
} }
case CharCode.GREATERTHAN: { case CharCode.GREATERTHAN: {
switch (str) { switch (arg) {
case ">" : return OperatorKind.GT; case ">" : return OperatorKind.GT;
case ">=": return OperatorKind.GE; case ">=": return OperatorKind.GE;
} }
break; break;
} }
case CharCode.LESSTHAN: { case CharCode.LESSTHAN: {
switch (str) { switch (arg) {
case "<" : return OperatorKind.LT; case "<" : return OperatorKind.LT;
case "<=": return OperatorKind.LE; case "<=": return OperatorKind.LE;
} }
@ -534,24 +560,21 @@ export class Program extends DiagnosticEmitter {
var presentFlags = DecoratorFlags.NONE; var presentFlags = DecoratorFlags.NONE;
for (let i = 0, k = decorators.length; i < k; ++i) { for (let i = 0, k = decorators.length; i < k; ++i) {
let decorator = decorators[i]; let decorator = decorators[i];
if (decorator.name.kind == NodeKind.IDENTIFIER) { let kind = decoratorNameToKind(decorator.name);
let name = (<IdentifierExpression>decorator.name).text; let flag = decoratorKindToFlag(kind);
let kind = stringToDecoratorKind(name); if (flag) {
let flag = decoratorKindToFlag(kind); if (!(acceptedFlags & flag)) {
if (flag) { this.error(
if (!(acceptedFlags & flag)) { DiagnosticCode.Decorator_0_is_not_valid_here,
this.error( decorator.range, decorator.name.range.toString()
DiagnosticCode.Decorator_0_is_not_valid_here, );
decorator.range, name } else if (presentFlags & flag) {
); this.error(
} else if (presentFlags & flag) { DiagnosticCode.Duplicate_decorator,
this.error( decorator.range, decorator.name.range.toString()
DiagnosticCode.Duplicate_decorator, );
decorator.range, name } else {
); presentFlags |= flag;
} else {
presentFlags |= flag;
}
} }
} }
} }
@ -796,7 +819,7 @@ export class Program extends DiagnosticEmitter {
var decoratorFlags = DecoratorFlags.NONE; var decoratorFlags = DecoratorFlags.NONE;
if (decorators) { if (decorators) {
decoratorFlags = this.filterDecorators(decorators, decoratorFlags = this.filterDecorators(decorators,
DecoratorFlags.OPERATOR | DecoratorFlags.OPERATOR_BINARY |
DecoratorFlags.INLINE DecoratorFlags.INLINE
); );
} }
@ -887,50 +910,54 @@ export class Program extends DiagnosticEmitter {
prototype: FunctionPrototype, prototype: FunctionPrototype,
classPrototype: ClassPrototype classPrototype: ClassPrototype
): void { ): 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) { if (decorators) {
for (let i = 0, k = decorators.length; i < k; ++i) { for (let i = 0, k = decorators.length; i < k; ++i) {
let decorator = decorators[i]; let decorator = decorators[i];
if (decorator.decoratorKind == DecoratorKind.OPERATOR) { switch (decorator.decoratorKind) {
let numArgs = decorator.arguments && decorator.arguments.length || 0; case DecoratorKind.OPERATOR:
if (numArgs == 1) { case DecoratorKind.OPERATOR_BINARY:
let firstArg = (<Expression[]>decorator.arguments)[0]; case DecoratorKind.OPERATOR_PREFIX:
if ( case DecoratorKind.OPERATOR_POSTFIX: {
firstArg.kind == NodeKind.LITERAL && let numArgs = decorator.arguments && decorator.arguments.length || 0;
(<LiteralExpression>firstArg).literalKind == LiteralKind.STRING if (numArgs == 1) {
) { let firstArg = (<Expression[]>decorator.arguments)[0];
let kind = operatorKindFromString((<StringLiteralExpression>firstArg).value); if (
if (kind == OperatorKind.INVALID) { firstArg.kind == NodeKind.LITERAL &&
this.error( (<LiteralExpression>firstArg).literalKind == LiteralKind.STRING
DiagnosticCode.Operation_not_supported, ) {
firstArg.range let kind = operatorKindFromDecorator(
decorator.decoratorKind,
(<StringLiteralExpression>firstArg).value
); );
} else { if (kind == OperatorKind.INVALID) {
let overloads = classPrototype.overloadPrototypes;
if (overloads.has(kind)) {
this.error( this.error(
DiagnosticCode.Duplicate_function_implementation, DiagnosticCode.Operation_not_supported,
firstArg.range firstArg.range
); );
} else { } else {
prototype.operatorKind = kind; let overloads = classPrototype.overloadPrototypes;
overloads.set(kind, prototype); 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 { } else {
this.error( this.error(
DiagnosticCode.String_literal_expected, DiagnosticCode.Expected_0_arguments_but_got_1,
firstArg.range 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, NONE = 0,
/** Is a program global. */ /** Is a program global. */
GLOBAL = 1 << 0, GLOBAL = 1 << 0,
/** Is an operator overload. */ /** Is a binary operator overload. */
OPERATOR = 1 << 1, 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. */ /** Is an unmanaged class. */
UNMANAGED = 1 << 2, UNMANAGED = 1 << 4,
/** Is a sealed class. */ /** Is a sealed class. */
SEALED = 1 << 3, SEALED = 1 << 5,
/** Is always inlined. */ /** Is always inlined. */
INLINE = 1 << 4 INLINE = 1 << 6
} }
export function decoratorKindToFlag(kind: DecoratorKind): DecoratorFlags { export function decoratorKindToFlag(kind: DecoratorKind): DecoratorFlags {
switch (kind) { switch (kind) {
case DecoratorKind.GLOBAL: return DecoratorFlags.GLOBAL; 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.UNMANAGED: return DecoratorFlags.UNMANAGED;
case DecoratorKind.SEALED: return DecoratorFlags.SEALED; case DecoratorKind.SEALED: return DecoratorFlags.SEALED;
case DecoratorKind.INLINE: return DecoratorFlags.INLINE; case DecoratorKind.INLINE: return DecoratorFlags.INLINE;

10
std/assembly.d.ts vendored
View File

@ -573,8 +573,16 @@ declare const Mathf: IMath<f32>;
/** Annotates an element as a program global. */ /** Annotates an element as a program global. */
declare function global(target: Function, propertyKey: string, descriptor: any): void; 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 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. */ /** Annotates a class as being unmanaged with limited capabilities. */
declare function unmanaged(target: Function): any; declare function unmanaged(target: Function): any;

View File

@ -0,0 +1,10 @@
@global
@operator("+")
@operator.binary("-")
@operator.prefix("~")
@operator.postfix("++")
@unmanaged
@sealed
@inline
@custom
function a(): void {}

View File

@ -0,0 +1,10 @@
@global
@operator("+")
@operator.binary("-")
@operator.prefix("~")
@operator.postfix("++")
@unmanaged
@sealed
@inline
@custom
function a(): void {}