Skip to content

Commit

Permalink
parser: parse expressions (wip)
Browse files Browse the repository at this point in the history
  • Loading branch information
StunxFS committed Dec 4, 2024
1 parent e660b5a commit c78ca1c
Show file tree
Hide file tree
Showing 10 changed files with 281 additions and 17 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
rivetc
main.ri
tests/gen_out_files
tests/run_tests
tests/run_tests
x.py
54 changes: 52 additions & 2 deletions compiler/ast/Expr.v
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,64 @@

module ast

pub type Expr = EmptyExpr | RuneLit | IntegerLit
pub type Expr = EmptyExpr | UnaryExpr | BinaryExpr | RuneLit | IntegerLit

pub struct EmptyExpr {
pub:
pos FilePos
}

pub const empty_expr = EmptyExpr{}
pub const empty_expr = Expr(EmptyExpr{})

pub enum UnaryOp {
unknown
amp // &
bang // !
bit_not // ~
minus // -
}

pub struct UnaryExpr {
pub:
right Expr
op UnaryOp
pos FilePos
}

pub enum BinaryOp {
unknown
plus // +
minus // -
mul // *
div // /
mod // %
xor // ^
pipe // |
amp // &
log_and // &&
log_or // ||
lshift // <<
rshift // >>
not_in // !in
not_is // !is
eq // ==
ne // !=
gt // >
lt // <
ge // >=
le // <=
or_else // ??
kw_in // !in
kw_is // !is
}

pub struct BinaryExpr {
pub:
left Expr
op BinaryOp
right Expr
pos FilePos
}

pub struct RuneLit {
pub:
Expand Down
21 changes: 19 additions & 2 deletions compiler/ast/FilePos.v
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,22 @@ pub mut:
end FileLoc
}

@[inline]
pub fn (fp FilePos) == (fp2 FilePos) bool {
if fp.file == unsafe { nil } || fp2.file == unsafe { nil } {
return false
}
return fp.file == fp2.file && fp.begin == fp2.begin && fp.end == fp2.end
}

@[inline]
pub fn (fp &FilePos) extend(end &FilePos) FilePos {
return FilePos{
...fp
end: end.end
}
}

pub fn (fp &FilePos) contains(loc &FileLoc) bool {
if loc.line > fp.begin.line && loc.line < fp.end.line {
return true
Expand All @@ -32,8 +48,9 @@ pub fn (fp &FilePos) contains(loc &FileLoc) bool {
}

pub fn (fp &FilePos) str() string {
filename := if fp.file == unsafe { nil } { '<unknown-file>' } else { fp.file.filename }
if fp.begin.line == fp.end.line {
return '${fp.file.filename}:${fp.begin.line + 1}:${fp.begin.col}'
return '${filename}:${fp.begin.line + 1}:${fp.begin.col}'
}
return '${fp.file.filename}:${fp.begin.line + 1}:${fp.begin.col}-${fp.end.line + 1}:${fp.end.col}'
return '${filename}:${fp.begin.line + 1}:${fp.begin.col}-${fp.end.line + 1}:${fp.end.col}'
}
2 changes: 1 addition & 1 deletion compiler/ast/Stmt.v
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pub type Stmt = EmptyStmt | FnStmt | ExprStmt

pub type EmptyStmt = u8

pub const empty_stmt = EmptyStmt(0)
pub const empty_stmt = Stmt(EmptyStmt(0))

pub struct ExprStmt {
pub:
Expand Down
182 changes: 181 additions & 1 deletion compiler/parser/expr.v
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,186 @@ fn (mut p Parser) parse_expr() ast.Expr {
if p.should_abort() {
return ast.empty_expr
}
return p.parse_or_expr()
}

fn (mut p Parser) parse_or_expr() ast.Expr {
mut left := p.parse_and_expr()
for p.accept(.log_or) {
right := p.parse_and_expr()
left = ast.BinaryExpr{
left: left
op: .log_or
right: right
pos: left.pos.extend(right.pos)
}
}
return left
}

fn (mut p Parser) parse_and_expr() ast.Expr {
mut left := p.parse_equality_expr()
for p.accept(.log_and) {
right := p.parse_equality_expr()
left = ast.BinaryExpr{
left: left
op: .log_and
right: right
pos: left.pos.extend(right.pos)
}
}
return left
}

fn (mut p Parser) parse_equality_expr() ast.Expr {
mut left := p.parse_relational_expr()
for {
if p.tok.kind in [.eq, .ne] {
op := p.tok.kind
p.next()
right := p.parse_relational_expr()
left = ast.BinaryExpr{
left: left
op: if op == .eq { .eq } else { .ne }
right: right
pos: left.pos.extend(right.pos)
}
} else {
break
}
}
return left
}

fn (mut p Parser) parse_relational_expr() ast.Expr {
mut left := p.parse_shift_expr()
for {
if p.tok.kind in [.gt, .lt, .le, .or_else, .kw_in, .not_in, .kw_is, .not_is] {
op := p.tok.kind
p.next()
right := p.parse_shift_expr()
left = ast.BinaryExpr{
left: left
op: match op {
.gt { .gt }
.lt { .lt }
.ge { .ge }
.or_else { .or_else }
.kw_in { .kw_in }
.kw_is { .kw_is }
else { .unknown }
}
right: right
pos: left.pos.extend(right.pos)
}
} else {
break
}
}
return left
}

fn (mut p Parser) parse_shift_expr() ast.Expr {
mut left := p.parse_additive_expr()
for {
if p.tok.kind in [.amp, .pipe, .xor, .lshift, .rshift] {
op := p.tok.kind
p.next()
right := p.parse_additive_expr()
left = ast.BinaryExpr{
left: left
op: match op {
.amp { .amp }
.pipe { .pipe }
.xor { .xor }
.lshift { .lshift }
.rshift { .rshift }
else { .unknown }
}
right: right
pos: left.pos.extend(right.pos)
}
} else {
break
}
}
return left
}

fn (mut p Parser) parse_additive_expr() ast.Expr {
mut left := p.parse_multiplicative_expr()
for {
if p.tok.kind in [.plus, .minus] {
op := p.tok.kind
p.next()
right := p.parse_multiplicative_expr()
left = ast.BinaryExpr{
left: left
op: match op {
.plus { .plus }
.minus { .minus }
else { .unknown }
}
right: right
pos: left.pos.extend(right.pos)
}
} else {
break
}
}
return left
}

fn (mut p Parser) parse_multiplicative_expr() ast.Expr {
mut left := p.parse_unary_expr()
for {
if p.tok.kind in [.mul, .div, .mod] {
op := p.tok.kind
p.next()
right := p.parse_unary_expr()
left = ast.BinaryExpr{
left: left
op: match op {
.mul { .mul }
.div { .div }
.mod { .mod }
else { .unknown }
}
right: right
pos: left.pos.extend(right.pos)
}
} else {
break
}
}
return left
}

fn (mut p Parser) parse_unary_expr() ast.Expr {
mut expr := ast.empty_expr
if p.tok.kind in [.amp, .bang, .bit_not, .minus] {
op := p.tok.kind
pos := p.tok.pos
p.next()
right := p.parse_unary_expr()
expr = ast.UnaryExpr{
right: expr
op: match op {
.amp { .amp }
.bang { .bang }
.bit_not { .bit_not }
.minus { .minus }
else { .unknown }
}
pos: pos.extend(right.pos)
}
} else {
expr = p.parse_primary_expr()
}
return expr
}

fn (mut p Parser) parse_primary_expr() ast.Expr {
match p.tok.kind {
.kw_if {}
.kw_match {}
Expand All @@ -24,7 +204,7 @@ fn (mut p Parser) parse_expr() ast.Expr {
return p.parse_rune_lit()
}
else {
context.error('invalid expression', p.tok.pos)
context.error('invalid expression: unexpected ${p.tok}', p.tok.pos)
p.abort = true
}
}
Expand Down
27 changes: 21 additions & 6 deletions compiler/parser/mod.v
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
module parser

import compiler.ast
import compiler.token
import compiler.context
import compiler.tokenizer

Expand All @@ -15,12 +16,15 @@ mut:
file &ast.File = unsafe { nil }

tokenizer tokenizer.Tokenizer
prev_tok tokenizer.Token
tok tokenizer.Token
next_tok tokenizer.Token
prev_tok token.Token
tok token.Token
next_tok token.Token

abort bool
inside_local_scope bool

expect_is_called bool
prev_expect_pos ast.FilePos
}

@[inline]
Expand Down Expand Up @@ -68,13 +72,24 @@ fn (mut p Parser) advance(n int) {
}
}

fn (mut p Parser) expect(kind tokenizer.Kind) {
fn (mut p Parser) expect(kind token.Kind) {
// this prevents an infinite loop due to an unexpected token, and also a double error message.
if p.tok.pos == p.prev_expect_pos && !p.expect_is_called {
p.expect_is_called = true
} else {
p.prev_expect_pos = p.tok.pos
}

if !p.accept(kind) {
context.error('expected `${kind}`, but found ${p.tok}', p.tok.pos)
if p.expect_is_called {
p.next()
} else {
context.error('expected `${kind}`, but found ${p.tok}', p.tok.pos)
}
}
}

fn (mut p Parser) accept(kind tokenizer.Kind) bool {
fn (mut p Parser) accept(kind token.Kind) bool {
if p.tok.kind == kind {
p.next()
return true
Expand Down
2 changes: 1 addition & 1 deletion compiler/parser/stmt.v
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ fn (mut p Parser) parse_fn_stmt(is_pub bool) ast.FnStmt {
arg_name_pos := p.prev_tok.pos
p.expect(.colon)
arg_type := p.parse_type()
mut arg_default_expr := ast.Expr(ast.empty_expr)
mut arg_default_expr := ast.empty_expr
if p.accept(.eq) {
arg_default_expr = p.parse_expr()
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/tokenizer/token.v → compiler/token/mod.v
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// source code is governed by an MIT license that can be found in the LICENSE
// file.

module tokenizer
module token

import compiler.ast
import compiler.context
Expand Down
Loading

0 comments on commit c78ca1c

Please sign in to comment.