From 4d5024c4f28e5c2969f7adf42176b80924389829 Mon Sep 17 00:00:00 2001 From: Drew Youngwerth Date: Mon, 23 Sep 2024 16:53:34 -0700 Subject: [PATCH 01/14] Renames --- src/__tests__/fixtures/e2e-file.ts | 4 +- src/assembler.ts | 6 +-- .../__snapshots__/parser.test.ts.snap | 2 +- src/parser/__tests__/fixtures/voyd-file.ts | 2 +- src/semantics/init-entities.ts | 12 ++--- src/semantics/resolution/get-call-fn.ts | 8 ++-- src/semantics/resolution/get-expr-type.ts | 4 +- .../resolution/resolve-call-types.ts | 6 +-- src/semantics/resolution/resolve-fn-type.ts | 4 +- src/semantics/resolution/resolve-impl.ts | 4 +- .../resolution/resolve-intersection.ts | 2 +- src/semantics/resolution/resolve-match.ts | 4 +- .../resolution/resolve-object-type.ts | 7 +-- src/semantics/resolution/resolve-types.ts | 48 +++++++++---------- src/semantics/resolution/resolve-union.ts | 2 +- src/semantics/resolution/resolve-use.ts | 4 +- .../resolution/types-are-compatible.ts | 2 +- src/syntax-objects/syntax.ts | 6 +-- src/syntax-objects/types.ts | 13 +++-- std/array.voyd | 8 ++-- 20 files changed, 72 insertions(+), 76 deletions(-) diff --git a/src/__tests__/fixtures/e2e-file.ts b/src/__tests__/fixtures/e2e-file.ts index c1e3bff..323c9c6 100644 --- a/src/__tests__/fixtures/e2e-file.ts +++ b/src/__tests__/fixtures/e2e-file.ts @@ -91,11 +91,11 @@ type DsArrayI32 = DsArray // Test generic functions, should return 143 pub fn test8() - let arr2 = ds_array_init(10) + let arr2 = new_fixed_array(10) arr2.set(0, 1.5) arr2.get(0) - let arr: DsArrayI32 = ds_array_init(10) + let arr: DsArrayI32 = new_fixed_array(10) arr.set(9, 143) arr.get(9) diff --git a/src/assembler.ts b/src/assembler.ts index 501d4eb..63d4906 100644 --- a/src/assembler.ts +++ b/src/assembler.ts @@ -8,7 +8,7 @@ import { Type, Primitive, ObjectType, - DsArrayType, + FixedArrayType, voydBaseObject, UnionType, IntersectionType, @@ -505,7 +505,7 @@ export const mapBinaryenType = ( if (isPrimitiveId(type, "voyd")) return binaryen.none; if (type.isObjectType()) return buildObjectType(opts, type); if (type.isUnionType()) return buildUnionType(opts, type); - if (type.isDsArrayType()) return buildDsArrayType(opts, type); + if (type.isFixedArrayType()) return buildFixedArrayType(opts, type); if (type.isIntersectionType()) return buildIntersectionType(opts, type); throw new Error(`Unsupported type ${type}`); }; @@ -513,7 +513,7 @@ export const mapBinaryenType = ( const isPrimitiveId = (type: Type, id: Primitive) => type.isPrimitiveType() && type.name.value === id; -const buildDsArrayType = (opts: CompileExprOpts, type: DsArrayType) => { +const buildFixedArrayType = (opts: CompileExprOpts, type: FixedArrayType) => { if (type.binaryenType) return type.binaryenType; const mod = opts.mod; const elemType = mapBinaryenType(opts, type.elemType!); diff --git a/src/parser/__tests__/__snapshots__/parser.test.ts.snap b/src/parser/__tests__/__snapshots__/parser.test.ts.snap index 3a82e3b..b2e92ee 100644 --- a/src/parser/__tests__/__snapshots__/parser.test.ts.snap +++ b/src/parser/__tests__/__snapshots__/parser.test.ts.snap @@ -819,7 +819,7 @@ exports[`parser supports generics 1`] = ` "=", "arr", [ - "ds_array_init", + "new_fixed_array", [ "generics", "i32", diff --git a/src/parser/__tests__/fixtures/voyd-file.ts b/src/parser/__tests__/fixtures/voyd-file.ts index 223f9d3..5df87b2 100644 --- a/src/parser/__tests__/fixtures/voyd-file.ts +++ b/src/parser/__tests__/fixtures/voyd-file.ts @@ -121,7 +121,7 @@ use std::all type DsArrayi32 = DsArray pub fn main() - let arr = ds_array_init(10) + let arr = new_fixed_array(10) arr.set(0, 1) arr.get(0) `; diff --git a/src/semantics/init-entities.ts b/src/semantics/init-entities.ts index cb13d92..fd79903 100644 --- a/src/semantics/init-entities.ts +++ b/src/semantics/init-entities.ts @@ -11,7 +11,7 @@ import { TypeAlias, ObjectType, ObjectLiteral, - DsArrayType, + FixedArrayType, nop, UnionType, IntersectionType, @@ -345,8 +345,8 @@ const initTypeExprEntities = (type?: Expr): Expr | undefined => { return initStructuralObjectType(type); } - if (type.calls("DsArray")) { - return initDsArray(type); + if (type.calls("FixedArray")) { + return initFixedArrayType(type); } if (type.calls("|")) { @@ -360,15 +360,15 @@ const initTypeExprEntities = (type?: Expr): Expr | undefined => { return initCall(type); }; -const initDsArray = (type: List) => { +const initFixedArrayType = (type: List) => { const generics = type.listAt(1); const elemTypeExpr = initTypeExprEntities(generics.at(1)); if (!elemTypeExpr) { - throw new Error("Invalid DsArray type"); + throw new Error("Invalid FixedArray type"); } - return new DsArrayType({ + return new FixedArrayType({ ...type.metadata, elemTypeExpr, name: type.syntaxId.toString(), diff --git a/src/semantics/resolution/get-call-fn.ts b/src/semantics/resolution/get-call-fn.ts index 5f52a68..2484697 100644 --- a/src/semantics/resolution/get-call-fn.ts +++ b/src/semantics/resolution/get-call-fn.ts @@ -1,7 +1,7 @@ import { Call, Expr, Fn } from "../../syntax-objects/index.js"; import { getExprType } from "./get-expr-type.js"; import { typesAreCompatible } from "./types-are-compatible.js"; -import { resolveFnTypes } from "./resolve-fn-type.js"; +import { resolveFn } from "./resolve-fn-type.js"; export const getCallFn = (call: Call): Fn | undefined => { if (isPrimitiveFnCall(call)) return undefined; @@ -42,7 +42,7 @@ const filterCandidates = (call: Call, candidates: Fn[]): Fn[] => return filterCandidateWithGenerics(call, candidate); } - resolveFnTypes(candidate); + resolveFn(candidate); return parametersMatch(candidate, call) && typeArgsMatch(call, candidate) ? candidate : []; @@ -50,7 +50,7 @@ const filterCandidates = (call: Call, candidates: Fn[]): Fn[] => const filterCandidateWithGenerics = (call: Call, candidate: Fn): Fn[] => { // Resolve generics - if (!candidate.genericInstances) resolveFnTypes(candidate, call); + if (!candidate.genericInstances) resolveFn(candidate, call); // Fn not compatible with call if (!candidate.genericInstances?.length) return []; @@ -64,7 +64,7 @@ const filterCandidateWithGenerics = (call: Call, candidate: Fn): Fn[] => { // If no instances, attempt to resolve generics with this call, as a compatible instance // is still possible const beforeLen = candidate.genericInstances.length; - resolveFnTypes(candidate, call); + resolveFn(candidate, call); const afterLen = candidate.genericInstances.length; if (beforeLen === afterLen) { diff --git a/src/semantics/resolution/get-expr-type.ts b/src/semantics/resolution/get-expr-type.ts index 70db29c..2fa3cb8 100644 --- a/src/semantics/resolution/get-expr-type.ts +++ b/src/semantics/resolution/get-expr-type.ts @@ -1,7 +1,7 @@ import { Expr } from "../../syntax-objects/expr.js"; import { Call, Identifier } from "../../syntax-objects/index.js"; import { Type, i32, f32, bool, i64, f64 } from "../../syntax-objects/types.js"; -import { resolveCallTypes } from "./resolve-call-types.js"; +import { resolveCall } from "./resolve-call-types.js"; export const getExprType = (expr?: Expr): Type | undefined => { if (!expr) return; @@ -9,7 +9,7 @@ export const getExprType = (expr?: Expr): Type | undefined => { if (expr.isFloat()) return typeof expr.value === "number" ? f32 : f64; if (expr.isBool()) return bool; if (expr.isIdentifier()) return getIdentifierType(expr); - if (expr.isCall()) return resolveCallTypes(expr)?.type; + if (expr.isCall()) return resolveCall(expr)?.type; if (expr.isFn()) return expr.getType(); if (expr.isTypeAlias()) return expr.type; if (expr.isType()) return expr; diff --git a/src/semantics/resolution/resolve-call-types.ts b/src/semantics/resolution/resolve-call-types.ts index dab9c97..cbf2736 100644 --- a/src/semantics/resolution/resolve-call-types.ts +++ b/src/semantics/resolution/resolve-call-types.ts @@ -3,11 +3,11 @@ import { Identifier, List, nop } from "../../syntax-objects/index.js"; import { dVoid, ObjectType, TypeAlias } from "../../syntax-objects/types.js"; import { getCallFn } from "./get-call-fn.js"; import { getExprType, getIdentifierType } from "./get-expr-type.js"; -import { resolveObjectTypeTypes } from "./resolve-object-type.js"; +import { resolveObjectType } from "./resolve-object-type.js"; import { resolveTypes } from "./resolve-types.js"; import { resolveExport } from "./resolve-use.js"; -export const resolveCallTypes = (call: Call): Call => { +export const resolveCall = (call: Call): Call => { if (call.type) return call; if (call.calls("export")) return resolveExport(call); if (call.calls("if")) return resolveIf(call); @@ -46,7 +46,7 @@ export const checkLabeledArg = (call: Call) => { }; export const resolveObjectInit = (call: Call, type: ObjectType): Call => { - type = resolveObjectTypeTypes(type, call); + type = resolveObjectType(type, call); call.type = type; call.fn = type; return call; diff --git a/src/semantics/resolution/resolve-fn-type.ts b/src/semantics/resolution/resolve-fn-type.ts index cba3ab1..7afc452 100644 --- a/src/semantics/resolution/resolve-fn-type.ts +++ b/src/semantics/resolution/resolve-fn-type.ts @@ -14,7 +14,7 @@ export type ResolveFnTypesOpts = { }; /** Pass call to potentially resolve generics */ -export const resolveFnTypes = (fn: Fn, call?: Call): Fn => { +export const resolveFn = (fn: Fn, call?: Call): Fn => { if (fn.typesResolved) { // Already resolved return fn; @@ -107,7 +107,7 @@ const resolveGenericsWithTypeArgs = (fn: Fn, args: List): Fn => { newFn.registerEntity(type); }); - const resolvedFn = resolveFnTypes(newFn); + const resolvedFn = resolveFn(newFn); fn.registerGenericInstance(resolvedFn); return fn; }; diff --git a/src/semantics/resolution/resolve-impl.ts b/src/semantics/resolution/resolve-impl.ts index 99f3fa4..9fc0530 100644 --- a/src/semantics/resolution/resolve-impl.ts +++ b/src/semantics/resolution/resolve-impl.ts @@ -2,7 +2,7 @@ import { nop } from "../../syntax-objects/helpers.js"; import { Implementation } from "../../syntax-objects/implementation.js"; import { ObjectType, TypeAlias } from "../../syntax-objects/types.js"; import { getExprType } from "./get-expr-type.js"; -import { resolveObjectTypeTypes } from "./resolve-object-type.js"; +import { resolveObjectType } from "./resolve-object-type.js"; import { resolveTypes } from "./resolve-types.js"; export const resolveImpl = ( @@ -57,7 +57,7 @@ const resolveTargetType = (impl: Implementation): ObjectType | undefined => { if (!type || !type.isObjectType()) return; if (type.typeParameters?.length && expr.isCall()) { - const obj = resolveObjectTypeTypes(type, expr); + const obj = resolveObjectType(type, expr); // Object fully resolved to non-generic version i.e. `Vec` if (!obj.typeParameters?.length) return obj; } diff --git a/src/semantics/resolution/resolve-intersection.ts b/src/semantics/resolution/resolve-intersection.ts index 2f43181..9a95506 100644 --- a/src/semantics/resolution/resolve-intersection.ts +++ b/src/semantics/resolution/resolve-intersection.ts @@ -2,7 +2,7 @@ import { IntersectionType } from "../../syntax-objects/types.js"; import { getExprType } from "./get-expr-type.js"; import { resolveTypes } from "./resolve-types.js"; -export const resolveIntersection = ( +export const resolveIntersectionType = ( inter: IntersectionType ): IntersectionType => { inter.nominalTypeExpr.value = resolveTypes(inter.nominalTypeExpr.value); diff --git a/src/semantics/resolution/resolve-match.ts b/src/semantics/resolution/resolve-match.ts index b4ec9ac..f7b3f64 100644 --- a/src/semantics/resolution/resolve-match.ts +++ b/src/semantics/resolution/resolve-match.ts @@ -10,7 +10,7 @@ import { } from "../../syntax-objects/index.js"; import { Match, MatchCase } from "../../syntax-objects/match.js"; import { getExprType } from "./get-expr-type.js"; -import { resolveTypes, resolveVarTypes } from "./resolve-types.js"; +import { resolveTypes, resolveVar } from "./resolve-types.js"; export const resolveMatch = (match: Match): Match => { match.operand = resolveTypes(match.operand); @@ -57,7 +57,7 @@ const resolveCase = ( const getBinding = (match: Match): Parameter | Variable => { if (match.bindVariable) { - return resolveVarTypes(match.bindVariable); + return resolveVar(match.bindVariable); } const binding = match.bindIdentifier.resolve(); diff --git a/src/semantics/resolution/resolve-object-type.ts b/src/semantics/resolution/resolve-object-type.ts index 83ef23c..f85e1e5 100644 --- a/src/semantics/resolution/resolve-object-type.ts +++ b/src/semantics/resolution/resolve-object-type.ts @@ -11,10 +11,7 @@ import { implIsCompatible, resolveImpl } from "./resolve-impl.js"; import { resolveTypes } from "./resolve-types.js"; import { typesAreCompatible } from "./types-are-compatible.js"; -export const resolveObjectTypeTypes = ( - obj: ObjectType, - call?: Call -): ObjectType => { +export const resolveObjectType = (obj: ObjectType, call?: Call): ObjectType => { if (obj.typesResolved) return obj; if (obj.typeParameters) { @@ -77,7 +74,7 @@ const resolveGenericsWithTypeArgs = ( }); if (typesNotResolved) return obj; - const resolvedObj = resolveObjectTypeTypes(newObj); + const resolvedObj = resolveObjectType(newObj); obj.registerGenericInstance(resolvedObj); const implementations = newObj.implementations; diff --git a/src/semantics/resolution/resolve-types.ts b/src/semantics/resolution/resolve-types.ts index 400b4fc..f075f53 100644 --- a/src/semantics/resolution/resolve-types.ts +++ b/src/semantics/resolution/resolve-types.ts @@ -5,20 +5,20 @@ import { List } from "../../syntax-objects/list.js"; import { VoidModule } from "../../syntax-objects/module.js"; import { ObjectLiteral } from "../../syntax-objects/object-literal.js"; import { - DsArrayType, + FixedArrayType, ObjectType, TypeAlias, voydBaseObject, } from "../../syntax-objects/types.js"; import { Variable } from "../../syntax-objects/variable.js"; import { getExprType } from "./get-expr-type.js"; -import { resolveCallTypes } from "./resolve-call-types.js"; -import { resolveFnTypes } from "./resolve-fn-type.js"; +import { resolveCall } from "./resolve-call-types.js"; +import { resolveFn } from "./resolve-fn-type.js"; import { resolveImpl } from "./resolve-impl.js"; -import { resolveIntersection } from "./resolve-intersection.js"; +import { resolveIntersectionType } from "./resolve-intersection.js"; import { resolveMatch } from "./resolve-match.js"; -import { resolveObjectTypeTypes } from "./resolve-object-type.js"; -import { resolveUnion } from "./resolve-union.js"; +import { resolveObjectType } from "./resolve-object-type.js"; +import { resolveUnionType } from "./resolve-union.js"; import { resolveUse } from "./resolve-use.js"; /** @@ -30,31 +30,31 @@ import { resolveUse } from "./resolve-use.js"; */ export const resolveTypes = (expr: Expr | undefined): Expr => { if (!expr) return nop(); - if (expr.isBlock()) return resolveBlockTypes(expr); - if (expr.isCall()) return resolveCallTypes(expr); - if (expr.isFn()) return resolveFnTypes(expr); - if (expr.isVariable()) return resolveVarTypes(expr); - if (expr.isModule()) return resolveModuleTypes(expr); + if (expr.isBlock()) return resolveBlock(expr); + if (expr.isCall()) return resolveCall(expr); + if (expr.isFn()) return resolveFn(expr); + if (expr.isVariable()) return resolveVar(expr); + if (expr.isModule()) return resolveModule(expr); if (expr.isList()) return resolveListTypes(expr); - if (expr.isUse()) return resolveUse(expr, resolveModuleTypes); - if (expr.isObjectType()) return resolveObjectTypeTypes(expr); - if (expr.isDsArrayType()) return resolveDsArrayTypeTypes(expr); - if (expr.isTypeAlias()) return resolveTypeAliasTypes(expr); - if (expr.isObjectLiteral()) return resolveObjectLiteralTypes(expr); + if (expr.isUse()) return resolveUse(expr, resolveModule); + if (expr.isObjectType()) return resolveObjectType(expr); + if (expr.isFixedArrayType()) return resolveFixedArrayType(expr); + if (expr.isTypeAlias()) return resolveTypeAlias(expr); + if (expr.isObjectLiteral()) return resolveObjectLiteral(expr); if (expr.isMatch()) return resolveMatch(expr); if (expr.isImpl()) return resolveImpl(expr); - if (expr.isUnionType()) return resolveUnion(expr); - if (expr.isIntersectionType()) return resolveIntersection(expr); + if (expr.isUnionType()) return resolveUnionType(expr); + if (expr.isIntersectionType()) return resolveIntersectionType(expr); return expr; }; -const resolveBlockTypes = (block: Block): Block => { +const resolveBlock = (block: Block): Block => { block.applyMap(resolveTypes); block.type = getExprType(block.body.at(-1)); return block; }; -export const resolveVarTypes = (variable: Variable): Variable => { +export const resolveVar = (variable: Variable): Variable => { const initializer = resolveTypes(variable.initializer); variable.initializer = initializer; variable.inferredType = getExprType(initializer); @@ -67,7 +67,7 @@ export const resolveVarTypes = (variable: Variable): Variable => { return variable; }; -export const resolveModuleTypes = (mod: VoidModule): VoidModule => { +export const resolveModule = (mod: VoidModule): VoidModule => { if (mod.phase >= 3) return mod; mod.phase = 3; mod.each(resolveTypes); @@ -81,21 +81,21 @@ const resolveListTypes = (list: List) => { return list.map(resolveTypes); }; -const resolveDsArrayTypeTypes = (arr: DsArrayType): DsArrayType => { +const resolveFixedArrayType = (arr: FixedArrayType): FixedArrayType => { arr.elemTypeExpr = resolveTypes(arr.elemTypeExpr); arr.elemType = getExprType(arr.elemTypeExpr); arr.id = `${arr.id}#${arr.elemType?.id}`; return arr; }; -const resolveTypeAliasTypes = (alias: TypeAlias): TypeAlias => { +const resolveTypeAlias = (alias: TypeAlias): TypeAlias => { if (alias.type) return alias; alias.typeExpr = resolveTypes(alias.typeExpr); alias.type = getExprType(alias.typeExpr); return alias; }; -const resolveObjectLiteralTypes = (obj: ObjectLiteral) => { +const resolveObjectLiteral = (obj: ObjectLiteral) => { obj.fields.forEach((field) => { field.initializer = resolveTypes(field.initializer); field.type = getExprType(field.initializer); diff --git a/src/semantics/resolution/resolve-union.ts b/src/semantics/resolution/resolve-union.ts index 11fcf4d..5a8986b 100644 --- a/src/semantics/resolution/resolve-union.ts +++ b/src/semantics/resolution/resolve-union.ts @@ -2,7 +2,7 @@ import { UnionType } from "../../syntax-objects/types.js"; import { getExprType } from "./get-expr-type.js"; import { resolveTypes } from "./resolve-types.js"; -export const resolveUnion = (union: UnionType): UnionType => { +export const resolveUnionType = (union: UnionType): UnionType => { union.childTypeExprs.applyMap((expr) => resolveTypes(expr)); union.types = union.childTypeExprs.toArray().flatMap((expr) => { const type = getExprType(expr); diff --git a/src/semantics/resolution/resolve-use.ts b/src/semantics/resolution/resolve-use.ts index 93dac38..a2c8fd6 100644 --- a/src/semantics/resolution/resolve-use.ts +++ b/src/semantics/resolution/resolve-use.ts @@ -4,7 +4,7 @@ import { List } from "../../syntax-objects/list.js"; import { VoidModule } from "../../syntax-objects/module.js"; import { NamedEntity } from "../../syntax-objects/named-entity.js"; import { Use, UseEntities } from "../../syntax-objects/use.js"; -import { resolveModuleTypes, resolveTypes } from "./resolve-types.js"; +import { resolveModule, resolveTypes } from "./resolve-types.js"; export type ModulePass = (mod: VoidModule) => VoidModule; @@ -146,7 +146,7 @@ export const resolveExport = (call: Call) => { if (!block?.isBlock()) return call; const entities = block.body.map(resolveTypes); - registerExports(call, entities, resolveModuleTypes); + registerExports(call, entities, resolveModule); return call; }; diff --git a/src/semantics/resolution/types-are-compatible.ts b/src/semantics/resolution/types-are-compatible.ts index 5d91b2c..4d218fb 100644 --- a/src/semantics/resolution/types-are-compatible.ts +++ b/src/semantics/resolution/types-are-compatible.ts @@ -48,7 +48,7 @@ export const typesAreCompatible = ( return a.extends(b.nominalType) && typesAreCompatible(a, b.structuralType); } - if (a.isDsArrayType() && b.isDsArrayType()) { + if (a.isFixedArrayType() && b.isFixedArrayType()) { return typesAreCompatible(a.elemType, b.elemType); } diff --git a/src/syntax-objects/syntax.ts b/src/syntax-objects/syntax.ts index e12ddc1..f7a5ac0 100644 --- a/src/syntax-objects/syntax.ts +++ b/src/syntax-objects/syntax.ts @@ -22,7 +22,7 @@ import type { ObjectType, Type, TypeAlias, - DsArrayType, + FixedArrayType, UnionType, IntersectionType, } from "./types.js"; @@ -208,8 +208,8 @@ export abstract class Syntax { return this.isType() && this.kindOfType === "intersection"; } - isDsArrayType(): this is DsArrayType { - return this.isType() && this.kindOfType === "ds-array"; + isFixedArrayType(): this is FixedArrayType { + return this.isType() && this.kindOfType === "fixed-array"; } isPrimitiveType(): this is PrimitiveType { diff --git a/src/syntax-objects/types.ts b/src/syntax-objects/types.ts index a1e33ee..f455362 100644 --- a/src/syntax-objects/types.ts +++ b/src/syntax-objects/types.ts @@ -15,7 +15,7 @@ export type Type = | IntersectionType | ObjectType | TupleType - | DsArrayType + | FixedArrayType | FnType | TypeAlias; @@ -279,9 +279,8 @@ export class ObjectType extends BaseType implements ScopedEntity { } /** Dynamically Sized Array (The raw gc array type) */ -export class DsArrayType extends BaseType { - readonly kindOfType = "ds-array"; - readonly size = Infinity; +export class FixedArrayType extends BaseType { + readonly kindOfType = "fixed-array"; elemTypeExpr: Expr; elemType?: Type; /** Type used for locals, globals, function return type */ @@ -294,15 +293,15 @@ export class DsArrayType extends BaseType { this.elemType = opts.elemType; } - clone(parent?: Expr): DsArrayType { - return new DsArrayType({ + clone(parent?: Expr): FixedArrayType { + return new FixedArrayType({ ...super.getCloneOpts(parent), elemTypeExpr: this.elemTypeExpr.clone(), }); } toJSON(): TypeJSON { - return ["type", ["DsArray", this.elemType]]; + return ["type", ["FixedArray", this.elemType]]; } } diff --git a/std/array.voyd b/std/array.voyd index e2ab1fe..396b18e 100644 --- a/std/array.voyd +++ b/std/array.voyd @@ -10,15 +10,15 @@ macro bin_type_to_heap_type(type) namespace: gc args: [BnrType<($type)>] -pub fn ds_array_init(size: i32) -> DsArray - binaryen_gc_call(arrayNew, [bin_type_to_heap_type(DsArray), size]) +pub fn new_fixed_array(size: i32) -> FixedArray + binaryen_gc_call(arrayNew, [bin_type_to_heap_type(FixedArray), size]) -pub fn get(arr: DsArray, index: i32) -> T +pub fn get(arr: FixedArray, index: i32) -> T binaryen_gc_call( arrayGet, [arr, index, BnrType, BnrConst(false)] ) -pub fn set(arr: DsArray, index: i32, value: T) -> DsArray +pub fn set(arr: FixedArray, index: i32, value: T) -> FixedArray binaryen_gc_call(arraySet, [arr, index, value]) arr From df503eaea00dd74334cd1f53cfae929a00b81283 Mon Sep 17 00:00:00 2001 From: Drew Youngwerth Date: Mon, 23 Sep 2024 16:56:36 -0700 Subject: [PATCH 02/14] Rename resolve types to resolve entities --- src/semantics/index.ts | 4 ++-- src/semantics/resolution/get-call-fn.ts | 2 +- src/semantics/resolution/index.ts | 2 +- src/semantics/resolution/resolve-call-types.ts | 14 +++++++------- .../{resolve-fn-type.ts => resolve-fn.ts} | 8 ++++---- src/semantics/resolution/resolve-impl.ts | 4 ++-- .../resolution/resolve-intersection.ts | 8 +++++--- src/semantics/resolution/resolve-match.ts | 6 +++--- .../resolution/resolve-object-type.ts | 4 ++-- src/semantics/resolution/resolve-types.ts | 18 +++++++++--------- src/semantics/resolution/resolve-union.ts | 4 ++-- src/semantics/resolution/resolve-use.ts | 4 ++-- 12 files changed, 40 insertions(+), 38 deletions(-) rename src/semantics/resolution/{resolve-fn-type.ts => resolve-fn.ts} (94%) diff --git a/src/semantics/index.ts b/src/semantics/index.ts index 33bb892..dbd9b5e 100644 --- a/src/semantics/index.ts +++ b/src/semantics/index.ts @@ -6,13 +6,13 @@ import { registerModules } from "./modules.js"; import { expandRegularMacros } from "./regular-macros.js"; import { ParsedModule } from "../parser/index.js"; import { Expr } from "../syntax-objects/expr.js"; -import { resolveTypes } from "./resolution/resolve-types.js"; +import { resolveEntities } from "./resolution/resolve-types.js"; const semanticPhases: SemanticProcessor[] = [ expandRegularMacros, // Also handles use and module declaration initialization initPrimitiveTypes, initEntities, - resolveTypes, + resolveEntities, checkTypes, ]; diff --git a/src/semantics/resolution/get-call-fn.ts b/src/semantics/resolution/get-call-fn.ts index 2484697..5a67caf 100644 --- a/src/semantics/resolution/get-call-fn.ts +++ b/src/semantics/resolution/get-call-fn.ts @@ -1,7 +1,7 @@ import { Call, Expr, Fn } from "../../syntax-objects/index.js"; import { getExprType } from "./get-expr-type.js"; import { typesAreCompatible } from "./types-are-compatible.js"; -import { resolveFn } from "./resolve-fn-type.js"; +import { resolveFn } from "./resolve-fn.js"; export const getCallFn = (call: Call): Fn | undefined => { if (isPrimitiveFnCall(call)) return undefined; diff --git a/src/semantics/resolution/index.ts b/src/semantics/resolution/index.ts index 07faf44..4659aa6 100644 --- a/src/semantics/resolution/index.ts +++ b/src/semantics/resolution/index.ts @@ -1,3 +1,3 @@ -export { resolveTypes } from "./resolve-types.js"; +export { resolveEntities } from "./resolve-types.js"; export { typesAreCompatible } from "./types-are-compatible.js"; export { resolveModulePath } from "./resolve-use.js"; diff --git a/src/semantics/resolution/resolve-call-types.ts b/src/semantics/resolution/resolve-call-types.ts index cbf2736..be2f371 100644 --- a/src/semantics/resolution/resolve-call-types.ts +++ b/src/semantics/resolution/resolve-call-types.ts @@ -4,7 +4,7 @@ import { dVoid, ObjectType, TypeAlias } from "../../syntax-objects/types.js"; import { getCallFn } from "./get-call-fn.js"; import { getExprType, getIdentifierType } from "./get-expr-type.js"; import { resolveObjectType } from "./resolve-object-type.js"; -import { resolveTypes } from "./resolve-types.js"; +import { resolveEntities } from "./resolve-types.js"; import { resolveExport } from "./resolve-use.js"; export const resolveCall = (call: Call): Call => { @@ -13,7 +13,7 @@ export const resolveCall = (call: Call): Call => { if (call.calls("if")) return resolveIf(call); if (call.calls(":")) return checkLabeledArg(call); if (call.calls("while")) return resolveWhile(call); - call.args = call.args.map(resolveTypes); + call.args = call.args.map(resolveEntities); const memberAccessCall = getMemberAccessCall(call); if (memberAccessCall) return memberAccessCall; @@ -30,7 +30,7 @@ export const resolveCall = (call: Call): Call => { } if (call.typeArgs) { - call.typeArgs = call.typeArgs.map(resolveTypes); + call.typeArgs = call.typeArgs.map(resolveEntities); } call.fn = getCallFn(call); @@ -39,7 +39,7 @@ export const resolveCall = (call: Call): Call => { }; export const checkLabeledArg = (call: Call) => { - call.args = call.args.map(resolveTypes); + call.args = call.args.map(resolveEntities); const expr = call.argAt(1); call.type = getExprType(expr); return call; @@ -68,7 +68,7 @@ export const resolveTypeAlias = (call: Call, type: TypeAlias): Call => { }); } - alias.typeExpr = resolveTypes(alias.typeExpr); + alias.typeExpr = resolveEntities(alias.typeExpr); alias.type = getExprType(alias.typeExpr); call.type = alias.type; call.fn = call.type?.isObjectType() ? call.type : undefined; @@ -112,7 +112,7 @@ const getMemberAccessCall = (call: Call): Call | undefined => { }; export const resolveIf = (call: Call) => { - call.args = call.args.map(resolveTypes); + call.args = call.args.map(resolveEntities); const thenExpr = call.argAt(1); const elseExpr = call.argAt(2); @@ -128,7 +128,7 @@ export const resolveIf = (call: Call) => { }; export const resolveWhile = (call: Call) => { - call.args = call.args.map(resolveTypes); + call.args = call.args.map(resolveEntities); call.type = dVoid; return call; }; diff --git a/src/semantics/resolution/resolve-fn-type.ts b/src/semantics/resolution/resolve-fn.ts similarity index 94% rename from src/semantics/resolution/resolve-fn-type.ts rename to src/semantics/resolution/resolve-fn.ts index 7afc452..353b2e2 100644 --- a/src/semantics/resolution/resolve-fn-type.ts +++ b/src/semantics/resolution/resolve-fn.ts @@ -6,7 +6,7 @@ import { List } from "../../syntax-objects/list.js"; import { Parameter } from "../../syntax-objects/parameter.js"; import { TypeAlias } from "../../syntax-objects/types.js"; import { getExprType } from "./get-expr-type.js"; -import { resolveTypes } from "./resolve-types.js"; +import { resolveEntities } from "./resolve-types.js"; export type ResolveFnTypesOpts = { typeArgs?: List; @@ -32,13 +32,13 @@ export const resolveFn = (fn: Fn, call?: Call): Fn => { resolveParameters(fn.parameters); if (fn.returnTypeExpr) { - fn.returnTypeExpr = resolveTypes(fn.returnTypeExpr); + fn.returnTypeExpr = resolveEntities(fn.returnTypeExpr); fn.annotatedReturnType = getExprType(fn.returnTypeExpr); fn.returnType = fn.annotatedReturnType; } fn.typesResolved = true; - fn.body = resolveTypes(fn.body); + fn.body = resolveEntities(fn.body); fn.inferredReturnType = getExprType(fn.body); fn.returnType = fn.annotatedReturnType ?? fn.inferredReturnType; fn.parentImpl?.registerMethod(fn); // Maybe do this for module when not in an impl @@ -68,7 +68,7 @@ const resolveParameters = (params: Parameter[]) => { throw new Error(`Unable to determine type for ${p}`); } - p.typeExpr = resolveTypes(p.typeExpr); + p.typeExpr = resolveEntities(p.typeExpr); p.type = getExprType(p.typeExpr); }); }; diff --git a/src/semantics/resolution/resolve-impl.ts b/src/semantics/resolution/resolve-impl.ts index 9fc0530..80b4d35 100644 --- a/src/semantics/resolution/resolve-impl.ts +++ b/src/semantics/resolution/resolve-impl.ts @@ -3,7 +3,7 @@ import { Implementation } from "../../syntax-objects/implementation.js"; import { ObjectType, TypeAlias } from "../../syntax-objects/types.js"; import { getExprType } from "./get-expr-type.js"; import { resolveObjectType } from "./resolve-object-type.js"; -import { resolveTypes } from "./resolve-types.js"; +import { resolveEntities } from "./resolve-types.js"; export const resolveImpl = ( impl: Implementation, @@ -41,7 +41,7 @@ export const resolveImpl = ( } impl.typesResolved = true; - impl.body.value = resolveTypes(impl.body.value); + impl.body.value = resolveEntities(impl.body.value); return impl; }; diff --git a/src/semantics/resolution/resolve-intersection.ts b/src/semantics/resolution/resolve-intersection.ts index 9a95506..8514d81 100644 --- a/src/semantics/resolution/resolve-intersection.ts +++ b/src/semantics/resolution/resolve-intersection.ts @@ -1,12 +1,14 @@ import { IntersectionType } from "../../syntax-objects/types.js"; import { getExprType } from "./get-expr-type.js"; -import { resolveTypes } from "./resolve-types.js"; +import { resolveEntities } from "./resolve-types.js"; export const resolveIntersectionType = ( inter: IntersectionType ): IntersectionType => { - inter.nominalTypeExpr.value = resolveTypes(inter.nominalTypeExpr.value); - inter.structuralTypeExpr.value = resolveTypes(inter.structuralTypeExpr.value); + inter.nominalTypeExpr.value = resolveEntities(inter.nominalTypeExpr.value); + inter.structuralTypeExpr.value = resolveEntities( + inter.structuralTypeExpr.value + ); const nominalType = getExprType(inter.nominalTypeExpr.value); const structuralType = getExprType(inter.structuralTypeExpr.value); diff --git a/src/semantics/resolution/resolve-match.ts b/src/semantics/resolution/resolve-match.ts index f7b3f64..7abfc7b 100644 --- a/src/semantics/resolution/resolve-match.ts +++ b/src/semantics/resolution/resolve-match.ts @@ -10,10 +10,10 @@ import { } from "../../syntax-objects/index.js"; import { Match, MatchCase } from "../../syntax-objects/match.js"; import { getExprType } from "./get-expr-type.js"; -import { resolveTypes, resolveVar } from "./resolve-types.js"; +import { resolveEntities, resolveVar } from "./resolve-types.js"; export const resolveMatch = (match: Match): Match => { - match.operand = resolveTypes(match.operand); + match.operand = resolveEntities(match.operand); match.baseType = getExprType(match.operand); const binding = getBinding(match); @@ -46,7 +46,7 @@ const resolveCase = ( // to avoid this. c.expr.registerEntity(localBinding); - const expr = resolveTypes(c.expr) as Call | Block; + const expr = resolveEntities(c.expr) as Call | Block; return { matchType: type?.isObjectType() ? type : undefined, diff --git a/src/semantics/resolution/resolve-object-type.ts b/src/semantics/resolution/resolve-object-type.ts index f85e1e5..6f1bc23 100644 --- a/src/semantics/resolution/resolve-object-type.ts +++ b/src/semantics/resolution/resolve-object-type.ts @@ -8,7 +8,7 @@ import { } from "../../syntax-objects/types.js"; import { getExprType } from "./get-expr-type.js"; import { implIsCompatible, resolveImpl } from "./resolve-impl.js"; -import { resolveTypes } from "./resolve-types.js"; +import { resolveEntities } from "./resolve-types.js"; import { typesAreCompatible } from "./types-are-compatible.js"; export const resolveObjectType = (obj: ObjectType, call?: Call): ObjectType => { @@ -19,7 +19,7 @@ export const resolveObjectType = (obj: ObjectType, call?: Call): ObjectType => { } obj.fields.forEach((field) => { - field.typeExpr = resolveTypes(field.typeExpr); + field.typeExpr = resolveEntities(field.typeExpr); field.type = getExprType(field.typeExpr); }); diff --git a/src/semantics/resolution/resolve-types.ts b/src/semantics/resolution/resolve-types.ts index f075f53..68a5886 100644 --- a/src/semantics/resolution/resolve-types.ts +++ b/src/semantics/resolution/resolve-types.ts @@ -13,7 +13,7 @@ import { import { Variable } from "../../syntax-objects/variable.js"; import { getExprType } from "./get-expr-type.js"; import { resolveCall } from "./resolve-call-types.js"; -import { resolveFn } from "./resolve-fn-type.js"; +import { resolveFn } from "./resolve-fn.js"; import { resolveImpl } from "./resolve-impl.js"; import { resolveIntersectionType } from "./resolve-intersection.js"; import { resolveMatch } from "./resolve-match.js"; @@ -28,7 +28,7 @@ import { resolveUse } from "./resolve-use.js"; * Should probably rename this to resolveEntities and separate type resolution * into a new resolveTypes function that returns Type | undefined */ -export const resolveTypes = (expr: Expr | undefined): Expr => { +export const resolveEntities = (expr: Expr | undefined): Expr => { if (!expr) return nop(); if (expr.isBlock()) return resolveBlock(expr); if (expr.isCall()) return resolveCall(expr); @@ -49,13 +49,13 @@ export const resolveTypes = (expr: Expr | undefined): Expr => { }; const resolveBlock = (block: Block): Block => { - block.applyMap(resolveTypes); + block.applyMap(resolveEntities); block.type = getExprType(block.body.at(-1)); return block; }; export const resolveVar = (variable: Variable): Variable => { - const initializer = resolveTypes(variable.initializer); + const initializer = resolveEntities(variable.initializer); variable.initializer = initializer; variable.inferredType = getExprType(initializer); @@ -70,7 +70,7 @@ export const resolveVar = (variable: Variable): Variable => { export const resolveModule = (mod: VoidModule): VoidModule => { if (mod.phase >= 3) return mod; mod.phase = 3; - mod.each(resolveTypes); + mod.each(resolveEntities); mod.phase = 4; return mod; }; @@ -78,11 +78,11 @@ export const resolveModule = (mod: VoidModule): VoidModule => { const resolveListTypes = (list: List) => { console.log("Unexpected list"); console.log(JSON.stringify(list, undefined, 2)); - return list.map(resolveTypes); + return list.map(resolveEntities); }; const resolveFixedArrayType = (arr: FixedArrayType): FixedArrayType => { - arr.elemTypeExpr = resolveTypes(arr.elemTypeExpr); + arr.elemTypeExpr = resolveEntities(arr.elemTypeExpr); arr.elemType = getExprType(arr.elemTypeExpr); arr.id = `${arr.id}#${arr.elemType?.id}`; return arr; @@ -90,14 +90,14 @@ const resolveFixedArrayType = (arr: FixedArrayType): FixedArrayType => { const resolveTypeAlias = (alias: TypeAlias): TypeAlias => { if (alias.type) return alias; - alias.typeExpr = resolveTypes(alias.typeExpr); + alias.typeExpr = resolveEntities(alias.typeExpr); alias.type = getExprType(alias.typeExpr); return alias; }; const resolveObjectLiteral = (obj: ObjectLiteral) => { obj.fields.forEach((field) => { - field.initializer = resolveTypes(field.initializer); + field.initializer = resolveEntities(field.initializer); field.type = getExprType(field.initializer); return field; }); diff --git a/src/semantics/resolution/resolve-union.ts b/src/semantics/resolution/resolve-union.ts index 5a8986b..012f314 100644 --- a/src/semantics/resolution/resolve-union.ts +++ b/src/semantics/resolution/resolve-union.ts @@ -1,9 +1,9 @@ import { UnionType } from "../../syntax-objects/types.js"; import { getExprType } from "./get-expr-type.js"; -import { resolveTypes } from "./resolve-types.js"; +import { resolveEntities } from "./resolve-types.js"; export const resolveUnionType = (union: UnionType): UnionType => { - union.childTypeExprs.applyMap((expr) => resolveTypes(expr)); + union.childTypeExprs.applyMap((expr) => resolveEntities(expr)); union.types = union.childTypeExprs.toArray().flatMap((expr) => { const type = getExprType(expr); diff --git a/src/semantics/resolution/resolve-use.ts b/src/semantics/resolution/resolve-use.ts index a2c8fd6..26ba95d 100644 --- a/src/semantics/resolution/resolve-use.ts +++ b/src/semantics/resolution/resolve-use.ts @@ -4,7 +4,7 @@ import { List } from "../../syntax-objects/list.js"; import { VoidModule } from "../../syntax-objects/module.js"; import { NamedEntity } from "../../syntax-objects/named-entity.js"; import { Use, UseEntities } from "../../syntax-objects/use.js"; -import { resolveModule, resolveTypes } from "./resolve-types.js"; +import { resolveModule, resolveEntities } from "./resolve-types.js"; export type ModulePass = (mod: VoidModule) => VoidModule; @@ -145,7 +145,7 @@ export const resolveExport = (call: Call) => { const block = call.argAt(0); if (!block?.isBlock()) return call; - const entities = block.body.map(resolveTypes); + const entities = block.body.map(resolveEntities); registerExports(call, entities, resolveModule); return call; From 6fc7f649272dac6c2c1263cb3e0255615de3294a Mon Sep 17 00:00:00 2001 From: Drew Youngwerth Date: Mon, 23 Sep 2024 16:56:55 -0700 Subject: [PATCH 03/14] Rename resolve types file --- src/semantics/index.ts | 2 +- src/semantics/resolution/index.ts | 2 +- src/semantics/resolution/resolve-call-types.ts | 2 +- .../resolution/{resolve-types.ts => resolve-entities.ts} | 0 src/semantics/resolution/resolve-fn.ts | 2 +- src/semantics/resolution/resolve-impl.ts | 2 +- src/semantics/resolution/resolve-intersection.ts | 2 +- src/semantics/resolution/resolve-match.ts | 2 +- src/semantics/resolution/resolve-object-type.ts | 2 +- src/semantics/resolution/resolve-union.ts | 2 +- src/semantics/resolution/resolve-use.ts | 2 +- 11 files changed, 10 insertions(+), 10 deletions(-) rename src/semantics/resolution/{resolve-types.ts => resolve-entities.ts} (100%) diff --git a/src/semantics/index.ts b/src/semantics/index.ts index dbd9b5e..35c3557 100644 --- a/src/semantics/index.ts +++ b/src/semantics/index.ts @@ -6,7 +6,7 @@ import { registerModules } from "./modules.js"; import { expandRegularMacros } from "./regular-macros.js"; import { ParsedModule } from "../parser/index.js"; import { Expr } from "../syntax-objects/expr.js"; -import { resolveEntities } from "./resolution/resolve-types.js"; +import { resolveEntities } from "./resolution/resolve-entities.js"; const semanticPhases: SemanticProcessor[] = [ expandRegularMacros, // Also handles use and module declaration initialization diff --git a/src/semantics/resolution/index.ts b/src/semantics/resolution/index.ts index 4659aa6..c608a66 100644 --- a/src/semantics/resolution/index.ts +++ b/src/semantics/resolution/index.ts @@ -1,3 +1,3 @@ -export { resolveEntities } from "./resolve-types.js"; +export { resolveEntities } from "./resolve-entities.js"; export { typesAreCompatible } from "./types-are-compatible.js"; export { resolveModulePath } from "./resolve-use.js"; diff --git a/src/semantics/resolution/resolve-call-types.ts b/src/semantics/resolution/resolve-call-types.ts index be2f371..5f30452 100644 --- a/src/semantics/resolution/resolve-call-types.ts +++ b/src/semantics/resolution/resolve-call-types.ts @@ -4,7 +4,7 @@ import { dVoid, ObjectType, TypeAlias } from "../../syntax-objects/types.js"; import { getCallFn } from "./get-call-fn.js"; import { getExprType, getIdentifierType } from "./get-expr-type.js"; import { resolveObjectType } from "./resolve-object-type.js"; -import { resolveEntities } from "./resolve-types.js"; +import { resolveEntities } from "./resolve-entities.js"; import { resolveExport } from "./resolve-use.js"; export const resolveCall = (call: Call): Call => { diff --git a/src/semantics/resolution/resolve-types.ts b/src/semantics/resolution/resolve-entities.ts similarity index 100% rename from src/semantics/resolution/resolve-types.ts rename to src/semantics/resolution/resolve-entities.ts diff --git a/src/semantics/resolution/resolve-fn.ts b/src/semantics/resolution/resolve-fn.ts index 353b2e2..8cfda0b 100644 --- a/src/semantics/resolution/resolve-fn.ts +++ b/src/semantics/resolution/resolve-fn.ts @@ -6,7 +6,7 @@ import { List } from "../../syntax-objects/list.js"; import { Parameter } from "../../syntax-objects/parameter.js"; import { TypeAlias } from "../../syntax-objects/types.js"; import { getExprType } from "./get-expr-type.js"; -import { resolveEntities } from "./resolve-types.js"; +import { resolveEntities } from "./resolve-entities.js"; export type ResolveFnTypesOpts = { typeArgs?: List; diff --git a/src/semantics/resolution/resolve-impl.ts b/src/semantics/resolution/resolve-impl.ts index 80b4d35..84e51a5 100644 --- a/src/semantics/resolution/resolve-impl.ts +++ b/src/semantics/resolution/resolve-impl.ts @@ -3,7 +3,7 @@ import { Implementation } from "../../syntax-objects/implementation.js"; import { ObjectType, TypeAlias } from "../../syntax-objects/types.js"; import { getExprType } from "./get-expr-type.js"; import { resolveObjectType } from "./resolve-object-type.js"; -import { resolveEntities } from "./resolve-types.js"; +import { resolveEntities } from "./resolve-entities.js"; export const resolveImpl = ( impl: Implementation, diff --git a/src/semantics/resolution/resolve-intersection.ts b/src/semantics/resolution/resolve-intersection.ts index 8514d81..c6da101 100644 --- a/src/semantics/resolution/resolve-intersection.ts +++ b/src/semantics/resolution/resolve-intersection.ts @@ -1,6 +1,6 @@ import { IntersectionType } from "../../syntax-objects/types.js"; import { getExprType } from "./get-expr-type.js"; -import { resolveEntities } from "./resolve-types.js"; +import { resolveEntities } from "./resolve-entities.js"; export const resolveIntersectionType = ( inter: IntersectionType diff --git a/src/semantics/resolution/resolve-match.ts b/src/semantics/resolution/resolve-match.ts index 7abfc7b..493c400 100644 --- a/src/semantics/resolution/resolve-match.ts +++ b/src/semantics/resolution/resolve-match.ts @@ -10,7 +10,7 @@ import { } from "../../syntax-objects/index.js"; import { Match, MatchCase } from "../../syntax-objects/match.js"; import { getExprType } from "./get-expr-type.js"; -import { resolveEntities, resolveVar } from "./resolve-types.js"; +import { resolveEntities, resolveVar } from "./resolve-entities.js"; export const resolveMatch = (match: Match): Match => { match.operand = resolveEntities(match.operand); diff --git a/src/semantics/resolution/resolve-object-type.ts b/src/semantics/resolution/resolve-object-type.ts index 6f1bc23..e28bfc3 100644 --- a/src/semantics/resolution/resolve-object-type.ts +++ b/src/semantics/resolution/resolve-object-type.ts @@ -8,7 +8,7 @@ import { } from "../../syntax-objects/types.js"; import { getExprType } from "./get-expr-type.js"; import { implIsCompatible, resolveImpl } from "./resolve-impl.js"; -import { resolveEntities } from "./resolve-types.js"; +import { resolveEntities } from "./resolve-entities.js"; import { typesAreCompatible } from "./types-are-compatible.js"; export const resolveObjectType = (obj: ObjectType, call?: Call): ObjectType => { diff --git a/src/semantics/resolution/resolve-union.ts b/src/semantics/resolution/resolve-union.ts index 012f314..61fbad9 100644 --- a/src/semantics/resolution/resolve-union.ts +++ b/src/semantics/resolution/resolve-union.ts @@ -1,6 +1,6 @@ import { UnionType } from "../../syntax-objects/types.js"; import { getExprType } from "./get-expr-type.js"; -import { resolveEntities } from "./resolve-types.js"; +import { resolveEntities } from "./resolve-entities.js"; export const resolveUnionType = (union: UnionType): UnionType => { union.childTypeExprs.applyMap((expr) => resolveEntities(expr)); diff --git a/src/semantics/resolution/resolve-use.ts b/src/semantics/resolution/resolve-use.ts index 26ba95d..608ba07 100644 --- a/src/semantics/resolution/resolve-use.ts +++ b/src/semantics/resolution/resolve-use.ts @@ -4,7 +4,7 @@ import { List } from "../../syntax-objects/list.js"; import { VoidModule } from "../../syntax-objects/module.js"; import { NamedEntity } from "../../syntax-objects/named-entity.js"; import { Use, UseEntities } from "../../syntax-objects/use.js"; -import { resolveModule, resolveEntities } from "./resolve-types.js"; +import { resolveModule, resolveEntities } from "./resolve-entities.js"; export type ModulePass = (mod: VoidModule) => VoidModule; From 8e8651f25d869a444d8de7af322c6a8cab1a6a48 Mon Sep 17 00:00:00 2001 From: Drew Youngwerth Date: Mon, 23 Sep 2024 16:57:28 -0700 Subject: [PATCH 04/14] One more file rename --- src/semantics/resolution/get-expr-type.ts | 2 +- .../resolution/{resolve-call-types.ts => resolve-call.ts} | 0 src/semantics/resolution/resolve-entities.ts | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename src/semantics/resolution/{resolve-call-types.ts => resolve-call.ts} (100%) diff --git a/src/semantics/resolution/get-expr-type.ts b/src/semantics/resolution/get-expr-type.ts index 2fa3cb8..0c3fb39 100644 --- a/src/semantics/resolution/get-expr-type.ts +++ b/src/semantics/resolution/get-expr-type.ts @@ -1,7 +1,7 @@ import { Expr } from "../../syntax-objects/expr.js"; import { Call, Identifier } from "../../syntax-objects/index.js"; import { Type, i32, f32, bool, i64, f64 } from "../../syntax-objects/types.js"; -import { resolveCall } from "./resolve-call-types.js"; +import { resolveCall } from "./resolve-call.js"; export const getExprType = (expr?: Expr): Type | undefined => { if (!expr) return; diff --git a/src/semantics/resolution/resolve-call-types.ts b/src/semantics/resolution/resolve-call.ts similarity index 100% rename from src/semantics/resolution/resolve-call-types.ts rename to src/semantics/resolution/resolve-call.ts diff --git a/src/semantics/resolution/resolve-entities.ts b/src/semantics/resolution/resolve-entities.ts index 68a5886..53e2e3b 100644 --- a/src/semantics/resolution/resolve-entities.ts +++ b/src/semantics/resolution/resolve-entities.ts @@ -12,7 +12,7 @@ import { } from "../../syntax-objects/types.js"; import { Variable } from "../../syntax-objects/variable.js"; import { getExprType } from "./get-expr-type.js"; -import { resolveCall } from "./resolve-call-types.js"; +import { resolveCall } from "./resolve-call.js"; import { resolveFn } from "./resolve-fn.js"; import { resolveImpl } from "./resolve-impl.js"; import { resolveIntersectionType } from "./resolve-intersection.js"; From f35bf279e4aa95a1be8a21723cf9c8a92b7ee9c5 Mon Sep 17 00:00:00 2001 From: Drew Youngwerth Date: Mon, 23 Sep 2024 17:21:41 -0700 Subject: [PATCH 05/14] Semantics for FixedArray --- src/semantics/check-types.ts | 31 ++++++++++++++ src/semantics/resolution/combine-types.ts | 50 +++++++++++++++++++++++ src/semantics/resolution/resolve-call.ts | 36 ++++++++++++++-- src/semantics/resolution/resolve-match.ts | 47 ++------------------- 4 files changed, 117 insertions(+), 47 deletions(-) create mode 100644 src/semantics/resolution/combine-types.ts diff --git a/src/semantics/check-types.ts b/src/semantics/check-types.ts index fb1f7fe..695a1b0 100644 --- a/src/semantics/check-types.ts +++ b/src/semantics/check-types.ts @@ -18,6 +18,7 @@ import { ObjectLiteral, UnionType, IntersectionType, + FixedArrayType, } from "../syntax-objects/index.js"; import { Match } from "../syntax-objects/match.js"; import { getExprType } from "./resolution/get-expr-type.js"; @@ -37,6 +38,7 @@ export const checkTypes = (expr: Expr | undefined): Expr => { if (expr.isTypeAlias()) return checkTypeAlias(expr); if (expr.isObjectLiteral()) return checkObjectLiteralType(expr); if (expr.isUnionType()) return checkUnionType(expr); + if (expr.isFixedArrayType()) return checkFixedArrayType(expr); if (expr.isMatch()) return checkMatch(expr); if (expr.isIntersectionType()) return checkIntersectionType(expr); return expr; @@ -56,6 +58,7 @@ const checkCallTypes = (call: Call): Call | ObjectLiteral => { if (call.calls(":")) return checkLabeledArg(call); if (call.calls("=")) return checkAssign(call); if (call.calls("while")) return checkWhile(call); + if (call.calls("FixedArray")) return checkFixedArrayInit(call); // TODO if (call.calls("member-access")) return call; // TODO if (call.fn?.isObjectType()) return checkObjectInit(call); @@ -81,6 +84,26 @@ const checkCallTypes = (call: Call): Call | ObjectLiteral => { return call; }; +const checkFixedArrayInit = (call: Call) => { + const type = call.type; + + if (!type || !type.isFixedArrayType()) { + throw new Error(`Expected FixedArray type at ${call.location}`); + } + + checkFixedArrayType(type); + call.args.each((arg) => { + const argType = getExprType(arg); + if (!argType || !typesAreCompatible(argType, type.elemType)) { + throw new Error( + `Expected ${type.elemType?.name} got ${argType?.name} at ${arg.location}` + ); + } + }); + + return call; +}; + const checkWhile = (call: Call) => { const cond = call.argAt(0); const condType = getExprType(cond); @@ -491,3 +514,11 @@ const checkUnionType = (union: UnionType) => { return union; }; + +const checkFixedArrayType = (array: FixedArrayType) => { + if (!array.elemType) { + throw new Error(`Unable to determine element type for ${array.location}`); + } + + return array; +}; diff --git a/src/semantics/resolution/combine-types.ts b/src/semantics/resolution/combine-types.ts new file mode 100644 index 0000000..35f8a19 --- /dev/null +++ b/src/semantics/resolution/combine-types.ts @@ -0,0 +1,50 @@ +import { + IntersectionType, + ObjectType, + Type, + UnionType, +} from "../../syntax-objects/types.js"; + +/** + * Combines types into their least common denominator. + * If all types are the same, it returns that type. + * If all types are different (but still object types), it returns a UnionType. + * If types are mixed and not all object types, it returns undefined. + */ +export const combineTypes = (types: Type[]): Type | undefined => { + const firstType = types[0]; + if (!types.length || !firstType?.isObjectType()) { + return firstType; + } + + let topType: ObjectType | IntersectionType | UnionType = firstType; + for (const type of types.slice(1)) { + if (type.id === topType.id) { + continue; + } + + if (isObjectOrIntersection(type) && isObjectOrIntersection(topType)) { + const union = new UnionType({ + name: `CombinedTypeUnion`, + }); + union.types = [topType, type]; + topType = union; + continue; + } + + if (isObjectOrIntersection(type) && topType.isUnionType()) { + topType.types.push(type); + continue; + } + + return undefined; + } + + return topType; +}; + +const isObjectOrIntersection = ( + type: Type +): type is ObjectType | IntersectionType => { + return type.isObjectType() || type.isIntersectionType(); +}; diff --git a/src/semantics/resolution/resolve-call.ts b/src/semantics/resolution/resolve-call.ts index 5f30452..6b40664 100644 --- a/src/semantics/resolution/resolve-call.ts +++ b/src/semantics/resolution/resolve-call.ts @@ -1,18 +1,25 @@ import { Call } from "../../syntax-objects/call.js"; import { Identifier, List, nop } from "../../syntax-objects/index.js"; -import { dVoid, ObjectType, TypeAlias } from "../../syntax-objects/types.js"; +import { + dVoid, + FixedArrayType, + ObjectType, + TypeAlias, +} from "../../syntax-objects/types.js"; import { getCallFn } from "./get-call-fn.js"; import { getExprType, getIdentifierType } from "./get-expr-type.js"; import { resolveObjectType } from "./resolve-object-type.js"; import { resolveEntities } from "./resolve-entities.js"; import { resolveExport } from "./resolve-use.js"; +import { combineTypes } from "./combine-types.js"; export const resolveCall = (call: Call): Call => { if (call.type) return call; if (call.calls("export")) return resolveExport(call); if (call.calls("if")) return resolveIf(call); - if (call.calls(":")) return checkLabeledArg(call); + if (call.calls(":")) return resolveLabeledArg(call); if (call.calls("while")) return resolveWhile(call); + if (call.calls("FixedArray")) return resolveFixedArray(call); call.args = call.args.map(resolveEntities); const memberAccessCall = getMemberAccessCall(call); @@ -38,7 +45,7 @@ export const resolveCall = (call: Call): Call => { return call; }; -export const checkLabeledArg = (call: Call) => { +export const resolveLabeledArg = (call: Call) => { call.args = call.args.map(resolveEntities); const expr = call.argAt(1); call.type = getExprType(expr); @@ -75,6 +82,29 @@ export const resolveTypeAlias = (call: Call, type: TypeAlias): Call => { return call; }; +const resolveFixedArray = (call: Call) => { + call.args = call.args.map(resolveEntities); + + const elemTypeExpr = + call.typeArgs?.at(0) ?? + combineTypes( + call.args + .toArray() + .map(getExprType) + .filter((t) => !!t) + ) ?? + nop(); + + const elemType = getExprType(elemTypeExpr); + call.type = new FixedArrayType({ + ...call.metadata, + name: Identifier.from(`FixedArray#${call.syntaxId}`), + elemTypeExpr, + elemType, + }); + return call; +}; + const getMemberAccessCall = (call: Call): Call | undefined => { if (call.args.length > 1) return; const a1 = call.argAt(0); diff --git a/src/semantics/resolution/resolve-match.ts b/src/semantics/resolution/resolve-match.ts index 493c400..43020b6 100644 --- a/src/semantics/resolution/resolve-match.ts +++ b/src/semantics/resolution/resolve-match.ts @@ -1,14 +1,7 @@ import { Block } from "../../syntax-objects/block.js"; -import { - Call, - IntersectionType, - ObjectType, - Parameter, - Type, - UnionType, - Variable, -} from "../../syntax-objects/index.js"; +import { Call, Parameter, Type, Variable } from "../../syntax-objects/index.js"; import { Match, MatchCase } from "../../syntax-objects/match.js"; +import { combineTypes } from "./combine-types.js"; import { getExprType } from "./get-expr-type.js"; import { resolveEntities, resolveVar } from "./resolve-entities.js"; @@ -75,39 +68,5 @@ const resolveMatchReturnType = (match: Match): Type | undefined => { .concat(match.defaultCase?.expr.type) .filter((t) => t !== undefined); - const firstType = cases[0]; - if (!cases.length || !firstType?.isObjectType()) { - return firstType; - } - - let type: ObjectType | IntersectionType | UnionType = firstType; - for (const mCase of cases.slice(1)) { - if (mCase.id === type.id) { - continue; - } - - if (isObjectOrIntersection(mCase) && isObjectOrIntersection(type)) { - const union = new UnionType({ - name: `Union#match#(${match.syntaxId}`, - }); - union.types = [type, mCase]; - type = union; - continue; - } - - if (isObjectOrIntersection(mCase) && type.isUnionType()) { - type.types.push(mCase); - continue; - } - - return undefined; - } - - return type; -}; - -const isObjectOrIntersection = ( - type: Type -): type is ObjectType | IntersectionType => { - return type.isObjectType() || type.isIntersectionType(); + return combineTypes(cases); }; From 620c48ac6ee336a3cf05c94b8f0ee10cb3576137 Mon Sep 17 00:00:00 2001 From: Drew Youngwerth Date: Mon, 23 Sep 2024 18:40:13 -0700 Subject: [PATCH 06/14] Direct fixed array init --- src/__tests__/fixtures/e2e-file.ts | 4 ++-- src/assembler.ts | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/__tests__/fixtures/e2e-file.ts b/src/__tests__/fixtures/e2e-file.ts index 323c9c6..a806006 100644 --- a/src/__tests__/fixtures/e2e-file.ts +++ b/src/__tests__/fixtures/e2e-file.ts @@ -87,7 +87,7 @@ pub fn test7() let vec = Bitly { x: 52, y: 2, z: 21 } get_num_from_vec_sub_obj(vec) -type DsArrayI32 = DsArray +type FixedArrayI32 = FixedArray // Test generic functions, should return 143 pub fn test8() @@ -95,7 +95,7 @@ pub fn test8() arr2.set(0, 1.5) arr2.get(0) - let arr: DsArrayI32 = new_fixed_array(10) + let arr: FixedArrayI32 = new_fixed_array(10) arr.set(9, 143) arr.get(9) diff --git a/src/assembler.ts b/src/assembler.ts index 63d4906..f4c29c0 100644 --- a/src/assembler.ts +++ b/src/assembler.ts @@ -225,6 +225,7 @@ const compileCall = (opts: CompileExprOpts): number => { if (expr.calls("member-access")) return compileObjMemberAccess(opts); if (expr.calls("while")) return compileWhile(opts); if (expr.calls("break")) return mod.br(opts.loopBreakId!); + if (expr.calls("FixedArray")) return compileFixedArray(opts); if (expr.calls("binaryen")) { return compileBnrCall(opts); } @@ -251,6 +252,15 @@ const compileCall = (opts: CompileExprOpts): number => { return mod.call(id, args, returnType); }; +const compileFixedArray = (opts: CompileExprOpts) => { + const type = opts.expr.type as FixedArrayType; + return gc.arrayNewFixed( + opts.mod, + gc.binaryenTypeToHeapType(mapBinaryenType(opts, type)), + opts.expr.argArrayMap((expr) => compileExpression({ ...opts, expr })) + ); +}; + const compileWhile = (opts: CompileExprOpts) => { const { expr, mod } = opts; const loopId = expr.syntaxId.toString(); From a3d04e9cb5e7b446cfca1f10f0af147f7d231b09 Mon Sep 17 00:00:00 2001 From: Drew Youngwerth Date: Mon, 23 Sep 2024 21:54:45 -0700 Subject: [PATCH 07/14] I think it works --- src/assembler.ts | 60 +++++++++++++++++++++++++++++------- src/lib/binaryen-gc/index.ts | 43 ++++++++++++++++++++++++-- src/semantics/check-types.ts | 2 +- std/array.voyd | 19 +++++++++++- std/index.voyd | 2 +- std/operators.voyd | 18 +++++++++++ std/string.voyd | 60 ++++++++++++++++++++++++++++++++++++ std/strings.voyd | 36 ---------------------- 8 files changed, 187 insertions(+), 53 deletions(-) create mode 100644 std/string.voyd delete mode 100644 std/strings.voyd diff --git a/src/assembler.ts b/src/assembler.ts index f4c29c0..cea9790 100644 --- a/src/assembler.ts +++ b/src/assembler.ts @@ -33,6 +33,7 @@ import { initExtensionHelpers } from "./assembler/extension-helpers.js"; import { returnCall } from "./assembler/return-call.js"; import { Float } from "./syntax-objects/float.js"; import { initFieldLookupHelpers } from "./assembler/field-lookup-helpers.js"; +import { List } from "./syntax-objects/list.js"; export const assemble = (ast: Expr) => { const mod = new binaryen.Module(); @@ -126,10 +127,20 @@ const compileType = (opts: CompileExprOpts) => { }; const compileModule = (opts: CompileExprOpts) => { - return opts.mod.block( + const result = opts.mod.block( opts.expr.id, opts.expr.value.map((expr) => compileExpression({ ...opts, expr })) ); + + if (opts.expr.isIndex) { + opts.expr.getAllExports().forEach((entity) => { + if (entity.isFn()) { + opts.mod.addFunctionExport(entity.id, entity.name.value); + } + }); + } + + return result; }; const compileBlock = (opts: CompileExprOpts) => { @@ -319,21 +330,21 @@ const compileObjectInit = (opts: CompileExprOpts) => { const compileExport = (opts: CompileExprOpts) => { const expr = opts.expr.exprArgAt(0); const result = compileExpression({ ...opts, expr }); - - if (expr.parentModule?.isIndex && expr.isBlock()) { - expr.getAllEntities().forEach((entity) => { - if (entity.isFn()) { - opts.mod.addFunctionExport(entity.id, entity.name.value); - } - }); - } - return result; }; const compileAssign = (opts: CompileExprOpts): number => { const { expr, mod } = opts; - const identifier = expr.argAt(0) as Identifier; + const identifier = expr.argAt(0); + + if (identifier?.isCall()) { + return compileFieldAssign(opts); + } + + if (!identifier?.isIdentifier()) { + throw new Error(`Invalid assignment target ${identifier}`); + } + const value = compileExpression({ ...opts, expr: expr.argAt(1)!, @@ -351,6 +362,32 @@ const compileAssign = (opts: CompileExprOpts): number => { throw new Error(`${identifier} cannot be re-assigned`); }; +const compileFieldAssign = (opts: CompileExprOpts) => { + const { expr, mod } = opts; + const access = expr.callArgAt(0); + const member = access.identifierArgAt(1); + const target = access.exprArgAt(0); + const value = compileExpression({ + ...opts, + expr: expr.argAt(1)!, + isReturnExpr: false, + }); + + const type = getExprType(target) as ObjectType; + const index = type.getFieldIndex(member); + if (index === -1) { + throw new Error(`Field ${member} not found in ${type.id}`); + } + const memberIndex = type.getFieldIndex(member) + OBJECT_FIELDS_OFFSET; + + return gc.structSetFieldValue({ + mod, + ref: compileExpression({ ...opts, expr: target }), + fieldIndex: memberIndex, + value, + }); +}; + const compileBnrCall = (opts: CompileExprOpts): number => { const { expr } = opts; const funcId = expr.labeledArgAt(0) as Identifier; @@ -588,6 +625,7 @@ const buildObjectType = (opts: MapBinTypeOpts, obj: ObjectType): TypeRef => { ...obj.fields.map((field) => ({ type: mapBinaryenType(opts, field.type!), name: field.name, + mutable: true, })), ], supertype: obj.parentObjType diff --git a/src/lib/binaryen-gc/index.ts b/src/lib/binaryen-gc/index.ts index 9248c78..90de11b 100644 --- a/src/lib/binaryen-gc/index.ts +++ b/src/lib/binaryen-gc/index.ts @@ -25,8 +25,31 @@ export const defineStructType = ( ) ); const fieldMutablesPtr = allocU32Array( - fields.map(({ mutable }) => (mutable ? 1 : 0)) + fields.reduce((acc, { mutable }, index) => { + // Calculate which u32 slot this boolean belongs to + const u32Index = Math.floor(index / 4); + + // Ensure the slot exists and initialize it to 0 if it doesn't + if (typeof acc[u32Index] === "undefined") { + acc[u32Index] = 0; + } + + // Pack the boolean into the appropriate position in the u32 + const shiftAmount = (index % 4) * 8; + acc[u32Index] |= (mutable ? 1 : 0) << shiftAmount; + + return acc; + }, [] as number[]) ); + if (struct.name === "StringReader#28474") { + console.log( + JSON.stringify( + fields.map(({ mutable }) => (mutable ? 1 : 0)), + undefined, + 2 + ) + ); + } bin._TypeBuilderSetStructType( typeBuilder, @@ -187,6 +210,20 @@ export const structGetFieldValue = ({ ); }; +export const structSetFieldValue = ({ + mod, + fieldIndex, + ref, + value, +}: { + mod: binaryen.Module; + fieldIndex: number; + ref: ExpressionRef; + value: ExpressionRef; +}): ExpressionRef => { + return bin._BinaryenStructSet(mod.ptr, fieldIndex, ref, value); +}; + export const arrayGet = ( mod: binaryen.Module, arrayRef: ExpressionRef, @@ -216,10 +253,10 @@ export const arrayLen = ( export const arrayNew = ( mod: binaryen.Module, type: HeapTypeRef, - initialLength: ExpressionRef, + size: ExpressionRef, init: ExpressionRef ): ExpressionRef => { - return bin._BinaryenArrayNew(mod.ptr, type, initialLength, init); + return bin._BinaryenArrayNew(mod.ptr, type, size, init); }; export const arrayNewFixed = ( diff --git a/src/semantics/check-types.ts b/src/semantics/check-types.ts index 695a1b0..cdf862d 100644 --- a/src/semantics/check-types.ts +++ b/src/semantics/check-types.ts @@ -135,7 +135,7 @@ const checkObjectInit = (call: Call): Call => { export const checkAssign = (call: Call) => { const id = call.argAt(0); if (!id?.isIdentifier()) { - throw new Error(`Can only assign to variables for now ${id}`); + return call; } const variable = id.resolve(); diff --git a/std/array.voyd b/std/array.voyd index 396b18e..1116bc5 100644 --- a/std/array.voyd +++ b/std/array.voyd @@ -21,4 +21,21 @@ pub fn get(arr: FixedArray, index: i32) -> T pub fn set(arr: FixedArray, index: i32, value: T) -> FixedArray binaryen_gc_call(arraySet, [arr, index, value]) - arr + +pub fn copy(destArr: FixedArray, opts: { + from: FixedArray, + toIndex: i32, + fromIndex: i32, + count: i32 +}) -> FixedArray + binaryen_gc_call(arrayCopy, [ + destArr, + opts.toIndex, + opts.from, + opts.fromIndex, + opts.count + ]) + destArr + +pub fn length(arr: FixedArray) -> i32 + binaryen_gc_call(arrayLen, [arr]) diff --git a/std/index.voyd b/std/index.voyd index 14f0720..1484207 100644 --- a/std/index.voyd +++ b/std/index.voyd @@ -3,5 +3,5 @@ pub mod macros pub mod macros::all pub mod operators::all pub mod utils::all -pub mod strings::all +pub mod string::all pub mod array::all diff --git a/std/operators.voyd b/std/operators.voyd index 17941c8..39337e0 100644 --- a/std/operators.voyd +++ b/std/operators.voyd @@ -58,3 +58,21 @@ pub def_wasm_operator('+', add, f64, f64) pub def_wasm_operator('-', sub, f64, f64) pub def_wasm_operator('*', mul, f64, f64) pub def_wasm_operator('/', div, f64, f64) + +pub fn '=='(left: bool, right: bool) -> bool + binaryen + func: eq + namespace: i32 + args: [left, right] + +pub fn 'and'(left: bool, right: bool) -> bool + binaryen + func: 'and' + namespace: i32 + args: [left, right] + +pub fn 'or'(left: bool, right: bool) -> bool + binaryen + func: 'or' + namespace: i32 + args: [left, right] diff --git a/std/string.voyd b/std/string.voyd new file mode 100644 index 0000000..06729f8 --- /dev/null +++ b/std/string.voyd @@ -0,0 +1,60 @@ +use std::macros::all +use array::all +use operators::all + +obj String { + chars: FixedArray +} + +impl String + pub fn slice(self, start: i32, end: i32) -> String + let new_length = end - start + let new_chars = new_fixed_array(new_length) + new_chars.copy({ + from: self.chars, + toIndex: 0, + fromIndex: start, + count: new_length + }) + String { chars: new_chars } + + pub fn char_code_at(self, index: i32) -> i32 + if (index < 0) or (index >= self.chars.length()) then: + -1 + else: self.chars.get(index) + + pub fn length(self) -> i32 + self.chars.length() + + pub fn concatenated(self, other: String) -> String + let new_length = self.chars.length() + other.chars.length() + let new_chars = new_fixed_array(new_length) + new_chars.copy({ + from: self.chars, + toIndex: 0, + fromIndex: 0, + count: self.chars.length() + }) + new_chars.copy({ + from: other.chars, + toIndex: self.chars.length(), + fromIndex: 0, + count: other.chars.length() + }) + String { chars: new_chars } + +obj StringReader { + str: String, + index: i32 +} + +pub fn new_string_reader(str: String) -> StringReader + StringReader { str: str, index: 0 } + +pub fn read_next_char(reader: StringReader) -> i32 + if reader.index >= reader.str.length() then: + -1 + else: + let char = reader.str.char_code_at(reader.index) + reader.index = reader.index + 1 + char diff --git a/std/strings.voyd b/std/strings.voyd deleted file mode 100644 index 49bbf5a..0000000 --- a/std/strings.voyd +++ /dev/null @@ -1,36 +0,0 @@ -use std::macros::all - -type String = i32 - -declare "strings" - pub fn alloc_string() -> String - - pub fn de_alloc_string(str: String) -> void - - pub fn str_len(str: String) -> i32 - - // Returns -1 if not found - pub fn get_char_code_from_string(charIndex: i32, str:String) -> i32 - - pub fn add_char_code_to_string(char:i32, str:String) -> void - - pub fn print_str(str: String) -> void - - pub fn str_equals(a:String, b:String) -> void - - pub fn str_starts_with(str: String, startsWith: String) -> void - - pub fn str_ends_with(str: String, endsWith: String) -> void - - pub fn str_includes(str:String, includes: String) -> void - -// Regex test (pass -1 to flags for default (g)) - pub fn str_test(str: String, regex:String, flags: String) -> void - -pub macro string(str) - let add_codes = str.split("").map (char) => - ` add_char_code_to_string $(char_to_code char) index - ` - let index: String = alloc_string() - $@add_codes - index From 4d04025b1bf5b317a236749f26997ad0f0c67334 Mon Sep 17 00:00:00 2001 From: Drew Youngwerth Date: Mon, 23 Sep 2024 21:58:22 -0700 Subject: [PATCH 08/14] Expose string module --- std/index.voyd | 1 + 1 file changed, 1 insertion(+) diff --git a/std/index.voyd b/std/index.voyd index 1484207..87246c9 100644 --- a/std/index.voyd +++ b/std/index.voyd @@ -5,3 +5,4 @@ pub mod operators::all pub mod utils::all pub mod string::all pub mod array::all +pub mod string From 03d6d86b6d5486661bbce69f3e06397eeea01194 Mon Sep 17 00:00:00 2001 From: Drew Youngwerth Date: Mon, 23 Sep 2024 22:07:37 -0700 Subject: [PATCH 09/14] Maybe working string literal --- src/parser/reader-macros/string.ts | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/parser/reader-macros/string.ts b/src/parser/reader-macros/string.ts index 77e394f..56300c9 100644 --- a/src/parser/reader-macros/string.ts +++ b/src/parser/reader-macros/string.ts @@ -1,4 +1,4 @@ -import { Identifier, StringLiteral } from "../../syntax-objects/index.js"; +import { Identifier, List } from "../../syntax-objects/index.js"; import { ReaderMacro } from "./types.js"; export const stringMacro: ReaderMacro = { @@ -31,9 +31,19 @@ export const stringMacro: ReaderMacro = { }); } - return new StringLiteral({ - value: token.value, - location: token.location, - }); + return new List([ + "String", + [ + "object", + [ + ":", + "chars", + [ + "FixedArray", + ...token.value.split("").map((char) => char.charCodeAt(0)), + ], + ], + ], + ]); }, }; From ecb7b1d9d7673732ae22b18ae967774f9acbece0 Mon Sep 17 00:00:00 2001 From: Drew Youngwerth Date: Mon, 23 Sep 2024 22:46:24 -0700 Subject: [PATCH 10/14] It works! --- src/lib/host-runtime/bool-to-int.ts | 1 - src/lib/host-runtime/index.ts | 1 - src/lib/host-runtime/strings.ts | 77 ----------------------------- src/parser/reader-macros/string.ts | 20 ++------ src/run.ts | 39 ++++++--------- src/semantics/init-entities.ts | 23 +++++++++ std/string.voyd | 4 +- 7 files changed, 46 insertions(+), 119 deletions(-) delete mode 100644 src/lib/host-runtime/bool-to-int.ts delete mode 100644 src/lib/host-runtime/index.ts delete mode 100644 src/lib/host-runtime/strings.ts diff --git a/src/lib/host-runtime/bool-to-int.ts b/src/lib/host-runtime/bool-to-int.ts deleted file mode 100644 index e5dd067..0000000 --- a/src/lib/host-runtime/bool-to-int.ts +++ /dev/null @@ -1 +0,0 @@ -export const boolToInt = (bool: boolean) => (bool ? 1 : 0); diff --git a/src/lib/host-runtime/index.ts b/src/lib/host-runtime/index.ts deleted file mode 100644 index e9a3ed1..0000000 --- a/src/lib/host-runtime/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./strings.js"; diff --git a/src/lib/host-runtime/strings.ts b/src/lib/host-runtime/strings.ts deleted file mode 100644 index a9ae868..0000000 --- a/src/lib/host-runtime/strings.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { boolToInt } from "./bool-to-int.js"; - -export class StringsTable { - readonly rotateAfterIndex = 500000; - readonly strings = new Map(); - nextStringIndex = 0; - - private getNextIndex() { - const index = this.nextStringIndex; - - if (this.nextStringIndex >= this.rotateAfterIndex) { - this.nextStringIndex = 0; - return index; - } - - this.nextStringIndex += 1; - return index; - } - - allocString(): number { - const index = this.getNextIndex(); - this.strings.set(index, ""); - return index; - } - - deAllocString(index: number) { - this.strings.delete(index); - } - - strLength(index: number) { - return this.strings.get(index)?.length ?? -1; - } - - printStr(index: number) { - console.log(this.strings.get(index)); - } - - addCharCodeToString(code: number, index: number) { - const str = this.strings.get(index) ?? ""; - this.strings.set(index, str + String.fromCharCode(code)); - } - - getCharCodeFromString(charIndex: number, strIndex: number) { - return this.strings.get(strIndex)?.[charIndex] ?? -1; - } - - strEquals(aIndex: number, bIndex: number): number { - return boolToInt(this.strings.get(aIndex) === this.strings.get(bIndex)); - } - - strStartsWith(aIndex: number, bIndex: number): number { - return boolToInt( - !!this.strings.get(aIndex)?.startsWith(this.strings.get(bIndex) ?? "") // this could probably cause bugs FYI, consider returning false if either string doesn't exist - ); - } - - strEndsWith(aIndex: number, bIndex: number): number { - return boolToInt( - !!this.strings.get(aIndex)?.endsWith(this.strings.get(bIndex) ?? "") // this could probably cause bugs FYI, consider returning false if either string doesn't exist - ); - } - - strIncludes(aIndex: number, bIndex: number): number { - return boolToInt( - !!this.strings.get(aIndex)?.endsWith(this.strings.get(bIndex) ?? "") // this could probably cause bugs FYI, consider returning false if either string doesn't exist - ); - } - - /** Pass -1 for default flags (g) */ - strTest(strIndex: number, regexIndex: number, flagsIndex: number): number { - const str = this.strings.get(strIndex); - const regex = this.strings.get(regexIndex); - const flags = flagsIndex !== -1 ? this.strings.get(flagsIndex) : "g"; - if (str === undefined || regex === undefined) return 0; - return boolToInt(new RegExp(regex, flags).test(str)); - } -} diff --git a/src/parser/reader-macros/string.ts b/src/parser/reader-macros/string.ts index 56300c9..77e394f 100644 --- a/src/parser/reader-macros/string.ts +++ b/src/parser/reader-macros/string.ts @@ -1,4 +1,4 @@ -import { Identifier, List } from "../../syntax-objects/index.js"; +import { Identifier, StringLiteral } from "../../syntax-objects/index.js"; import { ReaderMacro } from "./types.js"; export const stringMacro: ReaderMacro = { @@ -31,19 +31,9 @@ export const stringMacro: ReaderMacro = { }); } - return new List([ - "String", - [ - "object", - [ - ":", - "chars", - [ - "FixedArray", - ...token.value.split("").map((char) => char.charCodeAt(0)), - ], - ], - ], - ]); + return new StringLiteral({ + value: token.value, + location: token.location, + }); }, }; diff --git a/src/run.ts b/src/run.ts index 1657f1c..64be6e5 100644 --- a/src/run.ts +++ b/src/run.ts @@ -1,35 +1,28 @@ import binaryen from "binaryen"; -import { StringsTable } from "./lib/host-runtime/strings.js"; export function run(mod: binaryen.Module) { const binary = mod.emitBinary(); const compiled = new WebAssembly.Module(binary); - const strings = new StringsTable(); const instance = new WebAssembly.Instance(compiled, { - strings: { - "alloc-string": () => strings.allocString(), - "de-alloc-string": (index: number) => strings.deAllocString(index), - "add-char-code-to-string": (code: number, index: number) => - strings.addCharCodeToString(code, index), - "str-len": (index: number) => strings.strLength(index), - "print-str": (index: number) => strings.printStr(index), - "get-char-code-from-string": (charIndex: number, strIndex: number) => - strings.getCharCodeFromString(charIndex, strIndex), - "str-equals": (aIndex: number, bIndex: number) => - strings.strEquals(aIndex, bIndex), - "str-starts-with": (aIndex: number, bIndex: number) => - strings.strStartsWith(aIndex, bIndex), - "str-ends-with": (aIndex: number, bIndex: number) => - strings.strEndsWith(aIndex, bIndex), - "str-includes": (aIndex: number, bIndex: number) => - strings.strIncludes(aIndex, bIndex), - "str-test": (strIndex: number, regexIndex: number, flagsIndex: number) => - strings.strTest(strIndex, regexIndex, flagsIndex), - }, utils: { log: (val: number) => console.log(val), }, }); - console.log((instance.exports as any).main()); + const fns = instance.exports as any; + const newStringReader = fns.new_string_reader; + const read_next_char = fns.read_next_char; + const result = fns.main(); + const reader = newStringReader(result); + + let str = ""; + while (true) { + const char = read_next_char(reader); + if (char < 0) { + break; + } + str += String.fromCharCode(char); + } + + console.log(str); } diff --git a/src/semantics/init-entities.ts b/src/semantics/init-entities.ts index fd79903..f17ae5f 100644 --- a/src/semantics/init-entities.ts +++ b/src/semantics/init-entities.ts @@ -15,6 +15,7 @@ import { nop, UnionType, IntersectionType, + Identifier, } from "../syntax-objects/index.js"; import { Match, MatchCase } from "../syntax-objects/match.js"; import { SemanticProcessor } from "./types.js"; @@ -24,6 +25,28 @@ export const initEntities: SemanticProcessor = (expr) => { return expr.applyMap(initEntities); } + if (expr.isStringLiteral()) { + return initEntities( + new List({ + ...expr.metadata, + value: [ + "String", + [ + "object", + [ + ":", + "chars", + [ + "FixedArray", + ...expr.value.split("").map((c) => c.charCodeAt(0)), + ], + ], + ], + ], + }) + ); + } + if (!expr.isList()) return expr; if (expr.calls("define_function")) { diff --git a/std/string.voyd b/std/string.voyd index 06729f8..5e0be13 100644 --- a/std/string.voyd +++ b/std/string.voyd @@ -2,7 +2,7 @@ use std::macros::all use array::all use operators::all -obj String { +pub obj String { chars: FixedArray } @@ -26,7 +26,7 @@ impl String pub fn length(self) -> i32 self.chars.length() - pub fn concatenated(self, other: String) -> String + pub fn '+'(self, other: String) -> String let new_length = self.chars.length() + other.chars.length() let new_chars = new_fixed_array(new_length) new_chars.copy({ From 19f2fb68c422960f7bdb49386f7cbcf83c97defa Mon Sep 17 00:00:00 2001 From: Drew Youngwerth Date: Mon, 23 Sep 2024 23:30:10 -0700 Subject: [PATCH 11/14] Bugfix --- std/array.voyd | 1 + 1 file changed, 1 insertion(+) diff --git a/std/array.voyd b/std/array.voyd index 1116bc5..f343022 100644 --- a/std/array.voyd +++ b/std/array.voyd @@ -21,6 +21,7 @@ pub fn get(arr: FixedArray, index: i32) -> T pub fn set(arr: FixedArray, index: i32, value: T) -> FixedArray binaryen_gc_call(arraySet, [arr, index, value]) + arr pub fn copy(destArr: FixedArray, opts: { from: FixedArray, From f489e03d440d4b7afa596c7b41c8557e009c179e Mon Sep 17 00:00:00 2001 From: Drew Youngwerth Date: Tue, 24 Sep 2024 00:09:06 -0700 Subject: [PATCH 12/14] Change to namespace --- src/semantics/init-entities.ts | 8 ++++---- std/utils.voyd | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/semantics/init-entities.ts b/src/semantics/init-entities.ts index f17ae5f..263bbf5 100644 --- a/src/semantics/init-entities.ts +++ b/src/semantics/init-entities.ts @@ -279,10 +279,10 @@ const initVar = (varDef: List): Variable => { }; const initDeclaration = (decl: List) => { - const namespaceString = decl.at(1); + const namespace = decl.at(1); - if (!namespaceString?.isStringLiteral()) { - throw new Error("Expected namespace string"); + if (!namespace?.isIdentifier()) { + throw new Error("Expected namespace identifier"); } const fns = decl @@ -293,7 +293,7 @@ const initDeclaration = (decl: List) => { return new Declaration({ ...decl.metadata, - namespace: namespaceString.value, + namespace: namespace.value, fns, }); }; diff --git a/std/utils.voyd b/std/utils.voyd index a4f74c7..e4016a7 100644 --- a/std/utils.voyd +++ b/std/utils.voyd @@ -1,6 +1,6 @@ use std::macros::all -declare "utils" +declare 'utils' pub fn log(val:i32) -> void pub fn log(val:f32) -> void pub fn log(val:f64) -> void From 3de326b23ba73fbb64bc2c43d6062d792f6b6534 Mon Sep 17 00:00:00 2001 From: Drew Youngwerth Date: Tue, 24 Sep 2024 00:26:13 -0700 Subject: [PATCH 13/14] Remove log --- src/lib/binaryen-gc/index.ts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/lib/binaryen-gc/index.ts b/src/lib/binaryen-gc/index.ts index 90de11b..6b68734 100644 --- a/src/lib/binaryen-gc/index.ts +++ b/src/lib/binaryen-gc/index.ts @@ -41,15 +41,6 @@ export const defineStructType = ( return acc; }, [] as number[]) ); - if (struct.name === "StringReader#28474") { - console.log( - JSON.stringify( - fields.map(({ mutable }) => (mutable ? 1 : 0)), - undefined, - 2 - ) - ); - } bin._TypeBuilderSetStructType( typeBuilder, From 077522bbd24fd5ee891b43eb869cbdad5444cbf9 Mon Sep 17 00:00:00 2001 From: Drew Youngwerth Date: Tue, 24 Sep 2024 00:42:30 -0700 Subject: [PATCH 14/14] Add unit test --- src/__tests__/compiler.test.ts | 27 +++++++++++++++++++ src/__tests__/fixtures/e2e-file.ts | 5 +++- src/run.ts | 14 +--------- src/semantics/check-types.ts | 2 +- src/semantics/init-entities.ts | 42 ++++++++++++++---------------- 5 files changed, 53 insertions(+), 37 deletions(-) diff --git a/src/__tests__/compiler.test.ts b/src/__tests__/compiler.test.ts index 9e0fa76..c966a98 100644 --- a/src/__tests__/compiler.test.ts +++ b/src/__tests__/compiler.test.ts @@ -35,6 +35,15 @@ describe("E2E Compiler Pipeline", () => { expectedValues.forEach((v, i) => { const test = getWasmFn(`test${i + 1}`, instance); assert(test, `Test${i + 1} exists`); + + if (typeof v === "string") { + t.expect( + readString(test(), instance), + `test ${i + 1} returns correct value` + ).toEqual(v); + return; + } + t.expect(test(), `test ${i + 1} returns correct value`).toEqual(v); }); @@ -58,6 +67,7 @@ describe("E2E Compiler Pipeline", () => { 42, 2, // IntersectionType tests 20, // While loop + "Hello, world! This is a test.", ]); }); @@ -68,3 +78,20 @@ describe("E2E Compiler Pipeline", () => { t.expect(did); }); }); + +const readString = (ref: Object, instance: WebAssembly.Instance) => { + const newStringReader = getWasmFn("new_string_reader", instance)!; + const readNextChar = getWasmFn("read_next_char", instance)!; + const reader = newStringReader(ref); + + let str = ""; + while (true) { + const char = readNextChar(reader); + if (char < 0) { + break; + } + str += String.fromCharCode(char); + } + + return str; +}; diff --git a/src/__tests__/fixtures/e2e-file.ts b/src/__tests__/fixtures/e2e-file.ts index a806006..ca8c405 100644 --- a/src/__tests__/fixtures/e2e-file.ts +++ b/src/__tests__/fixtures/e2e-file.ts @@ -13,7 +13,7 @@ pub fn main() `; export const kitchenSink = ` -use std::all +pub use std::string::all obj Vec { x: i32, @@ -231,6 +231,9 @@ pub fn test19() -> i32 i = i + 1 if i == 5 then: break x + +pub fn test20() -> String + "Hello, world!" + " " + "This is a test." `; export const tcoText = ` diff --git a/src/run.ts b/src/run.ts index 64be6e5..af53331 100644 --- a/src/run.ts +++ b/src/run.ts @@ -10,19 +10,7 @@ export function run(mod: binaryen.Module) { }); const fns = instance.exports as any; - const newStringReader = fns.new_string_reader; - const read_next_char = fns.read_next_char; const result = fns.main(); - const reader = newStringReader(result); - let str = ""; - while (true) { - const char = read_next_char(reader); - if (char < 0) { - break; - } - str += String.fromCharCode(char); - } - - console.log(str); + console.log(result); } diff --git a/src/semantics/check-types.ts b/src/semantics/check-types.ts index cdf862d..929ea8e 100644 --- a/src/semantics/check-types.ts +++ b/src/semantics/check-types.ts @@ -58,7 +58,7 @@ const checkCallTypes = (call: Call): Call | ObjectLiteral => { if (call.calls(":")) return checkLabeledArg(call); if (call.calls("=")) return checkAssign(call); if (call.calls("while")) return checkWhile(call); - if (call.calls("FixedArray")) return checkFixedArrayInit(call); // TODO + if (call.calls("FixedArray")) return checkFixedArrayInit(call); if (call.calls("member-access")) return call; // TODO if (call.fn?.isObjectType()) return checkObjectInit(call); diff --git a/src/semantics/init-entities.ts b/src/semantics/init-entities.ts index 263bbf5..b415bfa 100644 --- a/src/semantics/init-entities.ts +++ b/src/semantics/init-entities.ts @@ -15,7 +15,7 @@ import { nop, UnionType, IntersectionType, - Identifier, + StringLiteral, } from "../syntax-objects/index.js"; import { Match, MatchCase } from "../syntax-objects/match.js"; import { SemanticProcessor } from "./types.js"; @@ -25,27 +25,7 @@ export const initEntities: SemanticProcessor = (expr) => { return expr.applyMap(initEntities); } - if (expr.isStringLiteral()) { - return initEntities( - new List({ - ...expr.metadata, - value: [ - "String", - [ - "object", - [ - ":", - "chars", - [ - "FixedArray", - ...expr.value.split("").map((c) => c.charCodeAt(0)), - ], - ], - ], - ], - }) - ); - } + if (expr.isStringLiteral()) return initStringLiteral(expr); if (!expr.isList()) return expr; @@ -544,3 +524,21 @@ const initImpl = (impl: List): Implementation => { /** Expects ["generics", ...Identifiers] */ const extractTypeParams = (list: List) => list.sliceAsArray(1).flatMap((p) => (p.isIdentifier() ? p : [])); + +const initStringLiteral = (str: StringLiteral) => + initEntities( + new List({ + ...str.metadata, + value: [ + "String", + [ + "object", + [ + ":", + "chars", + ["FixedArray", ...str.value.split("").map((c) => c.charCodeAt(0))], + ], + ], + ], + }) + );