suffix unary operators (++, --) working as intended

This commit is contained in:
uan
2026-02-07 14:39:15 +01:00
parent 99d63ff769
commit 90ab17da24
4 changed files with 111 additions and 44 deletions

110
parser.v
View File

@@ -5,12 +5,13 @@ import term
// ------------------------------------------- Precedence
enum Precedence {
lowest
base
assignment // = , +=, -=
comparison // ==, !=, <, >
sum // +, -
product // *, /
prefix // -x, !x
suffix // ++, --
call // function()
access // .
}
@@ -22,7 +23,8 @@ fn (p Parser) get_precedence(tok_type TokenType) Precedence {
.plus, .minus { .sum }
.star, .slash { .product }
.dot { .access }
else { .lowest }
.increment, .decrement { .suffix }
else { .base}
}
}
@@ -52,7 +54,7 @@ mut:
structs map[string]ClassSymbolInfo
}
fn (mut s SymbolTable) define_var(name string, typ string) {
fn (mut s SymbolTable) define_var(name string, typ string, is_const bool) {
$if debug {
dump(s.variable_scopes.len)
}
@@ -67,6 +69,7 @@ fn (mut s SymbolTable) define_var(name string, typ string) {
s.variable_scopes[s.variable_scopes.len-1][name] = VarSymbolInfo{
type: typ
is_immutable: is_const
}
}
@@ -124,8 +127,9 @@ type Expr = VoidExpr | UnaryExpr | BinaryExpr | IntegerLiteral | RealLiteral | B
struct VoidExpr {}
struct UnaryExpr {
ident string
expr Expr
op string
op_on_left bool
}
struct BinaryExpr {
@@ -281,7 +285,6 @@ fn (mut p Parser) expect(type TokenType) {
p.next()
}
fn (mut p Parser) expect_ident_is_class(ident string) {
if !p.is_ident_class(ident) {panic("Expected type or class type but got identifier")}
}
@@ -316,15 +319,16 @@ fn (mut p Parser) parse_primary() Expr {
p.dump_token()
return match token.type {
.integer {IntegerLiteral{token.text.int()}}
.real {RealLiteral{token.text.f32()}}
.boolean {BoolLiteral{token.text == 'true'}}
.string {StringLiteral{token.text}}
.kw_fn {Function{token.text}}
.identifier {p.parse_ident(token.text)}
.type {p.parse_type(token.text)}
.lparen {p.parse_paren()}
.kw_print {p.parse_print()}
.integer {IntegerLiteral{token.text.int()}}
.real {RealLiteral{token.text.f32()}}
.boolean {BoolLiteral{token.text == 'true'}}
.string {StringLiteral{token.text}}
.kw_fn {Function{token.text}}
.identifier {p.parse_ident(token.text)}
.type {p.parse_type(token.text)}
.lparen {p.parse_paren()}
.kw_print {p.parse_print()}
.plus, .minus {p.parse_unary_left(token.text)}
else {parse_error("Unexpected Token")}
}
}
@@ -332,15 +336,13 @@ fn (mut p Parser) parse_primary() Expr {
fn (mut p Parser) parse_expr(prec Precedence) Expr {
mut expr := p.parse_primary()
for int(prec) < int(p.get_precedence(p.peek().type)) {
op_tok := p.next()
if op_tok.type == .dot {
expr = p.parse_member_access(expr)
} else {
expr = p.parse_binary(expr, op_tok.text, p.get_precedence(op_tok.type))
expr = match op_tok.type {
.dot {p.parse_member_access(expr)}
.increment, .decrement {p.parse_unary_right(expr, op_tok.text)}
else {p.parse_binary(expr, op_tok.text, p.get_precedence(op_tok.type))}
}
}
@@ -356,7 +358,6 @@ fn (mut p Parser) parse_ident(ident string) Expr {
}
return match p.peek().type {
.increment, .decrement {UnaryExpr {ident: ident, op: p.next().text}}
.lparen {p.parse_call(ident)}
else {Variable{ident}}
}
@@ -401,7 +402,7 @@ fn (mut p Parser) parse_class_inst(name string) ClassInstantiation {
if p.peek().type != .rbracket {
for {
member_values << p.parse_expr(.lowest)
member_values << p.parse_expr(.base)
if p.peek().type == .comma {
p.next()
} else {
@@ -419,7 +420,7 @@ fn (mut p Parser) parse_call(name string) FnCall {
if p.peek().type != .rparen {
for {
args << p.parse_expr(.lowest)
args << p.parse_expr(.base)
if p.peek().type == .comma {
p.next()
} else {
@@ -436,7 +437,7 @@ fn (mut p Parser) parse_print() PrintExpr {
mut exprs := []Expr{}
mut types := []string{}
for p.peek().type != .rparen {
expr := p.parse_expr(.lowest)
expr := p.parse_expr(.base)
exprs << expr
types << p.get_expr_type(expr)
if p.peek().type == .comma {
@@ -449,18 +450,26 @@ fn (mut p Parser) parse_print() PrintExpr {
return PrintExpr{exprs: exprs, types: types}
}
fn (mut p Parser) parse_unary_right(expr Expr, op string) UnaryExpr {
if p.get_expr_is_immutable(expr) {
parse_error("Cannot perform operation ${op} on immutable expression ${expr}")
}
if !p.is_op_valid_for_type(p.get_expr_type(expr), op) {
parse_error("Invalid operation ${op} for type ${p.get_expr_type(expr)}")
}
return UnaryExpr {expr: expr, op: op, op_on_left: false}
}
fn (mut p Parser) parse_unary_left(op string) UnaryExpr {
expr := p.parse_expr(.prefix)
return UnaryExpr{expr: expr, op: op, op_on_left: true}
}
fn (mut p Parser) parse_binary(left Expr, op string, prec Precedence) BinaryExpr {
if op in ['=', '+=', '-=', '*=', '/='] {
if left is MemberAccess {
from_type := p.get_expr_type(left.from)
if class_info := p.symbols.lookup_class(from_type) {
if member_info := class_info.members_info[left.member] {
if member_info.is_immutable {
parse_error("Cannot assign to immutable member ${left.member} in class ${from_type}")
}
}
}
if p.get_expr_is_immutable(left) {
parse_error("Cannot assign to immutable expression ${left}")
}
}
@@ -478,7 +487,7 @@ fn (mut p Parser) parse_type(type string) Expr {
if p.peek().type == .lparen {
p.next()
expr := p.parse_expr(.lowest)
expr := p.parse_expr(.base)
p.expect(.rparen)
return TypeCast {
@@ -491,7 +500,7 @@ fn (mut p Parser) parse_type(type string) Expr {
}
fn (mut p Parser) parse_paren() ParenExpr {
expr := p.parse_expr(.lowest)
expr := p.parse_expr(.base)
p.expect(.rparen)
return ParenExpr{expr: expr}
}
@@ -508,6 +517,20 @@ fn (mut p Parser) is_ident_class(ident string) bool {
return p.symbols.lookup_class(ident) != none
}
fn (mut p Parser) get_expr_is_immutable(expr Expr) bool {
return match expr {
ParenExpr {p.get_expr_is_immutable(expr.expr)}
UnaryExpr {p.get_expr_is_immutable(expr.expr)}
TypeCast {p.get_expr_is_immutable(expr.expr)}
MemberAccess {
classinfo := p.symbols.lookup_class(p.get_expr_type(expr.from)) or {parse_error("Invalid class")}
memberinfo := classinfo.members_info[expr.member] or {parse_error("Undefined member ${expr.member}")}
memberinfo.is_immutable
}
else {true}
}
}
fn (mut p Parser) get_expr_type(expr Expr) string {
return match expr {
ParenExpr {p.get_expr_type(expr.expr)}
@@ -517,6 +540,7 @@ fn (mut p Parser) get_expr_type(expr Expr) string {
StringLiteral {'string'}
VoidExpr {'void'}
TypeExpr {expr.name}
UnaryExpr {p.get_expr_type(expr.expr)}
BinaryExpr {
p.check_binary_expr_types(expr)
left_t := p.get_expr_type(expr.left)
@@ -548,7 +572,7 @@ fn (mut p Parser) get_expr_type(expr Expr) string {
fn (mut p Parser) is_op_valid_for_type(type string, op string) bool {
global := ['=', '==', '!=']
mut legal_ops := match type {
'int', 'real' {['+', '-', '*', '/', '<', '>', '<=', '>=', '++', '--', '+=', '-=', '*=', '/=']}
'int', 'real' {['+', '-', '*', '/', '<', '>', '<=', '>=', '++', '--', '+=', '-=', '*=', '/=', '++', '--']}
'bool' {['=']}
else {[]}
}
@@ -605,7 +629,7 @@ fn (mut p Parser) parse_var_decl(is_const bool) VarDecl {
}
p.expect(.equals)
val := p.parse_expr(.lowest)
val := p.parse_expr(.base)
if type_tok.text == 'void' {
parse_error("Cannot declare a variable of type void")
@@ -615,7 +639,7 @@ fn (mut p Parser) parse_var_decl(is_const bool) VarDecl {
}
p.expect(.semicolon)
p.symbols.define_var(name_tok.text, type_tok.text)
p.symbols.define_var(name_tok.text, type_tok.text, is_const)
return VarDecl {
name: name_tok.text
@@ -659,7 +683,7 @@ fn (mut p Parser) parse_func_decl() FuncDecl {
}
p.next()
params << Param{p_name, p_type}
p.symbols.define_var(p_name, p_type)
p.symbols.define_var(p_name, p_type, false)
if p.peek().type == .comma {
p.next()
@@ -725,7 +749,7 @@ fn (mut p Parser) parse_class() ClassDecl {
fn (mut p Parser) parse_return_stmt() ReturnStmt {
p.expect(.kw_return)
expr := p.parse_expr(.lowest)
expr := p.parse_expr(.base)
p.expect(.semicolon)
@@ -736,7 +760,7 @@ fn (mut p Parser) parse_return_stmt() ReturnStmt {
fn (mut p Parser) parse_if() IfStmt {
p.expect(.kw_if)
cond := p.parse_expr(.lowest)
cond := p.parse_expr(.base)
if p.get_expr_type(cond) != 'bool' {
parse_error('If condition must be of type bool')
}
@@ -754,7 +778,7 @@ fn (mut p Parser) parse_else() ElseStmt {
fn (mut p Parser) parse_elif() ElifStmt {
p.expect(.kw_elif)
cond := p.parse_expr(.lowest)
cond := p.parse_expr(.base)
if p.get_expr_type(cond) != 'bool' {
parse_error('If condition must be of type bool')
}
@@ -764,7 +788,7 @@ fn (mut p Parser) parse_elif() ElifStmt {
}
fn (mut p Parser) parse_expr_stmt() ExprStmt {
expr := p.parse_expr(.lowest)
expr := p.parse_expr(.base)
p.expect(.semicolon)
return ExprStmt {