diff --git a/src/debug.ts b/src/debug.ts new file mode 100644 index 00000000..c31d1adf --- /dev/null +++ b/src/debug.ts @@ -0,0 +1,25 @@ +import { AstNode } from './ast' +import { inferredTypeToString } from './scope' + +export const debugAst = (node: AstNode): any => { + if (typeof node !== 'object') return node + return Object.fromEntries( + Object.entries(node) + .filter(([p]) => !['parseNode', 'source'].includes(p)) + .map(([p, v]) => { + if (p === 'type') { + return [p, inferredTypeToString(v)] + } + return [p, v] + }) + .map(([p, v]) => { + if (Array.isArray(v)) { + return [p, v.map(debugAst)] + } + if (typeof v === 'object' && 'parseNode' in v) { + return [p, debugAst(v)] + } + return [p, v] + }) + ) +} diff --git a/src/index.spec.ts b/src/index.spec.ts index a4534882..aa961f9d 100644 --- a/src/index.spec.ts +++ b/src/index.spec.ts @@ -30,7 +30,7 @@ describe('nois', () => { errors: [], warnings: [], silent: false, - variableCounter: 0, + variableCounter: 0 } const astRoot = buildModuleAst(root, idFromString('test'), source, false, ctx) diff --git a/src/index.ts b/src/index.ts index 5683756e..45b3f9e9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,11 +7,11 @@ import { Package } from './package' import { buildModule } from './package/build' import { emitPackage } from './package/emit' import { buildPackage } from './package/io' -import { resolveImport } from './phase/import-resolve' +import { resolveImports, setExports } from './phase/import-resolve' import { resolveModuleScope } from './phase/module-resolve' import { resolveName } from './phase/name-resolve' import { collectTypeBounds } from './phase/type-bound' -import { Context, eachModule as forEachModule, pathToId } from './scope' +import { Context, eachModule, pathToId } from './scope' import { Source } from './source' import { assert } from './util/todo' @@ -122,8 +122,8 @@ ctx.packages = packages ctx.prelude = std.modules.find(m => m.identifier.names.at(-1)!.value === 'prelude')! assert(!!ctx.prelude, 'no prelude') -const phases = [resolveModuleScope, resolveImport, resolveName, collectTypeBounds] -phases.forEach(f => forEachModule(f, ctx)) +const phases = [resolveModuleScope, setExports, resolveImports, resolveName, collectTypeBounds] +phases.forEach(f => eachModule(f, ctx)) reportErrors(ctx) reportWarnings(ctx) @@ -131,3 +131,5 @@ reportWarnings(ctx) if (config.emit) { await emitPackage(isDir, pkg, ctx) } + +// console.log(inspect(debugAst(pkg.modules[0]), { compact: true, depth: null })) diff --git a/src/output.ts b/src/output.ts index 58a62004..2f1de19c 100644 --- a/src/output.ts +++ b/src/output.ts @@ -1,4 +1,4 @@ -import { Config } from "./config" +import { Config } from './config' let colorOutput = true diff --git a/src/phase/import-resolve.ts b/src/phase/import-resolve.ts index e74ec8db..b9acefe1 100644 --- a/src/phase/import-resolve.ts +++ b/src/phase/import-resolve.ts @@ -6,13 +6,16 @@ import { idEq, idFromString, idToString } from '../scope' import { notFoundError } from '../semantic/error' import { flatUseExprs } from '../semantic/use-expr' +export const setExports = (module: Module, ctx: Context): void => { + module.references = module.useExprs.filter(e => !e.pub).flatMap(e => flatUseExprs(e)) + module.reExports = module.useExprs.filter(e => e.pub).flatMap(e => flatUseExprs(e)) +} + /** * Check use exprs and populate module.useScope */ -export const resolveImport = (module: Module, ctx: Context): void => { - module.references = module.useExprs.filter(e => !e.pub).flatMap(e => flatUseExprs(e)) - module.reExports = module.useExprs.filter(e => e.pub).flatMap(e => flatUseExprs(e)) - ;[...module.references, ...module.reExports].forEach(useExpr => { +export const resolveImports = (module: Module, ctx: Context): void => { + ;[...module.references!, ...module.reExports!].forEach(useExpr => { const node = resolvePubId(useExpr, ctx) if (node) { addDef(node, module, ctx) @@ -50,12 +53,23 @@ const resolvePubId = (id: Identifier, ctx: Context): Definition | undefined => { if (mod) { const node = mod.topScope.get(nodeName) if (node) return node + + // id is re exported + const reExport = mod.reExports!.find(re => re.names.at(-1)!.value === id.names.at(-1)!.value) + if (reExport) { + return resolvePubId(reExport, ctx) + } } // case of Variant | FnDef, e.g. std::option::Option::Some if (id.names.length < 3) return undefined nodeName = id.names.at(-2)!.value - modId = idFromString(id.names.slice(1, -2).join('::')) + modId = idFromString( + id.names + .slice(0, -2) + .map(n => n.value) + .join('::') + ) mod = pkg.modules.find(m => idEq(m.identifier, modId)) if (mod) { const node = mod.topScope.get(nodeName) diff --git a/src/phase/type-bound.ts b/src/phase/type-bound.ts index 35d1e522..12cca258 100644 --- a/src/phase/type-bound.ts +++ b/src/phase/type-bound.ts @@ -1,13 +1,12 @@ import { AstNode } from '../ast' -import { Type } from '../ast/type' import { Context } from '../scope' -import { InferredType, makeInferredType, resolveTypeRef } from '../typecheck' +import { InferredType, makeInferredType } from '../typecheck' import { boolVid, charVid, floatVid, intVid, stringVid } from '../typecheck/type' /** * Assign every suitable node its type and type bounds */ -export const collectTypeBounds = (node: AstNode, ctx: Context, parentBound?: Type): InferredType | undefined => { +export const collectTypeBounds = (node: AstNode, ctx: Context, parentBound?: InferredType) => { switch (node.kind) { case 'variant': case 'return-stmt': @@ -41,7 +40,7 @@ export const collectTypeBounds = (node: AstNode, ctx: Context, parentBound?: Typ switch (node.kind) { case 'module': { node.block.statements.forEach(s => collectTypeBounds(s, ctx)) - return undefined + break } case 'variant': { // TODO @@ -56,13 +55,14 @@ export const collectTypeBounds = (node: AstNode, ctx: Context, parentBound?: Typ break } case 'block': { + node.statements.forEach(s => collectTypeBounds(s, ctx)) // TODO break } case 'param': { // TODO: handle self if (node.paramType) return undefined - resolveTypeRef(node.type!).known = node.paramType + node.type!.known = node.paramType collectTypeBounds(node.pattern, ctx, node.paramType) return node.type } @@ -105,14 +105,14 @@ export const collectTypeBounds = (node: AstNode, ctx: Context, parentBound?: Typ ) return undefined node.def.type ??= makeInferredType() - return { kind: 'ref', ref: resolveTypeRef(node.def.type) } + return node.def.type } // TODO break } case 'string-interpolated': { node.tokens.filter(t => typeof t !== 'string').forEach(t => collectTypeBounds(t, ctx)) - resolveTypeRef(node.type!).known = stringVid + node.type!.known = stringVid break } case 'operand-expr': { @@ -124,6 +124,8 @@ export const collectTypeBounds = (node: AstNode, ctx: Context, parentBound?: Typ break } case 'binary-expr': { + collectTypeBounds(node.lOperand, ctx) + collectTypeBounds(node.rOperand, ctx) // TODO break } @@ -148,10 +150,16 @@ export const collectTypeBounds = (node: AstNode, ctx: Context, parentBound?: Typ break } case 'var-def': { - // TODO + if (node.expr) { + collectTypeBounds(node.expr, ctx) + } + collectTypeBounds(node.pattern, ctx, node.expr?.type) break } case 'fn-def': { + if (node.block) { + collectTypeBounds(node.block, ctx) + } // TODO break } @@ -164,23 +172,23 @@ export const collectTypeBounds = (node: AstNode, ctx: Context, parentBound?: Typ break } case 'string-literal': { - resolveTypeRef(node.type!).known = stringVid + node.type!.known = stringVid break } case 'char-literal': { - resolveTypeRef(node.type!).known = charVid + node.type!.known = charVid break } case 'int-literal': { - resolveTypeRef(node.type!).known = intVid + node.type!.known = intVid break } case 'float-literal': { - resolveTypeRef(node.type!).known = floatVid + node.type!.known = floatVid break } case 'bool-literal': { - resolveTypeRef(node.type!).known = boolVid + node.type!.known = boolVid break } case 'method-call-op': { @@ -211,6 +219,6 @@ export const collectTypeBounds = (node: AstNode, ctx: Context, parentBound?: Typ return node.type } -const addBound = (type: InferredType, bound: Type): void => { - resolveTypeRef(type).bounds.push(bound) +const addBound = (type: InferredType, bound: InferredType): void => { + type.bounds.push(bound) } diff --git a/src/scope/index.ts b/src/scope/index.ts index b008eca2..9226c303 100644 --- a/src/scope/index.ts +++ b/src/scope/index.ts @@ -7,6 +7,7 @@ import { Config } from '../config' import { Package } from '../package' import { ParseNode } from '../parser' import { SemanticError } from '../semantic/error' +import { InferredType } from '../typecheck' import { unreachable } from '../util/todo' export type Context = { @@ -77,6 +78,11 @@ export const idFromString = (str: string, parseNode?: ParseNode): Identifier => } } +export const inferredTypeToString = (t: InferredType): string => { + const bounds = t.bounds.length > 0 ? `: ${t.bounds.map(inferredTypeToString).join(' + ')}` : '' + return `${t.known ? typeToString(t.known) : '_'}${bounds}` +} + export const typeToString = (t: Type): string => { switch (t.kind) { case 'identifier': diff --git a/src/std/prelude.no b/src/std/prelude.no index 6327980d..fd7af285 100644 --- a/src/std/prelude.no +++ b/src/std/prelude.no @@ -6,7 +6,7 @@ pub use std::{ int::Int, string::String, char::Char, - bool::Bool::{ self, True, False }, + bool::Bool::{ self }, eq::Eq, copy::{ Copy, copy }, ord::{ Ordering, Ord }, diff --git a/src/typecheck/index.ts b/src/typecheck/index.ts index fe25c807..7116a065 100644 --- a/src/typecheck/index.ts +++ b/src/typecheck/index.ts @@ -1,14 +1,10 @@ import { Type } from '../ast/type' -export type InferredType = InferredTypeDef | { kind: 'ref'; ref: InferredType } - -export type InferredTypeDef = { +export type InferredType = { kind: 'inferred' known?: Type - bounds: Type[] + bounds: InferredType[] unified?: Type } -export const makeInferredType = (bounds: Type[] = []): InferredType => ({ kind: 'inferred', bounds }) - -export const resolveTypeRef = (t: InferredType): InferredTypeDef => (t.kind === 'ref' ? resolveTypeRef(t.ref) : t) +export const makeInferredType = (bounds: InferredType[] = []): InferredType => ({ kind: 'inferred', bounds }) diff --git a/src/typecheck/type.ts b/src/typecheck/type.ts index 8ecd9107..4b3a2a8f 100644 --- a/src/typecheck/type.ts +++ b/src/typecheck/type.ts @@ -1,8 +1,9 @@ -import { VirtualIdentifier } from '../scope/vid' +import { Identifier } from '../ast/operand' +import { idFromString } from '../scope' -export const stringVid: VirtualIdentifier = { names: ['String'], typeArgs: [] } -export const charVid: VirtualIdentifier = { names: ['Char'], typeArgs: [] } -export const intVid: VirtualIdentifier = { names: ['Int'], typeArgs: [] } -export const floatVid: VirtualIdentifier = { names: ['Float'], typeArgs: [] } -export const boolVid: VirtualIdentifier = { names: ['Bool'], typeArgs: [] } -export const unitVid: VirtualIdentifier = { names: ['Unit'], typeArgs: [] } +export const stringVid: Identifier = idFromString('std::string::String') +export const charVid: Identifier = idFromString('std::string::Char') +export const intVid: Identifier = idFromString('std::string::Int') +export const floatVid: Identifier = idFromString('std::string::Float') +export const boolVid: Identifier = idFromString('std::string::Bool') +export const unitVid: Identifier = idFromString('std::string::Unit')