Compare commits
17 Commits
a1c99b8b9e
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| ccd59f6a0a | |||
| 08686c2681 | |||
| 294fbbd69c | |||
| 7b5eef3bcb | |||
| a3b978a09d | |||
| 005828cec2 | |||
| 9d26256023 | |||
| 58f0dfa969 | |||
| 7fddf9ab30 | |||
| 90ab17da24 | |||
| 99d63ff769 | |||
| c0bbb82e71 | |||
| f5ba66c0ff | |||
| a09a642bb7 | |||
| db1b4b88c0 | |||
| dee3a5dfed | |||
| 5602765b67 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -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
303
README.md
Normal 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.
|
||||||
|
|
||||||
182
generator.v
182
generator.v
@@ -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'}
|
||||||
@@ -33,27 +41,31 @@ fn get_type_format(type string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g Generator) get_print_label(expr Expr) string {
|
fn (mut g Generator) get_print_label(expr Expr) string {
|
||||||
return match expr {
|
return match expr {
|
||||||
Variable { expr.name }
|
Variable { expr.name }
|
||||||
MemberAccess {
|
MemberAccess {
|
||||||
"${g.get_print_label(expr.from)}.${expr.member}"
|
"${g.get_print_label(expr.from)}.${expr.member}"
|
||||||
}
|
|
||||||
else { "" }
|
|
||||||
}
|
}
|
||||||
|
else { "" }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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,16 +136,15 @@ 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]{
|
||||||
g.out.write_string(', ')
|
g.out.write_string(', ')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
g.out.write_string(')')
|
g.out.write_string(') ')
|
||||||
g.gen_stmt(stmt.block)
|
g.gen_stmt(stmt.block)
|
||||||
}
|
}
|
||||||
Param {
|
Param {
|
||||||
@@ -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('\", ')
|
||||||
format := get_type_format(expr.type)
|
g.gen_expr(inner_expr)
|
||||||
g.out.write_string('%${format}\\n\", ')
|
if expr_type == 'string' {
|
||||||
g.gen_expr(expr.expr)
|
g.out.write_string('.string')
|
||||||
g.out.write_string(')')
|
}
|
||||||
}
|
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
46
lexer.v
@@ -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
2
main.v
@@ -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')
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
471
parser.v
471
parser.v
@@ -5,25 +5,27 @@ 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}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------- Symbol Table
|
// ------------------------------------------- Symbol Table
|
||||||
@@ -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")}
|
||||||
}
|
}
|
||||||
@@ -296,15 +357,18 @@ fn (mut p Parser) parse_primary() Expr {
|
|||||||
p.dump_token()
|
p.dump_token()
|
||||||
|
|
||||||
return match token.type {
|
return match token.type {
|
||||||
.integer {IntegerLiteral{token.text.int()}}
|
.integer {IntegerLiteral{token.text.int()}}
|
||||||
.real {RealLiteral{token.text.f32()}}
|
.real {RealLiteral{token.text.f32()}}
|
||||||
.boolean {BoolLiteral{token.text == 'true'}}
|
.boolean {BoolLiteral{token.text == 'true'}}
|
||||||
.string {StringLiteral{token.text}}
|
.string {StringLiteral{token.text}}
|
||||||
.kw_fn {Function{token.text}}
|
.kw_fn {Function{token.text}}
|
||||||
.identifier {p.parse_ident(token.text)}
|
.identifier {p.parse_ident(token.text)}
|
||||||
.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
|
||||||
@@ -485,18 +668,18 @@ fn (mut p Parser) get_expr_type(expr Expr) string {
|
|||||||
return info.type
|
return info.type
|
||||||
}
|
}
|
||||||
TypeCast {expr.type}
|
TypeCast {expr.type}
|
||||||
FnCall {
|
FnCall {
|
||||||
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,29 +743,34 @@ 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 {
|
||||||
.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 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
|
||||||
}
|
}
|
||||||
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}
|
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,19 +848,26 @@ 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}
|
||||||
.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 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)
|
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()
|
||||||
|
|
||||||
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 {
|
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()
|
||||||
|
|||||||
Reference in New Issue
Block a user