Skip to content

Commit

Permalink
InferredType redesign; debug ast print
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanjermakov committed Aug 3, 2024
1 parent 4a601c4 commit 0656ec3
Show file tree
Hide file tree
Showing 10 changed files with 93 additions and 41 deletions.
25 changes: 25 additions & 0 deletions src/debug.ts
Original file line number Diff line number Diff line change
@@ -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]
})
)
}
2 changes: 1 addition & 1 deletion src/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ describe('nois', () => {
errors: [],
warnings: [],
silent: false,
variableCounter: 0,
variableCounter: 0
}
const astRoot = buildModuleAst(root, idFromString('test'), source, false, ctx)

Expand Down
10 changes: 6 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand Down Expand Up @@ -122,12 +122,14 @@ 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)

if (config.emit) {
await emitPackage(isDir, pkg, ctx)
}

// console.log(inspect(debugAst(pkg.modules[0]), { compact: true, depth: null }))
2 changes: 1 addition & 1 deletion src/output.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Config } from "./config"
import { Config } from './config'

let colorOutput = true

Expand Down
24 changes: 19 additions & 5 deletions src/phase/import-resolve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
38 changes: 23 additions & 15 deletions src/phase/type-bound.ts
Original file line number Diff line number Diff line change
@@ -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':
Expand Down Expand Up @@ -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
Expand All @@ -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
}
Expand Down Expand Up @@ -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': {
Expand All @@ -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
}
Expand All @@ -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
}
Expand All @@ -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': {
Expand Down Expand Up @@ -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)
}
6 changes: 6 additions & 0 deletions src/scope/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -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':
Expand Down
2 changes: 1 addition & 1 deletion src/std/prelude.no
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
Expand Down
10 changes: 3 additions & 7 deletions src/typecheck/index.ts
Original file line number Diff line number Diff line change
@@ -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 })
15 changes: 8 additions & 7 deletions src/typecheck/type.ts
Original file line number Diff line number Diff line change
@@ -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')

0 comments on commit 0656ec3

Please sign in to comment.