Compare commits

...

9 Commits

Author SHA1 Message Date
uan
ccd59f6a0a specify immutable class members 2026-02-08 11:20:23 +01:00
uan
08686c2681 print specify immutable members 2026-02-08 08:47:13 +01:00
uan
294fbbd69c syntax now requires : before type in declarations 2026-02-08 08:40:49 +01:00
uan
7b5eef3bcb small fix 2026-02-07 19:33:18 +01:00
uan
a3b978a09d referencing and methods 2026-02-07 19:14:48 +01:00
uan
005828cec2 &&, ||, &, |, &=, |= 2026-02-07 16:11:00 +01:00
uan
9d26256023 while loops 2026-02-07 15:48:59 +01:00
uan
58f0dfa969 modulus operator 2026-02-07 15:32:52 +01:00
uan
7fddf9ab30 comments yay 2026-02-07 15:14:42 +01:00
6 changed files with 420 additions and 89 deletions

2
.gitignore vendored
View File

@@ -1,6 +1,6 @@
# Binaries for programs and plugins
main
onev
one
exec
*.exe
*.exe~

124
README.md
View File

@@ -20,23 +20,34 @@ In One, variables are declared with the `let` keyword.
The syntax will look something like this
```
let x int = 6;
let x: int = 6;
```
You can also declare a constant using the `const` keyword instead.
```
const x int = 9;
const x: int = 9;
```
---
### Comments
Comments in One start with a # and end with another # or a newline
```
#this is a comment
let #also a comment# a: int = 9;
```
---
### Scopes
Like in most languages, variables in One are only limited to their scope.
You can access a variable from a deeper nested scope, but not the opposite:
```
let x int = 5;
let x: int = 5;
if 5 > 0 {
x = 2;
}
@@ -46,7 +57,7 @@ is valid code, while
```
if 3 < 10 {
let y int = 288;
let y: int = 288;
}
y = 10;
```
@@ -57,14 +68,14 @@ You can also create scopes by wrapping statements in braces. This lets you decla
variables with the same name in different scopes, for example:
```
let x int = 3
let x: int = 3
{
let y real = 5.5;
let y: real = 5.5;
print(y);
}
print("y is now undefined again here");
#y is now undefined again here
{
let y real = 0.2;
let y: real = 0.2;
print(y);
}
```
@@ -77,8 +88,8 @@ As of now, One has a total of 4 primitive types: `void`. `int`, `real`, `bool` a
There are no methods for types, but you can cast a variable to one of them with this syntax
```
let myreal real = 1.5;
let myint int = int(myreal);
let myreal: real = 1.5;
let myint: int = int(myreal);
```
Right now, strings are implemented as direct counterparts to `char*` in C, which is what they
@@ -91,7 +102,7 @@ gives you access to the actual string (which in C corresponds to the `char*`), y
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";
let mystring: string = "this is my string";
print(mystring, mystring.len)
```
@@ -103,13 +114,47 @@ this is my string 17
---
### References
One's counterpart to C's pointers are references, which behave the exact same way.
The main difference is in how you reference/dereference a variable. We all know C's
referencing and dereferencing operators cause some confusion when first learning the language,
and to prevent this, One has designated `ref` and `deref` keywords that are meant to be used like
functions. Here's an example:
```
let x: int = 6;
let ref_to_x: ref(int) = ref(x);
let y: int = deref(ref_to_x) # same as y = x
```
References to objects are again quite similar to C, with the only real difference being
the access to an object's members. in One, you an access members of an object or of a
reference to an object with the `.` notation. But you cannot access the members if the
reference nesting goes further. For example:
```
let u: User = User{17} #user has age 17
print(u.age) # outputs 17
let ref_to_u: ref(User) = ref(u)
print(ref_to_u.age) # outputs 17
let nested_ref: ref(ref(User)) = ref(ref_to_u)
print(nested_ref.age) # not valid
```
Right now, in error messages, it's likely you will still find references marked as `*`s, such as `int*`
---
### Functions
Functions in One are very similar to what you see in languages such as go.
You declare a function called `foo` which takes an `int` and returns a `bool` with the following syntax:
```
fn foo(myarg int) bool {
fn foo(myarg: int) bool {
return myarg < 10;
}
```
@@ -119,7 +164,7 @@ If a function is not expected to return anything, the return type must be `void`
Calling a function looks like this:
```
let b bool = foo(9);
let b: bool = foo(9);
```
---
@@ -132,9 +177,9 @@ Defining a class User might look something like this:
```
class User {
age int
name string
mail_verified bool
age: int
name: string
mail_verified: bool
}
```
@@ -142,7 +187,7 @@ Creating an instance of a class is very simple, just write the name of the class
followed by braces and a list of initial values for the class members.
```
let myuser User = User{17, "uan", false}
let myuser: User = User{17, "uan", false}
```
Class types can be used anywhere primitive types can, such as function arguments
@@ -155,13 +200,13 @@ member's value.
```
class User {
age int
name string
mail_verified bool
immutable id int
age: int
name: string
mail_verified: bool
immutable id: int
}
fn change_values(u User) {
fn change_values(u: User) {
u.age = 10;
u.name = "new name";
u.id = 0;
@@ -170,6 +215,22 @@ fn change_values(u User) {
---
### Methods
Defining a method is very similar to declaring a function. You just have to add the name
of the class between parentheses before the function name.
```
fn (User) age() void {
this.age++
}
```
as you can see, a variable called `this` is automatically declared in methods and is always
of type `ref` of the class, in this case `ref(User)`
---
### Print
Printing is still a work in progress feature, but right now you can print any primitive and non-primitive type.
@@ -177,7 +238,7 @@ The 'print' built-in function accepts a variable number of arguments, and will p
of them separated by a space.
```
let x int = 144;
let x: int = 144;
print(x, 240);
```
@@ -200,10 +261,11 @@ User {
### Control Flow
Control flow in One is still quite limited, as the `for` keyword has not been implemented yet. `if`, `else` and `elif` statements are fully implemented and are written like this.
Control flow in One isn't fully implemented yet, but it is already functional.
`if`, `else` and `elif` statements are fully implemented and are written like this:
```
let x int = 17;
let x: int = 17;
if x >= 100 {
print(x, "is greater than 100");
@@ -214,7 +276,15 @@ if x >= 100 {
}
```
Using parentheses around the condition isn't necessary.
`while` loops are very similar, and can be written like this:
```
let i: int = 0;
while i < 10 {
print(i);
i++;
}
```
### The main function
@@ -223,7 +293,7 @@ The 'main' function must always return `int` and does not accept any arguments.
```
fn main() int {
print("my code here");
#my code here
}
```

View File

@@ -1,3 +1,4 @@
module main
import strings
@@ -13,18 +14,24 @@ fn mangle_var(name string) string {
return 'one_var_${name}'
}
fn mangle_func(name string) string {
fn mangle_func(name string, class_name ?string) string {
if name == 'main' {return 'main'}
return 'one_func_${name}'
mut mangled_name := 'one_func_${name}'
if class_name != none {
mangled_name += '_${class_name}'
}
return mangled_name
}
fn mangle_struct(name string) string {
return 'one_class_${name}_'
return 'one_class_${name}'
}
fn get_type_format(type string) string {
dump(type)
if type.contains('*') {
return 'p'
}
return match type {
'int', 'bool' {'d'}
'real' {'f'}
@@ -44,17 +51,21 @@ fn (mut g Generator) get_print_label(expr Expr) string {
}
fn (mut g Generator) get_c_type(typ string) string {
return match typ {
astkcount := typ.count('*')
clean := typ.replace('*', '')
return match clean {
'real' {'float'}
'int' {'int32_t'}
'string' {'OneString'}
else {typ}
}
else {clean}
} + '*'.repeat(astkcount)
}
fn (mut g Generator) mangle_if_class(name string) string {
if g.symbols.lookup_class(name) != none {
return 'struct ${mangle_struct(name)}'
astkcount := name.count('*')
noptr := name.replace('*', '')
if g.symbols.lookup_class(noptr) != none {
return 'struct ${mangle_struct(noptr)}' + '*'.repeat(astkcount)
}
return name
}
@@ -71,12 +82,20 @@ fn (mut g Generator) gen_class_print_func(stmt ClassDecl) {
g.out.writeln('for(int i=0; i<indent + 1; i++) printf(" ");')
g.out.write_string('printf("${member.name}: ");')
if member.type != 'string' && g.symbols.lookup_class(member.type) != none {
inner_struct_name := mangle_struct(member.type)
g.out.writeln('print_${inner_struct_name}(s.${member.name}, indent + 1);')
g.out.write_string('print_${inner_struct_name}(s.${member.name}, indent + 1);')
if member.is_immutable {
g.out.write_string('printf(" (immutable)\\n");')
}
} else {
format := get_type_format(member.type)
g.out.write_string('printf("%${format}\\n", s.${member.name}')
g.out.write_string('printf("%${format}')
if member.is_immutable {
g.out.write_string(' (immutable) ')
}
g.out.write_string('\\n", s.${member.name}')
if member.type == 'string' {
g.out.write_string('.string')
}
@@ -118,7 +137,7 @@ fn (mut g Generator) gen_stmt(stmt Stmt) {
}
FuncDecl {
c_type := g.mangle_if_class(g.get_c_type(stmt.ret_type))
g.out.write_string('${c_type} ${mangle_func(stmt.name)}(')
g.out.write_string('${c_type} ${mangle_func(stmt.name, stmt.class_name)}(')
for param in stmt.params {
g.gen_stmt(param)
if param != stmt.params[stmt.params.len-1]{
@@ -158,11 +177,27 @@ fn (mut g Generator) gen_stmt(stmt Stmt) {
g.out.write_string(') ')
g.gen_stmt(stmt.block)
}
WhileLoop {
g.out.write_string('while (')
g.gen_expr(stmt.guard)
g.out.write_string(') ')
g.gen_stmt(stmt.block)
}
}
}
fn (mut g Generator) gen_expr(expr Expr) {
match expr {
RefExpr {
g.out.write_string('(&')
g.gen_expr(expr.expr)
g.out.write_string(')')
}
DerefExpr {
g.out.write_string('(*')
g.gen_expr(expr.expr)
g.out.write_string(')')
}
RealLiteral {
g.out.write_string(expr.val.str())
}
@@ -236,7 +271,22 @@ fn (mut g Generator) gen_expr(expr Expr) {
g.out.write_string(')')
}
FnCall {
g.out.write_string('${mangle_func(expr.name)}(')
g.out.write_string('${mangle_func(expr.name, none)}(')
for arg in expr.args {
g.gen_expr(arg)
if arg != expr.args[expr.args.len-1]{
g.out.write_string(', ')
}
}
g.out.write_string(')')
}
MethodCall {
g.out.write_string('${mangle_func(expr.method, expr.from_type)}(')
g.out.write_string('&')
g.gen_expr(expr.from)
if expr.args.len > 0 {
g.out.write_string(', ')
}
for arg in expr.args {
g.gen_expr(arg)
if arg != expr.args[expr.args.len-1]{
@@ -261,7 +311,12 @@ fn (mut g Generator) gen_expr(expr Expr) {
}
MemberAccess {
g.gen_expr(expr.from)
g.out.write_string('.${expr.member}')
match expr.from_type.count('*') {
0 {g.out.write_string('.')}
1 {g.out.write_string('->')}
else {panic("Tried accessing member from an object of type ${expr.from_type}")}
}
g.out.write_string('${expr.member}')
}
else {panic("Unimplemented expression")}
}

42
lexer.v
View File

@@ -10,12 +10,14 @@ enum TokenType as u8 {
kw_if
kw_else
kw_elif
kw_for
kw_while
kw_break
kw_fn
kw_return
kw_print
kw_class
kw_ref
kw_deref
integer
real
boolean
@@ -25,6 +27,11 @@ enum TokenType as u8 {
minus
star
slash
percent
and
or
and_and
or_or
equals
less
greater
@@ -36,6 +43,9 @@ enum TokenType as u8 {
minus_eq
star_eq
slash_eq
percent_eq
and_eq
or_eq
increment
decrement
lparen
@@ -73,6 +83,7 @@ fn toktype_from_delimiter(delimiter string) TokenType {
'-' {.minus}
'*' {.star}
'/' {.slash}
'%' {.percent}
'=' {.equals}
'<' {.less}
'>' {.greater}
@@ -88,8 +99,15 @@ fn toktype_from_delimiter(delimiter string) TokenType {
'-=' {.minus_eq}
'*=' {.star_eq}
'/=' {.slash_eq}
'%=' {.percent_eq}
'++' {.increment}
'--' {.decrement}
'&' {.and}
'&&' {.and_and}
'|' {.or}
'||' {.or_or}
'&=' {.and_eq}
'|=' {.or_eq}
else {.unknown}
}
}
@@ -102,7 +120,7 @@ fn toktype_from_kw(kw string) TokenType {
'if' {.kw_if}
'else' {.kw_else}
'elif' {.kw_elif}
'for' {.kw_for}
'while' {.kw_while}
'break' {.kw_break}
'fn' {.kw_fn}
'return' {.kw_return}
@@ -110,14 +128,16 @@ fn toktype_from_kw(kw string) TokenType {
'print' {.kw_print}
'class' {.kw_class}
'immutable' {.kw_immutable}
'ref' {.kw_ref}
'deref' {.kw_deref}
else {.unknown}
}
}
fn is_delimiter(c u8, is_inside_number bool) bool {
valid_chars := match is_inside_number {
true {" +-*/,;:%<>()[]{}=\n\""}
false {". +-*/,;:%<>()[]{}=\n\""}
true {" #+-*/,;:%<>()[]{}=|&!\n\""}
false {". #+-*/,;:%<>()[]{}=|&!\n\""}
}
return valid_chars.contains(c.ascii_str())
}
@@ -129,7 +149,7 @@ fn is_real(str string) bool {
fn is_keyword(str string) bool {
return [
"void", "int", "real", "bool", "string", "if", "else", "elif", "for", "break", "fn", "return", "let", "const", "true", "false", "print", "class", "immutable"
"void", "int", "real", "bool", "string", "if", "else", "elif", "while", "break", "fn", "return", "let", "const", "true", "false", "print", "class", "immutable", "ref", "deref"
].contains(str)
}
@@ -150,8 +170,17 @@ fn lex(input string) ?[]Token {
mut tokens := []Token{}
mut is_inside_number := false
mut is_inside_string := false
mut is_inside_comment := false
for (right < input.len && left <= right) {
for is_inside_comment {
right++
if ['#', '\n'].contains(input[right].ascii_str()) {
is_inside_comment = false
right++
}
left = right
}
for is_inside_string {
right++
if input[right].ascii_str() == '\"' {
@@ -174,10 +203,11 @@ fn lex(input string) ?[]Token {
if is_delimiter(input[right], is_inside_number) && left == right {
if !input[right].is_space() {
if input[right].ascii_str() == '\"' {is_inside_string = true; continue}
if input[right].ascii_str() == '#' {is_inside_comment = true; continue}
mut tok_str := input[right].ascii_str()
if right + 1 < input.len {
combined := input.substr(right, right + 2)
if combined in ['==', '>=', '<=', '!=', '+=', '-=', '*=', '/=', '++', '--'] {
if combined in ['==', '>=', '<=', '!=', '+=', '-=', '*=', '/=', '%=', '++', '--', '||', '&&', '&=', '|='] {
tok_str = combined
right++
}

BIN
one

Binary file not shown.

258
parser.v
View File

@@ -18,13 +18,13 @@ enum Precedence {
fn (p Parser) get_precedence(tok_type TokenType) Precedence {
return match tok_type {
.equals, .plus_eq, .minus_eq, .star_eq, .slash_eq { .assignment }
.eq_eq, .not_eq, .less_eq, .greater_eq, .less, .greater { .comparison }
.plus, .minus { .sum }
.star, .slash { .product }
.dot { .access }
.increment, .decrement { .suffix }
else { .base}
.equals, .plus_eq, .minus_eq, .star_eq, .slash_eq, .percent_eq, .and_eq, .or_eq, .and_and, .or_or {.assignment}
.eq_eq, .not_eq, .less_eq, .greater_eq, .less, .greater {.comparison}
.plus, .minus {.sum}
.star, .slash, .percent {.product}
.dot {.access}
.increment, .decrement {.suffix}
else {.base}
}
}
@@ -45,6 +45,8 @@ struct FuncSymbolInfo {
struct ClassSymbolInfo {
name string
members_info map[string]VarSymbolInfo
mut:
methods_info map[string]FuncSymbolInfo
}
struct SymbolTable {
@@ -74,9 +76,6 @@ fn (mut s SymbolTable) define_var(name string, typ string, is_const bool) {
}
fn (mut s SymbolTable) lookup_var(name string) ?VarSymbolInfo {
$if debug {
dump(s.variable_scopes.len)
}
if s.variable_scopes.len == 0 {return none}
for variables in s.variable_scopes.reverse() {
if name in variables {
@@ -115,6 +114,19 @@ fn (mut s SymbolTable) lookup_class(name string) ?ClassSymbolInfo {
return none
}
fn (mut s SymbolTable) define_method(name string, class_name string, typ string, block Block) {
if s.lookup_class(class_name) == none {parse_error("Undefined class ${class_name}")}
s.structs[class_name].methods_info[name] = FuncSymbolInfo{type: typ, block: block}
}
fn (mut s SymbolTable) lookup_method(name string, class_name string) ?FuncSymbolInfo {
mut classinfo := s.lookup_class(class_name) or {parse_error("Undefined class ${class_name}")}
if name in classinfo.methods_info {
return classinfo.methods_info[name]
}
return none
}
fn (mut s SymbolTable) is_in_global_scope() bool {
return s.variable_scopes.len == 1
}
@@ -122,10 +134,20 @@ fn (mut s SymbolTable) is_in_global_scope() bool {
// ------------------------------------------- Expressions
type Expr = VoidExpr | UnaryExpr | BinaryExpr | IntegerLiteral | RealLiteral | BoolLiteral | StringLiteral | Variable | TypeExpr | Function | TypeCast | ParenExpr | PrintExpr | FnCall | ClassMember | ClassInstantiation | MemberAccess
type Expr = VoidExpr | UnaryExpr | BinaryExpr | IntegerLiteral | RealLiteral | BoolLiteral | StringLiteral |
Variable | TypeExpr | Function | TypeCast | ParenExpr | PrintExpr | FnCall | ClassMember |
ClassInstantiation | MemberAccess | MethodCall | RefExpr | DerefExpr
struct VoidExpr {}
struct RefExpr {
expr Expr
}
struct DerefExpr {
expr Expr
}
struct UnaryExpr {
expr Expr
op string
@@ -170,6 +192,7 @@ struct ClassMember {
struct MemberAccess {
from Expr
from_type string
member string
member_type string
}
@@ -202,9 +225,18 @@ struct FnCall {
args []Expr
}
struct MethodCall {
from Expr
from_type string
method string
ret_type string
args []Expr
}
// ------------------------------------------- Statements
type Stmt = VarDecl | ExprStmt | ReturnStmt | Block | IfStmt | ElseStmt | ElifStmt | FuncDecl | Param | ClassDecl
type Stmt = VarDecl | ExprStmt | ReturnStmt | Block | IfStmt | ElseStmt | ElifStmt | FuncDecl | Param |
ClassDecl | WhileLoop
struct VarDecl {
name string
@@ -218,6 +250,7 @@ struct FuncDecl {
params []Param
ret_type string
block Block
class_name ?string //if it is a method
}
struct ClassDecl {
@@ -251,6 +284,11 @@ struct ElifStmt {
block Block
}
struct WhileLoop {
guard Expr
block Block
}
struct Param {
name string
type string
@@ -329,6 +367,8 @@ fn (mut p Parser) parse_primary() Expr {
.lparen {p.parse_paren()}
.kw_print {p.parse_print()}
.plus, .minus {p.parse_unary_left(token.text)}
.kw_ref {p.parse_ref()}
.kw_deref {p.parse_deref()}
else {parse_error("Unexpected Token")}
}
}
@@ -363,13 +403,66 @@ fn (mut p Parser) parse_ident(ident string) Expr {
}
}
fn (mut p Parser) parse_member_access(from Expr) MemberAccess {
fn (mut p Parser) parse_ref() RefExpr {
p.expect(.lparen)
expr := p.parse_expr(.prefix)
match expr {
IntegerLiteral, StringLiteral,
RealLiteral, BoolLiteral {parse_error("Cannot get reference of literal value")}
else {}
}
p.expect(.rparen)
return RefExpr{expr: expr}
}
fn (mut p Parser) parse_deref() DerefExpr {
p.expect(.lparen)
expr := p.parse_expr(.prefix)
if !p.get_expr_type(expr).contains('*') {
parse_error('cannot dereference a variable of type ${p.get_expr_type(expr)} (not a reference)')
}
p.expect(.rparen)
return DerefExpr{expr: expr}
}
fn (mut p Parser) parse_member_access(from Expr) Expr {
if p.get_expr_type(from).count('*') > 1 {
base_class_name := p.get_expr_type(from).replace('*', '')
parse_error("Tried accessing member from object of type (${p.get_expr_type(from)}). \
You can only access members from objects of type (${base_class_name}) \
or (${base_class_name}*)")
}
member_tok := p.next()
if member_tok.type != .identifier {parse_error("Expected identifier after member access")}
from_class := p.symbols.lookup_class(p.get_expr_type(from)) or {panic("Accessing member from non-class type")}
base_class_name := p.get_expr_type(from).replace('*', '')
from_class := p.symbols.lookup_class(base_class_name) or {parse_error("Accessing member from non-class type")}
if p.peek().type == .lparen {
if p.get_expr_type(from).contains('*') {
parse_error("Cannot call method on a reference")
}
methodinfo := p.symbols.lookup_method(member_tok.text, base_class_name) or {
dump(p.symbols)
parse_error("Calling undefined method ${member_tok.text}")
}
call := p.parse_call(member_tok.text)
return MethodCall{
from: from
from_type: p.get_expr_type(from)
method: member_tok.text
ret_type: methodinfo.type
args: call.args
}
}
if !(member_tok.text in from_class.members_info) {panic("Accessing undefined member ${member_tok.text}")}
member_type := from_class.members_info[member_tok.text].type
return MemberAccess {from: from, member: member_tok.text, member_type: member_type}
return MemberAccess {
from: from,
from_type: p.get_expr_type(from)
member: member_tok.text,
member_type: member_type
}
}
fn (mut p Parser) parse_class_member() ClassMember {
@@ -381,17 +474,22 @@ fn (mut p Parser) parse_class_member() ClassMember {
member_name := p.peek().text
p.expect(.identifier)
type_tok := p.peek()
p.expect(.colon)
type_tok := p.next()
type_name := match type_tok.type {
.type {type_tok.text}
.identifier {
p.expect_ident_is_class(type_tok.text)
type_tok.text
}
.kw_ref {
ref_expr := p.parse_ref()
p.get_expr_type(ref_expr)
}
else{parse_error("Expected type after class member ${member_name} in declaration, got ${p.peek().type}")}
}
p.next()
p.expect(.semicolon)
return ClassMember{name: member_name, type: type_name is_immutable: is_immut}
}
@@ -467,12 +565,14 @@ fn (mut p Parser) parse_unary_left(op string) UnaryExpr {
fn (mut p Parser) parse_binary(left Expr, op string, prec Precedence) BinaryExpr {
if op in ['=', '+=', '-=', '*=', '/='] {
if op in ['=', '+=', '-=', '*=', '/=', '%='] {
if p.get_expr_is_immutable(left) {
parse_error("Cannot assign to immutable expression ${left}")
}
}
dump(op)
right := p.parse_expr(prec)
binary_expr := BinaryExpr{left, op, right}
@@ -508,6 +608,7 @@ fn (mut p Parser) parse_paren() ParenExpr {
fn (mut p Parser) check_binary_expr_types(expr BinaryExpr) {
left_t := p.get_expr_type(expr.left)
right_t := p.get_expr_type(expr.right)
dump("${left_t}, ${right_t}")
if left_t != right_t {
parse_error('Type mismatch in expression: ${left_t} and ${right_t}')
}
@@ -523,16 +624,26 @@ fn (mut p Parser) get_expr_is_immutable(expr Expr) bool {
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")}
clean_class_name := p.get_expr_type(expr.from).replace('*', '')
classinfo := p.symbols.lookup_class(clean_class_name) or {parse_error("Invalid class")}
memberinfo := classinfo.members_info[expr.member] or {parse_error("Undefined member ${expr.member}")}
memberinfo.is_immutable
}
Variable {
varinfo := p.symbols.lookup_var(expr.name) or {parse_error("Undefined variable ${expr.name}")}
varinfo.is_immutable
}
else {true}
}
}
fn (mut p Parser) get_expr_type(expr Expr) string {
return match expr {
RefExpr {p.get_expr_type(expr.expr)+'*'}
DerefExpr {
exprtype := p.get_expr_type(expr.expr)
exprtype[..exprtype.len-1]
}
ParenExpr {p.get_expr_type(expr.expr)}
IntegerLiteral {'int'}
RealLiteral {'real'}
@@ -544,7 +655,7 @@ fn (mut p Parser) get_expr_type(expr Expr) string {
BinaryExpr {
p.check_binary_expr_types(expr)
left_t := p.get_expr_type(expr.left)
if expr.op in ['<=', '==', '>=', '!=', '<', '>'] {
if expr.op in ['<=', '==', '>=', '!=', '<', '>', '||', '&&'] {
'bool'
} else {
left_t
@@ -557,10 +668,16 @@ fn (mut p Parser) get_expr_type(expr Expr) string {
return info.type
}
TypeCast {expr.type}
FnCall {
FnCall {
fninfo := p.symbols.lookup_func(expr.name) or {parse_error("Tried to call undefined function ${expr.name}")}
fninfo.type
}
MethodCall {
methodinfo := p.symbols.lookup_method(expr.method, p.get_expr_type(expr.from)) or {
parse_error("Tried to call undefined method ${expr.method}")
}
methodinfo.type
}
MemberAccess {
expr.member_type
}
@@ -572,8 +689,16 @@ 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' {['+', '-', '*', '/', '<', '>', '<=', '>=', '++', '--', '+=', '-=', '*=', '/=', '++', '--']}
'bool' {['=']}
'int' {[
'+', '-', '*', '/', '%', '<', '>', '<=',
'>=', '++', '--', '+=', '-=', '*=', '/=',
'%='
]}
'real' {[
'+', '-', '*', '/', '<', '>', '<=', '>=',
'++', '--', '+=', '-=', '*=', '/=',
]}
'bool' {['&&', '||', '&', '|', '&=', '|=']}
else {[]}
}
legal_ops << global
@@ -606,6 +731,7 @@ fn (mut p Parser) parse_statement() Stmt {
.kw_if {return p.parse_if()}
.kw_else {return p.parse_else()}
.kw_elif {return p.parse_elif()}
.kw_while {return p.parse_while()}
else {return p.parse_expr_stmt()}
}
}
@@ -617,14 +743,19 @@ fn (mut p Parser) parse_var_decl(is_const bool) VarDecl {
if name_tok.type != .identifier {
parse_error("Expected variable name after let")
}
p.expect(.colon)
type_tok := p.next()
type_name := match type_tok.type {
.type {type_tok.text}
.identifier {
p.expect_ident_is_class(type_tok.text)
type_tok.text
}
p.expect_ident_is_class(type_tok.text)
type_tok.text
}
.kw_ref {
ref_expr := p.parse_ref()
p.get_expr_type(ref_expr)
}
else{parse_error("Expected variable type after name when declaring ${name_tok.text}")}
}
@@ -634,12 +765,12 @@ fn (mut p Parser) parse_var_decl(is_const bool) VarDecl {
if type_tok.text == 'void' {
parse_error("Cannot declare a variable of type void")
}
if p.get_expr_type(val) != type_tok.text {
parse_error("Mismatch between declared type (${type_tok.text}) and actual type (${p.get_expr_type(val)})")
if p.get_expr_type(val) != type_name {
parse_error("Mismatch between declared type (${type_name}) and actual type (${p.get_expr_type(val)})")
}
p.expect(.semicolon)
p.symbols.define_var(name_tok.text, type_tok.text, is_const)
p.symbols.define_var(name_tok.text, type_name, is_const)
return VarDecl {
name: name_tok.text
@@ -655,7 +786,17 @@ fn (mut p Parser) parse_func_decl() FuncDecl {
p.expect(.kw_fn)
mut class_name := ?string(none)
if p.peek().type == .lparen {
p.next()
classname_tok := p.peek()
p.expect(.identifier)
class_name = classname_tok.text
p.expect(.rparen)
}
name_tok := p.next()
if name_tok.type != .identifier {
parse_error("Expected function name after let")
}
@@ -666,22 +807,34 @@ fn (mut p Parser) parse_func_decl() FuncDecl {
p.symbols.variable_scopes << map[string]VarSymbolInfo{}
if class_name != none {
params << Param{'this', class_name+'*'}
}
if p.peek().type != .rparen {
for {
if p.peek().type != .identifier {parse_error("Invalid syntax to declare function arguments! use f(myint int, myreal real)")}
if p.peek().type != .identifier {
parse_error("Invalid syntax to declare function arguments!\
use f(myint int, myreal real)")
}
p_name := p.next().text
//if p.peek().type != .type {parse_error("Invalid syntax to declare function arguments! use f(myint int, myreal real)")}
type_tok := p.peek()
p.expect(.colon)
type_tok := p.next()
p_type := match type_tok.type {
.type {type_tok.text}
.identifier {
p.expect_ident_is_class(type_tok.text)
type_tok.text
}
else{parse_error("Expected argument type after name when declaring ${p_name}")}
}
.kw_ref {
ref_expr := p.parse_ref()
dump(p.peek())
p.get_expr_type(ref_expr)
}
else{parse_error("Expected argument type after name when declaring ${p_name}")}
}
p.next()
params << Param{p_name, p_type}
p.symbols.define_var(p_name, p_type, false)
@@ -695,19 +848,26 @@ fn (mut p Parser) parse_func_decl() FuncDecl {
p.expect(.rparen)
p.dump_token()
type_tok := p.next()
ret_type := match type_tok.type {
.type {type_tok.text}
.identifier {
p.expect_ident_is_class(type_tok.text)
type_tok.text
}
else{parse_error("Expected function return type after name when declaring ${name_tok.text}")}
}
.kw_ref {
ref_expr := p.parse_ref()
p.get_expr_type(ref_expr)
}
else{parse_error("Expected function return type after name when declaring ${name_tok.text}")}
}
p.symbols.define_func(name_tok.text, type_tok.text, Block{})
if class_name != none {
p.symbols.define_method(name_tok.text, class_name, type_tok.text, Block{})
p.symbols.define_var('this', class_name+'*', false)
} else {
p.symbols.define_func(name_tok.text, type_tok.text, Block{})
}
block := p.parse_block(true)
@@ -722,13 +882,18 @@ fn (mut p Parser) parse_func_decl() FuncDecl {
p.symbols.variable_scopes.delete_last()
p.symbols.define_func(name_tok.text, type_tok.text, block)
if class_name != none {
p.symbols.define_method(name_tok.text, class_name, type_tok.text, block)
} else {
p.symbols.define_func(name_tok.text, type_tok.text, block)
}
return FuncDecl {
name: name_tok.text
ret_type: ret_type
block: block
params: params
class_name: class_name
}
}
@@ -758,6 +923,17 @@ fn (mut p Parser) parse_return_stmt() ReturnStmt {
}
}
fn (mut p Parser) parse_while() WhileLoop {
p.expect(.kw_while)
cond := p.parse_expr(.base)
if p.get_expr_type(cond) != 'bool' {
parse_error('While loop guard must be of type bool')
}
block := p.parse_block(false)
return WhileLoop {cond, block}
}
fn (mut p Parser) parse_if() IfStmt {
p.expect(.kw_if)
cond := p.parse_expr(.base)