Skip to content

Commit

Permalink
Codegen: con pattern destructuring for params
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanjermakov committed Mar 27, 2024
1 parent 506e431 commit ccba11a
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 22 deletions.
31 changes: 19 additions & 12 deletions src/codegen/js/expr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { relTypeName } from '../../scope/trait'
import { operatorImplMap } from '../../semantic/op'
import { ConcreteGeneric } from '../../typecheck'
import { unreachable } from '../../util/todo'
import { EmitNode, emitIntersperse, emitToken, emitTree, jsError, jsVariable } from './node'
import { EmitNode, emitToken, emitTree, jsError, jsVariable } from './node'
import { emitBlock, emitBlockStatements } from './statement'

export interface EmitExpr {
Expand Down Expand Up @@ -244,13 +244,14 @@ export const emitOperand = (operand: Operand, module: Module, ctx: Context): Emi
case 'match-expr':
return emitMatchExpr(operand, module, ctx, resultVar)
case 'closure-expr': {
const params = emitIntersperse(
operand.params.map(p => emitParam(p, module, ctx)),
','
)
const block = emitBlock(operand.block, module, ctx, true)
const params = operand.params.map(p => emitParam(p, module, ctx))
const statements = emitBlockStatements(operand.block, module, ctx, true)
const block = emitTree([emitToken('{'), ...params.map(p => p.emit), ...statements, emitToken('}')])
return {
emit: jsVariable(resultVar, emitTree([emitToken(`function(`), params, emitToken(')'), block])),
emit: jsVariable(
resultVar,
emitTree([emitToken(`function(${params.map(p => p.resultVar).join(',')})`), block])
),
resultVar
}
}
Expand Down Expand Up @@ -410,15 +411,21 @@ export const emitPatternExprCondition = (
}
}

export const emitParam = (param: Param, module: Module, ctx: Context): EmitNode => {
export const emitParam = (param: Param, module: Module, ctx: Context): EmitExpr => {
switch (param.pattern.expr.kind) {
case 'name':
return emitToken(param.pattern.expr.value, param.pattern.expr.parseNode)
return { emit: emitToken(''), resultVar: param.pattern.expr.value }
case 'hole':
return emitToken(nextVariable(ctx))
return { emit: emitToken(''), resultVar: nextVariable(ctx) }
case 'con-pattern':
// TODO
return emitToken('/*destructuring*/')
const paramVar = nextVariable(ctx)
const fields = param.pattern.expr.fieldPatterns.map(f => {
if (!f.name || f.pattern) {
return jsError('/*con pattern destructuring*/')
}
return jsVariable(f.name.value, emitToken(`${extractValue(paramVar)}.${f.name.value}`))
})
return { emit: emitTree(fields), resultVar: paramVar }
default:
return unreachable()
}
Expand Down
11 changes: 6 additions & 5 deletions src/codegen/js/statement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,15 @@ export const emitFnDef = (fnDef: FnDef, module: Module, ctx: Context, asProperty
if (!fnDef.block) return undefined
const name = fnDef.name.value
const params = fnDef.params.map(p => emitParam(p, module, ctx))
const generics = fnDef.generics.map(g => emitToken(g.name.value, g.name.parseNode))
const jsParams = emitIntersperse([...params, ...generics], ',')
const block = emitBlock(fnDef.block, module, ctx, true)
const generics = fnDef.generics.map(g => g.name.value)
const jsParams = [...params.map(p => p.resultVar), ...generics].join(',')
const statements = emitBlockStatements(fnDef.block, module, ctx, true)
const block = emitTree([emitToken('{'), ...params.map(p => p.emit), ...statements, emitToken('}')])
if (asProperty) {
return emitTree([emitToken(`${name}: function(`), jsParams, emitToken(')'), block], fnDef.name.parseNode)
return emitTree([emitToken(`${name}: function(${jsParams})`), block], fnDef.name.parseNode)
} else {
return emitTree(
[emitToken(`${fnDef.pub ? 'export ' : ''}function ${name}(`), jsParams, emitToken(')'), block],
[emitToken(`${fnDef.pub ? 'export ' : ''}function ${name}(${jsParams})`), block],
fnDef.name.parseNode
)
}
Expand Down
33 changes: 28 additions & 5 deletions src/e2e.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ const compileStd = async (): Promise<void> => {
}

const run = (ctx: Context): SpawnSyncReturns<Buffer> => {
if (ctx.errors.length > 0) throw Error('semantic errors')
return spawnSync('node', ['dist/test/mod.js'], { cwd: 'tmp' })
}

Expand All @@ -121,8 +122,8 @@ describe('e2e', () => {
it('minimal', async () => {
const files = { 'mod.no': 'pub fn main() {}' }
const res = run(await compile(files))
expect(res.stdout.toString()).toEqual('')
expect(res.stderr.toString()).toEqual('')
expect(res.stdout.toString()).toEqual('')
})

it('hello', async () => {
Expand All @@ -133,8 +134,8 @@ pub fn main(): Unit {
}`
}
const res = run(await compile(files))
expect(res.stdout.toString()).toEqual('Hello, World!\n')
expect(res.stderr.toString()).toEqual('')
expect(res.stdout.toString()).toEqual('Hello, World!\n')
})

it('example', async () => {
Expand Down Expand Up @@ -190,11 +191,13 @@ pub fn main() {
}
fn rule110(n: Int) {
let init = [true]
println(fmtGen(init, n))
range(1, n).fold(|gen, _| {
let ng = nextGen(gen)
println(fmtGen(ng, n))
ng
}, [true])
}, init)
unit
}
Expand Down Expand Up @@ -223,9 +226,29 @@ fn fmtGen(gen: List<Bool>, total: Int): String {
}`
}
const res = run(await compile(files))
expect(res.stderr.toString()).toEqual('')
expect(res.stdout.toString()).toEqual(
' xx\n xxx\n xx x\n xxxxx\n xx x\n xxx xx\n xx x xxx\n xxxxxxx x\nxx xxx\n'
' x\n xx\n xxx\n xx x\n xxxxx\n xx x\n xxx xx\n xx x xxx\n xxxxxxx x\nxx xxx\n'
)
expect(res.stderr.toString()).toEqual('')
})

describe('param destructuring', () => {
it('con pattern', async () => {
const files = {
'mod.no': `\
type Foo(x: Int, y: String)
pub fn main() {
println(foo(Foo(1, "y")))
}
fn foo(Foo(y ): Foo): String {
y
}`
}
const res = run(await compile(files))
expect(res.stderr.toString()).toEqual('')
expect(res.stdout.toString()).toEqual('y\n')
})
})
})

0 comments on commit ccba11a

Please sign in to comment.