Skip to content

Commit

Permalink
Semantic: refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanjermakov committed Nov 20, 2024
1 parent 2802a1b commit d1e33be
Show file tree
Hide file tree
Showing 11 changed files with 149 additions and 192 deletions.
42 changes: 21 additions & 21 deletions src/ast/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ParseNode, filterNonAstNodes } from '../parser'
import { Context, DefinitionMap } from '../scope'
import { Context, Scope } from '../scope'
import { Source } from '../source'
import { InferredType } from '../typecheck'
import { BinaryExpr, Expr, OperandExpr, UnaryExpr, buildExpr } from './expr'
Expand Down Expand Up @@ -29,8 +29,8 @@ import {
import {
BoolLiteral,
CharLiteral,
ClosureExpr,
FloatLiteral,
FnDef,
ForExpr,
Identifier,
IntLiteral,
Expand All @@ -44,16 +44,17 @@ import {
import {
Block,
BreakStmt,
FnDef,
ImplDef,
ReturnStmt,
TraitBlock,
TraitDef,
TraitStatement,
UseExpr,
VarDef,
buildStatement,
buildUseExpr
} from './statement'
import { FnType, TypeParam, Type, buildType } from './type'
import { FnType, ParamType, Type, TypeParam, buildType } from './type'
import { FieldDef, TypeDef, Variant } from './type-def'

export type AstNode =
Expand All @@ -66,6 +67,7 @@ export type AstNode =
| Block
| Param
| FnType
| ParamType
| TypeParam
| MatchClause
| Pattern
Expand All @@ -79,14 +81,15 @@ export type AstNode =
| OperandExpr
| UnaryExpr
| BinaryExpr
| ClosureExpr
| ListExpr
| FnDef
| WhileExpr
| ForExpr
| MatchExpr
| VarDef
| FnDef
| TraitDef
| TraitBlock
| TraitStatement
| ImplDef
| TypeDef
| FieldDef
Expand Down Expand Up @@ -127,12 +130,13 @@ export const astExprKinds = <const>[
'binary-expr',
'closure-expr',
'list-expr',
'fn-def',
'while-expr',
'for-expr',
'match-expr'
]

export const astDefKinds = <const>['var-def', 'fn-def', 'trait-def', 'impl-def', 'type-def', 'field-def']
export const astDefKinds = <const>['var-def', 'trait-def', 'impl-def', 'type-def', 'variant', 'field-def']

export const astLiteralKinds = <const>['string-literal', 'char-literal', 'int-literal', 'float-literal', 'bool-literal']

Expand All @@ -154,26 +158,22 @@ export const astInfixOpKinds = <const>[
'assign-op'
]

export const astPostfixOpKinds = <const>[
'compose-op',
'call-op',
'unwrap-op',
'bind-op',
'await-op'
]
export const astPostfixOpKinds = <const>['compose-op', 'call-op', 'unwrap-op', 'bind-op', 'await-op']

export const astKinds = <const>[
'module',
'use-expr',
'variant',
'return-stmt',
'break-stmt',
'arg',
'block',
'trait-block',
'trait-statement',
'param',
'type-bounds',
'fn-type',
'generic',
'param-type',
'type-param',
'match-clause',
'pattern',
'con-pattern',
Expand Down Expand Up @@ -219,7 +219,7 @@ export type Module = BaseAstNode & {
mod: boolean
block: Block

scopeStack: DefinitionMap[]
scopeStack: Scope[]
useExprs: UseExpr[]

/**
Expand All @@ -233,7 +233,7 @@ export type Module = BaseAstNode & {
/**
* Persistent top level scope
*/
topScope: DefinitionMap
topScope: Scope
compiled: boolean
/**
* List of resolved imports used by this module
Expand All @@ -242,7 +242,7 @@ export type Module = BaseAstNode & {
/**
* Map of definitions accessible in this module via use exprs
*/
useScope: DefinitionMap
useScope: Scope
astStack: AstNode[]
impls: ImplDef[]
}
Expand All @@ -268,10 +268,10 @@ export const buildModuleAst = (
block,
scopeStack: [],
useExprs,
topScope: new Map(),
topScope: { type: new Map(), value: new Map() },
compiled,
imports: [],
useScope: new Map(),
useScope: { type: new Map(), value: new Map() },
astStack: [],
impls: []
}
Expand Down
9 changes: 4 additions & 5 deletions src/ast/statement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,9 @@ export const buildTraitStatement = (node: ParseNode, ctx: Context): TraitStateme

export type ImplDef = BaseAstNode & {
kind: 'impl-def'
identifier: Identifier
trait: Identifier
typeParams: TypeParam[]
forTrait?: Identifier
for: Identifier
block: TraitBlock
}

Expand All @@ -152,10 +152,9 @@ export const buildImplDef = (node: ParseNode, ctx: Context): ImplDef => {
const typeParams =
nodes.at(idx)?.kind === 'type-params' ? filterNonAstNodes(nodes[idx++]).map(n => buildTypeParam(n, ctx)) : []
const identifier = buildIdentifier(nodes[idx++], ctx)
const forTrait =
nodes.at(idx)?.kind === 'impl-for' ? buildIdentifier(filterNonAstNodes(nodes[idx++])[1], ctx) : undefined
const forTrait = buildIdentifier(filterNonAstNodes(nodes[idx++])[1], ctx)
const block = buildTraitBlock(nodes[idx++], ctx)
return { kind: 'impl-def', parseNode: node, identifier, typeParams, forTrait, block }
return { kind: 'impl-def', parseNode: node, trait: identifier, typeParams, for: forTrait, block }
}

export type ReturnStmt = BaseAstNode & {
Expand Down
8 changes: 3 additions & 5 deletions src/ast/type-def.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { BaseAstNode } from '.'
import { ParseNode, filterNonAstNodes } from '../parser'
import { Context } from '../scope'
import { Name, buildName } from './operand'
import { ImplDef } from './statement'
import { Type, TypeParam, buildType, buildTypeParam } from './type'

export type TypeDef = BaseAstNode & {
Expand All @@ -11,7 +10,6 @@ export type TypeDef = BaseAstNode & {
typeParams: TypeParam[]
variants: Variant[]
pub: boolean
impl?: ImplDef
}

export const buildTypeDef = (node: ParseNode, ctx: Context): TypeDef => {
Expand All @@ -33,23 +31,23 @@ export const buildTypeDef = (node: ParseNode, ctx: Context): TypeDef => {
// Foo() -> variant Foo::Foo()
// Foo(x: Int) -> variant Foo::Foo(x: Int)
const fieldDefs = filterNonAstNodes(nodes[idx]).map(n => buildFieldDef(n, ctx))
variants = [{ kind: 'variant', parseNode: nodes[idx++], name, fieldDefs }]
variants = [{ kind: 'variant', parseNode: nodes[idx++], name, fields: fieldDefs }]
}
return { kind: 'type-def', parseNode: node, name, typeParams, variants, pub }
}

export type Variant = BaseAstNode & {
kind: 'variant'
name: Name
fieldDefs: FieldDef[]
fields: FieldDef[]
typeDef?: TypeDef
}

export const buildTypeCon = (node: ParseNode, ctx: Context): Variant => {
const nodes = filterNonAstNodes(node)
const name = buildName(nodes[0], ctx)
const fieldDefs = nodes.at(1) ? filterNonAstNodes(nodes[1]).map(n => buildFieldDef(n, ctx)) : []
return { kind: 'variant', parseNode: node, name, fieldDefs }
return { kind: 'variant', parseNode: node, name, fields: fieldDefs }
}

export type FieldDef = BaseAstNode & {
Expand Down
4 changes: 2 additions & 2 deletions src/ast/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export const buildTypeParam = (node: ParseNode, ctx: Context): TypeParam => {

export type FnType = BaseAstNode & {
kind: 'fn-type'
generics: TypeParam[]
typeParams: TypeParam[]
paramTypes: ParamType[]
returnType: Type
}
Expand All @@ -60,7 +60,7 @@ export const buildFnType = (node: ParseNode, ctx: Context): FnType => {
nodes[i].kind === 'type-params' ? filterNonAstNodes(nodes[i++]).map(n => buildTypeParam(n, ctx)) : []
const paramTypes = filterNonAstNodes(nodes[i++]).map(n => buildParamType(n, ctx))
const returnType = buildType(filterNonAstNodes(nodes[i++])[0], ctx)
return { kind: 'fn-type', parseNode: node, generics, paramTypes, returnType }
return { kind: 'fn-type', parseNode: node, typeParams: generics, paramTypes, returnType }
}

export type ParamType = BaseAstNode & {
Expand Down
1 change: 0 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ if (isDir) {
}

reportErrors(ctx)
console.log(inspect(debugAst(pkg.modules[0].block), { compact: true, depth: null, breakLength: 120 }))

const packages = [...lib, pkg]

Expand Down
73 changes: 31 additions & 42 deletions src/phase/import-resolve.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { Module } from '../ast'
import { Identifier } from '../ast/operand'
import { FnDef } from '../ast/statement'
import { Context, Definition, addError, defKey } from '../scope'
import { Context, Definition, addDef, addError } from '../scope'
import { idEq, idFromString, idToString } from '../scope'
import { duplicateUseError, notFoundError } from '../semantic/error'
import { notFoundError } from '../semantic/error'
import { flatUseExprs } from '../semantic/use-expr'
import { unreachable } from '../util/todo'

export const setExports = (module: Module, ctx: Context): void => {
module.references = module.useExprs.filter(e => !e.pub).flatMap(e => flatUseExprs(e))
Expand All @@ -16,30 +16,22 @@ export const setExports = (module: Module, ctx: Context): void => {
*/
export const resolveImports = (module: Module, ctx: Context): void => {
;[...module.references!, ...module.reExports!].forEach(useExpr => {
const node = resolvePubId(useExpr, ctx)
if (node) {
addDef(node, module, useExpr, ctx)
const defs = resolvePubId(useExpr, ctx)
if (defs.length > 0) {
defs.forEach(def => addDef(def, module.useScope, ctx, useExpr))
} else {
addError(ctx, notFoundError(ctx, useExpr, idToString(useExpr)))
}
})
}

const addDef = (node: Definition, module: Module, importId: Identifier, ctx: Context): void => {
const key = defKey(node)
if (module.useScope.has(key)) {
addError(ctx, duplicateUseError(ctx, importId))
return
}
module.useScope.set(key, node)
}

const resolvePubId = (id: Identifier, ctx: Context): Definition | undefined => {
if (id.names.length < 2) return undefined
const resolvePubId = (id: Identifier, ctx: Context): Definition[] => {
const defs: Definition[] = []
if (id.names.length < 2) return defs

const pkgName = id.names[0].value
const pkg = ctx.packages.find(p => p.name === pkgName)
if (!pkg) return undefined
if (!pkg) return defs

// base case, e.g. std::option::Option
let nodeName = id.names.at(-1)!.value
Expand All @@ -51,18 +43,27 @@ const resolvePubId = (id: Identifier, ctx: Context): Definition | undefined => {
)
let mod = pkg.modules.find(m => idEq(m.identifier, modId))
if (mod) {
const node = mod.topScope.get(nodeName)
if (node) return node
const typeDef = mod.topScope.type.get(nodeName)
if (typeDef) {
defs.push(typeDef)
}

const valueDef = mod.topScope.value.get(nodeName)
if (valueDef) {
defs.push(valueDef)
}

// 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)
defs.push(...resolvePubId(reExport, ctx))
}

return defs
}

// case of Variant | FnDef, e.g. std::option::Option::Some
if (id.names.length < 3) return undefined
// case of TraitStatement, e.g. std::iter::Iter::next
if (id.names.length < 3) return defs
nodeName = id.names.at(-2)!.value
modId = idFromString(
id.names
Expand All @@ -72,28 +73,16 @@ const resolvePubId = (id: Identifier, ctx: Context): Definition | undefined => {
)
mod = pkg.modules.find(m => idEq(m.identifier, modId))
if (mod) {
const node = mod.topScope.get(nodeName)
const node = mod.topScope.type.get(nodeName)
if (node) {
switch (node.kind) {
case 'type-def': {
const vName = id.names.at(-1)!
const v = node.variants.find(v => v.name.value === vName.value)
if (v) return v
break
}
case 'trait-def':
case 'impl-def': {
const mName = id.names.at(-1)!
// TODO: report private matches as private, not just "not found"
const m = <FnDef | undefined>(
node.block.statements.find(s => s.kind === 'fn-def' && s.pub && s.name.value === mName.value)
)
if (m) return m
break
}
if (node.kind !== 'trait-def') return unreachable()
const mName = id.names.at(-1)!
const stmt = node.block.statements.find(s => s.name.value === mName.value)
if (stmt) {
defs.push(stmt)
}
}
}

return undefined
return defs
}
Loading

0 comments on commit d1e33be

Please sign in to comment.