Skip to content

Commit c78ca1c

Browse files
committed
parser: parse expressions (wip)
1 parent e660b5a commit c78ca1c

File tree

10 files changed

+281
-17
lines changed

10 files changed

+281
-17
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
rivetc
33
main.ri
44
tests/gen_out_files
5-
tests/run_tests
5+
tests/run_tests
6+
x.py

compiler/ast/Expr.v

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,64 @@
44

55
module ast
66

7-
pub type Expr = EmptyExpr | RuneLit | IntegerLit
7+
pub type Expr = EmptyExpr | UnaryExpr | BinaryExpr | RuneLit | IntegerLit
88

99
pub struct EmptyExpr {
1010
pub:
1111
pos FilePos
1212
}
1313

14-
pub const empty_expr = EmptyExpr{}
14+
pub const empty_expr = Expr(EmptyExpr{})
15+
16+
pub enum UnaryOp {
17+
unknown
18+
amp // &
19+
bang // !
20+
bit_not // ~
21+
minus // -
22+
}
23+
24+
pub struct UnaryExpr {
25+
pub:
26+
right Expr
27+
op UnaryOp
28+
pos FilePos
29+
}
30+
31+
pub enum BinaryOp {
32+
unknown
33+
plus // +
34+
minus // -
35+
mul // *
36+
div // /
37+
mod // %
38+
xor // ^
39+
pipe // |
40+
amp // &
41+
log_and // &&
42+
log_or // ||
43+
lshift // <<
44+
rshift // >>
45+
not_in // !in
46+
not_is // !is
47+
eq // ==
48+
ne // !=
49+
gt // >
50+
lt // <
51+
ge // >=
52+
le // <=
53+
or_else // ??
54+
kw_in // !in
55+
kw_is // !is
56+
}
57+
58+
pub struct BinaryExpr {
59+
pub:
60+
left Expr
61+
op BinaryOp
62+
right Expr
63+
pos FilePos
64+
}
1565

1666
pub struct RuneLit {
1767
pub:

compiler/ast/FilePos.v

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,22 @@ pub mut:
1818
end FileLoc
1919
}
2020

21+
@[inline]
22+
pub fn (fp FilePos) == (fp2 FilePos) bool {
23+
if fp.file == unsafe { nil } || fp2.file == unsafe { nil } {
24+
return false
25+
}
26+
return fp.file == fp2.file && fp.begin == fp2.begin && fp.end == fp2.end
27+
}
28+
29+
@[inline]
30+
pub fn (fp &FilePos) extend(end &FilePos) FilePos {
31+
return FilePos{
32+
...fp
33+
end: end.end
34+
}
35+
}
36+
2137
pub fn (fp &FilePos) contains(loc &FileLoc) bool {
2238
if loc.line > fp.begin.line && loc.line < fp.end.line {
2339
return true
@@ -32,8 +48,9 @@ pub fn (fp &FilePos) contains(loc &FileLoc) bool {
3248
}
3349

3450
pub fn (fp &FilePos) str() string {
51+
filename := if fp.file == unsafe { nil } { '<unknown-file>' } else { fp.file.filename }
3552
if fp.begin.line == fp.end.line {
36-
return '${fp.file.filename}:${fp.begin.line + 1}:${fp.begin.col}'
53+
return '${filename}:${fp.begin.line + 1}:${fp.begin.col}'
3754
}
38-
return '${fp.file.filename}:${fp.begin.line + 1}:${fp.begin.col}-${fp.end.line + 1}:${fp.end.col}'
55+
return '${filename}:${fp.begin.line + 1}:${fp.begin.col}-${fp.end.line + 1}:${fp.end.col}'
3956
}

compiler/ast/Stmt.v

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ pub type Stmt = EmptyStmt | FnStmt | ExprStmt
88

99
pub type EmptyStmt = u8
1010

11-
pub const empty_stmt = EmptyStmt(0)
11+
pub const empty_stmt = Stmt(EmptyStmt(0))
1212

1313
pub struct ExprStmt {
1414
pub:

compiler/parser/expr.v

Lines changed: 181 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,186 @@ fn (mut p Parser) parse_expr() ast.Expr {
1111
if p.should_abort() {
1212
return ast.empty_expr
1313
}
14+
return p.parse_or_expr()
15+
}
16+
17+
fn (mut p Parser) parse_or_expr() ast.Expr {
18+
mut left := p.parse_and_expr()
19+
for p.accept(.log_or) {
20+
right := p.parse_and_expr()
21+
left = ast.BinaryExpr{
22+
left: left
23+
op: .log_or
24+
right: right
25+
pos: left.pos.extend(right.pos)
26+
}
27+
}
28+
return left
29+
}
30+
31+
fn (mut p Parser) parse_and_expr() ast.Expr {
32+
mut left := p.parse_equality_expr()
33+
for p.accept(.log_and) {
34+
right := p.parse_equality_expr()
35+
left = ast.BinaryExpr{
36+
left: left
37+
op: .log_and
38+
right: right
39+
pos: left.pos.extend(right.pos)
40+
}
41+
}
42+
return left
43+
}
44+
45+
fn (mut p Parser) parse_equality_expr() ast.Expr {
46+
mut left := p.parse_relational_expr()
47+
for {
48+
if p.tok.kind in [.eq, .ne] {
49+
op := p.tok.kind
50+
p.next()
51+
right := p.parse_relational_expr()
52+
left = ast.BinaryExpr{
53+
left: left
54+
op: if op == .eq { .eq } else { .ne }
55+
right: right
56+
pos: left.pos.extend(right.pos)
57+
}
58+
} else {
59+
break
60+
}
61+
}
62+
return left
63+
}
64+
65+
fn (mut p Parser) parse_relational_expr() ast.Expr {
66+
mut left := p.parse_shift_expr()
67+
for {
68+
if p.tok.kind in [.gt, .lt, .le, .or_else, .kw_in, .not_in, .kw_is, .not_is] {
69+
op := p.tok.kind
70+
p.next()
71+
right := p.parse_shift_expr()
72+
left = ast.BinaryExpr{
73+
left: left
74+
op: match op {
75+
.gt { .gt }
76+
.lt { .lt }
77+
.ge { .ge }
78+
.or_else { .or_else }
79+
.kw_in { .kw_in }
80+
.kw_is { .kw_is }
81+
else { .unknown }
82+
}
83+
right: right
84+
pos: left.pos.extend(right.pos)
85+
}
86+
} else {
87+
break
88+
}
89+
}
90+
return left
91+
}
92+
93+
fn (mut p Parser) parse_shift_expr() ast.Expr {
94+
mut left := p.parse_additive_expr()
95+
for {
96+
if p.tok.kind in [.amp, .pipe, .xor, .lshift, .rshift] {
97+
op := p.tok.kind
98+
p.next()
99+
right := p.parse_additive_expr()
100+
left = ast.BinaryExpr{
101+
left: left
102+
op: match op {
103+
.amp { .amp }
104+
.pipe { .pipe }
105+
.xor { .xor }
106+
.lshift { .lshift }
107+
.rshift { .rshift }
108+
else { .unknown }
109+
}
110+
right: right
111+
pos: left.pos.extend(right.pos)
112+
}
113+
} else {
114+
break
115+
}
116+
}
117+
return left
118+
}
119+
120+
fn (mut p Parser) parse_additive_expr() ast.Expr {
121+
mut left := p.parse_multiplicative_expr()
122+
for {
123+
if p.tok.kind in [.plus, .minus] {
124+
op := p.tok.kind
125+
p.next()
126+
right := p.parse_multiplicative_expr()
127+
left = ast.BinaryExpr{
128+
left: left
129+
op: match op {
130+
.plus { .plus }
131+
.minus { .minus }
132+
else { .unknown }
133+
}
134+
right: right
135+
pos: left.pos.extend(right.pos)
136+
}
137+
} else {
138+
break
139+
}
140+
}
141+
return left
142+
}
143+
144+
fn (mut p Parser) parse_multiplicative_expr() ast.Expr {
145+
mut left := p.parse_unary_expr()
146+
for {
147+
if p.tok.kind in [.mul, .div, .mod] {
148+
op := p.tok.kind
149+
p.next()
150+
right := p.parse_unary_expr()
151+
left = ast.BinaryExpr{
152+
left: left
153+
op: match op {
154+
.mul { .mul }
155+
.div { .div }
156+
.mod { .mod }
157+
else { .unknown }
158+
}
159+
right: right
160+
pos: left.pos.extend(right.pos)
161+
}
162+
} else {
163+
break
164+
}
165+
}
166+
return left
167+
}
168+
169+
fn (mut p Parser) parse_unary_expr() ast.Expr {
170+
mut expr := ast.empty_expr
171+
if p.tok.kind in [.amp, .bang, .bit_not, .minus] {
172+
op := p.tok.kind
173+
pos := p.tok.pos
174+
p.next()
175+
right := p.parse_unary_expr()
176+
expr = ast.UnaryExpr{
177+
right: expr
178+
op: match op {
179+
.amp { .amp }
180+
.bang { .bang }
181+
.bit_not { .bit_not }
182+
.minus { .minus }
183+
else { .unknown }
184+
}
185+
pos: pos.extend(right.pos)
186+
}
187+
} else {
188+
expr = p.parse_primary_expr()
189+
}
190+
return expr
191+
}
192+
193+
fn (mut p Parser) parse_primary_expr() ast.Expr {
14194
match p.tok.kind {
15195
.kw_if {}
16196
.kw_match {}
@@ -24,7 +204,7 @@ fn (mut p Parser) parse_expr() ast.Expr {
24204
return p.parse_rune_lit()
25205
}
26206
else {
27-
context.error('invalid expression', p.tok.pos)
207+
context.error('invalid expression: unexpected ${p.tok}', p.tok.pos)
28208
p.abort = true
29209
}
30210
}

compiler/parser/mod.v

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
module parser
66

77
import compiler.ast
8+
import compiler.token
89
import compiler.context
910
import compiler.tokenizer
1011

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

1718
tokenizer tokenizer.Tokenizer
18-
prev_tok tokenizer.Token
19-
tok tokenizer.Token
20-
next_tok tokenizer.Token
19+
prev_tok token.Token
20+
tok token.Token
21+
next_tok token.Token
2122

2223
abort bool
2324
inside_local_scope bool
25+
26+
expect_is_called bool
27+
prev_expect_pos ast.FilePos
2428
}
2529

2630
@[inline]
@@ -68,13 +72,24 @@ fn (mut p Parser) advance(n int) {
6872
}
6973
}
7074

71-
fn (mut p Parser) expect(kind tokenizer.Kind) {
75+
fn (mut p Parser) expect(kind token.Kind) {
76+
// this prevents an infinite loop due to an unexpected token, and also a double error message.
77+
if p.tok.pos == p.prev_expect_pos && !p.expect_is_called {
78+
p.expect_is_called = true
79+
} else {
80+
p.prev_expect_pos = p.tok.pos
81+
}
82+
7283
if !p.accept(kind) {
73-
context.error('expected `${kind}`, but found ${p.tok}', p.tok.pos)
84+
if p.expect_is_called {
85+
p.next()
86+
} else {
87+
context.error('expected `${kind}`, but found ${p.tok}', p.tok.pos)
88+
}
7489
}
7590
}
7691

77-
fn (mut p Parser) accept(kind tokenizer.Kind) bool {
92+
fn (mut p Parser) accept(kind token.Kind) bool {
7893
if p.tok.kind == kind {
7994
p.next()
8095
return true

compiler/parser/stmt.v

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ fn (mut p Parser) parse_fn_stmt(is_pub bool) ast.FnStmt {
7878
arg_name_pos := p.prev_tok.pos
7979
p.expect(.colon)
8080
arg_type := p.parse_type()
81-
mut arg_default_expr := ast.Expr(ast.empty_expr)
81+
mut arg_default_expr := ast.empty_expr
8282
if p.accept(.eq) {
8383
arg_default_expr = p.parse_expr()
8484
}

compiler/tokenizer/token.v renamed to compiler/token/mod.v

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// source code is governed by an MIT license that can be found in the LICENSE
33
// file.
44

5-
module tokenizer
5+
module token
66

77
import compiler.ast
88
import compiler.context

0 commit comments

Comments
 (0)