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 # Binaries for programs and plugins
main main
onev one
exec exec
*.exe *.exe
*.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 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. 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 ### Scopes
Like in most languages, variables in One are only limited to their scope. 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: 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 { if 5 > 0 {
x = 2; x = 2;
} }
@@ -46,7 +57,7 @@ is valid code, while
``` ```
if 3 < 10 { if 3 < 10 {
let y int = 288; let y: int = 288;
} }
y = 10; 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: 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);
} }
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); 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 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 myreal: real = 1.5;
let myint int = int(myreal); let myint: int = int(myreal);
``` ```
Right now, strings are implemented as direct counterparts to `char*` in C, which is what they 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. 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) 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
Functions in One are very similar to what you see in languages such as go. 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: 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; 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: 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 { class User {
age int age: int
name string name: string
mail_verified bool 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. 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 Class types can be used anywhere primitive types can, such as function arguments
@@ -155,13 +200,13 @@ member's value.
``` ```
class User { class User {
age int age: int
name string name: string
mail_verified bool mail_verified: bool
immutable id int immutable id: int
} }
fn change_values(u User) { fn change_values(u: User) {
u.age = 10; u.age = 10;
u.name = "new name"; u.name = "new name";
u.id = 0; 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 ### Print
Printing is still a work in progress feature, but right now you can print any primitive and non-primitive type. 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. of them separated by a space.
``` ```
let x int = 144; let x: int = 144;
print(x, 240); print(x, 240);
``` ```
@@ -200,10 +261,11 @@ User {
### Control Flow ### 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 { if x >= 100 {
print(x, "is greater than 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 ### The main function
@@ -223,7 +293,7 @@ The 'main' function must always return `int` and does not accept any arguments.
``` ```
fn main() int { fn main() int {
print("my code here"); #my code here
} }
``` ```

View File

@@ -1,3 +1,4 @@
module main module main
import strings import strings
@@ -13,18 +14,24 @@ fn mangle_var(name string) string {
return 'one_var_${name}' return 'one_var_${name}'
} }
fn mangle_func(name string) string { fn mangle_func(name string, class_name ?string) string {
if name == 'main' {return 'main'} 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 { fn mangle_struct(name string) string {
return 'one_class_${name}_' return 'one_class_${name}'
} }
fn get_type_format(type string) string { fn get_type_format(type string) string {
dump(type) if type.contains('*') {
return 'p'
}
return match type { return match type {
'int', 'bool' {'d'} 'int', 'bool' {'d'}
'real' {'f'} '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 { fn (mut g Generator) get_c_type(typ string) string {
return match typ { astkcount := typ.count('*')
clean := typ.replace('*', '')
return match clean {
'real' {'float'} 'real' {'float'}
'int' {'int32_t'} 'int' {'int32_t'}
'string' {'OneString'} 'string' {'OneString'}
else {typ} else {clean}
} } + '*'.repeat(astkcount)
} }
fn (mut g Generator) mangle_if_class(name string) string { fn (mut g Generator) mangle_if_class(name string) string {
if g.symbols.lookup_class(name) != none { astkcount := name.count('*')
return 'struct ${mangle_struct(name)}' noptr := name.replace('*', '')
if g.symbols.lookup_class(noptr) != none {
return 'struct ${mangle_struct(noptr)}' + '*'.repeat(astkcount)
} }
return name 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.writeln('for(int i=0; i<indent + 1; i++) printf(" ");')
g.out.write_string('printf("${member.name}: ");') g.out.write_string('printf("${member.name}: ");')
if member.type != 'string' && g.symbols.lookup_class(member.type) != none { if member.type != 'string' && g.symbols.lookup_class(member.type) != none {
inner_struct_name := mangle_struct(member.type) 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 { } else {
format := get_type_format(member.type) 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' { if member.type == 'string' {
g.out.write_string('.string') g.out.write_string('.string')
} }
@@ -118,7 +137,7 @@ fn (mut g Generator) gen_stmt(stmt Stmt) {
} }
FuncDecl { FuncDecl {
c_type := g.mangle_if_class(g.get_c_type(stmt.ret_type)) 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 { for param in stmt.params {
g.gen_stmt(param) g.gen_stmt(param)
if param != stmt.params[stmt.params.len-1]{ 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.out.write_string(') ')
g.gen_stmt(stmt.block) 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) { fn (mut g Generator) gen_expr(expr Expr) {
match 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 { RealLiteral {
g.out.write_string(expr.val.str()) g.out.write_string(expr.val.str())
} }
@@ -236,7 +271,22 @@ fn (mut g Generator) gen_expr(expr Expr) {
g.out.write_string(')') g.out.write_string(')')
} }
FnCall { 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 { for arg in expr.args {
g.gen_expr(arg) g.gen_expr(arg)
if arg != expr.args[expr.args.len-1]{ if arg != expr.args[expr.args.len-1]{
@@ -261,7 +311,12 @@ fn (mut g Generator) gen_expr(expr Expr) {
} }
MemberAccess { MemberAccess {
g.gen_expr(expr.from) 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")} else {panic("Unimplemented expression")}
} }

42
lexer.v
View File

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

BIN
one

Binary file not shown.

228
parser.v
View File

@@ -18,10 +18,10 @@ enum Precedence {
fn (p Parser) get_precedence(tok_type TokenType) Precedence { fn (p Parser) get_precedence(tok_type TokenType) Precedence {
return match tok_type { return match tok_type {
.equals, .plus_eq, .minus_eq, .star_eq, .slash_eq { .assignment } .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} .eq_eq, .not_eq, .less_eq, .greater_eq, .less, .greater {.comparison}
.plus, .minus {.sum} .plus, .minus {.sum}
.star, .slash { .product } .star, .slash, .percent {.product}
.dot {.access} .dot {.access}
.increment, .decrement {.suffix} .increment, .decrement {.suffix}
else {.base} else {.base}
@@ -45,6 +45,8 @@ struct FuncSymbolInfo {
struct ClassSymbolInfo { struct ClassSymbolInfo {
name string name string
members_info map[string]VarSymbolInfo members_info map[string]VarSymbolInfo
mut:
methods_info map[string]FuncSymbolInfo
} }
struct SymbolTable { 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 { fn (mut s SymbolTable) lookup_var(name string) ?VarSymbolInfo {
$if debug {
dump(s.variable_scopes.len)
}
if s.variable_scopes.len == 0 {return none} if s.variable_scopes.len == 0 {return none}
for variables in s.variable_scopes.reverse() { for variables in s.variable_scopes.reverse() {
if name in variables { if name in variables {
@@ -115,6 +114,19 @@ fn (mut s SymbolTable) lookup_class(name string) ?ClassSymbolInfo {
return none 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 { fn (mut s SymbolTable) is_in_global_scope() bool {
return s.variable_scopes.len == 1 return s.variable_scopes.len == 1
} }
@@ -122,10 +134,20 @@ fn (mut s SymbolTable) is_in_global_scope() bool {
// ------------------------------------------- Expressions // ------------------------------------------- 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 VoidExpr {}
struct RefExpr {
expr Expr
}
struct DerefExpr {
expr Expr
}
struct UnaryExpr { struct UnaryExpr {
expr Expr expr Expr
op string op string
@@ -170,6 +192,7 @@ struct ClassMember {
struct MemberAccess { struct MemberAccess {
from Expr from Expr
from_type string
member string member string
member_type string member_type string
} }
@@ -202,9 +225,18 @@ struct FnCall {
args []Expr args []Expr
} }
struct MethodCall {
from Expr
from_type string
method string
ret_type string
args []Expr
}
// ------------------------------------------- Statements // ------------------------------------------- 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 { struct VarDecl {
name string name string
@@ -218,6 +250,7 @@ struct FuncDecl {
params []Param params []Param
ret_type string ret_type string
block Block block Block
class_name ?string //if it is a method
} }
struct ClassDecl { struct ClassDecl {
@@ -251,6 +284,11 @@ struct ElifStmt {
block Block block Block
} }
struct WhileLoop {
guard Expr
block Block
}
struct Param { struct Param {
name string name string
type string type string
@@ -329,6 +367,8 @@ fn (mut p Parser) parse_primary() Expr {
.lparen {p.parse_paren()} .lparen {p.parse_paren()}
.kw_print {p.parse_print()} .kw_print {p.parse_print()}
.plus, .minus {p.parse_unary_left(token.text)} .plus, .minus {p.parse_unary_left(token.text)}
.kw_ref {p.parse_ref()}
.kw_deref {p.parse_deref()}
else {parse_error("Unexpected Token")} 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() member_tok := p.next()
if member_tok.type != .identifier {parse_error("Expected identifier after member access")} 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}")} 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 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 { 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 member_name := p.peek().text
p.expect(.identifier) p.expect(.identifier)
type_tok := p.peek() p.expect(.colon)
type_tok := p.next()
type_name := match type_tok.type { type_name := match type_tok.type {
.type {type_tok.text} .type {type_tok.text}
.identifier { .identifier {
p.expect_ident_is_class(type_tok.text) p.expect_ident_is_class(type_tok.text)
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}")} else{parse_error("Expected type after class member ${member_name} in declaration, got ${p.peek().type}")}
} }
p.next()
p.expect(.semicolon) p.expect(.semicolon)
return ClassMember{name: member_name, type: type_name is_immutable: is_immut} 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 { 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) { if p.get_expr_is_immutable(left) {
parse_error("Cannot assign to immutable expression ${left}") parse_error("Cannot assign to immutable expression ${left}")
} }
} }
dump(op)
right := p.parse_expr(prec) right := p.parse_expr(prec)
binary_expr := BinaryExpr{left, op, right} 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) { fn (mut p Parser) check_binary_expr_types(expr BinaryExpr) {
left_t := p.get_expr_type(expr.left) left_t := p.get_expr_type(expr.left)
right_t := p.get_expr_type(expr.right) right_t := p.get_expr_type(expr.right)
dump("${left_t}, ${right_t}")
if left_t != right_t { if left_t != right_t {
parse_error('Type mismatch in expression: ${left_t} and ${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)} UnaryExpr {p.get_expr_is_immutable(expr.expr)}
TypeCast {p.get_expr_is_immutable(expr.expr)} TypeCast {p.get_expr_is_immutable(expr.expr)}
MemberAccess { 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 := classinfo.members_info[expr.member] or {parse_error("Undefined member ${expr.member}")}
memberinfo.is_immutable memberinfo.is_immutable
} }
Variable {
varinfo := p.symbols.lookup_var(expr.name) or {parse_error("Undefined variable ${expr.name}")}
varinfo.is_immutable
}
else {true} else {true}
} }
} }
fn (mut p Parser) get_expr_type(expr Expr) string { fn (mut p Parser) get_expr_type(expr Expr) string {
return match expr { 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)} ParenExpr {p.get_expr_type(expr.expr)}
IntegerLiteral {'int'} IntegerLiteral {'int'}
RealLiteral {'real'} RealLiteral {'real'}
@@ -544,7 +655,7 @@ fn (mut p Parser) get_expr_type(expr Expr) string {
BinaryExpr { BinaryExpr {
p.check_binary_expr_types(expr) p.check_binary_expr_types(expr)
left_t := p.get_expr_type(expr.left) left_t := p.get_expr_type(expr.left)
if expr.op in ['<=', '==', '>=', '!=', '<', '>'] { if expr.op in ['<=', '==', '>=', '!=', '<', '>', '||', '&&'] {
'bool' 'bool'
} else { } else {
left_t left_t
@@ -561,6 +672,12 @@ fn (mut p Parser) get_expr_type(expr Expr) string {
fninfo := p.symbols.lookup_func(expr.name) or {parse_error("Tried to call undefined function ${expr.name}")} fninfo := p.symbols.lookup_func(expr.name) or {parse_error("Tried to call undefined function ${expr.name}")}
fninfo.type 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 { MemberAccess {
expr.member_type 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 { fn (mut p Parser) is_op_valid_for_type(type string, op string) bool {
global := ['=', '==', '!='] global := ['=', '==', '!=']
mut legal_ops := match type { mut legal_ops := match type {
'int', 'real' {['+', '-', '*', '/', '<', '>', '<=', '>=', '++', '--', '+=', '-=', '*=', '/=', '++', '--']} 'int' {[
'bool' {['=']} '+', '-', '*', '/', '%', '<', '>', '<=',
'>=', '++', '--', '+=', '-=', '*=', '/=',
'%='
]}
'real' {[
'+', '-', '*', '/', '<', '>', '<=', '>=',
'++', '--', '+=', '-=', '*=', '/=',
]}
'bool' {['&&', '||', '&', '|', '&=', '|=']}
else {[]} else {[]}
} }
legal_ops << global legal_ops << global
@@ -606,6 +731,7 @@ fn (mut p Parser) parse_statement() Stmt {
.kw_if {return p.parse_if()} .kw_if {return p.parse_if()}
.kw_else {return p.parse_else()} .kw_else {return p.parse_else()}
.kw_elif {return p.parse_elif()} .kw_elif {return p.parse_elif()}
.kw_while {return p.parse_while()}
else {return p.parse_expr_stmt()} else {return p.parse_expr_stmt()}
} }
} }
@@ -617,6 +743,7 @@ fn (mut p Parser) parse_var_decl(is_const bool) VarDecl {
if name_tok.type != .identifier { if name_tok.type != .identifier {
parse_error("Expected variable name after let") parse_error("Expected variable name after let")
} }
p.expect(.colon)
type_tok := p.next() type_tok := p.next()
type_name := match type_tok.type { type_name := match type_tok.type {
@@ -625,6 +752,10 @@ fn (mut p Parser) parse_var_decl(is_const bool) VarDecl {
p.expect_ident_is_class(type_tok.text) p.expect_ident_is_class(type_tok.text)
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}")} 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' { if type_tok.text == 'void' {
parse_error("Cannot declare a variable of type void") parse_error("Cannot declare a variable of type void")
} }
if p.get_expr_type(val) != type_tok.text { if p.get_expr_type(val) != type_name {
parse_error("Mismatch between declared type (${type_tok.text}) and actual type (${p.get_expr_type(val)})") parse_error("Mismatch between declared type (${type_name}) and actual type (${p.get_expr_type(val)})")
} }
p.expect(.semicolon) 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 { return VarDecl {
name: name_tok.text name: name_tok.text
@@ -655,7 +786,17 @@ fn (mut p Parser) parse_func_decl() FuncDecl {
p.expect(.kw_fn) 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() name_tok := p.next()
if name_tok.type != .identifier { if name_tok.type != .identifier {
parse_error("Expected function name after let") 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{} p.symbols.variable_scopes << map[string]VarSymbolInfo{}
if class_name != none {
params << Param{'this', class_name+'*'}
}
if p.peek().type != .rparen { if p.peek().type != .rparen {
for { 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 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 { p_type := match type_tok.type {
.type {type_tok.text} .type {type_tok.text}
.identifier { .identifier {
p.expect_ident_is_class(type_tok.text) p.expect_ident_is_class(type_tok.text)
type_tok.text type_tok.text
} }
.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}")} else{parse_error("Expected argument type after name when declaring ${p_name}")}
} }
p.next()
params << Param{p_name, p_type} params << Param{p_name, p_type}
p.symbols.define_var(p_name, p_type, false) p.symbols.define_var(p_name, p_type, false)
@@ -695,8 +848,6 @@ fn (mut p Parser) parse_func_decl() FuncDecl {
p.expect(.rparen) p.expect(.rparen)
p.dump_token()
type_tok := p.next() type_tok := p.next()
ret_type := match type_tok.type { ret_type := match type_tok.type {
.type {type_tok.text} .type {type_tok.text}
@@ -704,10 +855,19 @@ fn (mut p Parser) parse_func_decl() FuncDecl {
p.expect_ident_is_class(type_tok.text) p.expect_ident_is_class(type_tok.text)
type_tok.text type_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}")} else{parse_error("Expected function return type after name when declaring ${name_tok.text}")}
} }
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{}) p.symbols.define_func(name_tok.text, type_tok.text, Block{})
}
block := p.parse_block(true) 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.variable_scopes.delete_last()
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) p.symbols.define_func(name_tok.text, type_tok.text, block)
}
return FuncDecl { return FuncDecl {
name: name_tok.text name: name_tok.text
ret_type: ret_type ret_type: ret_type
block: block block: block
params: params 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 { fn (mut p Parser) parse_if() IfStmt {
p.expect(.kw_if) p.expect(.kw_if)
cond := p.parse_expr(.base) cond := p.parse_expr(.base)