Skip to content

Commit

Permalink
Destructuring & patterns
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanjermakov committed Jun 11, 2023
1 parent 807c37f commit 85c6e53
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 30 deletions.
11 changes: 7 additions & 4 deletions data/features.no
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,19 @@ fn foo(): Unit {
// alternative syntax
Vec2.fmt(vec)

// TODO: method call syntax for ambiguous methods
// Vec2.Display.fmt(vec)
// method call syntax for ambiguous methods
Vec2.Display.fmt(vec)

// if
let a = if True { 4 } else { 2 + 6 }

// TODO: loops

// TODO: destructuring
// let Vec { x } = vec
// destructuring
let Vec(x) = vec

// destructuring alias
let Vec(x: b) = vec

// TODO: match
// let b = match 4 {
Expand Down
14 changes: 11 additions & 3 deletions nois.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module ::= statement*
;
statement ::= var-def | fn-def | kind-def | impl-def | type-def | return-stmt | expr
;
var-def ::= LET-KEYWORD IDENTIFIER type-annot? EQUALS expr
var-def ::= LET-KEYWORD pattern type-annot? EQUALS expr
;
fn-def ::= FN-KEYWORD type-expr params? type-annot? block?
;
Expand Down Expand Up @@ -78,7 +78,7 @@ statement ::= var-def | fn-def | kind-def | impl-def | type-def | retu
;
params ::= O-PAREN (param (COMMA param)*)? COMMA? C-PAREN
;
param ::= IDENTIFIER type-annot?
param ::= pattern type-annot?
;
block ::= O-BRACE statement* C-BRACE
;
Expand All @@ -90,5 +90,13 @@ type-annot ::= COLON type-expr
;
if-expr ::= IF-KEYWORD expr block (ELSE-KEYWORD block)?
;
hole ::= UNDERSCORE
pattern ::= con-pattern | IDENTIFIER | hole
;
con-pattern ::= IDENTIFIER con-pattern-params
;
con-pattern-params::= O-PAREN (field-pattern (COMMA field-pattern)*)? COMMA? C-PAREN
;
field-pattern ::= IDENTIFIER (COLON pattern)? | spread-op
;
hole ::= UNDERSCORE
;
81 changes: 78 additions & 3 deletions src/parser/parser-fns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,12 @@ const parseStatement = (parser: Parser): void => {
}

/**
* var-def ::= LET-KEYWORD IDENTIFIER type-annot? EQUALS expr
* var-def ::= LET-KEYWORD pattern type-annot? EQUALS expr
*/
const parseVarDef = (parser: Parser): void => {
const mark = parser.open()
parser.expect('let-keyword')
parser.expect('identifier')
parsePattern(parser)
if (parser.at('colon')) {
parseTypeAnnot(parser)
}
Expand Down Expand Up @@ -357,6 +357,13 @@ const parsePrefixOp = (parser: Parser): void => {
parser.close(mark, 'prefix-op')
}

const parseSpreadOp = (parser: Parser): void => {
const mark = parser.open()
parser.expect('period')
parser.expect('period')
parser.close(mark, 'spread-op')
}

/**
* postfix-op ::= call-op
*/
Expand Down Expand Up @@ -484,7 +491,7 @@ const parseParams = (parser: Parser): void => {
*/
const parseParam = (parser: Parser): void => {
const mark = parser.open()
parser.expect('identifier')
parsePattern(parser)
if (parser.at('colon')) {
parseTypeAnnot(parser)
}
Expand Down Expand Up @@ -560,6 +567,74 @@ const parseIfExpr = (parser: Parser): void => {
parser.close(mark, 'if-expr')
}

/**
* pattern ::= con-pattern | 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.at('underscore')) {
parseHole(parser)
} else {
parser.advanceWithError('expected pattern')
}
parser.close(mark, 'pattern')
}

/**
* con-pattern ::= IDENTIFIER con-pattern-params
*/
const parseConPattern = (parser: Parser): void => {
const mark = parser.open()
parser.expect('identifier')
parseConPatternParams(parser)
parser.close(mark, 'con-pattern')
}

/**
* con-pattern-params::= O-PAREN (field-pattern (COMMA field-pattern)*)? COMMA? C-PAREN
*/
const parseConPatternParams = (parser: Parser): void => {
const mark = parser.open()
parser.expect('o-paren')
while (!parser.at('c-paren') && !parser.eof()) {
parseFieldPattern(parser)
if (!parser.at('c-paren')) {
parser.expect('comma')
}
}
parser.expect('c-paren')
parser.close(mark, 'con-pattern-params')
}

/**
* field-pattern ::= IDENTIFIER (COLON pattern) | spread-op
*/
const parseFieldPattern = (parser: Parser): void => {
const mark = parser.open()
if (parser.at('identifier')) {
parser.expect('identifier')
if (parser.consume('colon')) {
parsePattern(parser)
}
} else if (parser.at('period')) {
parseSpreadOp(parser)
}
parser.close(mark, 'field-pattern')
}

/**
* hole ::= UNDERSCORE
*/
const parseHole = (parser: Parser): void => {
const mark = parser.open()
parser.expect('underscore')
parser.close(mark, 'hole')
}

const parseTodo = (parser: Parser): void => {
parser.advanceWithError('todo')
}
26 changes: 6 additions & 20 deletions src/parser/parser.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,11 @@ describe('parser', () => {
describe('parse var-def', () => {
it('miss identifier', () => {
const { errors } = parse('let = 4')
expect(errors.length).toEqual(1)
expect(errors.length).toEqual(2)
expect(errors[0]).toEqual({
'expected': ['identifier'],
'got': {
'kind': 'equals',
'location': {
'end': 4,
'start': 4
},
'value': '='
}
'expected': [],
'got': { 'kind': 'equals', 'location': { 'end': 4, 'start': 4 }, 'value': '=' },
'message': 'expected pattern'
})
})
})
Expand All @@ -64,23 +58,15 @@ describe('parser', () => {
{
'block': [
{ 'o-brace': '{' },
{
'statement': [{
'expr': [{ 'sub-expr': [{ 'operand': [{ 'identifier': 'b' }] }] }]
}]
},
{ 'statement': [{ 'expr': [{ 'sub-expr': [{ 'operand': [{ 'identifier': 'b' }] }] }] }] },
{ 'c-brace': '}' }
]
},
{ 'else-keyword': 'else' },
{
'block': [
{ 'o-brace': '{' },
{
'statement': [{
'expr': [{ 'sub-expr': [{ 'operand': [{ 'identifier': 'c' }] }] }]
}]
},
{ 'statement': [{ 'expr': [{ 'sub-expr': [{ 'operand': [{ 'identifier': 'c' }] }] }] }] },
{ 'c-brace': '}' }
]
}
Expand Down
5 changes: 5 additions & 0 deletions src/parser/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ export const treeKinds = <const>[
'type-expr',
'type-params',
'if-expr',
'pattern',
'con-pattern',
'con-pattern-params',
'field-pattern',
'hole',
]
export type TreeKind = typeof treeKinds[number]

Expand Down

0 comments on commit 85c6e53

Please sign in to comment.