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
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;

View File

@ -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;

View File

@ -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
View File

@ -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;

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 {}