Skip to content

Commit

Permalink
Codegen: literal patterns
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanjermakov committed Apr 20, 2024
1 parent 23974bc commit 7a18bd0
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 7 deletions.
15 changes: 12 additions & 3 deletions src/ast/match.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,18 @@ import { LexerToken } from '../lexer/lexer'
import { ParseNode, filterNonAstNodes } from '../parser'
import { nameLikeTokens } from '../parser/fns'
import { Typed } from '../semantic'
import { Expr, buildExpr, buildOperandExpr } from './expr'
import { Expr, buildExpr } from './expr'
import { AstNode } from './index'
import { FloatLiteral, Identifier, IntLiteral, Name, Operand, buildIdentifier, buildName } from './operand'
import {
FloatLiteral,
Identifier,
IntLiteral,
Name,
Operand,
buildIdentifier,
buildName,
buildOperand
} from './operand'
import { Block, buildBlock } from './statement'

export interface MatchExpr extends AstNode<'match-expr'>, Partial<Typed> {
Expand Down Expand Up @@ -66,7 +75,7 @@ export const buildPatternExpr = (node: ParseNode): PatternExpr => {
if (n.kind === 'number') {
return buildNumber(n)
}
return buildOperandExpr(node)
return buildOperand(node)
}

export interface ConPattern extends AstNode<'con-pattern'>, Partial<Typed> {
Expand Down
14 changes: 10 additions & 4 deletions src/codegen/js/expr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -395,11 +395,12 @@ export const emitMatchExpr = (matchExpr: MatchExpr, ctx: Context, resultVar: str
export const emitPatternExprCondition = (patternExpr: PatternExpr, ctx: Context, sVar: string): EmitExpr => {
const resultVar = nextVariable(ctx)
switch (patternExpr.kind) {
case 'con-pattern':
case 'con-pattern': {
const variantName = patternExpr.identifier.names.at(-1)!.value
const cond = emitToken(`${sVar}.$noisVariant===${jsString(variantName)}`, patternExpr.identifier.parseNode)
// TODO: nested patterns
return { emit: jsVariable(resultVar, cond), resultVar }
}
case 'list-pattern':
return { emit: jsVariable(resultVar, jsError('list-pattern')), resultVar }
case 'hole':
Expand All @@ -408,8 +409,12 @@ export const emitPatternExprCondition = (patternExpr: PatternExpr, ctx: Context,
case 'char-literal':
case 'int-literal':
case 'float-literal':
case 'bool-literal':
return { emit: jsVariable(resultVar, jsError('literal')), resultVar }
case 'bool-literal': {
const operandEmit = emitOperand(patternExpr, ctx)
// safe value equality check, since it is limited to builtin types
const cond = emitToken(`${sVar}.value===${operandEmit.resultVar}.value`)
return { emit: emitTree([operandEmit.emit, jsVariable(resultVar, cond)]), resultVar }
}
case 'string-interpolated':
return { emit: jsVariable(resultVar, jsError('string-interpolated')), resultVar }
case 'list-expr':
Expand Down Expand Up @@ -470,14 +475,15 @@ export const emitPattern = (pattern: Pattern, ctx: Context, assignVar: string, p
return jsVariable(fieldAssign, emitToken(`${assignVar}.value.${fieldAssign}`))
})
return emitTree([...patterns])
case 'string-interpolated':
case 'list-expr':
return emitToken(`${jsError(pattern.expr.kind).value};`)
case 'string-literal':
case 'char-literal':
case 'int-literal':
case 'float-literal':
case 'bool-literal':
case 'identifier':
return emitToken(`${jsError(pattern.expr.kind).value};`)
case 'hole':
return emitToken('')
default:
Expand Down
71 changes: 71 additions & 0 deletions src/e2e.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,4 +268,75 @@ pub fn main() {
expect(res.stdout.toString()).toEqual('4\n')
})
})

describe('match', () => {
it('int', async () => {
const files = {
'mod.no': `\
pub fn main() {
let a = match 4 {
2 { false }
4 { true }
_ { false }
}
println(a.trace())
}`
}
const res = run(await compile(files))
expect(res.stderr.toString()).toEqual('')
expect(res.stdout.toString()).toEqual('true\n')
})

it('float', async () => {
const files = {
'mod.no': `\
pub fn main() {
let a = match 4.5 {
2. { false }
4.5 { true }
_ { false }
}
println(a.trace())
}`
}
const res = run(await compile(files))
expect(res.stderr.toString()).toEqual('')
expect(res.stdout.toString()).toEqual('true\n')
})

it('string', async () => {
const files = {
'mod.no': `\
pub fn main() {
let a = match "foo" {
"" { false }
"bar" { false }
"foo" { true }
_ { false }
}
println(a.trace())
}`
}
const res = run(await compile(files))
expect(res.stderr.toString()).toEqual('')
expect(res.stdout.toString()).toEqual('true\n')
})

it('char', async () => {
const files = {
'mod.no': `\
pub fn main() {
let a = match 'a' {
'b' { false }
'a' { true }
_ { false }
}
println(a.trace())
}`
}
const res = run(await compile(files))
expect(res.stderr.toString()).toEqual('')
expect(res.stdout.toString()).toEqual('true\n')
})
})
})
1 change: 1 addition & 0 deletions src/semantic/match.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export const checkPattern = (
case 'int-literal':
case 'float-literal':
case 'string-literal':
case 'char-literal':
case 'bool-literal':
if (!refutable) {
addError(ctx, unexpectedRefutablePatternError(ctx, pattern.expr))
Expand Down

0 comments on commit 7a18bd0

Please sign in to comment.