Implement isDefined and isConstant builtins

This commit is contained in:
dcodeIO 2018-07-14 16:42:00 +02:00
parent 10a9f407bf
commit 3b0fd9aac2
12 changed files with 611 additions and 479 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

@ -51,6 +51,11 @@ import {
FlowFlags
} from "./program";
import {
ReportMode
} from "./resolver";
import { CommonFlags } from "./common";
/** Compiles a call to a built-in function. */
export function compileCall(
compiler: Compiler,
@ -128,6 +133,57 @@ export function compileCall(
? module.createI32(1)
: module.createI32(0);
}
case "isDefined": { // isDefined(expression) -> bool
compiler.currentType = Type.bool;
if (operands.length != 1) {
if (typeArguments) {
compiler.error(
DiagnosticCode.Type_0_is_not_generic,
reportNode.range, prototype.internalName
);
}
compiler.error(
DiagnosticCode.Expected_0_arguments_but_got_1,
reportNode.range, "1", operands.length.toString(10)
);
return module.createUnreachable();
}
if (typeArguments) {
compiler.error(
DiagnosticCode.Type_0_is_not_generic,
reportNode.range, prototype.internalName
);
return module.createUnreachable();
}
let element = compiler.resolver.resolveExpression(operands[0], compiler.currentFunction, ReportMode.SWALLOW);
return module.createI32(element ? 1 : 0);
}
case "isConstant": { // isConstant(expression) -> bool
compiler.currentType = Type.bool;
if (operands.length != 1) {
if (typeArguments) {
compiler.error(
DiagnosticCode.Type_0_is_not_generic,
reportNode.range, prototype.internalName
);
}
compiler.error(
DiagnosticCode.Expected_0_arguments_but_got_1,
reportNode.range, "1", operands.length.toString(10)
);
return module.createUnreachable();
}
if (typeArguments) {
compiler.error(
DiagnosticCode.Type_0_is_not_generic,
reportNode.range, prototype.internalName
);
return module.createUnreachable();
}
let expr = compiler.compileExpressionRetainType(operands[0], Type.i32, WrapMode.NONE);
compiler.currentType = Type.bool;
return module.createI32(getExpressionId(expr) == ExpressionId.Const ? 1 : 0);
}
// math

View File

@ -4623,7 +4623,7 @@ export class Compiler extends DiagnosticEmitter {
return this.module.createUnreachable();
}
case ElementKind.CLASS: {
if (resolver.resolvedElementExpression) { // indexed access
if (resolver.currentElementExpression) { // indexed access
let isUnchecked = currentFunction.flow.is(FlowFlags.UNCHECKED_CONTEXT);
let indexedSet = (<Class>target).lookupOverload(OperatorKind.INDEXED_SET, isUnchecked);
if (!indexedSet) {
@ -4736,7 +4736,7 @@ export class Compiler extends DiagnosticEmitter {
);
return module.createUnreachable();
}
let thisExpression = assert(this.resolver.resolvedThisExpression);
let thisExpression = assert(this.resolver.currentThisExpression);
let thisExpr = this.compileExpressionRetainType(
thisExpression,
this.options.usizeType,
@ -4788,7 +4788,7 @@ export class Compiler extends DiagnosticEmitter {
// call just the setter if the return value isn't of interest
if (!tee) {
if (setterInstance.is(CommonFlags.INSTANCE)) {
let thisExpression = assert(this.resolver.resolvedThisExpression);
let thisExpression = assert(this.resolver.currentThisExpression);
let thisExpr = this.compileExpressionRetainType(
thisExpression,
this.options.usizeType,
@ -4808,7 +4808,7 @@ export class Compiler extends DiagnosticEmitter {
let returnType = getterInstance.signature.returnType;
let nativeReturnType = returnType.toNativeType();
if (setterInstance.is(CommonFlags.INSTANCE)) {
let thisExpression = assert(this.resolver.resolvedThisExpression);
let thisExpression = assert(this.resolver.currentThisExpression);
let thisExpr = this.compileExpressionRetainType(
thisExpression,
this.options.usizeType,
@ -4841,7 +4841,7 @@ export class Compiler extends DiagnosticEmitter {
return module.createUnreachable();
}
case ElementKind.CLASS: {
let elementExpression = this.resolver.resolvedElementExpression;
let elementExpression = this.resolver.currentElementExpression;
if (elementExpression) {
let isUnchecked = this.currentFunction.flow.is(FlowFlags.UNCHECKED_CONTEXT);
let indexedGet = (<Class>target).lookupOverload(OperatorKind.INDEXED_GET, isUnchecked);
@ -4862,7 +4862,7 @@ export class Compiler extends DiagnosticEmitter {
return module.createUnreachable();
}
let targetType = (<Class>target).type;
let thisExpression = assert(this.resolver.resolvedThisExpression);
let thisExpression = assert(this.resolver.currentThisExpression);
let thisExpr = this.compileExpressionRetainType(
thisExpression,
this.options.usizeType,
@ -5039,7 +5039,7 @@ export class Compiler extends DiagnosticEmitter {
let thisExpr: ExpressionRef = 0;
if (instance.is(CommonFlags.INSTANCE)) {
thisExpr = this.compileExpressionRetainType(
assert(this.resolver.resolvedThisExpression),
assert(this.resolver.currentThisExpression),
this.options.usizeType,
WrapMode.NONE
);
@ -5082,7 +5082,7 @@ export class Compiler extends DiagnosticEmitter {
case ElementKind.FIELD: {
let type = (<Field>target).type;
if (signature = type.signatureReference) {
let thisExpression = assert(this.resolver.resolvedThisExpression);
let thisExpression = assert(this.resolver.currentThisExpression);
let thisExpr = this.compileExpressionRetainType(
thisExpression,
this.options.usizeType,
@ -6598,7 +6598,7 @@ export class Compiler extends DiagnosticEmitter {
return module.createGetGlobal((<EnumValue>target).internalName, NativeType.I32);
}
case ElementKind.FIELD: { // instance field
let thisExpression = assert(this.resolver.resolvedThisExpression);
let thisExpression = assert(this.resolver.currentThisExpression);
assert((<Field>target).memoryOffset >= 0);
let thisExpr = this.compileExpressionRetainType(
thisExpression,
@ -6632,7 +6632,7 @@ export class Compiler extends DiagnosticEmitter {
if (instance.is(CommonFlags.INSTANCE)) {
let parent = assert(instance.parent);
assert(parent.kind == ElementKind.CLASS);
let thisExpression = assert(this.resolver.resolvedThisExpression);
let thisExpression = assert(this.resolver.currentThisExpression);
let thisExpr = this.compileExpressionRetainType(
thisExpression,
this.options.usizeType,

View File

@ -60,7 +60,7 @@ import {
} from "./common";
/** Indicates whether errors are reported or not. */
enum ReportMode {
export enum ReportMode {
/** Report errors. */
REPORT,
/** Swallow errors. */
@ -72,10 +72,11 @@ export class Resolver extends DiagnosticEmitter {
/** The program this resolver belongs to. */
program: Program;
/** Target expression of the previously resolved property or element access. */
resolvedThisExpression: Expression | null = null;
currentThisExpression: Expression | null = null;
/** Element expression of the previously resolved element access. */
resolvedElementExpression : Expression | null = null;
currentElementExpression : Expression | null = null;
/** Constructs the resolver for the specified program. */
constructor(program: Program) {
@ -306,8 +307,8 @@ export class Resolver extends DiagnosticEmitter {
case ElementKind.FUNCTION: { // search locals, use prototype
element = (<Function>context).flow.getScopedLocal(name);
if (element) {
this.resolvedThisExpression = null;
this.resolvedElementExpression = null;
this.currentThisExpression = null;
this.currentElementExpression = null;
return element;
}
context = (<Function>context).prototype.parent;
@ -324,8 +325,8 @@ export class Resolver extends DiagnosticEmitter {
let members = context.members;
if (members) {
if (element = members.get(name)) {
this.resolvedThisExpression = null;
this.resolvedElementExpression = null;
this.currentThisExpression = null;
this.currentElementExpression = null;
return element;
}
}
@ -336,15 +337,15 @@ export class Resolver extends DiagnosticEmitter {
// search current file
var elementsLookup = this.program.elementsLookup;
if (element = elementsLookup.get(identifier.range.source.internalPath + PATH_DELIMITER + name)) {
this.resolvedThisExpression = null;
this.resolvedElementExpression = null;
this.currentThisExpression = null;
this.currentElementExpression = null;
return element; // GLOBAL, FUNCTION_PROTOTYPE, CLASS_PROTOTYPE
}
// search global scope
if (element = elementsLookup.get(name)) {
this.resolvedThisExpression = null;
this.resolvedElementExpression = null;
this.currentThisExpression = null;
this.currentElementExpression = null;
return element; // GLOBAL, FUNCTION_PROTOTYPE, CLASS_PROTOTYPE
}
@ -407,7 +408,7 @@ export class Resolver extends DiagnosticEmitter {
break;
}
case ElementKind.CLASS: {
let elementExpression = this.resolvedElementExpression;
let elementExpression = this.currentElementExpression;
if (elementExpression) {
let indexedGet = (<Class>target).lookupOverload(OperatorKind.INDEXED_GET);
if (!indexedGet) {
@ -438,8 +439,8 @@ export class Resolver extends DiagnosticEmitter {
let members = target.members;
let member: Element | null;
if (members && (member = members.get(propertyName))) {
this.resolvedThisExpression = targetExpression;
this.resolvedElementExpression = null;
this.currentThisExpression = targetExpression;
this.currentElementExpression = null;
return member; // instance FIELD, static GLOBAL, FUNCTION_PROTOTYPE...
}
// traverse inherited static members on the base prototype if target is a class prototype
@ -467,8 +468,8 @@ export class Resolver extends DiagnosticEmitter {
if (members) {
let member = members.get(propertyName);
if (member) {
this.resolvedThisExpression = targetExpression;
this.resolvedElementExpression = null;
this.currentThisExpression = targetExpression;
this.currentElementExpression = null;
return member; // static ENUMVALUE, static GLOBAL, static FUNCTION_PROTOTYPE...
}
}
@ -496,8 +497,8 @@ export class Resolver extends DiagnosticEmitter {
case ElementKind.FIELD: {
let type = (<VariableLikeElement>target).type;
if (target = type.classReference) {
this.resolvedThisExpression = targetExpression;
this.resolvedElementExpression = elementAccess.elementExpression;
this.currentThisExpression = targetExpression;
this.currentElementExpression = elementAccess.elementExpression;
return target;
}
break;
@ -515,8 +516,8 @@ export class Resolver extends DiagnosticEmitter {
}
let returnType = indexedGet.signature.returnType;
if (target = returnType.classReference) {
this.resolvedThisExpression = targetExpression;
this.resolvedElementExpression = elementAccess.elementExpression;
this.currentThisExpression = targetExpression;
this.currentElementExpression = elementAccess.elementExpression;
return target;
}
break;
@ -549,8 +550,8 @@ export class Resolver extends DiagnosticEmitter {
if (type) {
let classType = type.classReference;
if (classType) {
this.resolvedThisExpression = null;
this.resolvedElementExpression = null;
this.currentThisExpression = null;
this.currentElementExpression = null;
return classType;
}
}
@ -563,15 +564,15 @@ export class Resolver extends DiagnosticEmitter {
if (contextualFunction.flow.is(FlowFlags.INLINE_CONTEXT)) {
let explicitLocal = contextualFunction.flow.getScopedLocal("this");
if (explicitLocal) {
this.resolvedThisExpression = null;
this.resolvedElementExpression = null;
this.currentThisExpression = null;
this.currentElementExpression = null;
return explicitLocal;
}
}
let parent = contextualFunction.parent;
if (parent) {
this.resolvedThisExpression = null;
this.resolvedElementExpression = null;
this.currentThisExpression = null;
this.currentElementExpression = null;
return parent;
}
if (reportMode == ReportMode.REPORT) {
@ -586,15 +587,15 @@ export class Resolver extends DiagnosticEmitter {
if (contextualFunction.flow.is(FlowFlags.INLINE_CONTEXT)) {
let explicitLocal = contextualFunction.flow.getScopedLocal("super");
if (explicitLocal) {
this.resolvedThisExpression = null;
this.resolvedElementExpression = null;
this.currentThisExpression = null;
this.currentElementExpression = null;
return explicitLocal;
}
}
let parent = contextualFunction.parent;
if (parent && parent.kind == ElementKind.CLASS && (parent = (<Class>parent).base)) {
this.resolvedThisExpression = null;
this.resolvedElementExpression = null;
this.currentThisExpression = null;
this.currentElementExpression = null;
return parent;
}
if (reportMode == ReportMode.REPORT) {
@ -611,8 +612,8 @@ export class Resolver extends DiagnosticEmitter {
case NodeKind.LITERAL: {
switch ((<LiteralExpression>expression).literalKind) {
case LiteralKind.STRING: {
this.resolvedThisExpression = expression;
this.resolvedElementExpression = null;
this.currentThisExpression = expression;
this.currentElementExpression = null;
return this.program.stringInstance;
}
// case LiteralKind.ARRAY: // TODO

View File

@ -10,6 +10,10 @@ export declare function isString<T>(value?: T): bool;
export declare function isArray<T>(value?: T): bool;
export declare function isDefined(expression: void): bool;
export declare function isConstant(expression: void): bool;
export const NaN: f64 = 0 / 0;
export const Infinity: f64 = 1 / 0;

View File

@ -304,6 +304,10 @@ declare function isReference<T>(value?: any): value is object | string;
declare function isString<T>(value?: any): value is string | String;
/** Tests if the specified type *or* expression can be used as an array. Compiles to a constant. */
declare function isArray<T>(value?: any): value is Array<any>;
/** Tests if the specified expression resolves to a defined element. */
declare function isDefined(expression: any): bool;
/** Tests if the specified expression evaluates to a constant value. */
declare function isConstant(expression: any): bool;
/** Traps if the specified value is not true-ish, otherwise returns the (non-nullable) value. */
declare function assert<T>(isTrueish: T, message?: string): T & object; // any better way to model `: T != null`?
/** Parses an integer string to a 64-bit float. */

View File

@ -87,7 +87,7 @@
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 43)
(i32.const 50)
(i32.const 19)
)
(unreachable)
@ -105,7 +105,7 @@
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 44)
(i32.const 51)
(i32.const 20)
)
(unreachable)
@ -123,7 +123,7 @@
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 45)
(i32.const 52)
(i32.const 20)
)
(unreachable)
@ -156,7 +156,7 @@
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 61)
(i32.const 68)
(i32.const 19)
)
(unreachable)
@ -174,7 +174,7 @@
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 62)
(i32.const 69)
(i32.const 20)
)
(unreachable)
@ -192,7 +192,7 @@
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 63)
(i32.const 70)
(i32.const 20)
)
(unreachable)
@ -206,7 +206,7 @@
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 80)
(i32.const 87)
(i32.const 0)
)
(unreachable)
@ -223,7 +223,7 @@
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 81)
(i32.const 88)
(i32.const 0)
)
(unreachable)
@ -240,7 +240,7 @@
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 82)
(i32.const 89)
(i32.const 0)
)
(unreachable)
@ -254,7 +254,7 @@
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 83)
(i32.const 90)
(i32.const 0)
)
(unreachable)
@ -268,7 +268,7 @@
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 84)
(i32.const 91)
(i32.const 0)
)
(unreachable)
@ -282,7 +282,7 @@
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 85)
(i32.const 92)
(i32.const 0)
)
(unreachable)
@ -339,7 +339,7 @@
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 116)
(i32.const 123)
(i32.const 0)
)
(unreachable)
@ -356,7 +356,7 @@
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 117)
(i32.const 124)
(i32.const 0)
)
(unreachable)
@ -373,7 +373,7 @@
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 118)
(i32.const 125)
(i32.const 0)
)
(unreachable)
@ -387,7 +387,7 @@
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 119)
(i32.const 126)
(i32.const 0)
)
(unreachable)
@ -401,7 +401,7 @@
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 120)
(i32.const 127)
(i32.const 0)
)
(unreachable)
@ -415,7 +415,7 @@
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 121)
(i32.const 128)
(i32.const 0)
)
(unreachable)
@ -777,7 +777,7 @@
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 264)
(i32.const 271)
(i32.const 0)
)
(unreachable)
@ -793,7 +793,7 @@
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 265)
(i32.const 272)
(i32.const 0)
)
(unreachable)
@ -807,7 +807,7 @@
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 266)
(i32.const 273)
(i32.const 0)
)
(unreachable)
@ -821,7 +821,7 @@
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 267)
(i32.const 274)
(i32.const 0)
)
(unreachable)
@ -835,7 +835,7 @@
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 268)
(i32.const 275)
(i32.const 0)
)
(unreachable)
@ -849,7 +849,7 @@
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 269)
(i32.const 276)
(i32.const 0)
)
(unreachable)
@ -865,7 +865,7 @@
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 270)
(i32.const 277)
(i32.const 0)
)
(unreachable)
@ -881,7 +881,7 @@
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 271)
(i32.const 278)
(i32.const 0)
)
(unreachable)

View File

@ -22,6 +22,13 @@ assert(!isString(1));
assert(isArray(changetype<i32[]>(null)));
assert(!isArray(changetype<usize>(null)));
// evaluation
assert(isDefined(b));
assert(!isDefined(c));
assert(isConstant(1));
assert(!isConstant(b));
// integers
var i: i32;

File diff suppressed because it is too large Load Diff