Skip to content

Commit

Permalink
Syntax: list patterns
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanjermakov committed Apr 19, 2024
1 parent 2639e42 commit 5dc90f8
Show file tree
Hide file tree
Showing 7 changed files with 46 additions and 3 deletions.
4 changes: 3 additions & 1 deletion nois.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -137,14 +137,16 @@ pattern ::= pattern-bind? pattern-expr
;
pattern-bind ::= NAME AT
;
pattern-expr ::= string | CHAR | number | bool | hole | NAME | con-pattern
pattern-expr ::= string | CHAR | number | bool | hole | NAME | con-pattern | list-pattern
;
con-pattern ::= identifier con-pattern-params
;
con-pattern-parms ::= O-PAREN (field-pattern (COMMA field-pattern)*)? COMMA? C-PAREN
;
field-pattern ::= NAME (COLON pattern)?
;
list-pattern ::= O-BRACKET (pattern (COMMA pattern)*)? COMMA? C-BRACKET
;
hole ::= UNDERSCORE
;
number ::= MINUS? (INT | FLOAT)
Expand Down
1 change: 1 addition & 0 deletions src/ast/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ export const astKinds = <const>[
'match-clause',
'pattern',
'con-pattern',
'list-pattern',
'field-pattern',
'hole',
'identifier',
Expand Down
11 changes: 10 additions & 1 deletion src/ast/match.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export const buildPattern = (node: ParseNode): Pattern => {
return { kind: 'pattern', parseNode: node, name, expr }
}

export type PatternExpr = Name | ConPattern | Operand | Hole
export type PatternExpr = Name | ConPattern | ListPattern | Operand | Hole

export const buildPatternExpr = (node: ParseNode): PatternExpr => {
const n = filterNonAstNodes(node)[0]
Expand Down Expand Up @@ -81,6 +81,15 @@ export const buildConPattern = (node: ParseNode): ConPattern => {
return { kind: 'con-pattern', parseNode: node, identifier, fieldPatterns }
}

export interface ListPattern extends AstNode<'list-pattern'>, Partial<Typed> {
itemPatterns: Pattern[]
}

export const buildListPattern = (node: ParseNode): ListPattern => {
const nodes = filterNonAstNodes(node)
return { kind: 'list-pattern', parseNode: node, itemPatterns: nodes.map(buildPattern) }
}

export interface FieldPattern extends AstNode<'field-pattern'> {
name: Name
pattern?: Pattern
Expand Down
2 changes: 2 additions & 0 deletions src/codegen/js/expr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,8 @@ export const emitPatternExprCondition = (patternExpr: PatternExpr, ctx: Context,
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':
return { emit: jsVariable(resultVar, emitToken('true')), resultVar }
case 'string-literal':
Expand Down
20 changes: 19 additions & 1 deletion src/parser/fns/match.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,15 @@ export const parsePatternBind = (parser: Parser): void => {
}

/**
* pattern-expr ::= STRING | CHAR | number | bool | hole | NAME | con-pattern
* pattern-expr ::= STRING | CHAR | number | bool | hole | NAME | con-pattern | list-pattern
*/
export const parsePatternExpr = (parser: Parser): void => {
const mark = parser.open()
const isCon = (parser.nth(1) === 'colon' && parser.nth(2) === 'colon') || parser.nth(1) === 'o-paren'
if (parser.atAny(nameLikeTokens) && isCon) {
parseConPattern(parser)
} else if (parser.at('o-bracket')) {
parseListPattern(parser)
} else if (parser.atAny(nameLikeTokens)) {
parser.expectAny(nameLikeTokens)
} else if (parser.at('d-quote')) {
Expand All @@ -110,6 +112,22 @@ export const parseConPattern = (parser: Parser): void => {
parser.close(mark, 'con-pattern')
}

/**
* list-pattern ::= O-BRACKET (pattern (COMMA pattern)*)? COMMA? C-BRACKET
*/
export const parseListPattern = (parser: Parser): void => {
const mark = parser.open()
parser.expect('o-bracket')
while (!parser.eof()) {
parsePattern(parser)
if (!parser.at('c-bracket')) {
parser.expect('comma')
}
}
parser.expect('c-bracket')
parser.close(mark, 'list-pattern')
}

/**
* con-pattern-params::= O-PAREN (field-pattern (COMMA field-pattern)*)? COMMA? C-PAREN
*/
Expand Down
1 change: 1 addition & 0 deletions src/parser/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ export const treeKinds = <const>[
'pattern-expr',
'con-pattern',
'con-pattern-params',
'list-pattern',
'field-pattern',
'hole',
'number',
Expand Down
10 changes: 10 additions & 0 deletions src/semantic/match.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { NameDef, resolveVid } from '../scope/vid'
import { VidType, VirtualFnType, VirtualType, isAssignable } from '../typecheck'
import { makeGenericMapOverStructure, resolveType } from '../typecheck/generic'
import { unknownType } from '../typecheck/type'
import { unreachable } from '../util/todo'
import {
nonDestructurableTypeError,
notFoundError,
Expand Down Expand Up @@ -42,6 +43,13 @@ export const checkPattern = (
checkOperand(expr.operand, ctx)
expr.type = expr.operand.type
break
case 'list-pattern':
if (!refutable) {
addError(ctx, unexpectedRefutablePatternError(ctx, pattern.expr))
break
}
// TODO: check item patterns
break
case 'con-pattern':
if (expectedType.kind !== 'vid-type') {
addError(ctx, nonDestructurableTypeError(ctx, pattern.expr, expectedType))
Expand All @@ -55,6 +63,8 @@ export const checkPattern = (
})
expr.type ??= unknownType
break
default:
unreachable()
}

if (pattern.name) {
Expand Down

0 comments on commit 5dc90f8

Please sign in to comment.