Skip to content

Commit

Permalink
Semantic: recursive upcast
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanjermakov committed Mar 25, 2024
1 parent 7c94922 commit ed849d3
Show file tree
Hide file tree
Showing 11 changed files with 51 additions and 62 deletions.
21 changes: 13 additions & 8 deletions src/codegen/js/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,19 @@ const makeJsImport = (vid: VirtualIdentifier, importModule: Module, module: Modu

export const emitUpcasts = (resultVar: string, upcasts: Upcast[] | undefined): EmitToken | undefined => {
if (!upcasts || upcasts.length === 0) return undefined
const args = [...upcasts.entries()].map(
([, v]) =>
`[${Object.entries(v)
.map(([tk, tv]) => `[${jsString(tk)}, ${jsRelName(tv)}]`)
.join(',')}]`
)
args.unshift(resultVar)
return emitToken(`${resultVar}.upcast(${args.join(',')});`)
return emitToken(upcasts.map(u => emitUpcast(resultVar, u)).join(''))
}

const emitUpcast = (resultVar: string, upcast: Upcast): string | undefined => {
if (Object.keys(upcast.self).length === 0 && upcast.generics.length === 0) return undefined
return `${resultVar}.upcast(${resultVar}, ...${upcastToArgString(upcast)});`
}

const upcastToArgString = (upcast: Upcast): string => {
if (Object.keys(upcast.self).length === 0) return ''
const gs = upcast.generics.length > 0 ? upcast.generics.map(upcastToArgString).join('') : ''
const self = `{${Object.entries(upcast.self).map(([k, v]) => `${jsString(k)}:${jsRelName(v)}`)}}`
return `[${[self, gs].filter(t => t.length > 0).join(',')}]`
}

export const extractValue = (str: string): string => {
Expand Down
14 changes: 9 additions & 5 deletions src/codegen/js/statement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export const emitInstanceDef = (instanceDef: ImplDef | TraitDef, module: Module,
const pVar = nextVariable(ctx)
const upcastMap = m.paramUpcasts ? m.paramUpcasts[i] : undefined
if (upcastMap) {
return { emit: emitUpcasts(pVar, upcastMap), resultVar: pVar }
return { emit: emitUpcasts(pVar, [upcastMap]), resultVar: pVar }
} else {
return { emit: emitToken(''), resultVar: pVar }
}
Expand Down Expand Up @@ -159,13 +159,17 @@ export const emitVariant = (v: Variant, typeDef: TypeDef, module: Module, ctx: C

export const emitUpcastFn = (v: Variant, typeDef: TypeDef, module: Module, ctx: Context): EmitNode => {
const params = ['value', 'Self', ...typeDef.generics.map(g => g.name.value)]
const selfG = 'for(const [trait,impl] of Self){value[trait]=impl;}'
const selfG = 'Object.assign(value, Self);'
const gs = typeDef.generics.flatMap(g => {
const fields = v.fieldDefs.filter(f => virtualTypeToString(f.type!) === g.name.value).map(f => f.name.value)
if (fields.length === 0) return []
return [
`for(const [trait,impl] of ${g.name.value}){${fields.map(f => `value.value.${f}[trait]=impl;`).join('')}}`
]
const fs = fields
.map(f => {
const fAccess = `value.value.${f}`
return `${fAccess}.upcast(${fAccess}, ...${g.name.value});`
})
.join('')
return [`if(${g.name.value}!==undefined){${fs}}`]
})
return emitToken(`function(${params.join(',')}) {${selfG}${gs.join('')}}`)
}
2 changes: 1 addition & 1 deletion src/scope/vid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export interface MethodDef {
kind: 'method-def'
fn: FnDef
rel: InstanceRelation
paramUpcasts?: (Upcast[] | undefined)[]
paramUpcasts?: (Upcast | undefined)[]
}

export interface VirtualIdentifierMatch<D = Definition> {
Expand Down
4 changes: 2 additions & 2 deletions src/semantic/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ import {
import { checkClosureExpr, checkExpr } from './expr'
import { checkPattern } from './match'
import { typeNames } from './type-def'
import { Upcast, makeUpcasts, upcast } from './upcast'
import { Upcast, makeUpcast, upcast } from './upcast'
import { VirtualUseExpr, useExprToVids } from './use-expr'

export interface Checked {
Expand Down Expand Up @@ -505,7 +505,7 @@ const checkImplDef = (implDef: ImplDef, ctx: Context) => {
m.paramUpcasts = m.fn.params.map(p => {
const genericMaps = [makeGenericMapOverStructure(implDef.rel!.forType, p.type!)]
const resolvedType = resolveType(p.type!, genericMaps, ctx)
return makeUpcasts(resolvedType, m.rel.forType, ctx)
return makeUpcast(resolvedType, m.rel.forType, ctx)
})
}
module.relImports.push(...implDef.superMethods.map(i => i.rel))
Expand Down
44 changes: 19 additions & 25 deletions src/semantic/upcast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,49 +4,43 @@ import { InstanceRelation, relTypeName, resolveTypeImpl } from '../scope/trait'
import { VirtualType } from '../typecheck'
import { makeGenericMapOverStructure } from '../typecheck/generic'

export type Upcast = { [trait: string]: InstanceRelation }
export interface Upcast {
self: { [trait: string]: InstanceRelation }
generics: Upcast[]
}

export const upcast = (virtual: Partial<Virtual>, type: VirtualType, traitType: VirtualType, ctx: Context): void => {
const upcastMap = makeUpcasts(type, traitType, ctx)
const upcastMap = makeUpcast(type, traitType, ctx)
if (upcastMap) {
writeUpcasts(virtual, upcastMap)
writeUpcast(virtual, upcastMap)
}
}

export const makeUpcasts = (type: VirtualType, traitType: VirtualType, ctx: Context): Upcast[] => {
export const makeUpcast = (type: VirtualType, traitType: VirtualType, ctx: Context): Upcast | undefined => {
const res = resolveTypeImpl(type, traitType, ctx)
if (!res) return []
if (!res) return undefined
const genericMap = makeGenericMapOverStructure(type, res.impl.forType)
const upcasts = []
upcasts.push({ [relTypeName(res.trait)]: res.impl })
const upcast: Upcast = { self: { [relTypeName(res.trait)]: res.impl }, generics: [] }
for (const g of res.impl.generics) {
const gUpcast: Upcast = {}
const gUpcast: Upcast = { self: {}, generics: [] }
const concreteG = genericMap.get(g.name)
if (concreteG) {
for (const b of g.bounds) {
const gRes = resolveTypeImpl(concreteG, b, ctx)
if (gRes) {
gUpcast[relTypeName(gRes.trait)] = gRes.impl
ctx.moduleStack.at(-1)!.relImports.push(gRes.impl)
const gUp = makeUpcast(concreteG, b, ctx)
if (gUp) {
upcast.generics.push(gUp)
} else {
// upcast.generics.push({ self: {}, generics: [] })
}
}
}
upcasts.push(gUpcast)
upcast.generics.push(gUpcast)
}
ctx.moduleStack.at(-1)!.relImports.push(res.impl)
return upcasts
return upcast
}

const writeUpcasts = (virtual: Partial<Virtual>, upcasts: Upcast[]): void => {
const writeUpcast = (virtual: Partial<Virtual>, upcast: Upcast): void => {
virtual.upcasts ??= []
for (let i = 0; i < upcasts.length; i++) {
const up = upcasts[i]
if (virtual.upcasts.length > i) {
for (const [k, v] of Object.entries(up)) {
virtual.upcasts[i][k] = v
}
} else {
virtual.upcasts.push(up)
}
}
virtual.upcasts.push(upcast)
}
4 changes: 1 addition & 3 deletions src/std/bool.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ Bool.Bool = value => ({
$noisType: 'std::bool::Bool',
value,
upcast: (value, self) => {
for (const [trait, impl] of self) {
value[trait] = impl
}
Object.assign(value, self)
}
})

Expand Down
4 changes: 1 addition & 3 deletions src/std/char.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ Char.Char = value => ({
$noisType: 'std::char::Char',
value,
upcast: (value, self) => {
for (const [trait, impl] of self) {
value[trait] = impl
}
Object.assign(value, self)
}
})

Expand Down
4 changes: 1 addition & 3 deletions src/std/float.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ Float.Float = value => ({
$noisType: 'std::float::Float',
value,
upcast: (value, self) => {
for (const [trait, impl] of self) {
value[trait] = impl
}
Object.assign(value, self)
}
})

Expand Down
4 changes: 1 addition & 3 deletions src/std/int.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ Int.Int = value => ({
$noisType: 'std::int::Int',
value,
upcast: (value, self) => {
for (const [trait, impl] of self) {
value[trait] = impl
}
Object.assign(value, self)
}
})

Expand Down
8 changes: 2 additions & 6 deletions src/std/list.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,10 @@ List.List = value => ({
$noisType: 'std::list::List',
value,
upcast: (value, self, t) => {
for (const [trait, impl] of self) {
value[trait] = impl
}
Object.assign(value, self)
if (t !== undefined) {
for (const item of value.value) {
for (const [itemTrait, itemImpl] of t) {
item[itemTrait] = itemImpl
}
item.upcast(item, ...t)
}
}
}
Expand Down
4 changes: 1 addition & 3 deletions src/std/string.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ String.String = value => ({
$noisType: 'std::string::String',
value,
upcast: (value, self) => {
for (const [trait, impl] of self) {
value[trait] = impl
}
Object.assign(value, self)
}
})

Expand Down

0 comments on commit ed849d3

Please sign in to comment.