I am creating a dialect of ActionScript 3.0 and now have a working verifier, or “symbol solving” verifier. The language upgrades ActionScript 3.0 with few operators, light syntax facilities, numeric enums and way more compile-time semantics.
To test verifying, run:
$ git clone https://github.com/shockscript/sxc
$ cd sxc
$ npm install
$ npm link
$ npm test
There's no proper test unit, but the command npm test runs sxc tests/sources, which includes all sources in sxc/library and few sources in sxc/tests/sources, parses and verifies them. Errors accompanied by the comment // Verify error will occur.
Generic functions are alternatively called multi-functions and do not involve type substitution at compile time:
generic function fn(value):Boolean
generic function fn(value:RegExp):Boolean
generic function fn(value:String):Boolean
Generic classes are final and therefore cannot be extended. The following is an example:
final class A.<T>
{
}
var a = new A.<A.<uint>>
Type substitution only occurs with instantiations of generic classes. It occurs directly with every access on the instantiation. I'm wondering if my substitution with structural types (aka. composed types) is correct: to prevent cycles, it pre-creates type symbols and mutates them later once the further composing types have any type parameter occurrence replaced.
class Type {
/*...*/
replace(params, args) {
return this._replace(params, args)
}
_replace(params, args, w = null, u = null) {
if (this instanceof ClassType) {
if (this.typeParameters) {
const args2 = []
for (const param of this.typeParameters) {
const i = params.indexOf(param)
args2.push(i === -1 ? param : args[i])
}
return this.instantiate(args2)
}
}
else if (this instanceof GI) {
w = w || new Map
let type2 = w.get(this)
if (type2) return type2
type2 = new GI(null, [])
w.set(this, type2)
const args = []
for (const type of this.originArguments)
args.push(type._replace(params, args, w))
return this.origin.instantiate(args, type2)
}
else if (this instanceof TypeParameter) {
const i = params.indexOf(this)
if (i !== -1) return args[i]
}
else if (this instanceof FunctionType) {
w = w || new Map
let type2 = w.get(this)
if (type2) return type2
type2 = new FunctionType
w.set(this, type2)
if (this.params) {
type2.params = []
for (const param of this.params)
type2.params.push(param._replace(params, args, w))
}
if (this.optParams) {
type2.optParams = []
for (const param of this.optParams)
type2.optParams.push(param._replace(params, args, w))
}
if (this.restParam)
type2.restParam = this.restParam._replace(params, args, w)
if (this.returnType)
type2.returnType = this.returnType._replace(params, args, w)
type2.delegate = this.context.statics.ObjectType.delegate
type2.context = this.context
return type2
}
else if (this instanceof TupleType) {
w = w || new Map
let type2 = w.get(this)
if (type2) return type2
type2 = new TupleType([])
w.set(this, type2)
for (const type of this.types)
type2.types.push(type._replace(params, args, w))
type2.delegate = this.context.statics.ObjectType.delegate
type2.context = this.context
return type2
}
else if (this instanceof UnionType) {
w = w || new Map
let type2 = w.get(this)
if (type2) return type2
type2 = new UnionType([])
w.set(this, type2)
u = u || []
u.push(type2)
for (const type of this.types) {
const type3 = type._replace(params, args, w, u)
if (u.indexOf(type3) === -1 && this.types.indexOf(type3) === -1)
type2.types.push(type3)
}
type2.context = this.context
u.pop()
return type2
}
else if (this instanceof NullableType) {
w = w || new Map
let type2 = w.get(this)
if (type2)
return type2
type2 = new NullableType(null)
w.set(this, type2)
let innerType = this.innerType._replace(params, args, w)
if (innerType instanceof NullableType)
if (!innerType.innerType.containsNullValue())
innerType = innerType.innerType
else if (innerType instanceof NonNullableType)
innerType = innerType.innerType
type2.innerType = innerType
type2.delegate = innerType.delegate
type2.context = this.context
return type2
}
else if (this instanceof NonNullableType) {
w = w || new Map
let type2 = w.get(this)
if (type2)
return type2
type2 = new NonNullableType(null)
w.set(this, type2)
let innerType = this.innerType._replace(params, args, w)
if (innerType instanceof NullableType)
if (innerType.innerType.containsNullValue())
innerType = innerType.innerType
else if (innerType instanceof NonNullableType)
innerType = innerType.innerType
type2.innerType = innerType
type2.delegate = innerType.delegate
type2.context = this.context
return type2
}
return this
}
/*...*/
}
The parameters w and u serve to prevent any cycle. w means wrapping types (given by a offending _replace() call) and u means union hierarchy (to prevent union reoccuring with itself).
NullableType of XequalsX, which sounds obviously wrong to me. Where are these tests and examples? \$\endgroup\$VerifyError: Error #1317: Parameter must be of type ?uint(for callback that takes?uint, but explicitly passesuint). A type annotation is not even required to begin with in this case, so it's redundant. \$\endgroup\$pkg install nodejs. After installing ShockScript (entering$ cd shockscript/comp-sx,$ npm installand$ npm link), you can verify all sources undertestsuiteusing the commandcomp-sx --builtins langlib/**/*.sx testsuite/**/*.sx(you must reside incomp-sxdirectory as these source directories are located there). \$\endgroup\$