2020-04-17 18:11:04 +02:00
|
|
|
// Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved.
|
|
|
|
// Use of this source code is governed by an MIT license
|
|
|
|
// that can be found in the LICENSE file.
|
|
|
|
module parser
|
|
|
|
|
|
|
|
import v.ast
|
|
|
|
import v.table
|
|
|
|
import v.token
|
|
|
|
|
2020-04-21 05:11:50 +02:00
|
|
|
pub fn (mut p Parser) expr(precedence int) ast.Expr {
|
2020-04-17 18:11:04 +02:00
|
|
|
// println('\n\nparser.expr()')
|
2020-04-21 05:11:50 +02:00
|
|
|
mut typ := table.void_type
|
|
|
|
mut node := ast.Expr{}
|
2020-04-25 10:57:12 +02:00
|
|
|
is_stmt_ident := p.is_stmt_ident
|
|
|
|
p.is_stmt_ident = false
|
2020-07-02 15:44:03 +02:00
|
|
|
p.eat_comments()
|
2020-04-17 18:11:04 +02:00
|
|
|
// Prefix
|
|
|
|
match p.tok.kind {
|
2020-07-07 01:57:31 +02:00
|
|
|
.key_mut, .key_shared, .key_atomic, .key_static {
|
2020-06-16 13:20:16 +02:00
|
|
|
node = p.name_expr()
|
|
|
|
p.is_stmt_ident = is_stmt_ident
|
2020-05-15 23:14:53 +02:00
|
|
|
}
|
2020-04-17 18:11:04 +02:00
|
|
|
.name {
|
2020-06-16 12:14:22 +02:00
|
|
|
if p.tok.lit == 'sql' && p.peek_tok.kind == .name {
|
2020-06-21 23:09:17 +02:00
|
|
|
p.inside_match = true // reuse the same var for perf instead of inside_sql TODO rename
|
2020-06-16 12:14:22 +02:00
|
|
|
node = p.sql_expr()
|
2020-06-21 23:09:17 +02:00
|
|
|
p.inside_match = false
|
2020-06-16 12:14:22 +02:00
|
|
|
} else {
|
|
|
|
node = p.name_expr()
|
|
|
|
p.is_stmt_ident = is_stmt_ident
|
|
|
|
}
|
2020-04-17 18:11:04 +02:00
|
|
|
}
|
|
|
|
.string {
|
|
|
|
node = p.string_expr()
|
|
|
|
}
|
|
|
|
.dot {
|
|
|
|
// .enum_val
|
|
|
|
node = p.enum_val()
|
|
|
|
}
|
2020-06-20 03:12:35 +02:00
|
|
|
.dollar {
|
|
|
|
if p.peek_tok.kind == .name {
|
|
|
|
return p.vweb()
|
|
|
|
} else {
|
|
|
|
p.error('unexpected $')
|
|
|
|
}
|
|
|
|
}
|
2020-04-17 18:11:04 +02:00
|
|
|
.chartoken {
|
|
|
|
node = ast.CharLiteral{
|
|
|
|
val: p.tok.lit
|
2020-04-20 14:49:26 +02:00
|
|
|
pos: p.tok.position()
|
2020-04-17 18:11:04 +02:00
|
|
|
}
|
|
|
|
p.next()
|
|
|
|
}
|
|
|
|
.minus, .amp, .mul, .not, .bit_not {
|
|
|
|
// -1, -a, !x, &x, ~x
|
|
|
|
node = p.prefix_expr()
|
|
|
|
}
|
|
|
|
.key_true, .key_false {
|
|
|
|
node = ast.BoolLiteral{
|
|
|
|
val: p.tok.kind == .key_true
|
2020-04-20 14:49:26 +02:00
|
|
|
pos: p.tok.position()
|
2020-04-17 18:11:04 +02:00
|
|
|
}
|
|
|
|
p.next()
|
|
|
|
}
|
|
|
|
.key_match {
|
|
|
|
node = p.match_expr()
|
|
|
|
}
|
|
|
|
.number {
|
|
|
|
node = p.parse_number_literal()
|
|
|
|
}
|
|
|
|
.lpar {
|
|
|
|
p.check(.lpar)
|
|
|
|
node = p.expr(0)
|
|
|
|
p.check(.rpar)
|
|
|
|
node = ast.ParExpr{
|
|
|
|
expr: node
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.key_if {
|
|
|
|
node = p.if_expr()
|
|
|
|
}
|
2020-07-04 12:44:25 +02:00
|
|
|
.key_lock, .key_rlock {
|
|
|
|
node = p.lock_expr()
|
|
|
|
}
|
2020-04-17 18:11:04 +02:00
|
|
|
.lsbr {
|
2020-05-05 14:19:31 +02:00
|
|
|
if p.expecting_type {
|
|
|
|
// parse json.decode type (`json.decode([]User, s)`)
|
|
|
|
node = p.name_expr()
|
|
|
|
} else {
|
|
|
|
node = p.array_init()
|
|
|
|
}
|
2020-04-17 18:11:04 +02:00
|
|
|
}
|
|
|
|
.key_none {
|
2020-06-15 07:10:45 +02:00
|
|
|
pos := p.tok.position()
|
2020-04-17 18:11:04 +02:00
|
|
|
p.next()
|
2020-06-15 07:10:45 +02:00
|
|
|
node = ast.None{
|
|
|
|
pos: pos
|
|
|
|
}
|
2020-04-17 18:11:04 +02:00
|
|
|
}
|
|
|
|
.key_sizeof {
|
2020-07-01 00:53:53 +02:00
|
|
|
pos := p.tok.position()
|
2020-04-20 07:04:31 +02:00
|
|
|
p.next() // sizeof
|
2020-04-17 18:11:04 +02:00
|
|
|
p.check(.lpar)
|
2020-07-01 00:53:53 +02:00
|
|
|
is_known_var := p.mark_var_as_used( p.tok.lit )
|
|
|
|
if is_known_var {
|
|
|
|
expr := p.parse_ident(table.Language.v)
|
2020-04-17 18:11:04 +02:00
|
|
|
node = ast.SizeOf{
|
2020-07-01 00:53:53 +02:00
|
|
|
is_type: false
|
|
|
|
expr: expr
|
|
|
|
pos: pos
|
2020-04-17 18:11:04 +02:00
|
|
|
}
|
|
|
|
} else {
|
2020-07-01 00:53:53 +02:00
|
|
|
sizeof_type := p.parse_type()
|
2020-04-17 18:11:04 +02:00
|
|
|
node = ast.SizeOf{
|
2020-07-02 15:44:03 +02:00
|
|
|
is_type: true
|
2020-04-17 18:11:04 +02:00
|
|
|
typ: sizeof_type
|
2020-07-01 00:53:53 +02:00
|
|
|
type_name: p.table.get_type_symbol(sizeof_type).name
|
|
|
|
pos: pos
|
2020-04-17 18:11:04 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
p.check(.rpar)
|
|
|
|
}
|
|
|
|
.key_typeof {
|
|
|
|
p.next()
|
|
|
|
p.check(.lpar)
|
|
|
|
expr := p.expr(0)
|
|
|
|
p.check(.rpar)
|
|
|
|
node = ast.TypeOf{
|
|
|
|
expr: expr
|
|
|
|
}
|
|
|
|
}
|
2020-06-09 17:08:31 +02:00
|
|
|
.key_likely, .key_unlikely {
|
|
|
|
is_likely := p.tok.kind == .key_likely
|
2020-06-09 16:36:18 +02:00
|
|
|
p.next()
|
|
|
|
p.check(.lpar)
|
|
|
|
lpos := p.tok.position()
|
|
|
|
expr := p.expr(0)
|
|
|
|
p.check(.rpar)
|
|
|
|
node = ast.Likely{
|
|
|
|
expr: expr
|
|
|
|
pos: lpos
|
2020-06-09 17:08:31 +02:00
|
|
|
is_likely: is_likely
|
2020-06-09 16:36:18 +02:00
|
|
|
}
|
|
|
|
}
|
2020-04-17 18:11:04 +02:00
|
|
|
.lcbr {
|
|
|
|
// Map `{"age": 20}` or `{ x | foo:bar, a:10 }`
|
|
|
|
p.next()
|
|
|
|
if p.tok.kind == .string {
|
|
|
|
node = p.map_init()
|
|
|
|
} else {
|
|
|
|
// it should be a struct
|
|
|
|
if p.peek_tok.kind == .pipe {
|
|
|
|
node = p.assoc()
|
|
|
|
} else if p.peek_tok.kind == .colon || p.tok.kind == .rcbr {
|
2020-04-20 07:04:31 +02:00
|
|
|
node = p.struct_init(true) // short_syntax: true
|
2020-04-17 18:11:04 +02:00
|
|
|
} else if p.tok.kind == .name {
|
|
|
|
p.next()
|
|
|
|
lit := if p.tok.lit != '' { p.tok.lit } else { p.tok.kind.str() }
|
2020-05-06 08:10:40 +02:00
|
|
|
p.error('unexpected `$lit`, expecting `:`')
|
2020-04-17 18:11:04 +02:00
|
|
|
} else {
|
2020-05-06 08:10:40 +02:00
|
|
|
p.error('unexpected `$p.tok.lit`, expecting struct key')
|
2020-04-17 18:11:04 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
p.check(.rcbr)
|
|
|
|
}
|
2020-04-20 07:04:31 +02:00
|
|
|
.key_fn {
|
|
|
|
// Anonymous function
|
|
|
|
node = p.anon_fn()
|
2020-07-07 17:10:39 +02:00
|
|
|
// its a call
|
|
|
|
// NOTE: this could be moved to just before the pratt loop
|
2020-07-07 17:36:48 +02:00
|
|
|
// then anything can be a call, eg. `index[2]()` or `struct.field()`
|
2020-07-07 17:10:39 +02:00
|
|
|
// but this would take a bit of modification
|
|
|
|
if p.tok.kind == .lpar {
|
|
|
|
p.next()
|
|
|
|
pos := p.tok.position()
|
|
|
|
args := p.call_args()
|
|
|
|
p.check(.rpar)
|
|
|
|
node = ast.CallExpr{
|
|
|
|
name: 'anon'
|
|
|
|
left: node
|
|
|
|
args: args
|
|
|
|
pos: pos
|
|
|
|
}
|
|
|
|
}
|
2020-04-20 07:04:31 +02:00
|
|
|
return node
|
|
|
|
}
|
2020-04-17 18:11:04 +02:00
|
|
|
else {
|
|
|
|
p.error('expr(): bad token `$p.tok.kind.str()`')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Infix
|
|
|
|
for precedence < p.tok.precedence() {
|
2020-06-16 13:20:16 +02:00
|
|
|
if p.tok.kind == .dot {
|
2020-04-17 18:11:04 +02:00
|
|
|
node = p.dot_expr(node)
|
2020-04-25 10:57:12 +02:00
|
|
|
p.is_stmt_ident = is_stmt_ident
|
2020-04-17 18:11:04 +02:00
|
|
|
} else if p.tok.kind == .lsbr {
|
|
|
|
node = p.index_expr(node)
|
2020-06-16 13:20:16 +02:00
|
|
|
p.is_stmt_ident = is_stmt_ident
|
2020-04-17 18:11:04 +02:00
|
|
|
} else if p.tok.kind == .key_as {
|
2020-06-18 16:33:16 +02:00
|
|
|
// sum type match `match x as alias` so return early
|
|
|
|
if p.inside_match {
|
|
|
|
return node
|
|
|
|
}
|
|
|
|
// sum type as cast `x := SumType as Variant`
|
2020-04-17 18:11:04 +02:00
|
|
|
pos := p.tok.position()
|
|
|
|
p.next()
|
|
|
|
typ = p.parse_type()
|
|
|
|
node = ast.AsCast{
|
|
|
|
expr: node
|
|
|
|
typ: typ
|
|
|
|
pos: pos
|
|
|
|
}
|
2020-04-25 10:57:12 +02:00
|
|
|
} else if p.tok.kind == .left_shift && p.is_stmt_ident {
|
2020-04-17 18:11:04 +02:00
|
|
|
// arr << elem
|
|
|
|
tok := p.tok
|
|
|
|
pos := tok.position()
|
|
|
|
p.next()
|
|
|
|
right := p.expr(precedence - 1)
|
|
|
|
node = ast.InfixExpr{
|
|
|
|
left: node
|
|
|
|
right: right
|
|
|
|
op: tok.kind
|
|
|
|
pos: pos
|
|
|
|
}
|
2020-06-18 20:21:08 +02:00
|
|
|
} else if p.tok.kind.is_infix() {
|
2020-04-25 10:07:30 +02:00
|
|
|
// return early for deref assign `*x = 2` goes to prefix expr
|
2020-04-25 17:49:16 +02:00
|
|
|
if p.tok.kind == .mul && p.tok.line_nr != p.prev_tok.line_nr && p.peek_tok2.kind ==
|
|
|
|
.assign {
|
2020-04-25 10:07:30 +02:00
|
|
|
return node
|
|
|
|
}
|
|
|
|
// continue on infix expr
|
2020-04-17 18:11:04 +02:00
|
|
|
node = p.infix_expr(node)
|
2020-07-09 17:14:14 +02:00
|
|
|
// return early `if bar is SumType as b {`
|
|
|
|
if p.tok.kind == .key_as && p.inside_if {
|
|
|
|
return node
|
|
|
|
}
|
2020-04-17 18:11:04 +02:00
|
|
|
} else if p.tok.kind in [.inc, .dec] {
|
|
|
|
// Postfix
|
|
|
|
node = ast.PostfixExpr{
|
|
|
|
op: p.tok.kind
|
|
|
|
expr: node
|
|
|
|
pos: p.tok.position()
|
|
|
|
}
|
|
|
|
p.next()
|
|
|
|
// return node // TODO bring back, only allow ++/-- in exprs in translated code
|
|
|
|
} else {
|
|
|
|
return node
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return node
|
|
|
|
}
|
|
|
|
|
2020-04-21 05:11:50 +02:00
|
|
|
fn (mut p Parser) infix_expr(left ast.Expr) ast.Expr {
|
2020-04-17 18:11:04 +02:00
|
|
|
op := p.tok.kind
|
|
|
|
// mut typ := p.
|
|
|
|
// println('infix op=$op.str()')
|
|
|
|
precedence := p.tok.precedence()
|
|
|
|
pos := p.tok.position()
|
|
|
|
p.next()
|
2020-04-21 05:11:50 +02:00
|
|
|
mut right := ast.Expr{}
|
2020-06-02 16:18:12 +02:00
|
|
|
if op in [.key_is, .not_is] {
|
2020-05-05 14:19:31 +02:00
|
|
|
p.expecting_type = true
|
2020-04-17 18:11:04 +02:00
|
|
|
}
|
|
|
|
right = p.expr(precedence)
|
2020-04-27 22:53:26 +02:00
|
|
|
return ast.InfixExpr{
|
2020-04-17 18:11:04 +02:00
|
|
|
left: left
|
|
|
|
right: right
|
|
|
|
op: op
|
|
|
|
pos: pos
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-21 05:11:50 +02:00
|
|
|
fn (mut p Parser) prefix_expr() ast.PrefixExpr {
|
2020-04-17 18:11:04 +02:00
|
|
|
pos := p.tok.position()
|
|
|
|
op := p.tok.kind
|
|
|
|
if op == .amp {
|
|
|
|
p.is_amp = true
|
|
|
|
}
|
2020-05-11 16:05:59 +02:00
|
|
|
// if op == .mul && !p.inside_unsafe {
|
|
|
|
// p.warn('unsafe')
|
|
|
|
// }
|
2020-04-17 18:11:04 +02:00
|
|
|
p.next()
|
2020-07-03 23:22:43 +02:00
|
|
|
right := if op == .minus { p.expr(token.Precedence.call) } else { p.expr(token.Precedence.prefix) }
|
2020-04-17 18:11:04 +02:00
|
|
|
p.is_amp = false
|
|
|
|
return ast.PrefixExpr{
|
|
|
|
op: op
|
|
|
|
right: right
|
|
|
|
pos: pos
|
|
|
|
}
|
|
|
|
}
|