Skip to content

Commit

Permalink
While & for loop; assign operator
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanjermakov committed Jun 12, 2023
1 parent 291763e commit e89977b
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 26 deletions.
22 changes: 16 additions & 6 deletions data/features.no
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ let vec = Vec2(x: 2., y: 4.)
vec.x

// function call
print("hello")
println("hello")

// method call
vec.fmt()
Expand All @@ -65,7 +65,17 @@ Vec2.Display.fmt(vec)
// if
let d = if True { 4 } else { 2 + 6 }

// TODO: loops
// while loop
let e = while a < 5 {
a = a + 1
}

// TODO: while let loop

// for loop
let f = for g in [1, 2, 3] {
println(g)
}

// destructuring
let Vec(x) = vec
Expand All @@ -92,17 +102,17 @@ let b = match vec {

// TODO: if match
// if let Option { value } = Some(42) {
// print("value is {}", value)
// println("value is {}", value)
// } else {
// print("no value")
// println("no value")
// }

// lambda
let square = |a| a^2
let here = || print("here")
let here = || println("here")

// fn reference
let p = print
let p = println

// method reference
// equivalent to |v: Vec2| v.fmt()
Expand Down
10 changes: 8 additions & 2 deletions nois.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ statement ::= var-def | fn-def | kind-def | impl-def | type-def | retu
sub-expr ::= prefix-op operand | operand postfix-op?
;
operand ::= if-expr
| while-expr
| for-expr
| match-expr
| lambda-expr
| O-PAREN expr C-PAREN
Expand All @@ -38,12 +40,11 @@ statement ::= var-def | fn-def | kind-def | impl-def | type-def | retu
| INT
| FLOAT
| IDENTIFIER
| type-expr
;
list-expr ::= O-BRACKET (expr (COMMA expr)*)? COMMA? C-BRACKET
;
infix-op ::= add-op | sub-op | mul-op | div-op | exp-op | mod-op | access-op | eq-op | ne-op
| ge-op | le-op | gt-op | lt-op | and-op | or-op;
| ge-op | le-op | gt-op | lt-op | and-op | or-op | assign-op;
add-op ::= PLUS;
sub-op ::= MINUS;
mul-op ::= ASTERISK;
Expand All @@ -59,6 +60,7 @@ statement ::= var-def | fn-def | kind-def | impl-def | type-def | retu
lt-op ::= O-ANGLE;
and-op ::= AMPERSAND AMPERSAND;
or-op ::= PIPE PIPE;
assign-op ::= EQUALS;

prefix-op ::= add-op | sub-op | not-op | spread-op
;
Expand Down Expand Up @@ -94,6 +96,10 @@ type-annot ::= COLON type-expr
;
if-expr ::= IF-KEYWORD expr block (ELSE-KEYWORD block)?
;
while-expr ::= WHILE-KEYWORD expr block
;
for-expr ::= FOR-KEYWORD pattern IN-KEYWORD expr block
;
match-expr ::= MATCH-KEYWORD expr match-clauses
;
match-clauses ::= O-BRACE (match-clause (COMMA match-clause)*)? COMMA? C-BRACE
Expand Down
4 changes: 4 additions & 0 deletions src/lexer/lexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ export const lexerTokenKinds = <const>[
'if-keyword',
'else-keyword',
'return-keyword',
'while-keyword',
'for-keyword',
'in-keyword',
'match-keyword',

// punctuation
Expand Down Expand Up @@ -76,7 +78,9 @@ export const constTokenKindMap: Map<TokenKind, string> = new Map([
['impl-keyword', 'impl'],
['let-keyword', 'let'],
['fn-keyword', 'fn'],
['while-keyword', 'while'],
['for-keyword', 'for'],
['in-keyword', 'in'],
['match-keyword', 'match'],

['o-paren', '('],
Expand Down
54 changes: 37 additions & 17 deletions src/parser/parser-fns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@ const prefixOpFirstTokens: TokenKind[] = ['excl', 'minus', 'period', 'plus']
const postfixOpFirstTokens: TokenKind[] = ['o-paren']
const infixOpFirstTokens: TokenKind[] = ['ampersand', 'asterisk', 'c-angle', 'caret', 'equals', 'excl', 'minus',
'o-angle', 'percent', 'period', 'pipe', 'plus', 'slash']
const exprFirstTokens: TokenKind[] = ['char', 'identifier', 'if-keyword', 'int', 'float', 'o-paren',
'string', ...prefixOpFirstTokens]
const statementFirstTokens: TokenKind[] = ['let-keyword', 'fn-keyword', 'kind-keyword', 'impl-keyword', 'type-keyword',
'return-keyword', 'type-keyword', ...exprFirstTokens]
const exprFirstTokens: TokenKind[] = ['char', 'identifier', 'if-keyword', 'while-keyword', 'for-keyword',
'match-keyword', 'int', 'float', 'o-paren', 'string', ...prefixOpFirstTokens]
const paramFirstTokens: TokenKind[] = ['identifier']

/**
Expand All @@ -17,11 +15,7 @@ const paramFirstTokens: TokenKind[] = ['identifier']
export const parseModule = (parser: Parser): void => {
const mark = parser.open()
while (!parser.eof()) {
if (parser.atAny(statementFirstTokens)) {
parseStatement(parser)
} else {
parser.advanceWithError('expected statement')
}
parseStatement(parser)
}
parser.close(mark, 'module')
}
Expand Down Expand Up @@ -244,6 +238,10 @@ const parseOperand = (parser: Parser): void => {
const mark = parser.open()
if (parser.at('if-keyword')) {
parseIfExpr(parser)
} else if (parser.at('while-keyword')) {
parseWhileExpr(parser)
} else if (parser.at('for-keyword')) {
parseForExpr(parser)
} else if (parser.at('match-keyword')) {
parseMatchExpr(parser)
} else if (parser.at('pipe')) {
Expand All @@ -254,8 +252,6 @@ const parseOperand = (parser: Parser): void => {
parser.expect('c-paren')
} else if (parser.at('o-bracket')) {
parseListExpr(parser)
} else if (parser.at('identifier') && parser.nth(1) === 'o-angle') {
parseTypeExpr(parser)
} else if (parser.atAny(dynamicTokens)) {
parser.expectAny(dynamicTokens)
} else {
Expand Down Expand Up @@ -283,7 +279,7 @@ const parseListExpr = (parser: Parser): void => {

/**
* infix-op ::= add-op | sub-op | mul-op | div-op | exp-op | mod-op | access-op | eq-op | ne-op | ge-op | le-op | gt-op
* | lt-op | and-op | or-op;
* | lt-op | and-op | or-op | assign-op;
*/
const parseInfixOp = (parser: Parser): void => {
const mark = parser.open()
Expand Down Expand Up @@ -353,6 +349,10 @@ const parseInfixOp = (parser: Parser): void => {
parser.close(mark, 'or-op')
return
}
if (parser.consume('equals')) {
parser.close(mark, 'assign-op')
return
}

parser.advanceWithError('expected infix operator')
}
Expand Down Expand Up @@ -530,11 +530,7 @@ const parseBlock = (parser: Parser): void => {
const mark = parser.open()
parser.expect('o-brace')
while (!parser.at('c-brace') && !parser.eof()) {
if (parser.atAny(statementFirstTokens)) {
parseStatement(parser)
} else {
parser.advanceWithError('expected statement or `}`')
}
parseStatement(parser)
}
parser.expect('c-brace')
parser.close(mark, 'block')
Expand Down Expand Up @@ -592,6 +588,30 @@ const parseIfExpr = (parser: Parser): void => {
parser.close(mark, 'if-expr')
}

/**
* while-expr ::= WHILE-KEYWORD expr block
*/
const parseWhileExpr = (parser: Parser): void => {
const mark = parser.open()
parser.expect('while-keyword')
parseExpr(parser)
parseBlock(parser)
parser.close(mark, 'while-expr')
}

/**
* for-expr ::= FOR-KEYWORD pattern IN-KEYWORD expr block
*/
const parseForExpr = (parser: Parser): void => {
const mark = parser.open()
parser.expect('for-keyword')
parsePattern(parser)
parser.expect('in-keyword')
parseExpr(parser)
parseBlock(parser)
parser.close(mark, 'for-expr')
}

/**
* match-expr ::= MATCH-KEYWORD expr match-clauses
*/
Expand Down
2 changes: 1 addition & 1 deletion src/parser/parser.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ describe('parser', () => {
expect(errors[0]).toEqual({
'expected': [],
'got': { 'kind': 'c-paren', 'location': { 'end': 8, 'start': 8 }, 'value': ')' },
'message': 'expected statement or `}`'
'message': 'expected statement'
})
})

Expand Down
3 changes: 3 additions & 0 deletions src/parser/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export const treeKinds = <const>[
'lt-op',
'and-op',
'or-op',
'assign-op',
'prefix-op',
'not-op',
'spread-op',
Expand All @@ -54,6 +55,8 @@ export const treeKinds = <const>[
'type-expr',
'type-params',
'if-expr',
'while-expr',
'for-expr',
'match-expr',
'match-clauses',
'match-clause',
Expand Down

0 comments on commit e89977b

Please sign in to comment.