suffix unary operators (++, --) working as intended
This commit is contained in:
35
README.md
35
README.md
@@ -86,6 +86,21 @@ get translated to during compilation. Comparisons and operations on strings are
|
||||
and quite buggy, but you are free to use string literals for prints or simple variable declarations with no issues.
|
||||
Variables cannot be declared with type `void`.
|
||||
|
||||
The `string` type actually behaves similarly to how a class would. While using the variable directly
|
||||
gives you access to the actual string (which in C corresponds to the `char*`), you can use `.len` to
|
||||
get the length of the string. Note that `len` is an immutable member, so you cannot maually set it.
|
||||
|
||||
```
|
||||
let mystring string = "this is my string";
|
||||
print(mystring, mystring.len)
|
||||
```
|
||||
|
||||
produces this output:
|
||||
|
||||
```
|
||||
this is my string 17
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Functions
|
||||
@@ -133,6 +148,26 @@ let myuser User = User{17, "uan", false}
|
||||
Class types can be used anywhere primitive types can, such as function arguments
|
||||
or other classes' members.
|
||||
|
||||
Class members can be defined as immutable with the `immutable` keyword before the member name.
|
||||
This is checked at compile time and prevents that member's value to be changed after instantiation.
|
||||
For example, this code would trigger a parse error, `as u.id = 0` attempts to change an immutable
|
||||
member's value.
|
||||
|
||||
```
|
||||
class User {
|
||||
age int
|
||||
name string
|
||||
mail_verified bool
|
||||
immutable id int
|
||||
}
|
||||
|
||||
fn change_values(u User) {
|
||||
u.age = 10;
|
||||
u.name = "new name";
|
||||
u.id = 0;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Print
|
||||
|
||||
10
generator.v
10
generator.v
@@ -179,7 +179,15 @@ fn (mut g Generator) gen_expr(expr Expr) {
|
||||
g.out.write_string(mangle_var(expr.name))
|
||||
}
|
||||
UnaryExpr {
|
||||
g.out.write_string('(${mangle_var(expr.ident)}${expr.op})')
|
||||
if expr.op_on_left {
|
||||
g.out.write_string('(${expr.op}')
|
||||
g.gen_expr(expr.expr)
|
||||
g.out.write_string(')')
|
||||
} else {
|
||||
g.out.write_string('(')
|
||||
g.gen_expr(expr.expr)
|
||||
g.out.write_string('${expr.op})')
|
||||
}
|
||||
}
|
||||
BinaryExpr {
|
||||
g.out.write_string('(')
|
||||
|
||||
92
parser.v
92
parser.v
@@ -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")}
|
||||
}
|
||||
@@ -325,6 +328,7 @@ fn (mut p Parser) parse_primary() Expr {
|
||||
.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 {
|
||||
|
||||
Reference in New Issue
Block a user