Skip to content

Commit

Permalink
Match expression
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanjermakov committed Jun 11, 2023
1 parent 1658fa4 commit 291763e
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 24 deletions.
29 changes: 14 additions & 15 deletions data/features.no
Original file line number Diff line number Diff line change
Expand Up @@ -73,23 +73,22 @@ let Vec(x) = vec
// destructuring alias
let Vec(x: b) = vec

// TODO: match
// let b = match 4 {
// 1 -> "one",
// n if n % 2 == 0 -> {
// if (n > 5) { "good" } else { "not good" }
// },
// _ -> "whatever",
// }
// match
let b = match 4 {
1 -> "one",
n if n % 2 == 0 -> {
if (n > 5) { "good" } else { "not good" }
},
_ -> "whatever",
}

// TODO: destructuring & patterns
// match type
// let b = match vec {
// Vec { x: 1, y: 1 } -> "one",
// Vec { x: 1, .. } -> "x one whatever",
// Vec { x } if x != 4 -> format("x is {} not 4", x),
// _ -> "whatever",
// }
let b = match vec {
Vec(x: 1, y: 1) -> "one",
Vec(x: 1, ..) -> "x one whatever",
Vec(x) if x != 4 -> format("x is {} not 4", x),
_ -> "whatever",
}

// TODO: if match
// if let Option { value } = Some(42) {
Expand Down
15 changes: 12 additions & 3 deletions nois.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,14 @@ statement ::= var-def | fn-def | kind-def | impl-def | type-def | retu
sub-expr ::= prefix-op operand | operand postfix-op?
;
operand ::= if-expr
| match-expr
| lambda-expr
| O-PAREN expr C-PAREN
| list-expr
| STRING
| CHAR
| NUMBER
| INT
| FLOAT
| IDENTIFIER
| type-expr
;
Expand Down Expand Up @@ -64,7 +66,6 @@ statement ::= var-def | fn-def | kind-def | impl-def | type-def | retu
;
spread-op ::= PERIOD PERIOD
;

postfix-op ::= call-op | con-op
;
call-op ::= args
Expand Down Expand Up @@ -93,7 +94,15 @@ type-annot ::= COLON type-expr
;
if-expr ::= IF-KEYWORD expr block (ELSE-KEYWORD block)?
;
pattern ::= con-pattern | IDENTIFIER | hole
match-expr ::= MATCH-KEYWORD expr match-clauses
;
match-clauses ::= O-BRACE (match-clause (COMMA match-clause)*)? COMMA? C-BRACE
;
match-clause ::= pattern guard? ARROW (block | expr)
;
guard ::= IF-KEYWORD expr
;
pattern ::= con-pattern | STRING | CHAR | prefix-op? (INT | FLOAT) | IDENTIFIER | hole
;
con-pattern ::= IDENTIFIER con-pattern-params
;
Expand Down
5 changes: 5 additions & 0 deletions src/lexer/lexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const lexerTokenKinds = <const>[
'else-keyword',
'return-keyword',
'for-keyword',
'match-keyword',

// punctuation
'o-paren',
Expand All @@ -25,6 +26,7 @@ export const lexerTokenKinds = <const>[
'comma',
'equals',
'underscore',
'arrow',

// operators
'plus',
Expand Down Expand Up @@ -75,6 +77,7 @@ export const constTokenKindMap: Map<TokenKind, string> = new Map([
['let-keyword', 'let'],
['fn-keyword', 'fn'],
['for-keyword', 'for'],
['match-keyword', 'match'],

['o-paren', '('],
['c-paren', ')'],
Expand All @@ -85,6 +88,8 @@ export const constTokenKindMap: Map<TokenKind, string> = new Map([
['o-angle', '<'],
['c-angle', '>'],

['arrow', '->'],

['plus', '+'],
['minus', '-'],
['asterisk', '*'],
Expand Down
81 changes: 75 additions & 6 deletions src/parser/parser-fns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,14 +235,17 @@ const parseSubExpr = (parser: Parser): void => {
}

/**
* operand ::= if-expr | lambda-expr | O-PAREN expr C-PAREN | list-expr | STRING | CHAR | NUMBER | IDENTIFIER | type-expr
* operand ::= if-expr | match-expr | lambda-expr | O-PAREN expr C-PAREN | list-expr | STRING | CHAR | INT | FLOAT
* | IDENTIFIER | type-expr
*/
const parseOperand = (parser: Parser): void => {
const dynamicTokens: TokenKind[] = ['string', 'char', 'int', 'float', 'identifier']

const mark = parser.open()
if (parser.at('if-keyword')) {
parseIfExpr(parser)
} else if (parser.at('match-keyword')) {
parseMatchExpr(parser)
} else if (parser.at('pipe')) {
parseLambdaExpr(parser)
} else if (parser.at('o-paren')) {
Expand Down Expand Up @@ -312,12 +315,15 @@ const parseInfixOp = (parser: Parser): void => {
parser.close(mark, 'access-op')
return
}
if (parser.nth(0) === 'equals' && parser.nth(1) === 'equals') {
if (parser.at('equals') && parser.nth(1) === 'equals') {
parser.advance()
parser.advance()
parser.close(mark, 'eq-op')
return
}
if (parser.consume('excl')) {
if (parser.at('excl') && parser.nth(1) === 'equals') {
parser.advance()
parser.advance()
parser.close(mark, 'ne-op')
return
}
Expand Down Expand Up @@ -587,14 +593,77 @@ const parseIfExpr = (parser: Parser): void => {
}

/**
* pattern ::= con-pattern | IDENTIFIER | hole
* match-expr ::= MATCH-KEYWORD expr match-clauses
*/
const parseMatchExpr = (parser: Parser): void => {
const mark = parser.open()
parser.expect('match-keyword')
parseExpr(parser)
parseMatchClauses(parser)
parser.close(mark, 'match-expr')
}

/**
* match-clauses ::= O-BRACE (match-clause (COMMA match-clause)*)? COMMA? C-BRACE
*/
const parseMatchClauses = (parser: Parser): void => {
const mark = parser.open()
parser.expect('o-brace')
while (!parser.at('c-brace') && !parser.eof()) {
parseMatchClause(parser)
if (!parser.at('c-brace')) {
parser.expect('comma')
}
}
parser.expect('c-brace')
parser.close(mark, 'match-clauses')
}

/**
* match-clause ::= pattern guard? ARROW (block | expr)
*/
const parseMatchClause = (parser: Parser): void => {
const mark = parser.open()
parsePattern(parser)
if (parser.at('if-keyword')) {
parseGuard(parser)
}
parser.expect('arrow')
if (parser.at('o-brace')) {
parseBlock(parser)
} else {
parseExpr(parser)
}
parser.close(mark, 'match-clause')
}

/**
* guard ::= IF-KEYWORD expr
*/
const parseGuard = (parser: Parser): void => {
const mark = parser.open()
parser.expect('if-keyword')
parseExpr(parser)
parser.close(mark, 'guard')
}

/**
* pattern ::= con-pattern | STRING | CHAR | prefix-op? (INT | FLOAT) | IDENTIFIER | hole
*/
const parsePattern = (parser: Parser): void => {
const mark = parser.open()
if (parser.at('identifier') && parser.nth(1) === 'o-paren') {
parseConPattern(parser)
} else if (parser.at('identifier')) {
parser.expect('identifier')
} else if (parser.consume('string')) {
} else if (parser.consume('char')) {
} else if (parser.atAny(prefixOpFirstTokens) || parser.at('int') || parser.at('float')) {
if (parser.atAny(prefixOpFirstTokens)) {
parsePrefixOp(parser)
}
if (parser.consume('int')) {
} else if (parser.consume('float')) {
}
} else if (parser.consume('identifier')) {
} else if (parser.at('underscore')) {
parseHole(parser)
} else {
Expand Down
4 changes: 4 additions & 0 deletions src/parser/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ export const treeKinds = <const>[
'type-expr',
'type-params',
'if-expr',
'match-expr',
'match-clauses',
'match-clause',
'guard',
'pattern',
'con-pattern',
'con-pattern-params',
Expand Down

0 comments on commit 291763e

Please sign in to comment.