Compare commits

...

17 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
uan
90ab17da24 suffix unary operators (++, --) working as intended 2026-02-07 14:39:15 +01:00
uan
99d63ff769 strings handled as built-in struct, and added immutable keyword 2026-02-07 11:06:55 +01:00
uan
c0bbb82e71 readme version 2 2026-02-06 19:29:18 +01:00
uan
f5ba66c0ff readme first version 2026-02-06 18:45:34 +01:00
uan
a09a642bb7 control flow 2026-02-06 17:03:35 +01:00
uan
db1b4b88c0 - 2026-02-06 16:02:40 +01:00
uan
dee3a5dfed better prints 2026-02-06 11:38:40 +01:00
uan
5602765b67 multiple expr per print 2026-02-06 11:15:44 +01:00
8 changed files with 857 additions and 153 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~

303
README.md Normal file
View File

@@ -0,0 +1,303 @@
# One
One is a statically typed language inspired by C, V, and go.
The compiler is still in development, and may present issues when compiling One code.
Note that you need to have eitehr gcc or clang installed to compile One code, as the compiler
produces C code that later gets compiled to an executable.
---
## Syntax
One's syntax rules are meant to be easily understandable and, more importantly, never ambiguous.
---
### Variables
In One, variables are declared with the `let` keyword.
The syntax will look something like this
```
let x: int = 6;
```
You can also declare a constant using the `const` keyword instead.
```
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;
if 5 > 0 {
x = 2;
}
```
is valid code, while
```
if 3 < 10 {
let y: int = 288;
}
y = 10;
```
isn't valid, as the variable `y` cannot be access outside its scope.
You can also create scopes by wrapping statements in braces. This lets you declare
variables with the same name in different scopes, for example:
```
let x: int = 3
{
let y: real = 5.5;
print(y);
}
#y is now undefined again here
{
let y: real = 0.2;
print(y);
}
```
---
### Primitive Types
As of now, One has a total of 4 primitive types: `void`. `int`, `real`, `bool` and `string`.
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);
```
Right now, strings are implemented as direct counterparts to `char*` in C, which is what they
get translated to during compilation. Comparisons and operations on strings are still work in progress
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
```
---
### 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 {
return myarg < 10;
}
```
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);
```
---
### Classes
Right now, Classes in One are very similar to C structs. They are declared using the `class` keyword
followed by the class name and a block with a list of class members.
Defining a class User might look something like this:
```
class User {
age: int
name: string
mail_verified: bool
}
```
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}
```
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;
}
```
---
### 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.
The 'print' built-in function accepts a variable number of arguments, and will print allf
of them separated by a space.
```
let x: int = 144;
print(x, 240);
```
produces this output:
```
144 240
```
class-type variables will be printed with special formatting which, if we takes
the User class and variable we defined earlier, will look like this.
```
User {
age: 17
name: uan
mail_verified: false
}
```
### Control Flow
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;
if x >= 100 {
print(x, "is greater than 100");
} elif x >= 10 {
print(x, "is greater than 10"):
} else {
print(x, "is less than 10");
}
```
`while` loops are very similar, and can be written like this:
```
let i: int = 0;
while i < 10 {
print(i);
i++;
}
```
### The main function
It is important that every One program has an entry point called `main`.
The 'main' function must always return `int` and does not accept any arguments.
```
fn main() int {
#my code here
}
```
It isn't necessary to explicitly insert a `return` statement in the main function.
Returning from the main function will exit the program with an exit code equal
to the returned integer.

View File

@@ -1,3 +1,4 @@
module main module main
import strings import strings
@@ -13,17 +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 {
if type.contains('*') {
return 'p'
}
return match type { return match type {
'int', 'bool' {'d'} 'int', 'bool' {'d'}
'real' {'f'} 'real' {'f'}
@@ -43,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' {'char*'} '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
} }
@@ -70,17 +82,29 @@ 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 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.writeln('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')
}
g.out.writeln(');')
} }
} }
g.out.writeln('for(int i=0; i<indent; i++) printf(" ");') g.out.writeln('for(int i=0; i<indent; i++) printf(" ");')
g.out.writeln('printf("}\\n");') g.out.writeln('printf("}\");')
g.out.writeln('}') g.out.writeln('}')
} }
@@ -112,9 +136,8 @@ fn (mut g Generator) gen_stmt(stmt Stmt) {
g.out.writeln('}') g.out.writeln('}')
} }
FuncDecl { FuncDecl {
dump(stmt.ret_type)
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]{
@@ -138,11 +161,43 @@ fn (mut g Generator) gen_stmt(stmt Stmt) {
g.gen_class_print_func(stmt) g.gen_class_print_func(stmt)
} }
IfStmt {
g.out.write_string('if (')
g.gen_expr(stmt.condition)
g.out.write_string(') ')
g.gen_stmt(stmt.block)
}
ElseStmt {
g.out.write_string('else ')
g.gen_stmt(stmt.block)
}
ElifStmt {
g.out.write_string('else if (')
g.gen_expr(stmt.condition)
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) { 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())
} }
@@ -153,13 +208,21 @@ fn (mut g Generator) gen_expr(expr Expr) {
g.out.write_string(expr.val.str()) g.out.write_string(expr.val.str())
} }
StringLiteral { StringLiteral {
g.out.write_string('\"${expr.val}\"') g.out.write_string('(OneString){\"${expr.val}\", ${expr.val.len}}')
} }
Variable { Variable {
g.out.write_string(mangle_var(expr.name)) g.out.write_string(mangle_var(expr.name))
} }
UnaryExpr { 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 { BinaryExpr {
g.out.write_string('(') g.out.write_string('(')
@@ -169,25 +232,32 @@ fn (mut g Generator) gen_expr(expr Expr) {
g.out.write_string(')') g.out.write_string(')')
} }
PrintExpr { PrintExpr {
label := g.get_print_label(expr.expr) mut i := 0;
if g.symbols.lookup_class(expr.type) != none { for i < expr.exprs.len {
class_name := mangle_struct(expr.type) inner_expr := expr.exprs[i];
if label != "" { expr_type := expr.types[i];
g.out.write_string('printf("${label}: ");') if expr_type != 'string' && g.symbols.lookup_class(expr_type) != none {
} class_name := mangle_struct(expr_type)
g.out.write_string('print_${class_name}(') g.out.write_string('print_${class_name}(')
g.gen_expr(expr.expr) g.gen_expr(inner_expr)
g.out.write_string(', 0)') g.out.write_string(', 0);')
} else { } else {
g.out.write_string('printf(\"') g.out.write_string('printf(\"')
if label != "" { format := get_type_format(expr_type)
g.out.write_string('${label}: ') g.out.write_string('%${format}')
g.out.write_string('\", ')
g.gen_expr(inner_expr)
if expr_type == 'string' {
g.out.write_string('.string')
} }
format := get_type_format(expr.type) g.out.write_string(');')
g.out.write_string('%${format}\\n\", ')
g.gen_expr(expr.expr)
g.out.write_string(')')
} }
i++;
if i < expr.exprs.len {
g.out.write_string('printf(\" \");')
}
}
g.out.write_string('printf(\"\\n\")')
} }
TypeCast { TypeCast {
c_type := g.mangle_if_class(g.get_c_type(expr.type)) c_type := g.mangle_if_class(g.get_c_type(expr.type))
@@ -201,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]{
@@ -226,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")}
} }
@@ -236,7 +326,7 @@ fn (mut g Generator) gen_c(program []Stmt) string {
g.out.writeln('#include <stdio.h>') g.out.writeln('#include <stdio.h>')
g.out.writeln('#include <stdbool.h>') g.out.writeln('#include <stdbool.h>')
g.out.writeln('#include <stdint.h>') g.out.writeln('#include <stdint.h>')
//g.out.writeln('typedef struct __one_string_builtin__ {\nchar* string;\nint len;\n} string;') g.out.writeln('typedef struct {\nchar* string;\nint len;\n} OneString;')
for stmt in program { for stmt in program {
g.gen_stmt(stmt) g.gen_stmt(stmt)
} }

46
lexer.v
View File

@@ -5,15 +5,19 @@ import term
enum TokenType as u8 { enum TokenType as u8 {
kw_let kw_let
kw_const kw_const
kw_immutable
type type
kw_if kw_if
kw_else kw_else
kw_for kw_elif
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
@@ -23,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
@@ -34,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
@@ -71,6 +83,7 @@ fn toktype_from_delimiter(delimiter string) TokenType {
'-' {.minus} '-' {.minus}
'*' {.star} '*' {.star}
'/' {.slash} '/' {.slash}
'%' {.percent}
'=' {.equals} '=' {.equals}
'<' {.less} '<' {.less}
'>' {.greater} '>' {.greater}
@@ -86,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}
} }
} }
@@ -99,21 +119,25 @@ fn toktype_from_kw(kw string) TokenType {
'void', 'real', 'bool', 'int', 'string'{.type} 'void', 'real', 'bool', 'int', 'string'{.type}
'if' {.kw_if} 'if' {.kw_if}
'else' {.kw_else} 'else' {.kw_else}
'for' {.kw_for} 'elif' {.kw_elif}
'while' {.kw_while}
'break' {.kw_break} 'break' {.kw_break}
'fn' {.kw_fn} 'fn' {.kw_fn}
'return' {.kw_return} 'return' {.kw_return}
'true', 'false' {.boolean} 'true', 'false' {.boolean}
'print' {.kw_print} 'print' {.kw_print}
'class' {.kw_class} 'class' {.kw_class}
'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())
} }
@@ -125,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", "for", "break", "fn", "return", "let", "const", "true", "false", "print", "class" "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)
} }
@@ -146,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() == '\"' {
@@ -170,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++
} }

2
main.v
View File

@@ -32,6 +32,6 @@ fn main() {
out_c := generator.gen_c(statements) out_c := generator.gen_c(statements)
compile(out_c, 'exec', true, 'clang') compile(out_c, 'exec', true, 'gcc')
} }

BIN
one

Binary file not shown.

419
parser.v
View File

@@ -5,24 +5,26 @@ import term
// ------------------------------------------- Precedence // ------------------------------------------- Precedence
enum Precedence { enum Precedence {
lowest base
assignment // = , +=, -= assignment // = , +=, -=
comparison // ==, !=, <, > comparison // ==, !=, <, >
sum // +, - sum // +, -
product // *, / product // *, /
prefix // -x, !x prefix // -x, !x
suffix // ++, --
call // function() call // function()
access // . access // .
} }
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 { .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}
else { .lowest } .increment, .decrement {.suffix}
else {.base}
} }
} }
@@ -32,6 +34,7 @@ type SymbolInfo = VarSymbolInfo | FuncSymbolInfo | ClassSymbolInfo
struct VarSymbolInfo { struct VarSymbolInfo {
type string type string
is_immutable bool
} }
struct FuncSymbolInfo { struct FuncSymbolInfo {
@@ -42,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 {
@@ -51,7 +56,7 @@ mut:
structs map[string]ClassSymbolInfo 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 { $if debug {
dump(s.variable_scopes.len) dump(s.variable_scopes.len)
} }
@@ -66,13 +71,11 @@ fn (mut s SymbolTable) define_var(name string, typ string) {
s.variable_scopes[s.variable_scopes.len-1][name] = VarSymbolInfo{ s.variable_scopes[s.variable_scopes.len-1][name] = VarSymbolInfo{
type: typ type: typ
is_immutable: is_const
} }
} }
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 {
@@ -96,7 +99,10 @@ fn (mut s SymbolTable) lookup_func(name string) ?FuncSymbolInfo {
fn (mut s SymbolTable) define_class(name string, members []ClassMember) { fn (mut s SymbolTable) define_class(name string, members []ClassMember) {
mut members_info := map[string]VarSymbolInfo{} mut members_info := map[string]VarSymbolInfo{}
for member in members { for member in members {
members_info[member.name] = VarSymbolInfo{member.type} members_info[member.name] = VarSymbolInfo{
type: member.type,
is_immutable: member.is_immutable
}
} }
s.structs[name] = ClassSymbolInfo{name: name, members_info: members_info} s.structs[name] = ClassSymbolInfo{name: name, members_info: members_info}
} }
@@ -108,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
} }
@@ -115,13 +134,24 @@ 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 {
ident string expr Expr
op string op string
op_on_left bool
} }
struct BinaryExpr { struct BinaryExpr {
@@ -157,11 +187,14 @@ struct TypeExpr {
struct ClassMember { struct ClassMember {
name string name string
type string type string
is_immutable bool
} }
struct MemberAccess { struct MemberAccess {
from Expr from Expr
from_type string
member string member string
member_type string
} }
struct ClassInstantiation { struct ClassInstantiation {
@@ -183,8 +216,8 @@ struct ParenExpr {
} }
struct PrintExpr { struct PrintExpr {
expr Expr exprs []Expr
type string types []string
} }
struct FnCall { struct FnCall {
@@ -192,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 | FuncDecl | Param | ClassDecl type Stmt = VarDecl | ExprStmt | ReturnStmt | Block | IfStmt | ElseStmt | ElifStmt | FuncDecl | Param |
ClassDecl | WhileLoop
struct VarDecl { struct VarDecl {
name string name string
@@ -208,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 {
@@ -227,6 +270,25 @@ struct Block {
stmts []Stmt stmts []Stmt
} }
struct IfStmt {
condition Expr
block Block
}
struct ElseStmt {
block Block
}
struct ElifStmt {
condition Expr
block Block
}
struct WhileLoop {
guard Expr
block Block
}
struct Param { struct Param {
name string name string
type string type string
@@ -261,7 +323,6 @@ fn (mut p Parser) expect(type TokenType) {
p.next() p.next()
} }
fn (mut p Parser) expect_ident_is_class(ident string) { 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")} if !p.is_ident_class(ident) {panic("Expected type or class type but got identifier")}
} }
@@ -305,6 +366,9 @@ fn (mut p Parser) parse_primary() Expr {
.type {p.parse_type(token.text)} .type {p.parse_type(token.text)}
.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)}
.kw_ref {p.parse_ref()}
.kw_deref {p.parse_deref()}
else {parse_error("Unexpected Token")} else {parse_error("Unexpected Token")}
} }
} }
@@ -312,15 +376,13 @@ fn (mut p Parser) parse_primary() Expr {
fn (mut p Parser) parse_expr(prec Precedence) Expr { fn (mut p Parser) parse_expr(prec Precedence) Expr {
mut expr := p.parse_primary() mut expr := p.parse_primary()
for int(prec) < int(p.get_precedence(p.peek().type)) { for int(prec) < int(p.get_precedence(p.peek().type)) {
op_tok := p.next() op_tok := p.next()
if op_tok.type == .dot { expr = match op_tok.type {
expr = p.parse_member_access(expr) .dot {p.parse_member_access(expr)}
} else { .increment, .decrement {p.parse_unary_right(expr, op_tok.text)}
expr = p.parse_binary(expr, op_tok.text, p.get_precedence(op_tok.type)) else {p.parse_binary(expr, op_tok.text, p.get_precedence(op_tok.type))}
} }
} }
@@ -335,39 +397,101 @@ fn (mut p Parser) parse_ident(ident string) Expr {
} }
} }
if p.inside_struct {
return p.parse_class_member(ident)
}
return match p.peek().type { return match p.peek().type {
.increment, .decrement {UnaryExpr {ident: ident, op: p.next().text}}
.lparen {p.parse_call(ident)} .lparen {p.parse_call(ident)}
else {Variable{ident}} else {Variable{ident}}
} }
} }
fn (mut p Parser) parse_member_access(from Expr) MemberAccess { fn (mut p Parser) parse_ref() RefExpr {
member_tok := p.next() p.expect(.lparen)
if member_tok.type != .identifier {parse_error("Expected identifier after member access")} expr := p.parse_expr(.prefix)
return MemberAccess {from: from, member: member_tok.text} 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_class_member(name string) ClassMember { 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")}
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,
from_type: p.get_expr_type(from)
member: member_tok.text,
member_type: member_type
}
}
fn (mut p Parser) parse_class_member() ClassMember {
mut is_immut := false
if p.peek().type == .kw_immutable {
is_immut = true
p.next()
}
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
} }
else{parse_error("Expected type after class member ${name} in declaration, got ${p.peek().type}")} .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) p.expect(.semicolon)
return ClassMember{name: name, type: type_name} return ClassMember{name: member_name, type: type_name is_immutable: is_immut}
} }
fn (mut p Parser) parse_class_inst(name string) ClassInstantiation { fn (mut p Parser) parse_class_inst(name string) ClassInstantiation {
@@ -376,7 +500,7 @@ fn (mut p Parser) parse_class_inst(name string) ClassInstantiation {
if p.peek().type != .rbracket { if p.peek().type != .rbracket {
for { for {
member_values << p.parse_expr(.lowest) member_values << p.parse_expr(.base)
if p.peek().type == .comma { if p.peek().type == .comma {
p.next() p.next()
} else { } else {
@@ -394,7 +518,7 @@ fn (mut p Parser) parse_call(name string) FnCall {
if p.peek().type != .rparen { if p.peek().type != .rparen {
for { for {
args << p.parse_expr(.lowest) args << p.parse_expr(.base)
if p.peek().type == .comma { if p.peek().type == .comma {
p.next() p.next()
} else { } else {
@@ -408,17 +532,50 @@ fn (mut p Parser) parse_call(name string) FnCall {
fn (mut p Parser) parse_print() PrintExpr { fn (mut p Parser) parse_print() PrintExpr {
p.expect(.lparen) p.expect(.lparen)
expr := p.parse_expr(.lowest) mut exprs := []Expr{}
mut types := []string{}
for p.peek().type != .rparen {
expr := p.parse_expr(.base)
exprs << expr
types << p.get_expr_type(expr)
if p.peek().type == .comma {
p.next()
} else {
break
}
}
p.expect(.rparen) p.expect(.rparen)
return PrintExpr{expr: expr, type: p.get_expr_type(expr)} 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 { fn (mut p Parser) parse_binary(left Expr, op string, prec Precedence) BinaryExpr {
//p.next()
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) right := p.parse_expr(prec)
binary_expr := BinaryExpr{left, op, right} binary_expr := BinaryExpr{left, op, right}
if !p.is_op_valid_for_type(p.get_expr_type(left), op) { if !p.is_op_valid_for_type(p.get_expr_type(left), op) {
parse_error("Illegal operation ${op} for type ${p.get_expr_type(left)}") parse_error("Illegal operation ${op} for type ${p.get_expr_type(left)}")
} }
@@ -430,7 +587,7 @@ fn (mut p Parser) parse_type(type string) Expr {
if p.peek().type == .lparen { if p.peek().type == .lparen {
p.next() p.next()
expr := p.parse_expr(.lowest) expr := p.parse_expr(.base)
p.expect(.rparen) p.expect(.rparen)
return TypeCast { return TypeCast {
@@ -443,7 +600,7 @@ fn (mut p Parser) parse_type(type string) Expr {
} }
fn (mut p Parser) parse_paren() ParenExpr { fn (mut p Parser) parse_paren() ParenExpr {
expr := p.parse_expr(.lowest) expr := p.parse_expr(.base)
p.expect(.rparen) p.expect(.rparen)
return ParenExpr{expr: expr} return ParenExpr{expr: expr}
} }
@@ -451,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}')
} }
@@ -460,8 +618,32 @@ fn (mut p Parser) is_ident_class(ident string) bool {
return p.symbols.lookup_class(ident) != none 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 {
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 { 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'}
@@ -469,10 +651,11 @@ fn (mut p Parser) get_expr_type(expr Expr) string {
StringLiteral {'string'} StringLiteral {'string'}
VoidExpr {'void'} VoidExpr {'void'}
TypeExpr {expr.name} TypeExpr {expr.name}
UnaryExpr {p.get_expr_type(expr.expr)}
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
@@ -489,14 +672,14 @@ 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
} }
MemberAccess { MethodCall {
mut type_from := p.get_expr_type(expr.from) methodinfo := p.symbols.lookup_method(expr.method, p.get_expr_type(expr.from)) or {
classinfo := p.symbols.lookup_class(type_from)or{parse_error("Non-existant class ${type_from}")} parse_error("Tried to call undefined method ${expr.method}")
if expr.member in classinfo.members_info {
classinfo.members_info[expr.member].type
} else {
parse_error("Undefined class member ${expr.member}")
} }
methodinfo.type
}
MemberAccess {
expr.member_type
} }
ClassInstantiation {expr.name} ClassInstantiation {expr.name}
else {"Tried getting type of unexpected Expr"} else {"Tried getting type of unexpected Expr"}
@@ -506,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
@@ -537,6 +728,10 @@ fn (mut p Parser) parse_statement() Stmt {
.kw_fn {return p.parse_func_decl()} .kw_fn {return p.parse_func_decl()}
.lbracket {return p.parse_block(false)} .lbracket {return p.parse_block(false)}
.kw_class {return p.parse_class()} .kw_class {return p.parse_class()}
.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()} else {return p.parse_expr_stmt()}
} }
} }
@@ -548,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 {
@@ -556,21 +752,25 @@ 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}")}
} }
p.expect(.equals) p.expect(.equals)
val := p.parse_expr(.lowest) val := p.parse_expr(.base)
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) p.symbols.define_var(name_tok.text, type_name, is_const)
return VarDecl { return VarDecl {
name: name_tok.text name: name_tok.text
@@ -586,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")
} }
@@ -597,24 +807,36 @@ 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) p.symbols.define_var(p_name, p_type, false)
if p.peek().type == .comma { if p.peek().type == .comma {
p.next() p.next()
@@ -626,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}
@@ -635,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)
@@ -653,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
} }
} }
@@ -669,8 +903,8 @@ fn (mut p Parser) parse_class() ClassDecl {
p.expect(.identifier) p.expect(.identifier)
p.expect(.lbracket) p.expect(.lbracket)
mut members := []ClassMember{} mut members := []ClassMember{}
for p.peek().type == .identifier { for p.peek().type != .rbracket {
members << p.parse_class_member(p.peek().text) members << p.parse_class_member()
} }
p.expect(.rbracket) p.expect(.rbracket)
p.symbols.define_class(name, members) p.symbols.define_class(name, members)
@@ -680,17 +914,57 @@ fn (mut p Parser) parse_class() ClassDecl {
fn (mut p Parser) parse_return_stmt() ReturnStmt { fn (mut p Parser) parse_return_stmt() ReturnStmt {
p.expect(.kw_return) p.expect(.kw_return)
expr := p.parse_expr(.lowest) expr := p.parse_expr(.base)
p.next() p.expect(.semicolon)
return ReturnStmt { return ReturnStmt {
expr: expr expr: expr
} }
} }
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)
if p.get_expr_type(cond) != 'bool' {
parse_error('If condition must be of type bool')
}
block := p.parse_block(false)
return IfStmt {cond, block}
}
fn (mut p Parser) parse_else() ElseStmt {
p.expect(.kw_else)
block := p.parse_block(false)
return ElseStmt {block}
}
fn (mut p Parser) parse_elif() ElifStmt {
p.expect(.kw_elif)
cond := p.parse_expr(.base)
if p.get_expr_type(cond) != 'bool' {
parse_error('If condition must be of type bool')
}
block := p.parse_block(false)
return ElifStmt {cond, block}
}
fn (mut p Parser) parse_expr_stmt() ExprStmt { fn (mut p Parser) parse_expr_stmt() ExprStmt {
expr := p.parse_expr(.lowest) expr := p.parse_expr(.base)
p.expect(.semicolon) p.expect(.semicolon)
return ExprStmt { return ExprStmt {
@@ -718,7 +992,6 @@ fn (mut p Parser) parse_block(no_scope bool) Block {
p.expect(.rbracket) p.expect(.rbracket)
return_stmts := (statements.filter(it is ReturnStmt).map(it as ReturnStmt)) return_stmts := (statements.filter(it is ReturnStmt).map(it as ReturnStmt))
if return_stmts.len > 0 && (return_stmts.len > 1 || Stmt(return_stmts[0]) != statements[statements.len - 1]) { if return_stmts.len > 0 && (return_stmts.len > 1 || Stmt(return_stmts[0]) != statements[statements.len - 1]) {
parse_error("Unexpected use of return. Unreachable code") parse_error("Unexpected use of return. Unreachable code")
@@ -739,6 +1012,10 @@ fn (mut p Parser) parse_block(no_scope bool) Block {
} }
fn (mut p Parser) parse_program() ([]Stmt, SymbolTable) { fn (mut p Parser) parse_program() ([]Stmt, SymbolTable) {
p.symbols.define_class('string',[
ClassMember{'len', 'int', true}
])
p.symbols.variable_scopes << map[string]VarSymbolInfo{} p.symbols.variable_scopes << map[string]VarSymbolInfo{}
for p.peek().type != .eof { for p.peek().type != .eof {
p.statements << p.parse_statement() p.statements << p.parse_statement()

4
v.mod
View File

@@ -1,6 +1,6 @@
Module { Module {
name: 'onev' name: 'one'
description: 'onelang compuler in v' description: 'onelang compiler in v'
version: '0.0.0' version: '0.0.0'
license: 'MIT' license: 'MIT'
dependencies: [] dependencies: []