Compare commits
21 Commits
debcf6ad6c
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| dd1b6af259 | |||
| 96f8aa3bbb | |||
| 660b9245fb | |||
| 4b61adea5d | |||
| 081ff9a27a | |||
| 834b5b1213 | |||
| 1f4645ed24 | |||
| 158b76fe39 | |||
|
06d8c1b0f3
|
|||
|
f338f07e7d
|
|||
|
cb297bf75e
|
|||
|
d5f02cf1d5
|
|||
|
b8728b8f8d
|
|||
|
d2b0e57ce6
|
|||
|
323d3342a3
|
|||
|
9d243e33b6
|
|||
|
77738f7e20
|
|||
|
bf7f44e776
|
|||
|
857471c6a9
|
|||
|
6c01265f0b
|
|||
|
8bb0e28d80
|
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1 +1,6 @@
|
||||
/target
|
||||
/*.c
|
||||
/*.s
|
||||
/*.o
|
||||
/*.ssa
|
||||
/test
|
||||
|
||||
4
Cargo.lock
generated
4
Cargo.lock
generated
@@ -188,9 +188,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.90"
|
||||
version = "2.0.96"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
|
||||
checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
[package]
|
||||
name = "mclangc"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
edition = "2024"
|
||||
default-run = "mclangc"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.94"
|
||||
camino = "1.1.9"
|
||||
clap = { version = "4.5.23", features = ["derive"] }
|
||||
# inkwell = { version = "0.5.0", default-features = false, features = ["target-x86"] }
|
||||
lazy_static = "1.5.0"
|
||||
#llvm-lib = "0.8.1"
|
||||
parse_int = "0.6.0"
|
||||
|
||||
174
docs/main.md
Normal file
174
docs/main.md
Normal file
@@ -0,0 +1,174 @@
|
||||
|
||||
## Quick warning
|
||||
|
||||
This language is still in its pre alpha stages, while it tends to work it still has numerous bugs, breaks, and fails with a smell of unusual input, it is not suitable for anything beyond experimenting and having fun. That said this project has been my biggest one yet, and is one of my favorites, having went under multiple rewrites over the 4 or so years its changed a lot from simple forth style syntax, to now, a c/rust style, which has been a struggle, but now its at a point where it actually produces code and you can *kinda* make something with it.
|
||||
|
||||
Hope you have fun figuring out how to use this damn thing, and maybe even build something with it.
|
||||
|
||||
Also, the docs are also written with ChatGPT (gross, i know), but i needed some docs fast.
|
||||
|
||||
\- MCorange
|
||||
|
||||
## Summary
|
||||
|
||||
This language is a small, low level, C and Rust inspired systems language with a strong focus on explicit control, simple syntax, and predictable behavior. It mixes familiar C style statements and loops with Rust-like ideas such as structs, methods, references, and explicit types. The goal is clarity over magic: most things are spelled out, lifetimes are implied by references, and there is little hidden behavior.
|
||||
|
||||
The language is suitable for experiments, embedded style programs, or as a foundation for a custom runtime or OS environment. It favors straightforward semantics, manual control flow, and a minimal standard library model.
|
||||
|
||||
## High level description
|
||||
|
||||
As a procedural language, barely higher level than C, programs are built from functions, structs, and constants. Types are explicit, references are written by prepending `&`, and arrays or slices are expressed directly in the type system. For now code is organized with `include`, for simpler development, this will change into modules eventually.
|
||||
|
||||
Control flow is traditional and imperative. There are `if`, `else`, `for`, `while`, and `loop` constructs, along with `break` and `continue`. Functions return values explicitly using `return`, and side effects are visible and direct.
|
||||
|
||||
Methods are just functions namespaced under a type, and there is no hidden constructor logic. Everything that allocates or returns a reference does so explicitly.
|
||||
|
||||
## Small language reference
|
||||
|
||||
## Types
|
||||
|
||||
Type aliases:
|
||||
|
||||
```no-test
|
||||
type cstr = [u8];
|
||||
```
|
||||
|
||||
Creates an alias. Here, `str` is defined as a byte array type.
|
||||
|
||||
Built-in simple types:
|
||||
|
||||
* usize/isize
|
||||
* i8-i64
|
||||
* u8-u64
|
||||
* bool
|
||||
* array or slice types like [u8]
|
||||
|
||||
References are written with `&T`.
|
||||
|
||||
## Includes
|
||||
|
||||
```no-test
|
||||
include "std.mcl";
|
||||
```
|
||||
|
||||
Textual inclusion of another source file. This is resolved at compile time.
|
||||
|
||||
## Structs
|
||||
|
||||
```no-test
|
||||
struct Foo {
|
||||
a: usize,
|
||||
b: &str
|
||||
}
|
||||
```
|
||||
|
||||
Structs group named fields. Fields have explicit types. There are no implicit constructors.
|
||||
|
||||
## Functions
|
||||
|
||||
Function definition syntax:
|
||||
|
||||
```no-test
|
||||
fn name(arg: Type, ...) -> ReturnType {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```no-test
|
||||
fn mul(n: usize, n2: usize) -> usize {
|
||||
return n * n2;
|
||||
}
|
||||
```
|
||||
|
||||
Functions may return values using `return`. Void functions omit the return type.
|
||||
|
||||
## Methods
|
||||
|
||||
Methods are functions scoped to a type:
|
||||
|
||||
As of now methods are only able to be linked to [Structs](#Structs)
|
||||
|
||||
```no-test
|
||||
fn Foo.new(a1: usize, b: &str) -> &Foo {
|
||||
return &Foo {
|
||||
a: a1,
|
||||
b: b
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
This defines a constructor-like function for `Foo`. It explicitly returns a reference to a `Foo` value.
|
||||
|
||||
## Variables
|
||||
|
||||
Variables are declared with `let`:
|
||||
|
||||
```no-test
|
||||
let obj = Foo::new(1, "owo");
|
||||
```
|
||||
|
||||
Type inference appears to exist, but types can likely be written explicitly if desired.
|
||||
|
||||
## Field access
|
||||
|
||||
Struct fields can be accessed through references:
|
||||
|
||||
```no-test
|
||||
obj->b;
|
||||
```
|
||||
|
||||
As well as without dereferencing:
|
||||
|
||||
```no-test
|
||||
obj.c
|
||||
```
|
||||
|
||||
## Control flow
|
||||
|
||||
Infinite loop:
|
||||
|
||||
```no-test
|
||||
loop {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
While loop:
|
||||
|
||||
```no-test
|
||||
while (true) {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
For loop:
|
||||
|
||||
```no-test
|
||||
for (let i = 0; i < 10; i += 1) {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Conditionals:
|
||||
|
||||
```no-test
|
||||
if (i > 7) {
|
||||
break;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
```
|
||||
|
||||
## Constants
|
||||
|
||||
```no-test
|
||||
const FOO: usize = main;
|
||||
```
|
||||
|
||||
## Statics
|
||||
|
||||
```no-test
|
||||
static FOO: usize = main;
|
||||
```
|
||||
55
editor_support/mclang4.vim
Normal file
55
editor_support/mclang4.vim
Normal file
@@ -0,0 +1,55 @@
|
||||
" Vim syntax file
|
||||
" Language: MClang4
|
||||
" Maintainer: MCorange
|
||||
" Maintainer: xomf
|
||||
|
||||
if exists("b:current_syntax")
|
||||
finish
|
||||
endif
|
||||
syn match MCLangSlash '/' containedin=ALLBUT,MCLangComment
|
||||
syn match MCLangComment '//.*'
|
||||
|
||||
syn region MCLangComment start='/\*' end='\*/' contains=@Spell
|
||||
|
||||
syn keyword MCLangKeywords fn if else struct enum type while for break continue let const mut static include extern return loop as
|
||||
" syn keyword MCLangOp + -
|
||||
syn keyword MCLangTypes void u8 u16 u32 u64 i8 i16 i32 i64 usize isize str
|
||||
syn keyword MCLangBool true false
|
||||
syn keyword MCLangConst NULL
|
||||
" i8 i16 i32 i64 f32 f64
|
||||
syn region MCLangString start=+"+ skip=+\\.+ end=+"+ contains=@Spell
|
||||
syn region MCLangChar start=+'+ skip=+\\.+ end=+'+
|
||||
|
||||
syn match MCLangFuncCall '\<[A-Za-z_][A-Za-z0-9_]*\>\ze\s*(' containedin=ALLBUT,MCLangString,MCLangChar,MCLangComment
|
||||
syn match MCLangFuncCall '\<[A-Za-z_][A-Za-z0-9_]*::[A-Za-z_][A-Za-z0-9_]*\>\ze\s*(' containedin=ALLBUT,MCLangString,MCLangChar,MCLangComment
|
||||
"syn match MCLangFuncDef '\<fn\s\+\zs[A-Za-z_][A-Za-z0-9_]*\.\?\>' containedin=ALLBUT,MCLangString,MCLangChar,MCLangComment
|
||||
"syn match MCLangConstName '\<const\s\+\zs[A-Za-z_][A-Za-z0-9_]*\>' containedin=ALLBUT,MCLangString,MCLangChar,MCLangComment
|
||||
syn match MCLangIdentifier '\<[A-Za-z_][A-Za-z0-9_]*\>'
|
||||
syn match MCLangOp '[+\-*%&|^!~<>]=\?\|==\|!=\|<=\|>=\|&&\|\\|\|::\|:\|[;,\.]' containedin=ALLBUT,MCLangString,MCLangChar,MCLangComment
|
||||
syn match MCLangNumber '\<\(0b[01]\+\|0x[0-9A-Fa-f]\+\|\d\+\.\d*\|\.\d\+\|\d\+\)\>' containedin=ALLBUT,MCLangString,MCLangChar,MCLangComment
|
||||
|
||||
|
||||
|
||||
syn match MCLangDelim /[()]/
|
||||
syn match MCLangDelim /[{}]/
|
||||
syn match MCLangDelim /[\[\]]/
|
||||
|
||||
let b:current_syntax = "MCLang4"
|
||||
|
||||
hi def link MCLangComment Comment
|
||||
hi def link MCLangTypes Type
|
||||
hi def link MCLangString String
|
||||
hi def link MCLangChar Character
|
||||
hi def link MCLangNumber Constant
|
||||
hi def link MCLangConst Constant
|
||||
"hi def link MCLangConstName Constant
|
||||
hi def link MCLangBool Boolean
|
||||
hi def link MCLangDelim Delimiter
|
||||
hi def link MCLangOp Operator
|
||||
hi def link MCLangKeywords Keyword
|
||||
"hi def link MCLangIdentifier Identifier
|
||||
hi def link MCLangFuncCall Function
|
||||
"hi def link MCLangFuncDef Function
|
||||
|
||||
|
||||
|
||||
BIN
libbeaker.a
Normal file
BIN
libbeaker.a
Normal file
Binary file not shown.
BIN
libbeaker.so
Executable file
BIN
libbeaker.so
Executable file
Binary file not shown.
55
old_tests/parser/enumerations.exp
Normal file
55
old_tests/parser/enumerations.exp
Normal file
@@ -0,0 +1,55 @@
|
||||
Program {
|
||||
ast: Block(
|
||||
[
|
||||
Statement(
|
||||
LocBox {
|
||||
inner: Enum(
|
||||
Enum {
|
||||
name: Ident(
|
||||
"Foo",
|
||||
),
|
||||
fields: [],
|
||||
},
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/enumerations.mcl",
|
||||
line: 1,
|
||||
col: 5,
|
||||
},
|
||||
},
|
||||
),
|
||||
Statement(
|
||||
LocBox {
|
||||
inner: Enum(
|
||||
Enum {
|
||||
name: Ident(
|
||||
"Bar",
|
||||
),
|
||||
fields: [
|
||||
Ident(
|
||||
"A",
|
||||
),
|
||||
Ident(
|
||||
"B",
|
||||
),
|
||||
Ident(
|
||||
"C",
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/enumerations.mcl",
|
||||
line: 3,
|
||||
col: 5,
|
||||
},
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
structs: {},
|
||||
enums: {},
|
||||
types: {},
|
||||
functions: {},
|
||||
member_functions: {},
|
||||
}
|
||||
7
old_tests/parser/enumerations.mcl
Normal file
7
old_tests/parser/enumerations.mcl
Normal file
@@ -0,0 +1,7 @@
|
||||
enum Foo {}
|
||||
|
||||
enum Bar {
|
||||
A,
|
||||
B,
|
||||
C,
|
||||
}
|
||||
908
old_tests/parser/expressions.exp
Normal file
908
old_tests/parser/expressions.exp
Normal file
@@ -0,0 +1,908 @@
|
||||
Program {
|
||||
ast: Block(
|
||||
[
|
||||
Statement(
|
||||
LocBox {
|
||||
inner: Let {
|
||||
name: Ident(
|
||||
"a",
|
||||
),
|
||||
typ: None,
|
||||
val: Some(
|
||||
LocBox {
|
||||
inner: BinOp {
|
||||
typ: EqEq,
|
||||
left: LocBox {
|
||||
inner: BinOp {
|
||||
typ: Star,
|
||||
left: LocBox {
|
||||
inner: Literal(
|
||||
Number(
|
||||
Number {
|
||||
val: 1,
|
||||
base: 10,
|
||||
signed: false,
|
||||
},
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 1,
|
||||
col: 10,
|
||||
},
|
||||
},
|
||||
right: LocBox {
|
||||
inner: Literal(
|
||||
Number(
|
||||
Number {
|
||||
val: 3,
|
||||
base: 10,
|
||||
signed: false,
|
||||
},
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 1,
|
||||
col: 14,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 1,
|
||||
col: 12,
|
||||
},
|
||||
},
|
||||
right: LocBox {
|
||||
inner: Literal(
|
||||
Number(
|
||||
Number {
|
||||
val: 4,
|
||||
base: 10,
|
||||
signed: false,
|
||||
},
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 1,
|
||||
col: 18,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 1,
|
||||
col: 16,
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 1,
|
||||
col: 4,
|
||||
},
|
||||
},
|
||||
),
|
||||
Statement(
|
||||
LocBox {
|
||||
inner: Let {
|
||||
name: Ident(
|
||||
"b",
|
||||
),
|
||||
typ: None,
|
||||
val: Some(
|
||||
LocBox {
|
||||
inner: BinOp {
|
||||
typ: EqEq,
|
||||
left: LocBox {
|
||||
inner: BinOp {
|
||||
typ: Div,
|
||||
left: LocBox {
|
||||
inner: Literal(
|
||||
Number(
|
||||
Number {
|
||||
val: 3,
|
||||
base: 10,
|
||||
signed: false,
|
||||
},
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 2,
|
||||
col: 10,
|
||||
},
|
||||
},
|
||||
right: LocBox {
|
||||
inner: Literal(
|
||||
Number(
|
||||
Number {
|
||||
val: 4,
|
||||
base: 10,
|
||||
signed: false,
|
||||
},
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 2,
|
||||
col: 12,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 2,
|
||||
col: 11,
|
||||
},
|
||||
},
|
||||
right: LocBox {
|
||||
inner: UnOp {
|
||||
typ: Star,
|
||||
right: LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"a",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 2,
|
||||
col: 17,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 2,
|
||||
col: 16,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 2,
|
||||
col: 14,
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 2,
|
||||
col: 4,
|
||||
},
|
||||
},
|
||||
),
|
||||
Statement(
|
||||
LocBox {
|
||||
inner: Let {
|
||||
name: Ident(
|
||||
"c",
|
||||
),
|
||||
typ: None,
|
||||
val: Some(
|
||||
LocBox {
|
||||
inner: BinOp {
|
||||
typ: Div,
|
||||
left: LocBox {
|
||||
inner: Group(
|
||||
LocBox {
|
||||
inner: PtrFieldAccess {
|
||||
left: Some(
|
||||
LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"a",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 3,
|
||||
col: 11,
|
||||
},
|
||||
},
|
||||
),
|
||||
right: LocBox {
|
||||
inner: FieldAccess {
|
||||
left: Some(
|
||||
LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"b",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 3,
|
||||
col: 13,
|
||||
},
|
||||
},
|
||||
),
|
||||
right: LocBox {
|
||||
inner: PtrFieldAccess {
|
||||
left: Some(
|
||||
LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"c",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 3,
|
||||
col: 15,
|
||||
},
|
||||
},
|
||||
),
|
||||
right: LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"d",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 3,
|
||||
col: 17,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 3,
|
||||
col: 16,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 3,
|
||||
col: 14,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 3,
|
||||
col: 12,
|
||||
},
|
||||
},
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 3,
|
||||
col: 10,
|
||||
},
|
||||
},
|
||||
right: LocBox {
|
||||
inner: Literal(
|
||||
Number(
|
||||
Number {
|
||||
val: 2,
|
||||
base: 10,
|
||||
signed: false,
|
||||
},
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 3,
|
||||
col: 22,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 3,
|
||||
col: 20,
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 3,
|
||||
col: 4,
|
||||
},
|
||||
},
|
||||
),
|
||||
Statement(
|
||||
LocBox {
|
||||
inner: Let {
|
||||
name: Ident(
|
||||
"d",
|
||||
),
|
||||
typ: None,
|
||||
val: Some(
|
||||
LocBox {
|
||||
inner: BinOp {
|
||||
typ: Div,
|
||||
left: LocBox {
|
||||
inner: Literal(
|
||||
Number(
|
||||
Number {
|
||||
val: 2,
|
||||
base: 10,
|
||||
signed: false,
|
||||
},
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 4,
|
||||
col: 10,
|
||||
},
|
||||
},
|
||||
right: LocBox {
|
||||
inner: PtrFieldAccess {
|
||||
left: Some(
|
||||
LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"a",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 4,
|
||||
col: 14,
|
||||
},
|
||||
},
|
||||
),
|
||||
right: LocBox {
|
||||
inner: FieldAccess {
|
||||
left: Some(
|
||||
LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"b",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 4,
|
||||
col: 16,
|
||||
},
|
||||
},
|
||||
),
|
||||
right: LocBox {
|
||||
inner: PtrFieldAccess {
|
||||
left: Some(
|
||||
LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"c",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 4,
|
||||
col: 18,
|
||||
},
|
||||
},
|
||||
),
|
||||
right: LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"d",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 4,
|
||||
col: 20,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 4,
|
||||
col: 19,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 4,
|
||||
col: 17,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 4,
|
||||
col: 15,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 4,
|
||||
col: 12,
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 4,
|
||||
col: 4,
|
||||
},
|
||||
},
|
||||
),
|
||||
Statement(
|
||||
LocBox {
|
||||
inner: Let {
|
||||
name: Ident(
|
||||
"e",
|
||||
),
|
||||
typ: None,
|
||||
val: Some(
|
||||
LocBox {
|
||||
inner: BinOp {
|
||||
typ: Div,
|
||||
left: LocBox {
|
||||
inner: PtrFieldAccess {
|
||||
left: Some(
|
||||
LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"a",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 5,
|
||||
col: 10,
|
||||
},
|
||||
},
|
||||
),
|
||||
right: LocBox {
|
||||
inner: FieldAccess {
|
||||
left: Some(
|
||||
LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"b",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 5,
|
||||
col: 12,
|
||||
},
|
||||
},
|
||||
),
|
||||
right: LocBox {
|
||||
inner: PtrFieldAccess {
|
||||
left: Some(
|
||||
LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"c",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 5,
|
||||
col: 14,
|
||||
},
|
||||
},
|
||||
),
|
||||
right: LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"d",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 5,
|
||||
col: 16,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 5,
|
||||
col: 15,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 5,
|
||||
col: 13,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 5,
|
||||
col: 11,
|
||||
},
|
||||
},
|
||||
right: LocBox {
|
||||
inner: Literal(
|
||||
Number(
|
||||
Number {
|
||||
val: 2,
|
||||
base: 10,
|
||||
signed: false,
|
||||
},
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 5,
|
||||
col: 20,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 5,
|
||||
col: 18,
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 5,
|
||||
col: 4,
|
||||
},
|
||||
},
|
||||
),
|
||||
Statement(
|
||||
LocBox {
|
||||
inner: Let {
|
||||
name: Ident(
|
||||
"f",
|
||||
),
|
||||
typ: None,
|
||||
val: Some(
|
||||
LocBox {
|
||||
inner: BinOp {
|
||||
typ: Div,
|
||||
left: LocBox {
|
||||
inner: FieldAccess {
|
||||
left: Some(
|
||||
LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"a",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 6,
|
||||
col: 10,
|
||||
},
|
||||
},
|
||||
),
|
||||
right: LocBox {
|
||||
inner: FieldAccess {
|
||||
left: Some(
|
||||
LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"b",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 6,
|
||||
col: 12,
|
||||
},
|
||||
},
|
||||
),
|
||||
right: LocBox {
|
||||
inner: FieldAccess {
|
||||
left: Some(
|
||||
LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"c",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 6,
|
||||
col: 14,
|
||||
},
|
||||
},
|
||||
),
|
||||
right: LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"d",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 6,
|
||||
col: 16,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 6,
|
||||
col: 15,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 6,
|
||||
col: 13,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 6,
|
||||
col: 11,
|
||||
},
|
||||
},
|
||||
right: LocBox {
|
||||
inner: Literal(
|
||||
Number(
|
||||
Number {
|
||||
val: 2,
|
||||
base: 10,
|
||||
signed: false,
|
||||
},
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 6,
|
||||
col: 20,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 6,
|
||||
col: 18,
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 6,
|
||||
col: 4,
|
||||
},
|
||||
},
|
||||
),
|
||||
Statement(
|
||||
LocBox {
|
||||
inner: Let {
|
||||
name: Ident(
|
||||
"g",
|
||||
),
|
||||
typ: None,
|
||||
val: Some(
|
||||
LocBox {
|
||||
inner: BinOp {
|
||||
typ: Star,
|
||||
left: LocBox {
|
||||
inner: ArrayIndex {
|
||||
name: LocBox {
|
||||
inner: FieldAccess {
|
||||
left: Some(
|
||||
LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"a",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 7,
|
||||
col: 10,
|
||||
},
|
||||
},
|
||||
),
|
||||
right: LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"b",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 7,
|
||||
col: 12,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 7,
|
||||
col: 11,
|
||||
},
|
||||
},
|
||||
index: LocBox {
|
||||
inner: UnOp {
|
||||
typ: Star,
|
||||
right: LocBox {
|
||||
inner: FieldAccess {
|
||||
left: Some(
|
||||
LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"a",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 7,
|
||||
col: 15,
|
||||
},
|
||||
},
|
||||
),
|
||||
right: LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"c",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 7,
|
||||
col: 17,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 7,
|
||||
col: 16,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 7,
|
||||
col: 14,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 7,
|
||||
col: 13,
|
||||
},
|
||||
},
|
||||
right: LocBox {
|
||||
inner: Literal(
|
||||
Number(
|
||||
Number {
|
||||
val: 5,
|
||||
base: 10,
|
||||
signed: false,
|
||||
},
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 7,
|
||||
col: 22,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 7,
|
||||
col: 20,
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 7,
|
||||
col: 4,
|
||||
},
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
structs: {},
|
||||
enums: {},
|
||||
types: {},
|
||||
functions: {},
|
||||
member_functions: {},
|
||||
}
|
||||
8
old_tests/parser/expressions.mcl
Normal file
8
old_tests/parser/expressions.mcl
Normal file
@@ -0,0 +1,8 @@
|
||||
let a = 1 * 3 == 4;
|
||||
let b = 3/4 == *a;
|
||||
let c = (a->b.c->d) / 2;
|
||||
let d = 2 / a->b.c->d;
|
||||
let e = a->b.c->d / 2;
|
||||
let f = a.b.c.d / 2;
|
||||
let g = a.b[*a.c] * 5;
|
||||
|
||||
306
old_tests/parser/functions.exp
Normal file
306
old_tests/parser/functions.exp
Normal file
@@ -0,0 +1,306 @@
|
||||
Program {
|
||||
ast: Block(
|
||||
[
|
||||
Statement(
|
||||
LocBox {
|
||||
inner: Fn(
|
||||
Function {
|
||||
struct_name: None,
|
||||
name: Ident(
|
||||
"main",
|
||||
),
|
||||
params: [
|
||||
(
|
||||
Ident(
|
||||
"argc",
|
||||
),
|
||||
LocBox {
|
||||
inner: Owned(
|
||||
Ident(
|
||||
"i32",
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/functions.mcl",
|
||||
line: 1,
|
||||
col: 18,
|
||||
},
|
||||
},
|
||||
),
|
||||
(
|
||||
Ident(
|
||||
"argv",
|
||||
),
|
||||
LocBox {
|
||||
inner: Ref {
|
||||
inner: Array {
|
||||
inner: Owned(
|
||||
Ident(
|
||||
"Str",
|
||||
),
|
||||
),
|
||||
},
|
||||
mutable: false,
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/functions.mcl",
|
||||
line: 1,
|
||||
col: 27,
|
||||
},
|
||||
},
|
||||
),
|
||||
],
|
||||
ret_type: Some(
|
||||
LocBox {
|
||||
inner: Owned(
|
||||
Ident(
|
||||
"i32",
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/functions.mcl",
|
||||
line: 1,
|
||||
col: 39,
|
||||
},
|
||||
},
|
||||
),
|
||||
qual_const: false,
|
||||
qual_extern: None,
|
||||
body: Some(
|
||||
Block(
|
||||
[
|
||||
Expr(
|
||||
LocBox {
|
||||
inner: Return(
|
||||
Some(
|
||||
LocBox {
|
||||
inner: Literal(
|
||||
Number(
|
||||
Number {
|
||||
val: 0,
|
||||
base: 10,
|
||||
signed: false,
|
||||
},
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/functions.mcl",
|
||||
line: 2,
|
||||
col: 13,
|
||||
},
|
||||
},
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/functions.mcl",
|
||||
line: 2,
|
||||
col: 11,
|
||||
},
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/functions.mcl",
|
||||
line: 1,
|
||||
col: 3,
|
||||
},
|
||||
},
|
||||
),
|
||||
Statement(
|
||||
LocBox {
|
||||
inner: Fn(
|
||||
Function {
|
||||
struct_name: Some(
|
||||
Ident(
|
||||
"Baz",
|
||||
),
|
||||
),
|
||||
name: Ident(
|
||||
"main",
|
||||
),
|
||||
params: [
|
||||
(
|
||||
Ident(
|
||||
"self",
|
||||
),
|
||||
LocBox {
|
||||
inner: Ref {
|
||||
inner: Owned(
|
||||
Ident(
|
||||
"Baz",
|
||||
),
|
||||
),
|
||||
mutable: true,
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/functions.mcl",
|
||||
line: 4,
|
||||
col: 20,
|
||||
},
|
||||
},
|
||||
),
|
||||
(
|
||||
Ident(
|
||||
"a",
|
||||
),
|
||||
LocBox {
|
||||
inner: Ref {
|
||||
inner: Owned(
|
||||
Ident(
|
||||
"Foo",
|
||||
),
|
||||
),
|
||||
mutable: false,
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/functions.mcl",
|
||||
line: 4,
|
||||
col: 33,
|
||||
},
|
||||
},
|
||||
),
|
||||
(
|
||||
Ident(
|
||||
"b",
|
||||
),
|
||||
LocBox {
|
||||
inner: Ref {
|
||||
inner: Owned(
|
||||
Ident(
|
||||
"Bar",
|
||||
),
|
||||
),
|
||||
mutable: true,
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/functions.mcl",
|
||||
line: 4,
|
||||
col: 42,
|
||||
},
|
||||
},
|
||||
),
|
||||
],
|
||||
ret_type: Some(
|
||||
LocBox {
|
||||
inner: Ref {
|
||||
inner: Owned(
|
||||
Ident(
|
||||
"Nya",
|
||||
),
|
||||
),
|
||||
mutable: false,
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/functions.mcl",
|
||||
line: 4,
|
||||
col: 54,
|
||||
},
|
||||
},
|
||||
),
|
||||
qual_const: false,
|
||||
qual_extern: None,
|
||||
body: None,
|
||||
},
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/functions.mcl",
|
||||
line: 4,
|
||||
col: 3,
|
||||
},
|
||||
},
|
||||
),
|
||||
Statement(
|
||||
LocBox {
|
||||
inner: Fn(
|
||||
Function {
|
||||
struct_name: Some(
|
||||
Ident(
|
||||
"Baz",
|
||||
),
|
||||
),
|
||||
name: Ident(
|
||||
"main",
|
||||
),
|
||||
params: [
|
||||
(
|
||||
Ident(
|
||||
"a",
|
||||
),
|
||||
LocBox {
|
||||
inner: Ref {
|
||||
inner: Owned(
|
||||
Ident(
|
||||
"Foo",
|
||||
),
|
||||
),
|
||||
mutable: false,
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/functions.mcl",
|
||||
line: 5,
|
||||
col: 17,
|
||||
},
|
||||
},
|
||||
),
|
||||
(
|
||||
Ident(
|
||||
"b",
|
||||
),
|
||||
LocBox {
|
||||
inner: Ref {
|
||||
inner: Owned(
|
||||
Ident(
|
||||
"Bar",
|
||||
),
|
||||
),
|
||||
mutable: true,
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/functions.mcl",
|
||||
line: 5,
|
||||
col: 26,
|
||||
},
|
||||
},
|
||||
),
|
||||
],
|
||||
ret_type: Some(
|
||||
LocBox {
|
||||
inner: Ref {
|
||||
inner: Owned(
|
||||
Ident(
|
||||
"Nya",
|
||||
),
|
||||
),
|
||||
mutable: false,
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/functions.mcl",
|
||||
line: 5,
|
||||
col: 38,
|
||||
},
|
||||
},
|
||||
),
|
||||
qual_const: false,
|
||||
qual_extern: None,
|
||||
body: None,
|
||||
},
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/functions.mcl",
|
||||
line: 5,
|
||||
col: 3,
|
||||
},
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
structs: {},
|
||||
enums: {},
|
||||
types: {},
|
||||
functions: {},
|
||||
member_functions: {},
|
||||
}
|
||||
5
old_tests/parser/functions.mcl
Normal file
5
old_tests/parser/functions.mcl
Normal file
@@ -0,0 +1,5 @@
|
||||
fn main(argc: i32, argv: &[Str]) -> i32 {
|
||||
return 0;
|
||||
}
|
||||
fn Baz.main(self: &mut Baz, a: &Foo, b: &mut Bar) -> &Nya;
|
||||
fn Baz.main(a: &Foo, b: &mut Bar) -> &Nya;
|
||||
70
old_tests/parser/if-statements.exp
Normal file
70
old_tests/parser/if-statements.exp
Normal file
@@ -0,0 +1,70 @@
|
||||
Program {
|
||||
ast: Block(
|
||||
[
|
||||
Expr(
|
||||
LocBox {
|
||||
inner: If(
|
||||
IfExpr {
|
||||
test: LocBox {
|
||||
inner: BinOp {
|
||||
typ: Gt,
|
||||
left: LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"i",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/if-statements.mcl",
|
||||
line: 2,
|
||||
col: 5,
|
||||
},
|
||||
},
|
||||
right: LocBox {
|
||||
inner: Literal(
|
||||
Number(
|
||||
Number {
|
||||
val: 3,
|
||||
base: 10,
|
||||
signed: false,
|
||||
},
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/if-statements.mcl",
|
||||
line: 2,
|
||||
col: 9,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/if-statements.mcl",
|
||||
line: 2,
|
||||
col: 7,
|
||||
},
|
||||
},
|
||||
body: Block(
|
||||
[],
|
||||
),
|
||||
else_if: None,
|
||||
},
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/if-statements.mcl",
|
||||
line: 2,
|
||||
col: 3,
|
||||
},
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
structs: {},
|
||||
enums: {},
|
||||
types: {},
|
||||
functions: {},
|
||||
member_functions: {},
|
||||
}
|
||||
18
old_tests/parser/if-statements.mcl
Normal file
18
old_tests/parser/if-statements.mcl
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
if i > 3 {
|
||||
|
||||
}
|
||||
|
||||
if 1 {
|
||||
|
||||
} else {
|
||||
|
||||
}
|
||||
|
||||
if 1 {
|
||||
|
||||
} else if a > 3 {
|
||||
|
||||
} else {
|
||||
|
||||
}
|
||||
212
old_tests/parser/loops.exp
Normal file
212
old_tests/parser/loops.exp
Normal file
@@ -0,0 +1,212 @@
|
||||
Program {
|
||||
ast: Block(
|
||||
[
|
||||
Expr(
|
||||
LocBox {
|
||||
inner: ForLoop {
|
||||
init: Statement(
|
||||
LocBox {
|
||||
inner: Let {
|
||||
name: Ident(
|
||||
"i",
|
||||
),
|
||||
typ: None,
|
||||
val: Some(
|
||||
LocBox {
|
||||
inner: Literal(
|
||||
Number(
|
||||
Number {
|
||||
val: 0,
|
||||
base: 10,
|
||||
signed: false,
|
||||
},
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/loops.mcl",
|
||||
line: 1,
|
||||
col: 14,
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/loops.mcl",
|
||||
line: 1,
|
||||
col: 8,
|
||||
},
|
||||
},
|
||||
),
|
||||
test: LocBox {
|
||||
inner: BinOp {
|
||||
typ: Lt,
|
||||
left: LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"i",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/loops.mcl",
|
||||
line: 1,
|
||||
col: 17,
|
||||
},
|
||||
},
|
||||
right: LocBox {
|
||||
inner: Literal(
|
||||
Number(
|
||||
Number {
|
||||
val: 10,
|
||||
base: 10,
|
||||
signed: false,
|
||||
},
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/loops.mcl",
|
||||
line: 1,
|
||||
col: 22,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/loops.mcl",
|
||||
line: 1,
|
||||
col: 19,
|
||||
},
|
||||
},
|
||||
on_loop: LocBox {
|
||||
inner: BinOp {
|
||||
typ: AddEq,
|
||||
left: LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"i",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/loops.mcl",
|
||||
line: 1,
|
||||
col: 25,
|
||||
},
|
||||
},
|
||||
right: LocBox {
|
||||
inner: Literal(
|
||||
Number(
|
||||
Number {
|
||||
val: 1,
|
||||
base: 10,
|
||||
signed: false,
|
||||
},
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/loops.mcl",
|
||||
line: 1,
|
||||
col: 29,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/loops.mcl",
|
||||
line: 1,
|
||||
col: 27,
|
||||
},
|
||||
},
|
||||
body: Block(
|
||||
[],
|
||||
),
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/loops.mcl",
|
||||
line: 1,
|
||||
col: 4,
|
||||
},
|
||||
},
|
||||
),
|
||||
Expr(
|
||||
LocBox {
|
||||
inner: WhileLoop {
|
||||
test: LocBox {
|
||||
inner: BinOp {
|
||||
typ: Gt,
|
||||
left: LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"i",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/loops.mcl",
|
||||
line: 2,
|
||||
col: 8,
|
||||
},
|
||||
},
|
||||
right: LocBox {
|
||||
inner: Literal(
|
||||
Number(
|
||||
Number {
|
||||
val: 3,
|
||||
base: 10,
|
||||
signed: false,
|
||||
},
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/loops.mcl",
|
||||
line: 2,
|
||||
col: 12,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/loops.mcl",
|
||||
line: 2,
|
||||
col: 10,
|
||||
},
|
||||
},
|
||||
body: Block(
|
||||
[],
|
||||
),
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/loops.mcl",
|
||||
line: 2,
|
||||
col: 6,
|
||||
},
|
||||
},
|
||||
),
|
||||
Expr(
|
||||
LocBox {
|
||||
inner: InfLoop {
|
||||
body: Block(
|
||||
[],
|
||||
),
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/loops.mcl",
|
||||
line: 3,
|
||||
col: 5,
|
||||
},
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
structs: {},
|
||||
enums: {},
|
||||
types: {},
|
||||
functions: {},
|
||||
member_functions: {},
|
||||
}
|
||||
3
old_tests/parser/loops.mcl
Normal file
3
old_tests/parser/loops.mcl
Normal file
@@ -0,0 +1,3 @@
|
||||
for let i = 0; i < 10; i += 1 {}
|
||||
while i > 3 {}
|
||||
loop {}
|
||||
66
old_tests/parser/structs.exp
Normal file
66
old_tests/parser/structs.exp
Normal file
@@ -0,0 +1,66 @@
|
||||
Program {
|
||||
ast: Block(
|
||||
[
|
||||
Statement(
|
||||
LocBox {
|
||||
inner: Struct(
|
||||
Struct {
|
||||
name: Ident(
|
||||
"foo_t",
|
||||
),
|
||||
fields: [],
|
||||
},
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/structs.mcl",
|
||||
line: 2,
|
||||
col: 7,
|
||||
},
|
||||
},
|
||||
),
|
||||
Statement(
|
||||
LocBox {
|
||||
inner: Struct(
|
||||
Struct {
|
||||
name: Ident(
|
||||
"bar_t",
|
||||
),
|
||||
fields: [
|
||||
(
|
||||
Ident(
|
||||
"a",
|
||||
),
|
||||
LocBox {
|
||||
inner: Ref {
|
||||
inner: Owned(
|
||||
Ident(
|
||||
"bar_t",
|
||||
),
|
||||
),
|
||||
mutable: false,
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/structs.mcl",
|
||||
line: 5,
|
||||
col: 9,
|
||||
},
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/structs.mcl",
|
||||
line: 4,
|
||||
col: 7,
|
||||
},
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
structs: {},
|
||||
enums: {},
|
||||
types: {},
|
||||
functions: {},
|
||||
member_functions: {},
|
||||
}
|
||||
6
old_tests/parser/structs.mcl
Normal file
6
old_tests/parser/structs.mcl
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
struct foo_t {}
|
||||
|
||||
struct bar_t {
|
||||
a: &bar_t
|
||||
}
|
||||
1
old_tests/tokeniser/comments.exp
Normal file
1
old_tests/tokeniser/comments.exp
Normal file
@@ -0,0 +1 @@
|
||||
[]
|
||||
@@ -1,5 +1,4 @@
|
||||
use std::{collections::HashMap, ffi::OsStr, io::Write, os::unix::ffi::OsStrExt, path::{Path, PathBuf}, process::ExitCode};
|
||||
use anyhow::bail;
|
||||
use camino::Utf8PathBuf;
|
||||
use clap::Parser;
|
||||
use mclangc;
|
||||
@@ -134,7 +133,7 @@ fn test_parser(cf: &CollectedFiles, compile: bool) -> anyhow::Result<usize> {
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let ast = match mclangc::parser::parse_program(tokens) {
|
||||
let ast = match mclangc::parser::parse_program(tokens, &mclangc::cli::CliArgs::default()) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
crate::error!("Test parser/{name} had an error: {e}");
|
||||
49
src/cli.rs
49
src/cli.rs
@@ -1,3 +1,52 @@
|
||||
use clap::{error::ErrorKind, CommandFactory};
|
||||
|
||||
use crate::logger::Level;
|
||||
|
||||
|
||||
#[derive(Debug, Default, clap::Parser)]
|
||||
pub struct CliArgs {
|
||||
/// Output more info, will get overwritten if -q|--quiet is specified
|
||||
#[arg(long, short)]
|
||||
verbose: bool,
|
||||
/// Output nothing, except errors, will overwrite -v|--verbose
|
||||
#[arg(long, short)]
|
||||
quiet: bool,
|
||||
#[arg(long, short, value_parser=crate::targets::get_all_targets(), default_value_t=crate::targets::get_default_target())]
|
||||
pub target: String,
|
||||
#[arg(long="include", short='I', default_values=[".", "include"])]
|
||||
pub include_paths: Vec<String>,
|
||||
/// Output file
|
||||
#[arg(long, short, default_value="a.out")]
|
||||
pub output: String,
|
||||
|
||||
/// Linker args
|
||||
#[arg(long, short='L')]
|
||||
pub linker_args: Vec<String>,
|
||||
|
||||
/// Link with C libs, required for any C libraries
|
||||
#[arg(long, short='C')]
|
||||
pub link_c: bool,
|
||||
|
||||
/// All input files
|
||||
#[clap(num_args = 1..)]
|
||||
pub input: Vec<String>
|
||||
}
|
||||
|
||||
impl CliArgs {
|
||||
pub fn set_log_level(&self) {
|
||||
if self.quiet {
|
||||
unsafe {
|
||||
crate::logger::LEVEL = Level::Error;
|
||||
}
|
||||
} else if self.verbose {
|
||||
unsafe {
|
||||
crate::logger::LEVEL = Level::Debug;
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn validate(&self) {
|
||||
if self.input.len() < 1 {
|
||||
CliArgs::command().error(ErrorKind::TooFewValues, "at least one value is required for '<INPUT>' but none was supplied").exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
use std::fmt::Display;
|
||||
use std::fmt::{Debug, Display};
|
||||
|
||||
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq)]
|
||||
#[derive(Debug, Clone, PartialOrd, Ord, Eq, Hash)]
|
||||
pub struct Loc {
|
||||
file: String,
|
||||
line: usize,
|
||||
col: usize,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl Loc {
|
||||
pub fn new(s: impl ToString, line: usize, col: usize) -> Self {
|
||||
Self {
|
||||
@@ -27,6 +26,28 @@ impl Loc {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct LocBox<T: Clone + Debug> {
|
||||
inner: T,
|
||||
loc: Loc
|
||||
}
|
||||
|
||||
impl<T: Clone + Debug> LocBox<T> {
|
||||
pub fn new(loc: &Loc, inner: T) -> Self {
|
||||
Self { loc: loc.clone(), inner }
|
||||
}
|
||||
pub fn inner(&self) -> &T {
|
||||
&self.inner
|
||||
}
|
||||
pub fn inner_mut(&mut self) -> &mut T {
|
||||
&mut self.inner
|
||||
}
|
||||
pub fn loc(&self) -> &Loc {
|
||||
&self.loc
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Loc {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}:{}:{}", self.file, self.line, self.col)
|
||||
@@ -43,6 +64,12 @@ impl Default for Loc {
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Loc {
|
||||
fn eq(&self, _: &Self) -> bool {
|
||||
true // ignore eq cause comparing 2 locations is irrelevant
|
||||
}
|
||||
}
|
||||
|
||||
pub trait LocIncr {
|
||||
fn inc_line(&mut self);
|
||||
fn inc_col(&mut self);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
#![doc = include_str!("../docs/main.md")]
|
||||
|
||||
|
||||
pub mod common;
|
||||
pub mod tokeniser;
|
||||
pub mod parser;
|
||||
@@ -5,3 +8,5 @@ pub mod cli;
|
||||
#[macro_use]
|
||||
pub mod logger;
|
||||
pub mod validator;
|
||||
#[macro_use]
|
||||
pub mod targets;
|
||||
|
||||
112
src/logger.rs
112
src/logger.rs
@@ -1,8 +1,8 @@
|
||||
use crate::common::Loc;
|
||||
|
||||
pub static mut LEVEL: Level = Level::Info;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(u8)]
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum Level {
|
||||
Off = 0,
|
||||
Error,
|
||||
@@ -20,25 +20,37 @@ const C_INFO: &'static str = "\x1B[1;32m";
|
||||
const C_DEBUG: &'static str = "\x1B[1;35m";
|
||||
const C_HELP: &'static str = "\x1B[1;36m";
|
||||
|
||||
pub fn _log(level: Level, str: &str) {
|
||||
pub fn _log(dbg_loc: Option<(&'static str, u32, u32)>, level: Level, str: &str) {
|
||||
if level > unsafe { LEVEL }{
|
||||
return;
|
||||
}
|
||||
let dbg = if let Some((file, line, col)) = dbg_loc {
|
||||
format!("{file}:{line}:{col}: ")
|
||||
} else {String::new()};
|
||||
match level {
|
||||
Level::Off => return,
|
||||
Level::Error => println!("{C_ERROR}error{C_RESET}: {str}"),
|
||||
Level::Warn => println!("{C_WARN}warn{C_RESET}: {str}"),
|
||||
Level::Info => println!("{C_INFO}info{C_RESET}: {str}"),
|
||||
Level::Help => println!("{C_HELP}help{C_RESET}: {str}"),
|
||||
Level::Debug => println!("{C_DEBUG}debug{C_RESET}: {str}"),
|
||||
Level::Error => println!("{dbg}{C_ERROR}error{C_RESET}: {str}"),
|
||||
Level::Warn => println!("{dbg}{C_WARN}warn{C_RESET}: {str}"),
|
||||
Level::Info => println!("{dbg}{C_INFO}info{C_RESET}: {str}"),
|
||||
Level::Help => println!("{dbg}{C_HELP}help{C_RESET}: {str}"),
|
||||
Level::Debug => println!("{dbg}{C_DEBUG}debug{C_RESET}: {str}"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn _log_with_loc(loc: &Loc, level: Level, str: &str) {
|
||||
pub fn _log_with_loc(dbg_loc: Option<(&'static str, u32, u32)>, loc: String, level: Level, str: &str) {
|
||||
if level > unsafe { LEVEL }{
|
||||
return;
|
||||
}
|
||||
let dbg = if let Some((file, line, col)) = dbg_loc {
|
||||
format!("{file}:{line}:{col}: ")
|
||||
} else {String::new()};
|
||||
match level {
|
||||
Level::Off => return,
|
||||
Level::Error => println!("{loc}: {C_ERROR}error{C_RESET}: {str}"),
|
||||
Level::Warn => println!("{loc}: {C_WARN}warn{C_RESET}: {str}"),
|
||||
Level::Info => println!("{loc}: {C_INFO}info{C_RESET}: {str}"),
|
||||
Level::Help => println!("{loc}: {C_HELP}help{C_RESET}: {str}"),
|
||||
Level::Debug => println!("{loc}: {C_DEBUG}debug{C_RESET}: {str}"),
|
||||
Level::Error => println!("{dbg}{loc}: {C_ERROR}error{C_RESET}: {str}"),
|
||||
Level::Warn => println!("{dbg}{loc}: {C_WARN}warn{C_RESET}: {str}"),
|
||||
Level::Info => println!("{dbg}{loc}: {C_INFO}info{C_RESET}: {str}"),
|
||||
Level::Help => println!("{dbg}{loc}: {C_HELP}help{C_RESET}: {str}"),
|
||||
Level::Debug => println!("{dbg}{loc}: {C_DEBUG}debug{C_RESET}: {str}"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,63 +59,103 @@ pub mod log {
|
||||
#[macro_export]
|
||||
macro_rules! error {
|
||||
($($arg:tt)*) => {
|
||||
crate::logger::_log(crate::logger::Level::Error, &format!($($arg)*))
|
||||
if cfg!(debug_assertions) {
|
||||
crate::logger::_log(Some((file!(), line!(), column!())), crate::logger::Level::Error, &format!($($arg)*))
|
||||
} else {
|
||||
crate::logger::_log(None, crate::logger::Level::Error, &format!($($arg)*))
|
||||
}
|
||||
};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! warn {
|
||||
($($arg:tt)*) => {
|
||||
crate::logger::_log(crate::logger::Level::Warn, &format!($($arg)*))
|
||||
if cfg!(debug_assertions) {
|
||||
crate::logger::_log(Some((file!(), line!(), column!())), crate::logger::Level::Warn, &format!($($arg)*))
|
||||
} else {
|
||||
crate::logger::_log(None, crate::logger::Level::Warn, &format!($($arg)*))
|
||||
}
|
||||
};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! info {
|
||||
($($arg:tt)*) => {
|
||||
crate::logger::_log(crate::logger::Level::Info, &format!($($arg)*))
|
||||
if cfg!(debug_assertions) {
|
||||
crate::logger::_log(Some((file!(), line!(), column!())), crate::logger::Level::Info, &format!($($arg)*))
|
||||
} else {
|
||||
crate::logger::_log(None, crate::logger::Level::Info, &format!($($arg)*))
|
||||
}
|
||||
};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! help {
|
||||
($($arg:tt)*) => {
|
||||
crate::logger::_log(crate::logger::Level::Help, &format!($($arg)*))
|
||||
if cfg!(debug_assertions) {
|
||||
crate::logger::_log(Some((file!(), line!(), column!())), crate::logger::Level::Help, &format!($($arg)*))
|
||||
} else {
|
||||
crate::logger::_log(None, crate::logger::Level::Help, &format!($($arg)*))
|
||||
}
|
||||
};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! debug {
|
||||
($($arg:tt)*) => {
|
||||
crate::logger::_log(crate::logger::Level::Debug, &format!($($arg)*))
|
||||
if cfg!(debug_assertions) {
|
||||
crate::logger::_log(Some((file!(), line!(), column!())), crate::logger::Level::Debug, &format!($($arg)*))
|
||||
} else {
|
||||
crate::logger::_log(None, crate::logger::Level::Debug, &format!($($arg)*))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! lerror {
|
||||
($loc:expr, $($arg:tt)*) => {
|
||||
crate::logger::_log_with_loc($loc, crate::logger::Level::Error, &format!($($arg)*))
|
||||
($loc:expr_2021, $($arg:tt)*) => {
|
||||
if cfg!(debug_assertions) {
|
||||
crate::logger::_log_with_loc(Some((file!(), line!(), column!())), format!("{}", $loc), crate::logger::Level::Error, &format!($($arg)*))
|
||||
} else {
|
||||
crate::logger::_log_with_loc(None, format!("{}", $loc), crate::logger::Level::Error, &format!($($arg)*))
|
||||
}
|
||||
};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! lwarn {
|
||||
($loc:expr, $($arg:tt)*) => {
|
||||
crate::logger::_log_with_loc($loc, crate::logger::Level::Warn, &format!($($arg)*))
|
||||
($loc:expr_2021, $($arg:tt)*) => {
|
||||
if cfg!(debug_assertions) {
|
||||
crate::logger::_log_with_loc(Some((file!(), line!(), column!())), format!("{}", $loc), crate::logger::Level::Warn, &format!($($arg)*))
|
||||
} else {
|
||||
crate::logger::_log_with_loc(None, format!("{}", $loc), crate::logger::Level::Warn, &format!($($arg)*))
|
||||
}
|
||||
};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! linfo {
|
||||
($loc:expr, $($arg:tt)*) => {
|
||||
crate::logger::_log_with_loc($loc, crate::logger::Level::Info, &format!($($arg)*))
|
||||
($loc:expr_2021, $($arg:tt)*) => {
|
||||
if cfg!(debug_assertions) {
|
||||
crate::logger::_log_with_loc(Some((file!(), line!(), column!())), format!("{}", $loc), crate::logger::Level::Info, &format!($($arg)*))
|
||||
} else {
|
||||
crate::logger::_log_with_loc(None, format!("{}", $loc), crate::logger::Level::Info, &format!($($arg)*))
|
||||
}
|
||||
};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! lhelp {
|
||||
($loc:expr, $($arg:tt)*) => {
|
||||
crate::logger::_log_with_loc($loc, crate::logger::Level::Help, &format!($($arg)*))
|
||||
($loc:expr_2021, $($arg:tt)*) => {
|
||||
if cfg!(debug_assertions) {
|
||||
crate::logger::_log_with_loc(Some((file!(), line!(), column!())), format!("{}", $loc), crate::logger::Level::Help, &format!($($arg)*))
|
||||
} else {
|
||||
crate::logger::_log_with_loc(None, format!("{}", $loc), crate::logger::Level::Help, &format!($($arg)*))
|
||||
}
|
||||
};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! ldebug {
|
||||
($loc:expr, $($arg:tt)*) => {
|
||||
crate::logger::_log_with_loc($loc, crate::logger::Level::Debug, &format!($($arg)*))
|
||||
($loc:expr_2021, $($arg:tt)*) => {
|
||||
if cfg!(debug_assertions) {
|
||||
crate::logger::_log_with_loc(Some((file!(), line!(), column!())), format!("{}", $loc), crate::logger::Level::Debug, &format!($($arg)*))
|
||||
} else {
|
||||
crate::logger::_log_with_loc(None, format!("{}", $loc), crate::logger::Level::Debug, &format!($($arg)*))
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
38
src/main.rs
38
src/main.rs
@@ -1,11 +1,41 @@
|
||||
#![doc = include_str!("../docs/main.md")]
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use clap::Parser;
|
||||
// Importing logger here too cause the logger macros dont work outside the mclanc lib
|
||||
mod logger;
|
||||
|
||||
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
let data = std::fs::read_to_string("test.mcl").unwrap();
|
||||
let cli = mclangc::cli::CliArgs::parse();
|
||||
cli.set_log_level();
|
||||
cli.validate();
|
||||
let mut progs = Vec::new();
|
||||
for file in &cli.input {
|
||||
let fp = PathBuf::from(file);
|
||||
if !fp.exists() {
|
||||
error!("File {fp:?} doesnt exits, exiting");
|
||||
anyhow::bail!("")
|
||||
}
|
||||
|
||||
let tokens = mclangc::tokeniser::tokenise(&data, "test.mcl")?;
|
||||
let prog = mclangc::parser::parse_program(tokens)?;
|
||||
mclangc::validator::validate_code(&prog);
|
||||
let data = std::fs::read_to_string(&fp).unwrap();
|
||||
info!("Tokenising {file}");
|
||||
let tokens = mclangc::tokeniser::tokenise(&data, &file)?;
|
||||
// dbg!(&tokens);
|
||||
info!("Parsing {file}");
|
||||
let mut prog = mclangc::parser::parse_program(tokens, &cli)?;
|
||||
info!("Validating {file}");
|
||||
mclangc::validator::validate_code(&mut prog)?;
|
||||
dbg!(&prog.functions.len());
|
||||
for f in &prog.functions {
|
||||
error!("owo {}", f.1.inner());
|
||||
}
|
||||
// dbg!(&prog.literals);
|
||||
progs.push((fp, prog));
|
||||
}
|
||||
|
||||
mclangc::targets::compile(&cli, progs)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,72 +1,85 @@
|
||||
use std::collections::HashMap;
|
||||
use std::{collections::BTreeMap, fmt::Display};
|
||||
|
||||
use crate::tokeniser::tokentype::*;
|
||||
use crate::{common::loc::LocBox, parser::ast::{Program, literal::Literal}, tokeniser::tokentype::*};
|
||||
|
||||
use super::{typ::Type, Ast};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
|
||||
pub enum Expr {
|
||||
// Comment(Comment),
|
||||
Group(Box<Expr>),
|
||||
Group(Box<LocBox<Expr>>),
|
||||
UnOp {
|
||||
typ: Punctuation,
|
||||
right: Box<Expr>,
|
||||
right: Box<LocBox<Expr>>,
|
||||
},
|
||||
BinOp {
|
||||
typ: Punctuation,
|
||||
left: Box<Expr>,
|
||||
right: Box<Expr>,
|
||||
left: Box<LocBox<Expr>>,
|
||||
right: Box<LocBox<Expr>>,
|
||||
signed: bool,
|
||||
},
|
||||
Literal(super::literal::Literal),
|
||||
Literal(String, super::literal::Literal), // string is id
|
||||
ArrayIndex {
|
||||
name: Box<Expr>,
|
||||
index: Box<Expr>,
|
||||
name: Box<LocBox<Expr>>,
|
||||
index: Box<LocBox<Expr>>,
|
||||
},
|
||||
Path(Path),
|
||||
Call {
|
||||
path: Box<Expr>,
|
||||
params: CallParams, // Expr ~ (, Expr)*
|
||||
path: Box<LocBox<Expr>>,
|
||||
params: CallParams, // LocBox<Expr> ~ (, Expr)*
|
||||
},
|
||||
MethodCall {
|
||||
left: Box<LocBox<Expr>>,
|
||||
strct: Option<Ident>,
|
||||
params: CallParams,
|
||||
},
|
||||
//MethodCall {
|
||||
// var_name: Box<Expr>,
|
||||
// method_name: Ident,
|
||||
// params: CallParams,
|
||||
//},
|
||||
|
||||
/// the left side only exists on the /.|->/ chain
|
||||
FieldAccess {
|
||||
left: Box<Option<Expr>>,
|
||||
right: Box<Expr>,
|
||||
left: Box<Option<LocBox<Expr>>>,
|
||||
right: Box<LocBox<Expr>>,
|
||||
offset: usize,
|
||||
l_typ: Option<Box<Type>>,
|
||||
r_typ: Option<Box<Type>>,
|
||||
},
|
||||
PtrFieldAccess {
|
||||
left: Box<Option<Expr>>,
|
||||
right: Box<Expr>,
|
||||
left: Box<Option<LocBox<Expr>>>,
|
||||
right: Box<LocBox<Expr>>,
|
||||
offset: usize,
|
||||
l_typ: Option<Box<Type>>,
|
||||
r_typ: Option<Box<Type>>
|
||||
},
|
||||
ForLoop {
|
||||
init: Box<Ast>,
|
||||
test: Box<Expr>,
|
||||
on_loop: Box<Expr>,
|
||||
test: Box<LocBox<Expr>>,
|
||||
on_loop: Box<LocBox<Expr>>,
|
||||
body: Block,
|
||||
},
|
||||
WhileLoop {
|
||||
test: Box<Expr>,
|
||||
test: Box<LocBox<Expr>>,
|
||||
body: Block,
|
||||
},
|
||||
InfLoop {
|
||||
body: Block,
|
||||
},
|
||||
If(IfExpr),
|
||||
Struct {
|
||||
path: Path,
|
||||
fields: HashMap<Ident, Expr>,
|
||||
},
|
||||
Return(Box<Option<Expr>>),
|
||||
Struct(String, StructLit),
|
||||
Return(Box<Option<LocBox<Expr>>>),
|
||||
Break,
|
||||
Continue,
|
||||
Cast {
|
||||
left: Box<Expr>,
|
||||
right: Box<Type>
|
||||
left: Box<LocBox<Expr>>,
|
||||
right: Box<LocBox<Type>>
|
||||
},
|
||||
Self_ {
|
||||
strct: Option<Ident>
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash, PartialEq, PartialOrd)]
|
||||
pub struct StructLit {
|
||||
pub path: Path,
|
||||
pub fields: BTreeMap<Ident, LocBox<Expr>>,
|
||||
}
|
||||
|
||||
impl Expr {
|
||||
@@ -74,28 +87,65 @@ impl Expr {
|
||||
let Expr::Path(p) = self else {panic!("Unwrapping")};
|
||||
p.clone()
|
||||
}
|
||||
pub fn as_number(&self, prog: &Program) -> Option<Number> {
|
||||
match self {
|
||||
Self::Literal(_, Literal::Number(num)) => Some(*num),
|
||||
Self::Path(Path(path)) => {
|
||||
if let Some(val) = prog.get_const_var(path.last().unwrap()) {
|
||||
val.1.inner().val.inner().as_number(prog)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CallParams(pub Vec<Expr>);
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
|
||||
pub struct CallParams(pub Vec<LocBox<Expr>>);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
|
||||
pub struct Block(pub Vec<Ast>);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
impl Default for Block {
|
||||
fn default() -> Self {
|
||||
Self(vec![])
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Hash)]
|
||||
pub struct Path(pub Vec<Ident>);
|
||||
|
||||
impl Display for Path {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let mut buf = String::new();
|
||||
for p in &self.0 {
|
||||
if !buf.is_empty() {
|
||||
buf.push_str("::");
|
||||
}
|
||||
buf.push_str(&p.to_string());
|
||||
}
|
||||
write!(f, "{}", buf)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
impl Path {
|
||||
pub fn display_asm_compat(&self) -> String {
|
||||
self.to_string().replace("::", "$")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
|
||||
pub struct IfExpr {
|
||||
pub test: Box<Expr>,
|
||||
pub test: Box<LocBox<Expr>>,
|
||||
pub body: Block,
|
||||
pub else_if: Option<IfBranchExpr>
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
|
||||
pub enum IfBranchExpr {
|
||||
ElseIf(Box<IfExpr>),
|
||||
Else(Block)
|
||||
|
||||
@@ -1,22 +1,61 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::tokeniser::tokentype::*;
|
||||
use crate::{common::{Loc, loc::LocBox}, debug, parser::ast::{Program, typ::Type}, tokeniser::tokentype::*, validator::{predefined::SIZE, validate_expr}};
|
||||
|
||||
use super::{expr::Expr, typ::Type, Ast};
|
||||
use super::expr::Expr;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
|
||||
pub enum Literal {
|
||||
Number(Number),
|
||||
Ident(Ident),
|
||||
String(TString),
|
||||
Char(Char),
|
||||
Array(Vec<Expr>),
|
||||
ArrayRepeat {
|
||||
typ: Box<Type>,
|
||||
count: Box<Expr>,
|
||||
Array{
|
||||
items: Vec<LocBox<Expr>>,
|
||||
item_size: Option<usize>,
|
||||
},
|
||||
Struct {
|
||||
name: Ident,
|
||||
fields: HashMap<Ident, Ast>
|
||||
Bool(bool),
|
||||
ArrayRepeat {
|
||||
val: Box<LocBox<Expr>>,
|
||||
item_size: Option<usize>,
|
||||
count: usize,
|
||||
},
|
||||
}
|
||||
|
||||
impl Literal {
|
||||
pub fn to_type(&self, prog: &mut Program) -> anyhow::Result<Type> {
|
||||
let t = match self {
|
||||
Self::Number(_) => Type::Builtin { name: String::from("usize"), size: SIZE as u8, signed: false },
|
||||
Self::Ident(ident) => Type::Owned(ident.clone()),
|
||||
Self::String(str) if str.cstr => {
|
||||
debug!("is cstr");
|
||||
Type::Ref { inner: Box::new(Type::Owned(Ident(String::from("cstr")))), mutable: false}
|
||||
},
|
||||
Self::String(str) if !str.cstr => Type::Ref { inner: Box::new(Type::Owned(Ident(String::from("str")))), mutable: false},
|
||||
Self::String(_) => unreachable!("stupid rust"),
|
||||
Self::Char(_) => Type::Owned(Ident(String::from("char"))),
|
||||
Self::Bool(_) => Type::Owned(Ident(String::from("bool"))),
|
||||
Self::Array { items: arr, ..} => {
|
||||
if arr.is_empty() {
|
||||
Type::SizedArray {
|
||||
inner: Box::new(Type::Builtin { name: "void".to_string(), size: 0, signed: false }),
|
||||
count: LocBox::new(&Loc::default(), Expr::Literal(String::new(), Literal::Number(Number { val: 0, base: 10, signed: false })))
|
||||
}
|
||||
} else {
|
||||
let mut item = arr.first().unwrap().clone();
|
||||
let loc = item.loc().clone();
|
||||
Type::SizedArray {
|
||||
inner: Box::new(validate_expr(prog, &mut item)?.unwrap()),
|
||||
count: LocBox::new(&loc, Expr::Literal(String::new(), Literal::Number(Number { val: arr.len(), base: 10, signed: false })))
|
||||
}
|
||||
}
|
||||
}
|
||||
Self::ArrayRepeat { val, count, ..} => {
|
||||
let loc = val.loc().clone();
|
||||
let mut val = val.clone();
|
||||
Type::SizedArray { inner: Box::new(validate_expr(prog, &mut val)?.unwrap()), count: LocBox::new(&loc, Expr::Literal(String::new(), Literal::Number(Number { val: *count, base: 10, signed: false })))}
|
||||
}
|
||||
|
||||
};
|
||||
Ok(t)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use typ::Type;
|
||||
use statement::{ConstVar, Enum, Function, StaticVar, Struct};
|
||||
|
||||
use crate::{common::loc::LocBox, parser::ast::{expr::StructLit, literal::Literal, statement::Let, typ::Type}};
|
||||
pub use crate::tokeniser::tokentype::*;
|
||||
|
||||
pub mod expr;
|
||||
@@ -9,20 +10,107 @@ pub mod literal;
|
||||
pub mod statement;
|
||||
pub mod typ;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct Scope {
|
||||
pub vars: HashMap<Ident, LocBox<Let>>,
|
||||
pub static_vars: HashMap<Ident, StaticVar>,
|
||||
pub const_vars: HashMap<Ident, ConstVar>,
|
||||
pub inner_scope: Option<Box<Scope>>
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct Program {
|
||||
pub ast: expr::Block,
|
||||
pub structs: HashMap<Ident, HashMap<Ident, usize>>,
|
||||
pub enums: HashMap<Ident, usize>,
|
||||
pub types: HashMap<Type, Type>,
|
||||
pub functions: HashMap<Ident, (Vec<(Ident, Type)>, Type)>,
|
||||
pub member_functions: HashMap<Ident, HashMap<Ident, (Vec<(Ident, Type)>, Type)>>,
|
||||
pub structs: HashMap<Ident, LocBox<Struct>>,
|
||||
pub enums: HashMap<Ident, LocBox<Enum>>,
|
||||
pub types: HashMap<Ident, LocBox<Type>>,
|
||||
pub functions: HashMap<Ident, LocBox<Function>>,
|
||||
pub member_functions: HashMap<Ident, HashMap<Ident, LocBox<Function>>>,
|
||||
pub static_vars: HashMap<Ident, StaticVar>,
|
||||
pub const_vars: HashMap<Ident, ConstVar>,
|
||||
pub literals: HashMap<String, Literal>, // string is id of literal
|
||||
pub struct_literals: HashMap<String, StructLit>, // string is id of literal
|
||||
pub scope: Option<Scope>,
|
||||
pub curr_fn_args: HashMap<Ident, LocBox<Type>>,
|
||||
pub curr_struct: Option<Ident>,
|
||||
pub included_files: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
||||
impl Scope {
|
||||
pub fn get_var(&self, name: &Ident) -> Option<(Ident, LocBox<Let>)> {
|
||||
if let Some(inner) = &self.inner_scope {
|
||||
if let Some((ident, var)) = inner.get_var(name) {
|
||||
return Some((ident, var));
|
||||
}
|
||||
}
|
||||
if let Some(var) = self.vars.get(name) {
|
||||
return Some((name.clone(), var.clone()));
|
||||
}
|
||||
None
|
||||
}
|
||||
pub fn get_static_var(&self, name: &Ident) -> Option<(Ident, LocBox<StaticVar>)> {
|
||||
if let Some(inner) = &self.inner_scope {
|
||||
if let Some((ident, var)) = inner.get_static_var(name) {
|
||||
return Some((ident, var));
|
||||
}
|
||||
}
|
||||
if let Some(var) = self.static_vars.get(name) {
|
||||
return Some((name.clone(), LocBox::new(var.typ.loc(), var.clone())));
|
||||
}
|
||||
None
|
||||
}
|
||||
pub fn get_const_var(&self, name: &Ident) -> Option<(Ident, LocBox<ConstVar>)> {
|
||||
if let Some(inner) = &self.inner_scope {
|
||||
if let Some((ident, var)) = inner.get_const_var(name) {
|
||||
return Some((ident, var));
|
||||
}
|
||||
}
|
||||
if let Some(var) = self.const_vars.get(name) {
|
||||
return Some((name.clone(), LocBox::new(var.typ.loc(), var.clone())));
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Program {
|
||||
pub fn get_var(&self, name: &Ident) -> Option<(Ident, LocBox<Let>)> {
|
||||
if let Some(scope) = &self.scope {
|
||||
scope.get_var(name)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
pub fn get_static_var(&self, name: &Ident) -> Option<(Ident, LocBox<StaticVar>)> {
|
||||
if let Some(scope) = &self.scope {
|
||||
if let Some((name, var)) = scope.get_static_var(name) {
|
||||
return Some((name, var));
|
||||
}
|
||||
}
|
||||
if let Some(var) = self.static_vars.get(name) {
|
||||
return Some((name.clone(), LocBox::new(var.typ.loc(), var.clone())));
|
||||
}
|
||||
None
|
||||
}
|
||||
pub fn get_const_var(&self, name: &Ident) -> Option<(Ident, LocBox<ConstVar>)> {
|
||||
if let Some(scope) = &self.scope {
|
||||
if let Some((name, var)) = scope.get_const_var(name) {
|
||||
return Some((name, var));
|
||||
}
|
||||
}
|
||||
if let Some(var) = self.const_vars.get(name) {
|
||||
return Some((name.clone(), LocBox::new(var.typ.loc(), var.clone())));
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
|
||||
pub enum Ast {
|
||||
Expr(expr::Expr),
|
||||
Statement(statement::Statement),
|
||||
Expr(LocBox<expr::Expr>),
|
||||
Statement(LocBox<statement::Statement>),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,44 +1,360 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::{expr::{Block, Expr}, typ::Type, Ident, TString};
|
||||
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
use std::{collections::HashMap, fmt::{Display, write}, sync::Mutex};
|
||||
|
||||
use anyhow::bail;
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
use crate::{common::loc::LocBox, lerror};
|
||||
|
||||
use super::{expr::{Block, Expr}, literal::Literal, typ::Type, Char, Ident, Number, Program, TString};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
|
||||
pub enum Statement {
|
||||
Fn {
|
||||
struct_name: Option<Ident>,
|
||||
name: Ident,
|
||||
params: Vec<(Ident, Type)>,
|
||||
ret_type: Option<Type>,
|
||||
qual_const: bool,
|
||||
qual_extern: Option<TString>, // abi
|
||||
body: Option<Block>, // If None then its a type declaration
|
||||
},
|
||||
TypeAlias {
|
||||
name: Ident,
|
||||
typ: Type,
|
||||
},
|
||||
Struct {
|
||||
name: Ident,
|
||||
fields: Vec<(Ident, Type)>,
|
||||
},
|
||||
Enum {
|
||||
name: Ident,
|
||||
fields: Vec<Ident>,
|
||||
},
|
||||
ConstVar {
|
||||
name: Ident,
|
||||
typ: Type,
|
||||
val: Expr
|
||||
},
|
||||
StaticVar {
|
||||
name: Ident,
|
||||
typ: Type,
|
||||
val: Expr,
|
||||
},
|
||||
Let {
|
||||
name: Ident,
|
||||
typ: Option<Type>,
|
||||
val: Option<Expr>,
|
||||
},
|
||||
Fn(Function),
|
||||
TypeAlias(TypeAlias),
|
||||
Struct(Struct),
|
||||
Enum(Enum),
|
||||
ConstVar(ConstVar),
|
||||
StaticVar(StaticVar),
|
||||
Include,
|
||||
Let(Let),
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
|
||||
pub struct Let {
|
||||
pub name: Ident,
|
||||
pub typ: Option<LocBox<Type>>,
|
||||
pub val: Option<LocBox<Expr>>,
|
||||
}
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
|
||||
pub struct ConstVar {
|
||||
pub name: Ident,
|
||||
pub typ: LocBox<Type>,
|
||||
pub val: LocBox<Expr>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
|
||||
pub struct StaticVar {
|
||||
pub name: Ident,
|
||||
pub typ: LocBox<Type>,
|
||||
pub val: LocBox<Expr>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
|
||||
pub struct TypeAlias {
|
||||
pub name: Ident,
|
||||
pub typ: LocBox<Type>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
|
||||
pub struct Struct {
|
||||
pub name: Ident,
|
||||
pub fields: Vec<(Ident, LocBox<Type>)>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
|
||||
pub struct Enum {
|
||||
pub name: Ident,
|
||||
pub fields: Vec<Ident>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialOrd, Hash)]
|
||||
pub struct Function {
|
||||
pub struct_name: Option<Ident>,
|
||||
pub name: Option<Ident>,
|
||||
pub params: Vec<(Ident, LocBox<Type>)>,
|
||||
pub ret_type: Option<Box<LocBox<Type>>>,
|
||||
pub qual_const: bool,
|
||||
pub qual_extern: Option<TString>, // abi
|
||||
pub body: Option<Block>, // If None then its a type declaration
|
||||
}
|
||||
|
||||
impl PartialEq for Function {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
dbg!(self.params == other.params);
|
||||
dbg!(self.ret_type == other.ret_type);
|
||||
dbg!(self.qual_extern == other.qual_extern);
|
||||
dbg!(self.qual_const == other.qual_const);
|
||||
dbg!(&self.params);
|
||||
dbg!(&self.ret_type);
|
||||
self.params == other.params &&
|
||||
self.ret_type == other.ret_type &&
|
||||
self.qual_extern == other.qual_extern &&
|
||||
self.qual_const == other.qual_const
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Function {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let mut extrn = String::new();
|
||||
let mut args = Vec::new();
|
||||
let mut ret_t = String::new();
|
||||
|
||||
if let Some(ex) = &self.qual_extern {
|
||||
if ex.val.is_empty() {
|
||||
extrn = format!("extern ");
|
||||
} else {
|
||||
extrn = format!("extern \"{}\"", ex.val);
|
||||
}
|
||||
}
|
||||
|
||||
for arg in &self.params {
|
||||
args.push(format!("{}: {}", arg.0, arg.1.inner()));
|
||||
}
|
||||
if let Some(ret) = &self.ret_type {
|
||||
ret_t = format!(" -> {}", ret.inner());
|
||||
}
|
||||
|
||||
|
||||
write!(f, "{extrn}fn {}({}){ret_t}", self.get_full_name_pretty(), args.join(", "))
|
||||
}
|
||||
}
|
||||
|
||||
impl Function {
|
||||
pub fn get_full_name(&self) -> String {
|
||||
if let Some(name) = &self.name {
|
||||
if let Some(sn) = &self.struct_name {
|
||||
format!("{sn}__S__{}", name.0)
|
||||
} else {
|
||||
format!("{}", name.0)
|
||||
}
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
pub fn get_full_name_pretty(&self) -> String {
|
||||
if let Some(name) = &self.name {
|
||||
if let Some(sn) = &self.struct_name {
|
||||
format!("{sn}::{}", name.0)
|
||||
} else {
|
||||
format!("{}", name.0)
|
||||
}
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
pub fn get_def_as_str(&self) -> String {
|
||||
let mut f = String::new();
|
||||
f.push_str(&format!("fn {} (", self.get_full_name_pretty()));
|
||||
let mut first = true;
|
||||
for (name, typ) in &self.params {
|
||||
if first {
|
||||
first = false;
|
||||
} else {
|
||||
f.push_str(", ");
|
||||
}
|
||||
f.push_str(&format!("{name}: {}", typ.inner()));
|
||||
}
|
||||
f.push_str(")");
|
||||
if let Some(ret) = &self.ret_type {
|
||||
f.push_str(&format!(" -> {}", ret.inner()));
|
||||
}
|
||||
f.push_str(";");
|
||||
f
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Struct {
|
||||
pub fn get_size(&self, program: &Program) -> anyhow::Result<usize> {
|
||||
let mut size = 0;
|
||||
for (_, v) in &self.fields {
|
||||
size += v.inner().size_of(program)?;
|
||||
}
|
||||
Ok(size)
|
||||
}
|
||||
|
||||
pub fn get_offset_of(&self, program: &Program, field: &Ident) -> anyhow::Result<usize> {
|
||||
let mut size = 0;
|
||||
for (k, v) in &self.fields {
|
||||
if k == field {
|
||||
return Ok(size);
|
||||
}
|
||||
size += v.inner().size_of(program)?;
|
||||
}
|
||||
bail!("Field not found")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
|
||||
pub enum ConstDataTyp {
|
||||
Bytes(Vec<u8>),
|
||||
AddrOfConst(Ident),
|
||||
AddrOfStatic(Ident),
|
||||
AddrOfFunc(Ident),
|
||||
Variable(Ident, usize),
|
||||
Array(Vec<ConstDataTyp>)
|
||||
}
|
||||
|
||||
lazy_static!(
|
||||
static ref CTX: Mutex<ConstantCalcCtx> = Mutex::new(ConstantCalcCtx::default());
|
||||
);
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
struct ConstantCalcCtx {
|
||||
current_array_item_size: Option<usize>,
|
||||
current_struct_items: Option<HashMap<Ident, usize>>,
|
||||
current_struct_item_name: Option<Ident>
|
||||
}
|
||||
|
||||
|
||||
pub fn get_constant_data_as_bytes(program: &Program, value: LocBox<Expr>, is_big_endian: bool, must_be_number: bool) -> anyhow::Result<ConstDataTyp> {
|
||||
match value.inner() {
|
||||
Expr::Literal(_, lit) => {
|
||||
match lit {
|
||||
Literal::Char(Char(c)) => {
|
||||
if must_be_number {
|
||||
lerror!(value.loc(), "Expected number got char");
|
||||
bail!("")
|
||||
}
|
||||
Ok(ConstDataTyp::Bytes(vec![*c as u8]))
|
||||
},
|
||||
Literal::String(TString { val, cstr }) => {
|
||||
if must_be_number {
|
||||
lerror!(value.loc(), "Expected number got string");
|
||||
bail!("")
|
||||
}
|
||||
|
||||
if *cstr {
|
||||
let mut val = val.chars().into_iter().map(|v| v as u8).collect::<Vec<u8>>();
|
||||
val.push(0);
|
||||
Ok(ConstDataTyp::Bytes(val))
|
||||
} else {
|
||||
let mut buf = Vec::new();
|
||||
let mut inc = 0;
|
||||
while inc < 8 {
|
||||
buf.push(((val.len() << 8*inc) & 0xff) as u8);
|
||||
inc += 1;
|
||||
}
|
||||
|
||||
if is_big_endian {
|
||||
buf.reverse();
|
||||
}
|
||||
let val = val.chars().into_iter().map(|v| v as u8).collect::<Vec<u8>>();
|
||||
Ok(ConstDataTyp::Array(vec![
|
||||
ConstDataTyp::Bytes(buf),
|
||||
ConstDataTyp::Bytes(val)
|
||||
]))
|
||||
}
|
||||
}
|
||||
Literal::Number(Number { val, base: _, signed: _ }) => {
|
||||
let mut buf = Vec::new();
|
||||
let mut inc = 0;
|
||||
while inc < 8 {
|
||||
buf.push(((val << 8*inc) & 0xff) as u8);
|
||||
inc += 1;
|
||||
}
|
||||
|
||||
if is_big_endian {
|
||||
buf.reverse();
|
||||
}
|
||||
|
||||
Ok(ConstDataTyp::Bytes(buf))
|
||||
}
|
||||
Literal::Array { items: arr, item_size } => {
|
||||
if must_be_number {
|
||||
lerror!(value.loc(), "Expected number got array");
|
||||
bail!("")
|
||||
}
|
||||
let mut bytes = Vec::new();
|
||||
if arr.len() < 1 {
|
||||
return Ok(ConstDataTyp::Bytes(vec![]));
|
||||
}
|
||||
CTX.lock().unwrap().current_array_item_size = *item_size;
|
||||
for item in arr {
|
||||
let data = get_constant_data_as_bytes(program, item.clone(), is_big_endian, must_be_number)?;
|
||||
bytes.push(data);
|
||||
}
|
||||
CTX.lock().unwrap().current_array_item_size = None;
|
||||
|
||||
Ok(ConstDataTyp::Array(bytes))
|
||||
}
|
||||
Literal::ArrayRepeat { val, count, item_size} => {
|
||||
if must_be_number {
|
||||
lerror!(value.loc(), "Expected number got repeating array");
|
||||
bail!("")
|
||||
}
|
||||
|
||||
CTX.lock().unwrap().current_array_item_size = *item_size;
|
||||
let val = get_constant_data_as_bytes(program, (**val).clone(), is_big_endian, must_be_number)?;
|
||||
CTX.lock().unwrap().current_array_item_size = None;
|
||||
|
||||
let mut num = Vec::new();
|
||||
let mut inc = 0;
|
||||
while inc < 8 {
|
||||
num.push(((count << 8*inc) & 0xff) as u8);
|
||||
inc += 1;
|
||||
}
|
||||
|
||||
if is_big_endian {
|
||||
num.reverse();
|
||||
}
|
||||
|
||||
let mut count = 0 as usize;
|
||||
for b in num {
|
||||
count = b as usize;
|
||||
count <<= 8;
|
||||
}
|
||||
let orig = val.clone();
|
||||
let mut arr = Vec::new();
|
||||
for _ in 0..count {
|
||||
arr.push(orig.clone());
|
||||
}
|
||||
Ok(ConstDataTyp::Array(arr))
|
||||
}
|
||||
Literal::Bool(v) => {
|
||||
Ok(ConstDataTyp::Bytes(vec![*v as u8]))
|
||||
}
|
||||
Literal::Ident(_) => unreachable!()
|
||||
}
|
||||
|
||||
}
|
||||
Expr::Struct(_, strct) => {
|
||||
let mut bytes = Vec::new();
|
||||
if must_be_number {
|
||||
lerror!(value.loc(), "Expected number got struct literal");
|
||||
bail!("")
|
||||
}
|
||||
|
||||
let mut items = HashMap::new();
|
||||
|
||||
for (name, item) in program.structs.get(&strct.path.0.first().unwrap()).unwrap().inner().fields.iter() {
|
||||
items.insert(name.clone(), item.inner().size_of(program)?);
|
||||
}
|
||||
|
||||
CTX.lock().unwrap().current_struct_items = Some(items);
|
||||
|
||||
for field in &strct.fields {
|
||||
CTX.lock().unwrap().current_struct_item_name = Some(field.0.clone());
|
||||
bytes.push(get_constant_data_as_bytes(program, field.1.clone(), is_big_endian, must_be_number)?);
|
||||
}
|
||||
CTX.lock().unwrap().current_struct_item_name = None;
|
||||
|
||||
Ok(ConstDataTyp::Array(bytes))
|
||||
}
|
||||
Expr::Path(path) => {
|
||||
let ctx = {CTX.lock().unwrap().clone()};
|
||||
let name = path.0.last().unwrap();
|
||||
if let Some(var) = program.const_vars.get(name) {
|
||||
Ok(get_constant_data_as_bytes(program, var.val.clone(), is_big_endian, must_be_number)?)
|
||||
} else if let Some(_) = program.static_vars.get(name) {
|
||||
lerror!(value.loc(), "Statics cannot be passed by value, use a reference");
|
||||
bail!("")
|
||||
} else if let Some(_) = program.functions.get(name) {
|
||||
Ok(ConstDataTyp::AddrOfFunc(name.clone()))
|
||||
} else if let Some(struct_items) = ctx.current_struct_items &&
|
||||
let Some(typ) = struct_items.get(&ctx.current_struct_item_name.unwrap()) {
|
||||
Ok(ConstDataTyp::Variable(name.clone(), *typ))
|
||||
} else if let Some(array_item_size) = {CTX.lock().unwrap().clone()}.current_array_item_size {
|
||||
Ok(ConstDataTyp::Variable(name.clone(), array_item_size))
|
||||
} else {
|
||||
lerror!(value.loc(), "Unable to find ident '{name}'");
|
||||
bail!("")
|
||||
}
|
||||
|
||||
}
|
||||
v => unreachable!("{v:?}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,368 @@
|
||||
use super::{expr::Expr, Ident, Number};
|
||||
use std::{clone, collections::HashSet, fmt::Display};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
use anyhow::bail;
|
||||
|
||||
use crate::{common::loc::LocBox, error, info, parser::ast::statement::Function, validator::predefined::{BuiltinType, get_builtin_from_name}};
|
||||
|
||||
use super::{expr::Expr, literal::Literal, Ident, Program};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
|
||||
pub enum Type {
|
||||
Ref {
|
||||
inner: Box<Type>,
|
||||
mutable: bool,
|
||||
},
|
||||
Array {
|
||||
SizedArray {
|
||||
inner: Box<Type>,
|
||||
count: LocBox<Expr>,
|
||||
},
|
||||
ArrayRepeat {
|
||||
UnsizedArray {
|
||||
inner: Box<Type>,
|
||||
count: Expr,
|
||||
},
|
||||
Owned(Ident),
|
||||
Builtin {
|
||||
name: String,
|
||||
size: u8,
|
||||
signed: bool,
|
||||
},
|
||||
FnPtr(Function)
|
||||
}
|
||||
|
||||
impl Type {
|
||||
pub fn is_void(&self) -> bool {
|
||||
*self == BuiltinType::void()
|
||||
}
|
||||
pub fn as_ref(self) -> Self {
|
||||
Self::Ref {
|
||||
inner: Box::new(self),
|
||||
mutable: false
|
||||
}
|
||||
}
|
||||
pub fn as_ref_mut(self) -> Self {
|
||||
Self::Ref {
|
||||
inner: Box::new(self),
|
||||
mutable: true
|
||||
}
|
||||
}
|
||||
pub fn new_builtin(name: &str, size: u8, signed: bool) -> Self {
|
||||
Self::Builtin { name: name.to_string(), size, signed }
|
||||
}
|
||||
pub fn should_deref_pointer(&self, program: &Program) -> bool {
|
||||
dbg!(&self);
|
||||
let mut slf = self.clone();
|
||||
match &mut slf {
|
||||
Self::Ref { inner, .. } => {
|
||||
match inner.as_mut() {
|
||||
Self::Ref { .. } => true,
|
||||
Self::Owned(ident) => {
|
||||
if program.structs.get(ident).is_some() {
|
||||
false
|
||||
} else
|
||||
if let Some(typ) = program.types.get(ident) {
|
||||
**inner = typ.inner().clone();
|
||||
slf.should_deref_pointer(program)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
_ => true,
|
||||
}
|
||||
},
|
||||
Self::FnPtr(_) |
|
||||
Self::Owned(_) |
|
||||
Self::SizedArray { .. } |
|
||||
Self::Builtin { .. } |
|
||||
Self::UnsizedArray { .. } => false,
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
pub fn get_real_value_of_alias(&self, name: &Ident, program: &Program) -> anyhow::Result<Self> {
|
||||
let mut used_types = HashSet::new();
|
||||
used_types.insert(name.clone());
|
||||
fn f(slf: &mut Type, program: &Program, used_types: &mut HashSet<Ident>) -> anyhow::Result<Type> {
|
||||
match slf {
|
||||
Type::Ref {inner, ..} => {
|
||||
f(inner, program, used_types)
|
||||
}
|
||||
Type::Owned(ident) => {
|
||||
if let Some(name) = used_types.get(ident) {
|
||||
error!("Recursive type alias: {name}");
|
||||
bail!("")
|
||||
}
|
||||
if let Some(t) = program.types.get(ident) {
|
||||
used_types.insert(ident.clone());
|
||||
let mut t = t.inner().clone();
|
||||
f(&mut t, program, used_types)
|
||||
} else {
|
||||
Ok(slf.clone())
|
||||
}
|
||||
}
|
||||
Type::SizedArray { inner, .. } |
|
||||
Type::UnsizedArray { inner } => {
|
||||
f(inner, program, used_types)
|
||||
}
|
||||
Type::FnPtr(_) |
|
||||
Type::Builtin { .. } => Ok(slf.clone()),
|
||||
}
|
||||
}
|
||||
let mut t = self.clone();
|
||||
f(&mut t, program, &mut used_types)
|
||||
}
|
||||
|
||||
pub fn get_absolute_value(&self, program: &Program) -> anyhow::Result<Self> {
|
||||
match self {
|
||||
Self::Ref { inner, .. } => inner.get_absolute_value(program),
|
||||
Self::Owned(ident) => {
|
||||
if let Some(t) = get_builtin_from_name(ident.0.as_str()) {
|
||||
return Ok(t)
|
||||
}
|
||||
if let Some(t) = program.curr_fn_args.get(ident) {
|
||||
return Ok(t.inner().clone().get_absolute_value(program)?);
|
||||
} else
|
||||
if program.types.get(ident).is_some() ||
|
||||
program.structs.get(ident).is_some() ||
|
||||
program.enums.get(ident).is_some()
|
||||
{
|
||||
return Ok(self.clone());
|
||||
}
|
||||
if let Some(var) = program.get_var(ident) {
|
||||
Ok(var.1.inner().typ.clone().expect("type should be computed if were already using it").inner().clone().get_absolute_value(program)?)
|
||||
} else {
|
||||
bail!("owo? missing value: {ident}");
|
||||
}
|
||||
}
|
||||
Self::FnPtr(_) |
|
||||
Self::SizedArray { .. } |
|
||||
Self::Builtin { .. } |
|
||||
Self::UnsizedArray { .. } => Ok(self.clone()),
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
pub fn convert_owned_to_real_type(&mut self, program: &Program) {
|
||||
match self {
|
||||
Self::Owned(ident) => {
|
||||
if let Some(t) = crate::validator::predefined::get_builtin_from_name(&ident.0) {
|
||||
*self = t;
|
||||
} else
|
||||
if let Some(t) = program.types.get(ident) {
|
||||
*self = t.inner().clone();
|
||||
} else
|
||||
if let Some(t) = program.curr_fn_args.get(ident) {
|
||||
*self = t.inner().clone();
|
||||
} else
|
||||
if let Some(var) = program.get_var(ident) {
|
||||
*self = var.1.inner().typ.clone().expect("type should be computed if were already using it").inner().clone();
|
||||
}
|
||||
}
|
||||
Self::Ref { inner, .. } |
|
||||
Self::SizedArray { inner, .. } |
|
||||
Self::UnsizedArray { inner, .. } => inner.convert_owned_to_real_type(program),
|
||||
Self::FnPtr(_) |
|
||||
Self::Builtin { .. } => (),
|
||||
}
|
||||
}
|
||||
pub fn get_variable_type(&self, program: &Program) -> anyhow::Result<Self> {
|
||||
match self {
|
||||
Self::Owned(ident) => {
|
||||
dbg!(&ident);
|
||||
dbg!(&crate::validator::predefined::get_builtin_from_name(&ident.0));
|
||||
if let Some(t) = crate::validator::predefined::get_builtin_from_name(&ident.0) {
|
||||
Ok(t)
|
||||
} else
|
||||
if let Some(t) = program.types.get(ident) {
|
||||
Ok(t.inner().clone())
|
||||
} else
|
||||
if let Some(t) = program.curr_fn_args.get(ident) {
|
||||
return Ok(t.inner().clone());
|
||||
} else
|
||||
if let Some(var) = program.get_var(ident) {
|
||||
Ok(var.1.inner().typ.clone().expect("type should be computed if were already using it").inner().clone())
|
||||
} else
|
||||
if let Some(func) = program.functions.get(ident) {
|
||||
Ok(Type::FnPtr(func.inner().clone()))
|
||||
} else {
|
||||
bail!("owo? missing value: {ident}");
|
||||
}
|
||||
}
|
||||
Self::Ref { .. } |
|
||||
Self::SizedArray { .. } |
|
||||
Self::Builtin { .. } |
|
||||
Self::FnPtr(_) |
|
||||
Self::UnsizedArray { .. } => Ok(self.clone()),
|
||||
|
||||
}
|
||||
}
|
||||
pub fn is_numeric(&self, program: &Program) -> bool {
|
||||
match self {
|
||||
Self::Ref { inner, .. } => {
|
||||
inner.is_numeric(program)
|
||||
}
|
||||
Self::Owned(name) => {
|
||||
match program.types.get(&name) {
|
||||
Some(t) => t.inner().is_numeric(program),
|
||||
_ => false
|
||||
}
|
||||
},
|
||||
Self::SizedArray { .. } => false,
|
||||
Self::FnPtr(_) |
|
||||
Self::UnsizedArray { .. } => false,
|
||||
Self::Builtin { name, .. } => {
|
||||
match name.as_str() {
|
||||
"u8" | "i8" |
|
||||
"u16" | "i16" |
|
||||
"u32" | "i32" |
|
||||
"u64" | "i64" |
|
||||
"usize" | "isize" => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Returns None if non numeric
|
||||
pub fn is_signed(&self, program: &Program) -> Option<bool> {
|
||||
if self.is_ptr(program) {
|
||||
return Some(false);
|
||||
}
|
||||
match self {
|
||||
Self::Ref { inner, .. } => {
|
||||
inner.is_signed(program)
|
||||
}
|
||||
Self::Owned(name) => {
|
||||
match program.types.get(&name) {
|
||||
Some(t) => t.inner().is_signed(program),
|
||||
_ => None
|
||||
}
|
||||
},
|
||||
Self::FnPtr(_) |
|
||||
Self::SizedArray { .. } => None,
|
||||
Self::UnsizedArray { .. } => None,
|
||||
Self::Builtin { name, .. } => {
|
||||
match name.as_str() {
|
||||
"i8" |
|
||||
"i16" |
|
||||
"i32" |
|
||||
"i64" |
|
||||
"isize" => Some(true),
|
||||
"u8" |
|
||||
"u16" |
|
||||
"u32" |
|
||||
"u64" |
|
||||
"usize" => Some(false),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_ptr(&self, program: &Program) -> bool {
|
||||
match self {
|
||||
Self::Owned(name) => {
|
||||
match program.types.get(&name) {
|
||||
Some(t) => t.inner().is_ptr(program),
|
||||
_ => false
|
||||
}
|
||||
},
|
||||
Self::Ref { .. } => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_bool(&self, program: &Program) -> bool {
|
||||
match self {
|
||||
Self::Owned(name) => {
|
||||
match program.types.get(&name) {
|
||||
Some(t) => t.inner().is_bool(program),
|
||||
_ => false
|
||||
}
|
||||
},
|
||||
Self::Builtin { name, .. } if name.as_str() == "bool" => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
pub fn size_of(&self, program: &Program) -> anyhow::Result<usize> {
|
||||
fn f(t: &Type, program: &Program, behind_a_ptr: bool) -> anyhow::Result<usize> {
|
||||
match t {
|
||||
Type::FnPtr(_) |
|
||||
Type::Ref { .. } => {
|
||||
// TODO: Use the actual ptr size
|
||||
Ok(size_of::<*const ()>())
|
||||
}
|
||||
Type::UnsizedArray { .. } => {
|
||||
if behind_a_ptr {
|
||||
Ok(size_of::<*const ()>())
|
||||
} else {
|
||||
bail!("Unsized arrays dont have a known size and must be behind a pointer");
|
||||
}
|
||||
}
|
||||
Type::SizedArray { inner, .. } => {
|
||||
Ok(inner.size_of(program)? )
|
||||
}
|
||||
Type::Builtin { size, .. } => {
|
||||
Ok(*size as usize)
|
||||
}
|
||||
Type::Owned(name) => {
|
||||
if let Some(v) = program.structs.get(&name) {
|
||||
return Ok(v.inner().get_size(program)?);
|
||||
}
|
||||
if let Some(v) = program.types.get(&name) {
|
||||
return Ok(f(v.inner(), program, behind_a_ptr)?);
|
||||
}
|
||||
if let Some(_) = program.enums.get(&name) {
|
||||
return Ok(4); // TODO: Make enum size changeable
|
||||
}
|
||||
bail!("Unknown type '{name}'")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
f(self, program, false)
|
||||
}
|
||||
pub fn size_of_allow_unsized_arrays(&self, program: &Program) -> anyhow::Result<usize> {
|
||||
match self.size_of(program) {
|
||||
Ok(v) => Ok(v),
|
||||
Err(e) => {
|
||||
if e.to_string().as_str() == "Unsized arrays dont have a known size and must be behind a pointer" {
|
||||
return Ok(0);
|
||||
}
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Type {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::FnPtr(func) => {
|
||||
write!(f, "{func}")
|
||||
}
|
||||
Self::Ref { inner, mutable } => {
|
||||
if *mutable {
|
||||
write!(f, "&mut {inner}")
|
||||
} else {
|
||||
write!(f, "&{inner}")
|
||||
}
|
||||
}
|
||||
Self::UnsizedArray { inner } => {
|
||||
write!(f, "[{inner}]")
|
||||
}
|
||||
Self::SizedArray { inner, count } => {
|
||||
match count.inner() {
|
||||
Expr::Path(p) => {
|
||||
write!(f, "[{inner}; {p}]")
|
||||
}
|
||||
Expr::Literal(_, Literal::Number(n)) => {
|
||||
write!(f, "[{inner}; {n}]")
|
||||
}
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
Self::Owned(ident) => write!(f, "{ident}"),
|
||||
Self::Builtin { name, size, signed } => {
|
||||
write!(f, "(builtin) {} {}({})", name, if *signed { "signed" } else { "unsigned" }, size)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
|
||||
use crate::{debug, lerror, parser::{typ::parse_type, Punctuation}, tokeniser::Token};
|
||||
use crate::{cli::CliArgs, common::loc::LocBox, debug, lerror, parser::{Punctuation, ast::{Program, expr::StructLit}, typ::parse_type}, tokeniser::Token};
|
||||
|
||||
use super::{ast::{expr::{Block, CallParams, Expr, IfBranchExpr, IfExpr, Path}, literal::Literal, TokenType}, parse_item, utils, Delimiter, Keyword};
|
||||
|
||||
@@ -31,20 +31,25 @@ const BINOP_LIST: &[TokenType] = &[
|
||||
TokenType::Punct(Punctuation::XorEq),
|
||||
TokenType::Punct(Punctuation::Eq),
|
||||
TokenType::Punct(Punctuation::EqEq),
|
||||
TokenType::Punct(Punctuation::Neq),
|
||||
TokenType::Punct(Punctuation::Lt),
|
||||
TokenType::Punct(Punctuation::Gt),
|
||||
TokenType::Punct(Punctuation::Le),
|
||||
TokenType::Punct(Punctuation::Ge),
|
||||
];
|
||||
|
||||
pub fn parse_expr(tokens: &mut Vec<Token>, precedence: usize, consume_semi: bool) -> Result<Option<Expr>> {
|
||||
pub fn parse_expr(tokens: &mut Vec<Token>, precedence: usize, consume_semi: bool, cli: &CliArgs, prog: &mut Program) -> Result<Option<LocBox<Expr>>> {
|
||||
let res = if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::ParenL)) {
|
||||
Some(parse_group(tokens)?)
|
||||
Some(parse_group(tokens, cli, prog)?)
|
||||
} else
|
||||
if let Some(kw) = utils::check_consume(tokens, TokenType::Keyword(Keyword::Self_)) {
|
||||
Some(LocBox::new(kw.loc(), Expr::Self_ { strct: None }))
|
||||
} else
|
||||
if let Some(_) = utils::check(tokens, TokenType::ident("")) {
|
||||
debug!("is ident");
|
||||
let p = parse_path(tokens)?;
|
||||
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::CurlyL)) {
|
||||
Some(parse_struct_literal(tokens, p.unwrap_path())?)
|
||||
Some(parse_struct_literal(tokens, p.inner().unwrap_path(), cli, prog)?)
|
||||
} else {
|
||||
Some(p)
|
||||
}
|
||||
@@ -56,61 +61,67 @@ pub fn parse_expr(tokens: &mut Vec<Token>, precedence: usize, consume_semi: bool
|
||||
TokenType::Punct(Punctuation::Ampersand),
|
||||
TokenType::Punct(Punctuation::Star),
|
||||
]) {
|
||||
Some(parse_unop(tokens)?)
|
||||
Some(parse_unop(tokens, cli, prog)?)
|
||||
} else
|
||||
if let Some(_) = utils::check_from_many(tokens, &[
|
||||
TokenType::string("", false),
|
||||
TokenType::number(0, 0, false),
|
||||
TokenType::char('\0'),
|
||||
TokenType::Delim(Delimiter::SquareL),
|
||||
TokenType::Keyword(Keyword::True),
|
||||
TokenType::Keyword(Keyword::False)
|
||||
]) {
|
||||
Some(parse_literal(tokens)?)
|
||||
Some(parse_literal(tokens, cli, prog)?)
|
||||
} else if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::While)) {
|
||||
return Ok(Some(parse_while_loop(tokens)?));
|
||||
return Ok(Some(parse_while_loop(tokens, cli, prog)?));
|
||||
} else if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::For)) {
|
||||
return Ok(Some(parse_for_loop(tokens)?));
|
||||
return Ok(Some(parse_for_loop(tokens, cli, prog)?));
|
||||
} else if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Loop)) {
|
||||
return Ok(Some(parse_inf_loop(tokens)?));
|
||||
return Ok(Some(parse_inf_loop(tokens, cli, prog)?));
|
||||
} else if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Return)) {
|
||||
return Ok(Some(parse_return(tokens)?));
|
||||
} else if let Some(_) = utils::check_consume(tokens, TokenType::Keyword(Keyword::Break)) {
|
||||
return Ok(Some(Expr::Break));
|
||||
} else if let Some(_) = utils::check_consume(tokens, TokenType::Keyword(Keyword::Continue)) {
|
||||
return Ok(Some(Expr::Continue));
|
||||
} else if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::If)) {
|
||||
return Ok(Some(Expr::If(parse_if(tokens)?)));
|
||||
return Ok(Some(parse_return(tokens, cli, prog)?));
|
||||
} else if let Some(kw) = utils::check_consume(tokens, TokenType::Keyword(Keyword::Break)) {
|
||||
let _ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "Expected ; at the end of the expression")?;
|
||||
return Ok(Some(LocBox::new(kw.loc(), Expr::Break)));
|
||||
} else if let Some(kw) = utils::check_consume(tokens, TokenType::Keyword(Keyword::Continue)) {
|
||||
let _ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "Expected ; at the end of the expression")?;
|
||||
return Ok(Some(LocBox::new(kw.loc(), Expr::Continue)));
|
||||
} else if let Some(kw) = utils::check(tokens, TokenType::Keyword(Keyword::If)) {
|
||||
return Ok(Some(LocBox::new(&kw.loc().clone(), Expr::If(parse_if(tokens, cli, prog)?))));
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
|
||||
if let Some(res) = res {
|
||||
// check for binop
|
||||
let res = match res {
|
||||
_ if utils::check(tokens, TokenType::Punct(Punctuation::Fieldaccess)).is_some() => {
|
||||
parse_field_access(tokens, res)?
|
||||
if let Some(mut res) = res {
|
||||
if utils::check(tokens, TokenType::Punct(Punctuation::Fieldaccess)).is_some() {
|
||||
res = parse_field_access(tokens, res)?;
|
||||
}
|
||||
_ if utils::check(tokens, TokenType::Punct(Punctuation::Arrow)).is_some() => {
|
||||
parse_ptr_field_access(tokens, res)?
|
||||
if utils::check(tokens, TokenType::Punct(Punctuation::Arrow)).is_some() {
|
||||
res = parse_ptr_field_access(tokens, res)?;
|
||||
}
|
||||
_ if utils::check(tokens, TokenType::Delim(Delimiter::ParenL)).is_some() => {
|
||||
parse_fn_call(tokens, res)?
|
||||
if utils::check(tokens, TokenType::Delim(Delimiter::ParenL)).is_some() {
|
||||
res = parse_fn_call(tokens, res, cli, prog)?;
|
||||
}
|
||||
_ if utils::check(tokens, TokenType::Keyword(Keyword::As)).is_some() => {
|
||||
parse_cast(tokens, res)?
|
||||
if utils::check(tokens, TokenType::Keyword(Keyword::As)).is_some() {
|
||||
res = parse_cast(tokens, res)?;
|
||||
}
|
||||
_ if utils::check(tokens, TokenType::Delim(Delimiter::SquareL)).is_some() => {
|
||||
parse_array_index(tokens, res)?
|
||||
if utils::check(tokens, TokenType::Delim(Delimiter::SquareL)).is_some() {
|
||||
res = parse_array_index(tokens, res, cli, prog)?;
|
||||
}
|
||||
_ => res
|
||||
};
|
||||
|
||||
if let Some(_) = utils::check_from_many(tokens, BINOP_LIST) {
|
||||
return Ok(Some(parse_binop(tokens, res, precedence)?));
|
||||
let v = parse_binop(tokens, res, precedence, cli, prog)?;
|
||||
if consume_semi {
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "Expected ; at the end of the expression")?;
|
||||
}
|
||||
return Ok(Some(v));
|
||||
} else {
|
||||
if consume_semi {
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "Expected ; at the end of the expression")?;
|
||||
}
|
||||
return Ok(Some(res));
|
||||
}
|
||||
|
||||
}
|
||||
if consume_semi {
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "Expected ; at the end of the expression")?;
|
||||
@@ -118,37 +129,49 @@ pub fn parse_expr(tokens: &mut Vec<Token>, precedence: usize, consume_semi: bool
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn parse_return(tokens: &mut Vec<Token>) -> Result<Expr> {
|
||||
_ = utils::check_consume(tokens, TokenType::Keyword(Keyword::Return));
|
||||
let item = parse_expr(tokens, 0, true)?;
|
||||
Ok(Expr::Return(Box::new(item)))
|
||||
fn parse_return(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Expr>> {
|
||||
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Return), "")?;
|
||||
let item = parse_expr(tokens, 0, true, cli, prog)?;
|
||||
Ok(LocBox::new(kw.loc(), Expr::Return(Box::new(item))))
|
||||
}
|
||||
|
||||
fn parse_cast(tokens: &mut Vec<Token>, left: Expr) -> Result<Expr> {
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::As), "")?;
|
||||
fn parse_cast(tokens: &mut Vec<Token>, left: LocBox<Expr>) -> Result<LocBox<Expr>> {
|
||||
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::As), "")?;
|
||||
let typ = parse_type(tokens)?;
|
||||
Ok(Expr::Cast {
|
||||
Ok(LocBox::new(kw.loc(), Expr::Cast {
|
||||
left: Box::new(left),
|
||||
right: Box::new(typ)
|
||||
})
|
||||
}))
|
||||
}
|
||||
fn parse_if(tokens: &mut Vec<Token>) -> Result<IfExpr> {
|
||||
fn parse_if(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<IfExpr> {
|
||||
let loc = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::If), "")?;
|
||||
let Some(test) = parse_expr(tokens, 0, false)? else {
|
||||
let _ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "")?;
|
||||
let Some(test) = parse_expr(tokens, 0, false, cli, prog)? else {
|
||||
lerror!(loc.loc(), "Expected test for if statement, got nothing");
|
||||
bail!("")
|
||||
};
|
||||
let block = parse_block(tokens)?;
|
||||
let _ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenR), "")?;
|
||||
let block = if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::CurlyL)) {
|
||||
if let Some(_) = utils::check_2_last(tokens, TokenType::Delim(Delimiter::CurlyR)) {
|
||||
_ = utils::check_consume(tokens, TokenType::Delim(Delimiter::CurlyR));
|
||||
Block(Vec::new())
|
||||
} else {
|
||||
parse_block(tokens, cli, prog)?
|
||||
}
|
||||
} else {
|
||||
lerror!(loc.loc(), "Expected '{{'");
|
||||
bail!("")
|
||||
};
|
||||
if let Some(_) = utils::check_consume(tokens, TokenType::Keyword(Keyword::Else)) {
|
||||
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::If)) {
|
||||
let branch = IfBranchExpr::ElseIf(Box::new(parse_if(tokens)?));
|
||||
let branch = IfBranchExpr::ElseIf(Box::new(parse_if(tokens, cli, prog)?));
|
||||
Ok(IfExpr {
|
||||
test: Box::new(test),
|
||||
body: block,
|
||||
else_if: Some(branch)
|
||||
})
|
||||
} else {
|
||||
let branch = IfBranchExpr::Else(parse_block(tokens)?);
|
||||
let branch = IfBranchExpr::Else(parse_block(tokens, cli, prog)?);
|
||||
Ok(IfExpr {
|
||||
test: Box::new(test),
|
||||
body: block,
|
||||
@@ -163,57 +186,70 @@ fn parse_if(tokens: &mut Vec<Token>) -> Result<IfExpr> {
|
||||
})
|
||||
}
|
||||
}
|
||||
fn parse_while_loop(tokens: &mut Vec<Token>) -> Result<Expr> {
|
||||
let loc = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::While), "")?;
|
||||
let Some(test) = parse_expr(tokens, 0, false)? else {
|
||||
lerror!(loc.loc(), "Expected test comparrison for while loop, got nothing");
|
||||
fn parse_while_loop(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Expr>> {
|
||||
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::While), "")?;
|
||||
let _ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "")?;
|
||||
let Some(test) = parse_expr(tokens, 0, false, cli, prog)? else {
|
||||
lerror!(kw.loc(), "Expected test comparrison for while loop, got nothing");
|
||||
bail!("")
|
||||
};
|
||||
let block = parse_block(tokens)?;
|
||||
Ok(Expr::WhileLoop {
|
||||
let _ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenR), "")?;
|
||||
let block = parse_block(tokens, cli, prog)?;
|
||||
Ok(LocBox::new(kw.loc(), Expr::WhileLoop {
|
||||
test: Box::new(test),
|
||||
body: block
|
||||
})
|
||||
}))
|
||||
}
|
||||
fn parse_for_loop(tokens: &mut Vec<Token>) -> Result<Expr> {
|
||||
let loc = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::For), "")?;
|
||||
let Some(pre) = parse_item(tokens)? else {
|
||||
lerror!(loc.loc(), "Expected init stat for a for loop, got nothing");
|
||||
fn parse_for_loop(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Expr>> {
|
||||
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::For), "")?;
|
||||
let _ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "")?;
|
||||
let Some(pre) = parse_item(tokens, cli, prog)? else {
|
||||
lerror!(kw.loc(), "Expected init stat for a for loop, got nothing");
|
||||
bail!("")
|
||||
};
|
||||
// Semicolon parsed out by parse_item above
|
||||
let Some(test) = parse_expr(tokens, 0, false, cli, prog)? else {
|
||||
lerror!(kw.loc(), "Expected test comparrison for a for loop, got nothing");
|
||||
bail!("")
|
||||
};
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "");
|
||||
let Some(test) = parse_expr(tokens, 0, false)? else {
|
||||
lerror!(loc.loc(), "Expected test comparrison for a for loop, got nothing");
|
||||
let Some(post) = parse_expr(tokens, 0, false, cli, prog)? else {
|
||||
lerror!(kw.loc(), "Expected post expression (usually an index increment) for a for loop, got nothing");
|
||||
bail!("")
|
||||
};
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "");
|
||||
let Some(post) = parse_expr(tokens, 0, false)? else {
|
||||
lerror!(loc.loc(), "Expected post expression (usually an index increment) for a for loop, got nothing");
|
||||
bail!("")
|
||||
};
|
||||
let block = parse_block(tokens)?;
|
||||
let _ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenR), "")?;
|
||||
let block = parse_block(tokens, cli, prog)?;
|
||||
|
||||
Ok(Expr::ForLoop {
|
||||
Ok(LocBox::new(kw.loc(), Expr::ForLoop {
|
||||
init: Box::new(pre),
|
||||
test: Box::new(test),
|
||||
on_loop: Box::new(post),
|
||||
body: block
|
||||
})
|
||||
}))
|
||||
}
|
||||
fn parse_inf_loop(tokens: &mut Vec<Token>) -> Result<Expr> {
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Loop), "");
|
||||
let block = parse_block(tokens)?;
|
||||
Ok(Expr::InfLoop { body: block })
|
||||
fn parse_inf_loop(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Expr>> {
|
||||
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Loop), "")?;
|
||||
let block = parse_block(tokens, cli, prog)?;
|
||||
Ok(LocBox::new(kw.loc(), Expr::InfLoop { body: block }))
|
||||
}
|
||||
fn parse_fn_call(tokens: &mut Vec<Token>, left: Expr) -> Result<Expr> {
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "");
|
||||
fn parse_fn_call(tokens: &mut Vec<Token>, left: LocBox<Expr>, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Expr>> {
|
||||
match left.inner() {
|
||||
Expr::FieldAccess { .. } |
|
||||
Expr::PtrFieldAccess { .. } => {
|
||||
return parse_member_function_call(tokens, left, cli, prog);
|
||||
}
|
||||
_ => ()
|
||||
}
|
||||
|
||||
|
||||
let start = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "")?;
|
||||
let mut params = Vec::new();
|
||||
|
||||
while !tokens.is_empty() {
|
||||
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::ParenR)) {
|
||||
break;
|
||||
}
|
||||
let Some(param) = parse_expr(tokens, 0, false)? else {break};
|
||||
let Some(param) = parse_expr(tokens, 0, false, cli, prog)? else {break};
|
||||
params.push(param);
|
||||
if let None = utils::check_consume(tokens, TokenType::Punct(Punctuation::Comma)) {
|
||||
if let None = utils::check(tokens, TokenType::Delim(Delimiter::ParenR)) {
|
||||
@@ -223,23 +259,23 @@ fn parse_fn_call(tokens: &mut Vec<Token>, left: Expr) -> Result<Expr> {
|
||||
}
|
||||
}
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenR), "");
|
||||
Ok(Expr::Call { path: Box::new(left), params: CallParams(params) })
|
||||
Ok(LocBox::new(start.loc(), Expr::Call { path: Box::new(left), params: CallParams(params) }))
|
||||
}
|
||||
fn parse_array_index(tokens: &mut Vec<Token>, left: Expr) -> Result<Expr> {
|
||||
let loc = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::SquareL), "")?;
|
||||
let Some(idx) = parse_expr(tokens, 0, false)? else {
|
||||
lerror!(loc.loc(), "Expected index for in array index but found nothing.");
|
||||
fn parse_array_index(tokens: &mut Vec<Token>, left: LocBox<Expr>, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Expr>> {
|
||||
let start = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::SquareL), "")?;
|
||||
let Some(idx) = parse_expr(tokens, 0, false, cli, prog)? else {
|
||||
lerror!(start.loc(), "Expected index for in array index but found nothing.");
|
||||
bail!("")
|
||||
};
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::SquareR), "");
|
||||
Ok(Expr::ArrayIndex {
|
||||
Ok(LocBox::new(start.loc(), Expr::ArrayIndex {
|
||||
name: Box::new(left),
|
||||
index: Box::new(idx)
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
fn parse_field_access(tokens: &mut Vec<Token>, left: Expr) -> Result<Expr> {
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Fieldaccess), "unreachable")?;
|
||||
fn parse_field_access(tokens: &mut Vec<Token>, left: LocBox<Expr>) -> Result<LocBox<Expr>> {
|
||||
let start = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Fieldaccess), "unreachable")?;
|
||||
|
||||
let right = if let Some(_) = utils::check_2_last(tokens, TokenType::Punct(Punctuation::Arrow)) {
|
||||
let right = parse_path(tokens)?;
|
||||
@@ -250,14 +286,43 @@ fn parse_field_access(tokens: &mut Vec<Token>, left: Expr) -> Result<Expr> {
|
||||
} else {
|
||||
parse_path(tokens)?
|
||||
};
|
||||
Ok(Expr::FieldAccess {
|
||||
Ok(LocBox::new(start.loc(), Expr::FieldAccess {
|
||||
left: Box::new(Some(left)),
|
||||
right: Box::new(right)
|
||||
})
|
||||
right: Box::new(right),
|
||||
offset: 0,
|
||||
l_typ: None,
|
||||
r_typ: None
|
||||
}))
|
||||
}
|
||||
|
||||
fn parse_ptr_field_access(tokens: &mut Vec<Token>, left: Expr) -> Result<Expr> {
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Arrow), "unreachable")?;
|
||||
fn parse_member_function_call(tokens: &mut Vec<Token>, left: LocBox<Expr>, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Expr>> {
|
||||
let start = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "unreachable")?;
|
||||
let mut params = Vec::new();
|
||||
|
||||
while !tokens.is_empty() {
|
||||
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::ParenR)) {
|
||||
break;
|
||||
}
|
||||
let Some(param) = parse_expr(tokens, 0, false, cli, prog)? else {break};
|
||||
params.push(param);
|
||||
if let None = utils::check_consume(tokens, TokenType::Punct(Punctuation::Comma)) {
|
||||
if let None = utils::check(tokens, TokenType::Delim(Delimiter::ParenR)) {
|
||||
lerror!(&utils::get_last_loc(), "Expected ',' or ')' but didnt find either");
|
||||
bail!("")
|
||||
}
|
||||
}
|
||||
}
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenR), "");
|
||||
|
||||
Ok(LocBox::new(start.loc(), Expr::MethodCall {
|
||||
left: Box::new(left),
|
||||
strct: None,
|
||||
params: CallParams(params)
|
||||
}))
|
||||
}
|
||||
|
||||
fn parse_ptr_field_access(tokens: &mut Vec<Token>, left: LocBox<Expr>) -> Result<LocBox<Expr>> {
|
||||
let start = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Arrow), "unreachable")?;
|
||||
let right = if let Some(_) = utils::check_2_last(tokens, TokenType::Punct(Punctuation::Arrow)) {
|
||||
let right = parse_path(tokens)?;
|
||||
parse_ptr_field_access(tokens, right)?
|
||||
@@ -267,37 +332,63 @@ fn parse_ptr_field_access(tokens: &mut Vec<Token>, left: Expr) -> Result<Expr> {
|
||||
} else {
|
||||
parse_path(tokens)?
|
||||
};
|
||||
Ok(Expr::PtrFieldAccess {
|
||||
Ok(LocBox::new(start.loc(), Expr::PtrFieldAccess {
|
||||
left: Box::new(Some(left)),
|
||||
right: Box::new(right)
|
||||
})
|
||||
right: Box::new(right),
|
||||
offset: 0,
|
||||
l_typ: None,
|
||||
r_typ: None
|
||||
}))
|
||||
}
|
||||
|
||||
fn parse_literal(tokens: &mut Vec<Token>) -> Result<Expr> {
|
||||
fn parse_literal(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Expr>> {
|
||||
if let Some(tkn) = utils::check_consume(tokens, TokenType::Keyword(Keyword::True)) {
|
||||
return Ok(LocBox::new(tkn.loc(), Expr::Literal(String::new(), Literal::Bool(true))));
|
||||
} else
|
||||
if let Some(tkn) = utils::check_consume(tokens, TokenType::Keyword(Keyword::False)) {
|
||||
return Ok(LocBox::new(tkn.loc(), Expr::Literal(String::new(), Literal::Bool(false))));
|
||||
} else
|
||||
if let Some(tkn) = utils::check_consume(tokens, TokenType::string("", false)) {
|
||||
let TokenType::String(str) = tkn.tt() else {unreachable!()};
|
||||
return Ok(Expr::Literal(Literal::String(str.clone())));
|
||||
return Ok(LocBox::new(tkn.loc(), Expr::Literal(String::new(), Literal::String(str.clone()))));
|
||||
} else
|
||||
if let Some(tkn) = utils::check_consume(tokens, TokenType::number(0, 0, false)) {
|
||||
let TokenType::Number(val) = tkn.tt() else {unreachable!()};
|
||||
return Ok(Expr::Literal(Literal::Number(val.clone())));
|
||||
return Ok(LocBox::new(tkn.loc(), Expr::Literal(String::new(), Literal::Number(val.clone()))));
|
||||
} else
|
||||
if let Some(tkn) = utils::check_consume(tokens, TokenType::char('\0')) {
|
||||
let TokenType::Char(val) = tkn.tt() else {unreachable!()};
|
||||
return Ok(Expr::Literal(Literal::Char(val.clone())));
|
||||
return Ok(LocBox::new(tkn.loc(), Expr::Literal(String::new(), Literal::Char(val.clone()))));
|
||||
} else
|
||||
if let Some(start) = utils::check_consume(tokens, TokenType::Delim(Delimiter::SquareL)) {
|
||||
if let Some(_) = utils::check_consume(tokens, TokenType::Delim(Delimiter::SquareR)) {
|
||||
return Ok(Expr::Literal(Literal::Array(Vec::new())));
|
||||
return Ok(LocBox::new(start.loc(), Expr::Literal(String::new(), Literal::Array { items: Vec::new(), item_size: None })));
|
||||
}
|
||||
if *tokens[tokens.len()-2].tt() == TokenType::Punct(Punctuation::Comma) {
|
||||
let first = parse_expr(tokens, 0, false)?;
|
||||
let Some(first) = first else { unreachable!() };
|
||||
/*if *tokens[tokens.len()-2].tt() == TokenType::Punct(Punctuation::Comma) {
|
||||
|
||||
} else */
|
||||
if *tokens[tokens.len()-2].tt() == TokenType::Punct(Punctuation::Semi) {
|
||||
let Some(typ) = parse_expr(tokens, 0, true, cli, prog)? else {
|
||||
lerror!(start.loc(), "Expected value, found nothing");
|
||||
bail!("")
|
||||
};
|
||||
let count = parse_expr(tokens, 0, false, cli, prog)?.unwrap();
|
||||
|
||||
let Expr::Literal(_, Literal::Number(count)) = count.inner() else {
|
||||
lerror!(count.loc(), "a repeating array accepts only literal numbers for count argument");
|
||||
bail!("")
|
||||
};
|
||||
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::SquareR), "")?;
|
||||
return Ok(LocBox::new(start.loc(), Expr::Literal(String::new(), Literal::ArrayRepeat {
|
||||
val: Box::new(typ),
|
||||
count: count.val,
|
||||
item_size: None,
|
||||
})));
|
||||
} else {
|
||||
|
||||
let mut values = Vec::new();
|
||||
values.push(first);
|
||||
while !tokens.is_empty() {
|
||||
let Some(val) = parse_expr(tokens, 0, false)? else{break};
|
||||
let Some(val) = parse_expr(tokens, 0, false, cli, prog)? else{break};
|
||||
|
||||
values.push(val);
|
||||
if let None = utils::check_consume(tokens, TokenType::Punct(Punctuation::Comma)) {
|
||||
@@ -305,59 +396,52 @@ fn parse_literal(tokens: &mut Vec<Token>) -> Result<Expr> {
|
||||
}
|
||||
}
|
||||
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::SquareR), "")?;
|
||||
return Ok(Expr::Literal(Literal::Array(values)));
|
||||
} else if *tokens[tokens.len()-2].tt() == TokenType::Punct(Punctuation::Semi) {
|
||||
let typ = parse_type(tokens)?;
|
||||
let count = parse_expr(tokens, 0, false)?.unwrap();
|
||||
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::SquareR), "")?;
|
||||
return Ok(Expr::Literal(Literal::ArrayRepeat {
|
||||
typ: Box::new(typ),
|
||||
count: Box::new(count)
|
||||
}));
|
||||
} else {
|
||||
return Ok(LocBox::new(start.loc(), Expr::Literal(String::new(), Literal::Array { items: values, item_size: None })));
|
||||
/*
|
||||
if let Some(curr) = tokens.last() {
|
||||
lerror!(start.loc(), "Expected a , or ; as a separator in a literal array (normal, or repeating, respectively), but found {}", curr.tt());
|
||||
} else {
|
||||
lerror!(start.loc(), "Expected a , or ; as a separator in a literal array (normal, or repeating, respectively), but found nothing");
|
||||
}
|
||||
bail!("")
|
||||
*/
|
||||
}
|
||||
}
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn parse_struct_literal(tokens: &mut Vec<Token>, name: Path) -> Result<Expr> {
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::CurlyL), "")?;
|
||||
let mut fields = HashMap::new();
|
||||
fn parse_struct_literal(tokens: &mut Vec<Token>, name: Path, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Expr>> {
|
||||
let start = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::CurlyL), "")?;
|
||||
let mut fields = BTreeMap::new();
|
||||
while !tokens.is_empty() {
|
||||
if let Some(_) = utils::check_consume(tokens, TokenType::Delim(Delimiter::CurlyR)) {
|
||||
break;
|
||||
}
|
||||
|
||||
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?;
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Colon), "")?;
|
||||
let typ = parse_expr(tokens, 0, false)?.unwrap();
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Colon), "wtf?")?;
|
||||
let typ = parse_expr(tokens, 0, false, cli, prog)?.unwrap();
|
||||
fields.insert(name.tt().unwrap_ident(), typ);
|
||||
if let None = utils::check_consume(tokens, TokenType::Punct(Punctuation::Comma)) {
|
||||
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::CurlyR), "")?;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(Expr::Struct { path: name, fields })
|
||||
Ok(LocBox::new(start.loc(), Expr::Struct(String::new(), StructLit { path: name, fields })))
|
||||
}
|
||||
|
||||
fn parse_group(tokens: &mut Vec<Token>) -> Result<Expr> {
|
||||
let loc = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "")?;
|
||||
let Some(expr) = parse_expr(tokens, 0, false)? else {
|
||||
lerror!(loc.loc(), "Expected expr found nothing");
|
||||
fn parse_group(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Expr>> {
|
||||
let start = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "")?;
|
||||
let Some(expr) = parse_expr(tokens, 0, false, cli, prog)? else {
|
||||
lerror!(start.loc(), "Expected expr found nothing");
|
||||
bail!("")
|
||||
};
|
||||
|
||||
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenR), "")?;
|
||||
Ok(Expr::Group(Box::new(expr)))
|
||||
Ok(LocBox::new(start.loc(), Expr::Group(Box::new(expr))))
|
||||
}
|
||||
|
||||
fn parse_path(tokens: &mut Vec<Token>) -> Result<Expr> {
|
||||
fn parse_path(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
|
||||
let mut buf = Vec::new();
|
||||
let part = utils::check_consume(tokens, TokenType::ident("")).unwrap();
|
||||
|
||||
@@ -370,36 +454,38 @@ fn parse_path(tokens: &mut Vec<Token>) -> Result<Expr> {
|
||||
buf.push(part.tt().unwrap_ident());
|
||||
}
|
||||
|
||||
Ok(Expr::Path(Path(buf)))
|
||||
Ok(LocBox::new(part.loc(), Expr::Path(Path(buf))))
|
||||
}
|
||||
|
||||
fn parse_unop(tokens: &mut Vec<Token>) -> Result<Expr> {
|
||||
fn parse_unop(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Expr>> {
|
||||
let typ = utils::check_consume_or_err_from_many(tokens, &[
|
||||
TokenType::Punct(Punctuation::Not),
|
||||
TokenType::Punct(Punctuation::Plus),
|
||||
TokenType::Punct(Punctuation::Minus),
|
||||
TokenType::Punct(Punctuation::Plus), // Make number positive
|
||||
TokenType::Punct(Punctuation::Minus), // make number negative
|
||||
TokenType::Punct(Punctuation::Ampersand),
|
||||
TokenType::Punct(Punctuation::Star),
|
||||
], "")?;
|
||||
let loc = typ.loc().clone();
|
||||
let TokenType::Punct(typ) = typ.tt().clone() else {unreachable!()};
|
||||
|
||||
let Some(right) = parse_expr(tokens, 5, false)? else {
|
||||
let Some(right) = parse_expr(tokens, 5, false, cli, prog)? else {
|
||||
lerror!(&loc, "Expected expression after unary token, found nothing");
|
||||
bail!("")
|
||||
};
|
||||
Ok(Expr::UnOp {
|
||||
Ok(LocBox::new(&loc, Expr::UnOp {
|
||||
typ,
|
||||
right: Box::new(right)
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
fn parse_binop(tokens: &mut Vec<Token>, mut lhs: Expr, precedence: usize) -> Result<Expr> {
|
||||
fn parse_binop(tokens: &mut Vec<Token>, mut lhs: LocBox<Expr>, precedence: usize, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Expr>> {
|
||||
// TODO: https://en.wikipedia.org/wiki/Operator-precedence_parser#Pseudocode
|
||||
|
||||
loop {
|
||||
let op = match tokens.last() {
|
||||
let op_loc;
|
||||
let mut op = match tokens.last() {
|
||||
Some(op) if BINOP_LIST.contains(&op.tt()) => {
|
||||
op_loc = op.loc().clone();
|
||||
let TokenType::Punct(op) = op.tt() else {unreachable!()};
|
||||
op.clone()
|
||||
}
|
||||
@@ -416,19 +502,35 @@ fn parse_binop(tokens: &mut Vec<Token>, mut lhs: Expr, precedence: usize) -> Res
|
||||
Some(_) |
|
||||
None => break,
|
||||
};
|
||||
|
||||
_ = tokens.pop();
|
||||
|
||||
// Fix for parsing multiple refs in types and references being parsed as && (logical and)
|
||||
if op == Punctuation::Ampersand {
|
||||
if let Some(tok) = tokens.last() {
|
||||
match tok.tt() {
|
||||
TokenType::Punct(Punctuation::Ampersand) => {
|
||||
_ = tokens.pop();
|
||||
op = Punctuation::AndAnd;
|
||||
}
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug!("OP: {op:?}");
|
||||
let (lp, rp) = op.precedence().unwrap();
|
||||
if lp < precedence {
|
||||
break
|
||||
}
|
||||
|
||||
_ = tokens.pop();
|
||||
let Some(rhs) = parse_expr(tokens, rp, false)? else {break;};
|
||||
lhs = Expr::BinOp {
|
||||
let Some(rhs) = parse_expr(tokens, rp, false, cli, prog)? else {break;};
|
||||
lhs = LocBox::new(&op_loc, Expr::BinOp {
|
||||
typ: op,
|
||||
left: Box::new(lhs),
|
||||
right: Box::new(rhs)
|
||||
};
|
||||
right: Box::new(rhs),
|
||||
signed: false
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -436,16 +538,16 @@ fn parse_binop(tokens: &mut Vec<Token>, mut lhs: Expr, precedence: usize) -> Res
|
||||
|
||||
}
|
||||
|
||||
pub fn parse_block(tokens: &mut Vec<Token>) -> Result<Block> {
|
||||
pub fn parse_block(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<Block> {
|
||||
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::CurlyL), "")?;
|
||||
let mut items = Vec::new();
|
||||
while !tokens.is_empty() {
|
||||
if let Some(item) = parse_item(tokens)? {
|
||||
items.push(item);
|
||||
} else {
|
||||
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::CurlyR)) {
|
||||
break;
|
||||
}
|
||||
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::CurlyR)) {
|
||||
if let Some(item) = parse_item(tokens, cli, prog)? {
|
||||
items.push(item);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,43 +2,51 @@ use std::collections::HashMap;
|
||||
|
||||
use ast::{expr::Block, Ast, Program};
|
||||
|
||||
use crate::tokeniser::{Token, tokentype::*};
|
||||
use crate::{cli::CliArgs, tokeniser::{Token, tokentype::*}};
|
||||
|
||||
pub mod ast;
|
||||
mod expr;
|
||||
mod stat;
|
||||
mod utils;
|
||||
mod typ;
|
||||
pub mod expr;
|
||||
pub mod stat;
|
||||
pub mod utils;
|
||||
pub mod typ;
|
||||
|
||||
type Result<T> = anyhow::Result<T>;
|
||||
|
||||
pub fn parse_program(mut tokens: Vec<Token>) -> Result<Program> {
|
||||
pub fn parse_program(mut tokens: Vec<Token>, cli: &CliArgs) -> Result<Program> {
|
||||
let mut prog_body = Vec::new();
|
||||
|
||||
let mut prog = Program {
|
||||
ast: Block(prog_body.clone()),
|
||||
enums: HashMap::new(),
|
||||
functions: HashMap::new(),
|
||||
member_functions: HashMap::new(),
|
||||
types: HashMap::new(),
|
||||
structs: HashMap::new(),
|
||||
static_vars: HashMap::new(),
|
||||
const_vars: HashMap::new(),
|
||||
literals: HashMap::new(),
|
||||
struct_literals: HashMap::new(),
|
||||
scope: None,
|
||||
curr_fn_args: HashMap::new(),
|
||||
curr_struct: None,
|
||||
included_files: Vec::new()
|
||||
};
|
||||
while !tokens.is_empty() {
|
||||
if let Some(item) = parse_item(&mut tokens)? {
|
||||
if let Some(item) = parse_item(&mut tokens, cli, &mut prog)? {
|
||||
prog_body.push(item);
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Ok(Program {
|
||||
ast: Block(prog_body),
|
||||
enums: HashMap::new(),
|
||||
functions: HashMap::new(),
|
||||
member_functions: HashMap::new(),
|
||||
types: HashMap::new(),
|
||||
structs: HashMap::new()
|
||||
})
|
||||
prog.ast = Block(prog_body);
|
||||
Ok(prog)
|
||||
}
|
||||
|
||||
fn parse_item(tokens: &mut Vec<Token>) -> Result<Option<Ast>> {
|
||||
if let Some(stat) = stat::parse_statement(tokens)? {
|
||||
fn parse_item(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<Option<Ast>> {
|
||||
if let Some(stat) = stat::parse_statement(tokens, cli, prog)? {
|
||||
return Ok(Some(Ast::Statement(stat)));
|
||||
}
|
||||
if let Some(expr) = expr::parse_expr(tokens, 0, true)? {
|
||||
if let Some(expr) = expr::parse_expr(tokens, 0, true, cli, prog)? {
|
||||
return Ok(Some(Ast::Expr(expr)));
|
||||
}
|
||||
Ok(None)
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
use anyhow::bail;
|
||||
|
||||
use crate::lerror;
|
||||
use crate::parser::ast::TokenType;
|
||||
use crate::cli::CliArgs;
|
||||
use crate::common::loc::LocBox;
|
||||
use crate::{error, lerror};
|
||||
use crate::parser::ast::{Program, TString, TokenType};
|
||||
use crate::parser::ast::statement::Let;
|
||||
use crate::parser::expr::parse_expr;
|
||||
use crate::parser::{Delimiter, Ident, Keyword, Punctuation};
|
||||
use crate::tokeniser::Token;
|
||||
@@ -9,22 +12,22 @@ use super::ast::typ::Type;
|
||||
use super::expr::parse_block;
|
||||
use super::typ::parse_type;
|
||||
use super::utils;
|
||||
use super::ast::statement::Statement;
|
||||
use super::ast::statement::{ConstVar, Enum, Function, Statement, StaticVar, Struct, TypeAlias};
|
||||
|
||||
type Result<T> = anyhow::Result<T>;
|
||||
|
||||
pub fn parse_statement(tokens: &mut Vec<Token>) -> Result<Option<Statement>> {
|
||||
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Fn)) {
|
||||
Ok(Some(parse_fn(tokens)?))
|
||||
pub fn parse_statement(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<Option<LocBox<Statement>>> {
|
||||
if let Some(_) = utils::check_from_many(tokens, &[TokenType::Keyword(Keyword::Fn), TokenType::Keyword(Keyword::Extern)]) {
|
||||
Ok(Some(parse_fn(tokens, cli, prog)?))
|
||||
} else
|
||||
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Type)) {
|
||||
Ok(Some(parse_type_alias(tokens)?))
|
||||
} else
|
||||
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Const)) {
|
||||
Ok(Some(parse_constant(tokens)?))
|
||||
Ok(Some(parse_constant(tokens, cli, prog)?))
|
||||
} else
|
||||
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Static)) {
|
||||
Ok(Some(parse_static(tokens)?))
|
||||
Ok(Some(parse_static(tokens, cli, prog)?))
|
||||
} else
|
||||
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Struct)) {
|
||||
Ok(Some(parse_struct(tokens)?))
|
||||
@@ -33,14 +36,51 @@ pub fn parse_statement(tokens: &mut Vec<Token>) -> Result<Option<Statement>> {
|
||||
Ok(Some(parse_enum(tokens)?))
|
||||
} else
|
||||
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Let)) {
|
||||
Ok(Some(parse_let(tokens)?))
|
||||
Ok(Some(parse_let(tokens, cli, prog)?))
|
||||
} else
|
||||
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Include)) {
|
||||
Ok(Some(parse_include(tokens, cli, prog)?))
|
||||
} else {
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_enum(tokens: &mut Vec<Token>) -> Result<Statement> {
|
||||
_ = utils::check_consume(tokens, TokenType::Keyword(Keyword::Enum));
|
||||
pub fn parse_include(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Statement>> {
|
||||
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Include), "")?;
|
||||
let TokenType::String(include_path) = utils::check_consume_or_err(tokens, TokenType::String(TString::default()), "")?.tt().clone() else {panic!()};
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "")?;
|
||||
let cwd = std::env::current_dir()?;
|
||||
let mut found = false;
|
||||
for p in &cli.include_paths {
|
||||
let path = cwd.join(p);
|
||||
let path = path.join(&include_path.val);
|
||||
if path.exists() {
|
||||
// This is the first path that the include found the file, so if its already included,
|
||||
// skip it. Because the context is per file specified in the cli (not includes), that
|
||||
// means weird includes (probably) wont clash
|
||||
if prog.included_files.contains(&path.to_string_lossy().to_string()) {
|
||||
break;
|
||||
}
|
||||
prog.included_files.push(path.to_string_lossy().to_string().clone());
|
||||
let data = std::fs::read_to_string(&path)?;
|
||||
let mut tokens_imp = crate::tokeniser::tokenise(&data, &path.to_string_lossy().to_string())?;
|
||||
tokens.append(&mut tokens_imp);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
lerror!(kw.loc(), "Could not find file {:?}", include_path.val);
|
||||
bail!("")
|
||||
}
|
||||
|
||||
Ok(LocBox::new(kw.loc(), Statement::Include))
|
||||
}
|
||||
|
||||
pub fn parse_enum(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
|
||||
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Enum), "")?;
|
||||
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
|
||||
_ = utils::check_consume(tokens, TokenType::Delim(Delimiter::CurlyL));
|
||||
let mut fields = Vec::new();
|
||||
@@ -60,11 +100,11 @@ fn parse_enum(tokens: &mut Vec<Token>) -> Result<Statement> {
|
||||
fields.push(field_name);
|
||||
}
|
||||
|
||||
Ok(Statement::Enum { name, fields })
|
||||
Ok(LocBox::new(kw.loc(), Statement::Enum(Enum { name, fields })))
|
||||
}
|
||||
|
||||
fn parse_struct(tokens: &mut Vec<Token>) -> Result<Statement> {
|
||||
_ = utils::check_consume(tokens, TokenType::Keyword(Keyword::Struct));
|
||||
pub fn parse_struct(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
|
||||
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Struct), "")?;
|
||||
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
|
||||
_ = utils::check_consume(tokens, TokenType::Delim(Delimiter::CurlyL));
|
||||
let mut fields = Vec::new();
|
||||
@@ -86,26 +126,26 @@ fn parse_struct(tokens: &mut Vec<Token>) -> Result<Statement> {
|
||||
fields.push((field_name, typ));
|
||||
}
|
||||
|
||||
Ok(Statement::Struct { name, fields })
|
||||
Ok(LocBox::new(kw.loc(), Statement::Struct(Struct { name, fields })))
|
||||
}
|
||||
|
||||
fn parse_static(tokens: &mut Vec<Token>) -> Result<Statement> {
|
||||
_ = utils::check_consume(tokens, TokenType::Keyword(Keyword::Static));
|
||||
pub fn parse_static(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Statement>> {
|
||||
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Static), "")?;
|
||||
|
||||
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Colon), "")?;
|
||||
let typ = parse_type(tokens)?;
|
||||
let eq = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Eq), "")?;
|
||||
let Some(val) = parse_expr(tokens, 0, false)? else {
|
||||
let Some(val) = parse_expr(tokens, 0, false, cli, prog)? else {
|
||||
lerror!(eq.loc(), "Expected expression found nothing");
|
||||
bail!("")
|
||||
};
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "")?;
|
||||
Ok(Statement::StaticVar { name, typ, val })
|
||||
Ok(LocBox::new(kw.loc(), Statement::StaticVar(StaticVar { name, typ, val })))
|
||||
}
|
||||
|
||||
fn parse_let(tokens: &mut Vec<Token>) -> Result<Statement> {
|
||||
_ = utils::check_consume(tokens, TokenType::Keyword(Keyword::Let));
|
||||
pub fn parse_let(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Statement>> {
|
||||
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Let), "")?;
|
||||
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
|
||||
let mut typ = None;
|
||||
let mut val = None;
|
||||
@@ -113,17 +153,17 @@ fn parse_let(tokens: &mut Vec<Token>) -> Result<Statement> {
|
||||
typ = Some(parse_type(tokens)?);
|
||||
}
|
||||
if let Some(eq) = utils::check_consume(tokens, TokenType::Punct(Punctuation::Eq)) {
|
||||
let Some(_val) = parse_expr(tokens, 0, false)? else {
|
||||
let Some(_val) = parse_expr(tokens, 0, false, cli, prog)? else {
|
||||
lerror!(eq.loc(), "Expected expression found nothing");
|
||||
bail!("")
|
||||
};
|
||||
val = Some(_val);
|
||||
}
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "")?;
|
||||
Ok(Statement::Let { name, typ, val })
|
||||
Ok(LocBox::new(kw.loc(), Statement::Let(Let{ name, typ, val })))
|
||||
}
|
||||
fn parse_constant(tokens: &mut Vec<Token>) -> Result<Statement> {
|
||||
_ = utils::check_consume(tokens, TokenType::Keyword(Keyword::Const));
|
||||
pub fn parse_constant(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Statement>> {
|
||||
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Const), "")?;
|
||||
|
||||
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Fn)) {
|
||||
unimplemented!()
|
||||
@@ -132,28 +172,39 @@ fn parse_constant(tokens: &mut Vec<Token>) -> Result<Statement> {
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Colon), "")?;
|
||||
let typ = parse_type(tokens)?;
|
||||
let eq = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Eq), "")?;
|
||||
let Some(val) = parse_expr(tokens, 0, false)? else {
|
||||
let Some(val) = parse_expr(tokens, 0, false, cli, prog)? else {
|
||||
lerror!(eq.loc(), "Expected expression found nothing");
|
||||
bail!("")
|
||||
};
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "")?;
|
||||
Ok(Statement::ConstVar { name, typ, val })
|
||||
Ok(LocBox::new(kw.loc(), Statement::ConstVar(ConstVar { name, typ, val })))
|
||||
}
|
||||
|
||||
fn parse_type_alias(tokens: &mut Vec<Token>) -> Result<Statement> {
|
||||
_ = utils::check_consume(tokens, TokenType::Keyword(Keyword::Type));
|
||||
pub fn parse_type_alias(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
|
||||
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Type), "")?;
|
||||
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Eq), "")?;
|
||||
let typ = parse_type(tokens)?;
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "")?;
|
||||
|
||||
Ok(Statement::TypeAlias { name, typ })
|
||||
Ok(LocBox::new(kw.loc(), Statement::TypeAlias(TypeAlias { name, typ })))
|
||||
}
|
||||
|
||||
fn parse_fn(tokens: &mut Vec<Token>) -> Result<Statement> {
|
||||
// Just remove the kw since we checked it before
|
||||
_ = utils::check_consume(tokens, TokenType::Keyword(Keyword::Fn));
|
||||
pub fn parse_fn(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Statement>> {
|
||||
error!("fnc");
|
||||
let mut qual_extern = None;
|
||||
if let Some(_) = utils::check_consume(tokens, TokenType::Keyword(Keyword::Extern)) {
|
||||
if let Some(tok) = utils::check_consume(tokens, TokenType::string("", false)) {
|
||||
if let TokenType::String(str) = tok.tt() {
|
||||
qual_extern = Some(str.clone());
|
||||
}
|
||||
} else {
|
||||
qual_extern = Some(TString::default());
|
||||
}
|
||||
}
|
||||
|
||||
// Just remove the kw since we checked it before
|
||||
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Fn), "")?;
|
||||
let mut struct_name = None;
|
||||
let mut name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
|
||||
// Check if this is a struct method
|
||||
@@ -161,40 +212,66 @@ fn parse_fn(tokens: &mut Vec<Token>) -> Result<Statement> {
|
||||
struct_name = Some(name);
|
||||
name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
|
||||
}
|
||||
let params = parse_fn_params(tokens)?;
|
||||
let mut params_partial = parse_fn_params(tokens)?;
|
||||
|
||||
// Check for return type cause it optional
|
||||
let mut ret_type = None;
|
||||
if let Some(_) = utils::check_consume(tokens, TokenType::Punct(Punctuation::Arrow)) {
|
||||
ret_type = Some(parse_type(tokens)?);
|
||||
ret_type = Some(Box::new(parse_type(tokens)?));
|
||||
}
|
||||
let body;
|
||||
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::CurlyL)) {
|
||||
body = Some(parse_block(tokens)?);
|
||||
body = Some(parse_block(tokens, cli, prog)?);
|
||||
} else {
|
||||
// Check if its just a declaration
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "")?;
|
||||
body = None;
|
||||
}
|
||||
Ok(Statement::Fn {
|
||||
let mut params = Vec::new();
|
||||
if let Some(name) = &struct_name {
|
||||
match params_partial.0 {
|
||||
1 => params.push((Ident(String::from("self")), LocBox::new(kw.loc(), Type::Owned(name.clone()).as_ref()))),
|
||||
2 => params.push((Ident(String::from("self")), LocBox::new(kw.loc(), Type::Owned(name.clone()).as_ref_mut()))),
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
|
||||
params.append(&mut params_partial.1);
|
||||
Ok(LocBox::new(kw.loc(), Statement::Fn(Function{
|
||||
struct_name,
|
||||
name,
|
||||
name: Some(name),
|
||||
params,
|
||||
ret_type,
|
||||
qual_const: false,
|
||||
qual_extern: None,
|
||||
qual_extern,
|
||||
body,
|
||||
})
|
||||
})))
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn parse_fn_params(tokens: &mut Vec<Token>) -> Result<Vec<(Ident, Type)>> {
|
||||
// usize is: 0 = no self, static; 1 = self, ref; 2 = self, mut ref
|
||||
pub fn parse_fn_params(tokens: &mut Vec<Token>) -> Result<(usize, Vec<(Ident, LocBox<Type>)>)> {
|
||||
let mut args = Vec::new();
|
||||
|
||||
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "")?;
|
||||
|
||||
let mut dis = 0;
|
||||
|
||||
if let Some(_) = utils::check_consume(tokens, TokenType::Punct(Punctuation::Ampersand)) {
|
||||
if let Some(_) = utils::check_consume(tokens, TokenType::Keyword(Keyword::Mut)) {
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Self_), "Expected 'this' keyword after &mut")?;
|
||||
dis = 1
|
||||
} else {
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Self_), "Expected 'this' keyword after &")?;
|
||||
dis = 2
|
||||
}
|
||||
}
|
||||
while !tokens.is_empty() {
|
||||
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::ParenR)) {
|
||||
break;
|
||||
}
|
||||
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?;
|
||||
utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Colon), "")?;
|
||||
utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Colon), "huhhhhhhhh")?;
|
||||
//dbg!(&name);
|
||||
let typ = parse_type(tokens)?;
|
||||
args.push((name.tt().unwrap_ident(), typ));
|
||||
@@ -205,7 +282,7 @@ fn parse_fn_params(tokens: &mut Vec<Token>) -> Result<Vec<(Ident, Type)>> {
|
||||
}
|
||||
|
||||
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenR), "")?;
|
||||
Ok(args)
|
||||
Ok((dis, args))
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
use anyhow::Result;
|
||||
use anyhow::{Result, bail};
|
||||
|
||||
use crate::tokeniser::Token;
|
||||
use crate::{cli::CliArgs, common::loc::LocBox, lerror, parser::{Delimiter, ast::{Ident, Program, expr::Expr, literal::Literal, statement::{Function, Statement}}, stat::parse_fn_params}, tokeniser::Token, validator::predefined::get_builtin_from_name};
|
||||
|
||||
use super::{ast::{typ::Type, TokenType}, expr::parse_expr, utils, Keyword, Punctuation};
|
||||
|
||||
pub fn parse_type(tokens: &mut Vec<Token>) -> Result<Type> {
|
||||
pub fn parse_type(tokens: &mut Vec<Token>) -> Result<LocBox<Type>> {
|
||||
let mut ref_cnt = Vec::new();
|
||||
let mut loc = None;
|
||||
while let Some(tok) = utils::check_consume(tokens, TokenType::Punct(Punctuation::Ampersand)) {
|
||||
if let None = loc {
|
||||
loc = Some(tok.loc().clone());
|
||||
}
|
||||
if let Some(tok) = utils::check_consume(tokens, TokenType::Keyword(Keyword::Mut)) {
|
||||
ref_cnt.push(tok.clone());
|
||||
} else {
|
||||
@@ -15,23 +19,48 @@ pub fn parse_type(tokens: &mut Vec<Token>) -> Result<Type> {
|
||||
}
|
||||
|
||||
let mut typ;
|
||||
if let Some(_) = utils::check(tokens, TokenType::Delim(super::Delimiter::SquareL)) {
|
||||
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Fn)) {
|
||||
let decl = parse_fn(tokens)?;
|
||||
loc = Some(decl.loc().clone());
|
||||
typ = Type::FnPtr(decl.inner().clone());
|
||||
} else
|
||||
if let Some(start) = utils::check_consume(tokens, TokenType::Delim(super::Delimiter::SquareL)) {
|
||||
if let None = loc {
|
||||
loc = Some(start.loc().clone());
|
||||
}
|
||||
let itm_typ = parse_type(tokens)?;
|
||||
if let Some(_) = utils::check_consume(tokens, TokenType::Punct(Punctuation::Semi)) {
|
||||
let count = parse_expr(tokens, 0, false)?.unwrap();
|
||||
typ = Type::ArrayRepeat {
|
||||
inner: Box::new(itm_typ),
|
||||
count
|
||||
let count = parse_expr(tokens, 0, false, &CliArgs::default(), &mut Program::default())?.unwrap();
|
||||
|
||||
match count.inner() {
|
||||
Expr::Literal(_, Literal::Number(_)) |
|
||||
Expr::Path(_) => (),
|
||||
_ => {
|
||||
lerror!(count.loc(), "Only literal numbers are allowed in sized arrays");
|
||||
bail!("")
|
||||
}
|
||||
}
|
||||
typ = Type::SizedArray {
|
||||
inner: Box::new(itm_typ.inner().clone()),
|
||||
count: count
|
||||
};
|
||||
} else {
|
||||
typ = Type::Array {
|
||||
inner: Box::new(itm_typ),
|
||||
}
|
||||
typ = Type::UnsizedArray {
|
||||
inner: Box::new(itm_typ.inner().clone()),
|
||||
};
|
||||
}
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::SquareR), "")?;
|
||||
} else {
|
||||
let ident = utils::check_consume_or_err(tokens, TokenType::ident(""), "a")?;
|
||||
if let Some(t) = get_builtin_from_name(&ident.tt().unwrap_ident().0) {
|
||||
typ = t;
|
||||
} else {
|
||||
typ = Type::Owned(ident.tt().unwrap_ident());
|
||||
}
|
||||
if let None = loc {
|
||||
loc = Some(ident.loc().clone());
|
||||
}
|
||||
}
|
||||
while let Some(reft) = ref_cnt.pop() {
|
||||
match reft.tt() {
|
||||
TokenType::Keyword(Keyword::Mut) => {
|
||||
@@ -49,5 +78,34 @@ pub fn parse_type(tokens: &mut Vec<Token>) -> Result<Type> {
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
Ok(typ)
|
||||
Ok(LocBox::new(&loc.unwrap(), typ))
|
||||
}
|
||||
|
||||
pub fn parse_fn(tokens: &mut Vec<Token>) -> Result<LocBox<Function>> {
|
||||
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Fn), "")?;
|
||||
|
||||
// disable member functions for types (for now)
|
||||
// let mut struct_name = None;
|
||||
// let mut name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
|
||||
// Check if this is a struct method
|
||||
// if let Some(_) = utils::check_consume(tokens, TokenType::Punct(Punctuation::Fieldaccess)) {
|
||||
// struct_name = Some(name);
|
||||
// name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
|
||||
// }
|
||||
let params = parse_fn_params(tokens)?;
|
||||
|
||||
// Check for return type cause it optional
|
||||
let mut ret_type = None;
|
||||
if let Some(_) = utils::check_consume(tokens, TokenType::Punct(Punctuation::Arrow)) {
|
||||
ret_type = Some(Box::new(parse_type(tokens)?));
|
||||
}
|
||||
Ok(LocBox::new(kw.loc(),Function{
|
||||
struct_name: None,
|
||||
name: None,
|
||||
params: params.1,
|
||||
ret_type,
|
||||
qual_const: false,
|
||||
qual_extern: None,
|
||||
body: None,
|
||||
}))
|
||||
}
|
||||
|
||||
78
src/targets/mod.rs
Normal file
78
src/targets/mod.rs
Normal file
@@ -0,0 +1,78 @@
|
||||
use std::{fs::File, path::{Path, PathBuf}};
|
||||
use crate::{cli::CliArgs, parser::ast::Program};
|
||||
|
||||
pub mod x86_64;
|
||||
//mod none;
|
||||
|
||||
pub fn get_default_target() -> String {
|
||||
// x86_64::cgen::linux::CGen::get_target_triple().to_string()
|
||||
x86_64::asmgen::linux::AsmGen::get_target_triple().to_string()
|
||||
}
|
||||
|
||||
pub fn get_all_targets() -> Vec<&'static str> {
|
||||
vec![
|
||||
x86_64::asmgen::linux::AsmGen::get_target_triple(),
|
||||
// x86_64::cgen::linux::CGen::get_target_triple(),
|
||||
// x86_64::llvmgen::linux::LlvmGen::get_target_triple(),
|
||||
//none::luagen::cct::LuaGen::get_target_triple(),
|
||||
// x86_64::cgen::linux::CGen::get_target_triple(),
|
||||
]
|
||||
}
|
||||
|
||||
fn get_target_from_triple(triple: &str) -> Box<dyn Target> {
|
||||
match triple {
|
||||
_ if triple == x86_64::asmgen::linux::AsmGen::get_target_triple() => {
|
||||
Box::new(x86_64::asmgen::linux::AsmGen::new())
|
||||
}
|
||||
// _ if triple == x86_64::cgen::linux::CGen::get_target_triple() => {
|
||||
// Box::new(x86_64::cgen::linux::CGen::new())
|
||||
// }
|
||||
// _ if triple == x86_64::qbegen::linux::QbeGen::get_target_triple() => {
|
||||
// Box::new(x86_64::qbegen::linux::QbeGen::new())
|
||||
// }
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compile(cli: &CliArgs, programs: Vec<(PathBuf, Program)>) -> anyhow::Result<()> {
|
||||
let mut target = get_target_from_triple(&cli.target);
|
||||
let build_dir = PathBuf::from("./");
|
||||
let mut objs = Vec::new();
|
||||
for (fp, program) in programs {
|
||||
let int_p = build_dir.join(&fp).with_extension(target.get_int_ext());
|
||||
let obj_p = build_dir.join(&fp).with_extension("o");
|
||||
|
||||
if !int_p.exists() {
|
||||
std::fs::File::create(&int_p)?;
|
||||
}
|
||||
|
||||
let mut f = std::fs::File::options()
|
||||
.write(true)
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.open(&int_p)?;
|
||||
target.write_code(&program, &mut f)?;
|
||||
target.compile(&int_p, &obj_p)?;
|
||||
objs.push(obj_p);
|
||||
}
|
||||
let out = Path::new(&cli.output);
|
||||
|
||||
let mut args = Vec::new();
|
||||
args.append(&mut cli.linker_args.clone());
|
||||
target.link(objs, &out, &args)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub trait Target {
|
||||
fn new() -> Self where Self: Sized;
|
||||
/// Get extension for intermediate file (aka 'asm' or 'c')
|
||||
fn get_int_ext(&self) -> &'static str {
|
||||
""
|
||||
}
|
||||
fn get_target_triple() -> &'static str where Self: Sized {
|
||||
""
|
||||
}
|
||||
fn write_code(&mut self, program: &Program, f: &mut File) -> anyhow::Result<()>;
|
||||
fn compile(&mut self, from: &Path, to: &Path) -> anyhow::Result<()>;
|
||||
fn link(&mut self, from: Vec<PathBuf>, to: &Path, extra_args: &Vec<String>) -> anyhow::Result<()>;
|
||||
}
|
||||
26
src/targets/none/luagen/cct/mod.rs
Normal file
26
src/targets/none/luagen/cct/mod.rs
Normal file
@@ -0,0 +1,26 @@
|
||||
use std::fs::File;
|
||||
|
||||
use crate::{targets::Target, validator::predefined::TypeType};
|
||||
|
||||
pub struct LuaGen;
|
||||
|
||||
impl Target for LuaGen {
|
||||
fn new() -> Self where Self: Sized {
|
||||
Self
|
||||
}
|
||||
fn get_target_triple() -> &'static str {
|
||||
"none-luagen-cct"
|
||||
}
|
||||
fn get_int_ext(&self) -> &'static str {
|
||||
"lua"
|
||||
}
|
||||
fn write_code(&mut self, program: &crate::parser::ast::Program, f: &mut File) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn compile(&mut self, _from: &std::path::Path, _to: &std::path::Path) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn link(&mut self, _from: Vec<std::path::PathBuf>, _to: &std::path::Path) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
1
src/targets/none/luagen/mod.rs
Normal file
1
src/targets/none/luagen/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod cct;
|
||||
1
src/targets/none/mod.rs
Normal file
1
src/targets/none/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod luagen;
|
||||
783
src/targets/x86_64/asmgen/linux/mod.rs
Normal file
783
src/targets/x86_64/asmgen/linux/mod.rs
Normal file
@@ -0,0 +1,783 @@
|
||||
use crate::{common::{Loc, loc::LocBox}, parser::ast::{Ast, Ident, Program, Punctuation, expr::{Expr, IfBranchExpr, Path}, literal::Literal, statement::{ConstDataTyp, Function, Statement, get_constant_data_as_bytes}, typ::Type}, targets::Target};
|
||||
use std::{collections::HashMap, fs::File, io::Write, process};
|
||||
pub struct AsmGen;
|
||||
|
||||
const RUNTIME_CODE: &'static str = include_str!("./runtime.s");
|
||||
|
||||
impl Target for AsmGen {
|
||||
fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
fn get_target_triple() -> &'static str {
|
||||
"x86_64-asmgen-linux"
|
||||
}
|
||||
fn get_int_ext(&self) -> &'static str {
|
||||
"s"
|
||||
}
|
||||
fn write_code(&mut self, program: &crate::parser::ast::Program, f: &mut File) -> anyhow::Result<()> {
|
||||
writeln!(f, "bits 64")?;
|
||||
writeln!(f, "global _start")?;
|
||||
self.write_extern_func_decls(program, f)?;
|
||||
writeln!(f, "section .text")?;
|
||||
writeln!(f, "{}", RUNTIME_CODE)?;
|
||||
|
||||
for item in &program.ast.0 {
|
||||
match item {
|
||||
Ast::Statement(stat) => {
|
||||
match stat.inner() {
|
||||
Statement::Fn(func) => self.write_func(program, f, func.clone(), stat.loc().clone())?,
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
writeln!(f, "section .data")?;
|
||||
self.write_constants(program, f)?;
|
||||
self.write_literals(program, f)?;
|
||||
writeln!(f, "section .data")?;
|
||||
self.write_statics(program, f)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
fn compile(&mut self, from: &std::path::Path, to: &std::path::Path) -> anyhow::Result<()> {
|
||||
let mut cmd = std::process::Command::new("nasm");
|
||||
let cmd = cmd.args(&[
|
||||
"-felf64",
|
||||
"-o",
|
||||
to.to_string_lossy().to_string().as_str(),
|
||||
from.to_string_lossy().to_string().as_str(),
|
||||
]);
|
||||
info!("Running: {} {}", cmd.get_program().to_string_lossy(), cmd.get_args().map(|f| f.to_string_lossy().to_string()).collect::<Vec<String>>().join(" "));
|
||||
cmd.output()?;
|
||||
Ok(())
|
||||
}
|
||||
fn link(&mut self, from: Vec<std::path::PathBuf>, to: &std::path::Path, extra_args: &Vec<String>) -> anyhow::Result<()> {
|
||||
let mut cmd = std::process::Command::new("ld");
|
||||
cmd.args(extra_args);
|
||||
|
||||
cmd.args(&[
|
||||
"-o",
|
||||
to.to_string_lossy().to_string().as_str(),
|
||||
]);
|
||||
|
||||
|
||||
for item in &from {
|
||||
cmd.arg(item.to_string_lossy().to_string().as_str());
|
||||
}
|
||||
info!("Running: {} {}", cmd.get_program().to_string_lossy(), cmd.get_args().map(|f| f.to_string_lossy().to_string()).collect::<Vec<String>>().join(" "));
|
||||
cmd.output()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// also used for args
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum VarMapT {
|
||||
Stack(usize, Type),
|
||||
}
|
||||
|
||||
pub struct FunctionCtx {
|
||||
vars: HashMap<Ident, VarMapT>,
|
||||
stack_offset: usize,
|
||||
loop_level: usize, // used for loops
|
||||
if_level: usize, // used for if stats
|
||||
cmp_level: usize, // used for logical comparisons
|
||||
pub emit_short_circuit_label: bool,
|
||||
pub is_last_item: bool,
|
||||
|
||||
}
|
||||
|
||||
impl FunctionCtx {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
vars: Default::default(),
|
||||
stack_offset: 0,
|
||||
loop_level: 0,
|
||||
if_level: 0,
|
||||
cmp_level: 0,
|
||||
emit_short_circuit_label: true,
|
||||
is_last_item: false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_stack(&mut self, name: &Ident, size: usize, typ: Type) -> usize {
|
||||
let offset = self.stack_offset;
|
||||
self.vars.insert(name.clone(), VarMapT::Stack(self.stack_offset, typ));
|
||||
self.stack_offset += size;
|
||||
offset
|
||||
}
|
||||
|
||||
pub fn get(&self, name: &Ident) -> Option<&VarMapT> {
|
||||
self.vars.get(name)
|
||||
}
|
||||
pub fn get_stack_offset(&self) -> usize {
|
||||
self.stack_offset
|
||||
}
|
||||
pub fn register_id_to_str(&self, id: usize) -> &str {
|
||||
match id {
|
||||
0 => "rdi",
|
||||
1 => "rsi",
|
||||
2 => "rdx",
|
||||
3 => "rcx",
|
||||
4 => "r8",
|
||||
5 => "r9",
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
pub fn inc_loop_level(&mut self) -> usize {
|
||||
self.loop_level += 1;
|
||||
self.loop_level
|
||||
}
|
||||
pub fn dec_loop_level(&mut self) -> usize {
|
||||
self.loop_level -= 1;
|
||||
self.loop_level
|
||||
}
|
||||
pub fn loop_level(&self) -> usize {
|
||||
self.loop_level
|
||||
}
|
||||
pub fn inc_if_level(&mut self) -> usize {
|
||||
self.if_level += 1;
|
||||
self.if_level
|
||||
}
|
||||
pub fn dec_if_level(&mut self) -> usize {
|
||||
self.if_level -= 1;
|
||||
self.if_level
|
||||
}
|
||||
pub fn if_level(&self) -> usize {
|
||||
self.if_level
|
||||
}
|
||||
pub fn inc_cmp_level(&mut self) -> usize {
|
||||
self.if_level += 1;
|
||||
self.if_level
|
||||
}
|
||||
pub fn dec_cmp_level(&mut self) -> usize {
|
||||
self.if_level -= 1;
|
||||
self.if_level
|
||||
}
|
||||
pub fn cmp_level(&self) -> usize {
|
||||
self.cmp_level
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl AsmGen {
|
||||
pub fn write_func(&self, program: &Program, f: &mut File, func: Function, loc: Loc) -> anyhow::Result<()> {
|
||||
if func.qual_extern.is_some() {
|
||||
return Ok(());
|
||||
}
|
||||
if let Some(body) = &func.body && let Some(name) = &func.name {
|
||||
let mut fc = FunctionCtx::new();
|
||||
let name = if let Some(struct_name) = &func.struct_name {
|
||||
format!("{}${}", struct_name, name)
|
||||
} else {
|
||||
name.to_string()
|
||||
};
|
||||
info!("writing norm function: {name}");
|
||||
writeln!(f, "{}: ; {} {}", name, loc, func.get_full_name_pretty())?;
|
||||
if body.0.is_empty() {
|
||||
writeln!(f, " ret")?;
|
||||
} else {
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
let mut last_item = None;
|
||||
for (i, param) in func.params.iter().enumerate() {
|
||||
let typ = param.1.clone();
|
||||
let typ = typ.inner().clone();
|
||||
let offset = fc.insert_stack(¶m.0.clone(), typ.size_of(program)?, typ.clone());
|
||||
|
||||
writeln!(&mut buf, " mov [rsp+{offset}], {} ; func arg", fc.register_id_to_str(i))?;
|
||||
}
|
||||
let body = func.body.expect("Safe as its checked already").0;
|
||||
for (i, item) in body.iter().enumerate() {
|
||||
if i == body.len() - 1 {
|
||||
fc.is_last_item = true;
|
||||
}
|
||||
self.write_ast(program, &mut buf, &mut fc, item)?;
|
||||
last_item = Some(item.clone());
|
||||
}
|
||||
|
||||
if fc.stack_offset > 0 {
|
||||
writeln!(f, " sub rsp, {}", fc.stack_offset)?;
|
||||
}
|
||||
f.write(&buf)?;
|
||||
if fc.stack_offset > 0 {
|
||||
writeln!(f, " add rsp, {}", fc.stack_offset)?;
|
||||
}
|
||||
match last_item {
|
||||
Some(Ast::Expr(expr)) => {
|
||||
match expr.inner() {
|
||||
Expr::Return(_) => (),
|
||||
_ => writeln!(f, " ret")?,
|
||||
}
|
||||
}
|
||||
_ => ()
|
||||
}
|
||||
// while writing the return expr, it changes is_last_item to false if it didnt write ret
|
||||
// and it was the last item in a body
|
||||
if !fc.is_last_item {
|
||||
writeln!(f, " ret")?;
|
||||
}
|
||||
}
|
||||
} else if let Some(_) = &func.name {
|
||||
info!("writing function stub: {}", func.get_full_name_pretty());
|
||||
writeln!(f, " ret")?;
|
||||
}
|
||||
writeln!(f, "\n")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
pub fn write_extern_func_decls(&self, program: &Program, f: &mut File) -> anyhow::Result<()> {
|
||||
for (name, func) in &program.functions {
|
||||
match func.inner().qual_extern {
|
||||
Some(_) if func.inner().body.is_some() => {
|
||||
writeln!(f, "global {name}")?;
|
||||
}
|
||||
Some(_) if func.inner().body.is_none() => {
|
||||
writeln!(f, "extern {name}")?;
|
||||
}
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn write_ast(&self, program: &Program, f: &mut impl Write, fc: &mut FunctionCtx, ast: &Ast) -> anyhow::Result<()> {
|
||||
match ast {
|
||||
Ast::Expr(expr) => self.write_expr(program, f, fc, expr.inner())?,
|
||||
Ast::Statement(stat) => self.write_stat(program, f, fc, stat.inner())?,
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
pub fn write_expr(&self, program: &Program, f: &mut impl Write, fc: &mut FunctionCtx, expr: &Expr) -> anyhow::Result<()> {
|
||||
match expr {
|
||||
Expr::Cast { left, .. } => self.write_expr(program, f, fc, left.inner())?,
|
||||
Expr::Literal(id, val) => {
|
||||
match val {
|
||||
Literal::Array{ items, item_size } => {
|
||||
dbg!(&val);
|
||||
writeln!(f, " lea r10, [rel mcl_lit_{id}] ; arr")?;
|
||||
for (i, item) in items.iter().enumerate() {
|
||||
self.write_expr(program, f, fc, item.inner())?;
|
||||
let offset = i * item_size.unwrap();
|
||||
writeln!(f, " mov [r10+{offset}], rax")?;
|
||||
}
|
||||
writeln!(f, " mov rax, r10")?;
|
||||
}
|
||||
Literal::ArrayRepeat { val, item_size, count} => {
|
||||
writeln!(f, " lea r10, [rel mcl_lit_{id}] ; arr repeat")?;
|
||||
for i in 0..*count {
|
||||
self.write_expr(program, f, fc, val.inner())?;
|
||||
let offset = i * item_size.unwrap();
|
||||
writeln!(f, " mov [r10+{offset}], rax")?;
|
||||
}
|
||||
writeln!(f, " mov rax, r10")?;
|
||||
},
|
||||
Literal::Ident(_) => unreachable!(),
|
||||
Literal::String(_) => {
|
||||
writeln!(f, " lea rax, [rel mcl_lit_{id}] ; str")?;
|
||||
}
|
||||
Literal::Bool(v) => {
|
||||
writeln!(f, " mov rax, {} ; {}", *v as u8, v)?;
|
||||
}
|
||||
Literal::Number(_) |
|
||||
Literal::Char(_) => {
|
||||
writeln!(f, " mov rax, [rel mcl_lit_{id}] ; num/char")?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Expr::Struct(id, strct) => {
|
||||
writeln!(f, " lea r10, [rel mcl_lit_{id}]")?;
|
||||
let strct_t = program.structs.get(&strct.path.0[0]).unwrap();
|
||||
for (name, expr) in &strct.fields {
|
||||
self.write_expr(program, f, fc, expr.inner())?;
|
||||
let offset = strct_t.inner().get_offset_of(program, name)?;
|
||||
writeln!(f, " mov [r10+{offset}], rax")?;
|
||||
}
|
||||
writeln!(f, " mov rax, r10")?;
|
||||
}
|
||||
Expr::PtrFieldAccess { left, right, offset, l_typ, r_typ } => {
|
||||
self.write_expr(program, f, fc, left.clone().unwrap().inner())?;
|
||||
dbg!(&l_typ);
|
||||
if l_typ.clone().unwrap().should_deref_pointer(program) {
|
||||
writeln!(f, " mov rax, [rel rax] ; rqawr ->{:?}", right.inner())?;
|
||||
}
|
||||
if *offset > 0 {
|
||||
writeln!(f, " add rax, {offset} ; ->{:?}", right.inner())?;
|
||||
}
|
||||
|
||||
if let Some(t) = r_typ && t.is_numeric(program) {
|
||||
writeln!(f, " mov rax, [rax] ; ->{:?}", right.inner())?;
|
||||
}
|
||||
},
|
||||
Expr::FieldAccess { left, right, offset, l_typ: _, r_typ } => {
|
||||
self.write_expr(program, f, fc, left.clone().unwrap().inner())?;
|
||||
if *offset > 0 {
|
||||
writeln!(f, " add rax, {offset} ; .{:?}", right.inner())?;
|
||||
}
|
||||
if let Some(t) = r_typ && t.is_numeric(program) {
|
||||
writeln!(f, " mov rax, [rax] ; .{:?}", right.inner())?;
|
||||
}
|
||||
},
|
||||
Expr::InfLoop { body } => {
|
||||
let sl = fc.inc_loop_level();
|
||||
writeln!(f, ".L{sl}_test: ; inf loop (named test for tehnical reason)")?;
|
||||
for item in &body.0 {
|
||||
self.write_ast(program, f, fc, item)?;
|
||||
}
|
||||
writeln!(f, ".L{sl}_end: ; inf loop")?;
|
||||
|
||||
}
|
||||
Expr::ForLoop { init, test, on_loop, body } => {
|
||||
let sl = fc.inc_loop_level();
|
||||
writeln!(f, ".L{sl}_init: ; for loop")?;
|
||||
self.write_ast(program, f, fc, &init)?;
|
||||
writeln!(f, ".L{sl}_test: ; for loop")?;
|
||||
writeln!(f, " xor rax, rax")?;
|
||||
self.write_expr(program, f, fc, &test.inner())?;
|
||||
writeln!(f, " test rax, rax")?;
|
||||
writeln!(f, " jz .L{sl}_end")?;
|
||||
writeln!(f, ".L{sl}_on_loop: ; for loop")?;
|
||||
self.write_expr(program, f, fc, &on_loop.inner())?;
|
||||
for item in &body.0 {
|
||||
self.write_ast(program, f, fc, item)?;
|
||||
}
|
||||
writeln!(f, ".L{sl}_end: ; for loop")?;
|
||||
}
|
||||
Expr::WhileLoop { test, body } => {
|
||||
let sl = fc.inc_loop_level();
|
||||
writeln!(f, ".L{sl}_test: ; while loop")?;
|
||||
writeln!(f, " xor rax, rax")?;
|
||||
self.write_expr(program, f, fc, &test.inner())?;
|
||||
writeln!(f, " test rax, rax")?;
|
||||
writeln!(f, " jz .L{sl}_end")?;
|
||||
for item in &body.0 {
|
||||
self.write_ast(program, f, fc, item)?;
|
||||
}
|
||||
writeln!(f, ".L{sl}_end: ; while loop")?;
|
||||
}
|
||||
Expr::Continue => {
|
||||
let sl = fc.loop_level();
|
||||
writeln!(f, " jmp .L{sl}_test ; continue")?;
|
||||
}
|
||||
Expr::Break => {
|
||||
let sl = fc.loop_level();
|
||||
writeln!(f, " jmp .L{sl}_end ; break")?;
|
||||
}
|
||||
Expr::If(ifs) => {
|
||||
let cl = fc.inc_if_level();
|
||||
writeln!(f, " xor rax, rax")?;
|
||||
self.write_expr(program, f, fc, ifs.test.inner())?;
|
||||
writeln!(f, " test rax, rax")?;
|
||||
if ifs.else_if.is_some() {
|
||||
writeln!(f, " jz .C{cl}_branch0")?;
|
||||
} else {
|
||||
writeln!(f, " jz .C{cl}_end")?;
|
||||
}
|
||||
for item in &ifs.body.0 {
|
||||
self.write_ast(program, f, fc, item)?;
|
||||
}
|
||||
self.write_expr(program, f, fc, ifs.test.inner())?;
|
||||
if ifs.else_if.is_some() {
|
||||
writeln!(f, " jmp .C{cl}_end")?;
|
||||
}
|
||||
fn x(slf: &AsmGen, program: &Program, f: &mut impl Write, fc: &mut FunctionCtx, els: &Option<IfBranchExpr>, depth: usize) -> anyhow::Result<()> {
|
||||
let cl = fc.if_level();
|
||||
if let Some(els) = els {
|
||||
match els {
|
||||
IfBranchExpr::Else(els) => {
|
||||
for item in &els.0 {
|
||||
writeln!(f, ".C{cl}_branch{depth}: ; if")?;
|
||||
slf.write_ast(program, f, fc, item)?;
|
||||
}
|
||||
}
|
||||
IfBranchExpr::ElseIf(elsif) => {
|
||||
slf.write_expr(program, f, fc, elsif.test.inner())?;
|
||||
writeln!(f, " test rax, rax")?;
|
||||
if elsif.else_if.is_some() {
|
||||
writeln!(f, " jz .C{cl}_branch{}", depth + 1)?;
|
||||
} else {
|
||||
writeln!(f, " jz .C{cl}_end")?;
|
||||
}
|
||||
for item in &elsif.body.0 {
|
||||
slf.write_ast(program, f, fc, item)?;
|
||||
}
|
||||
writeln!(f, " jmp .C{cl}_end")?;
|
||||
x(slf, program, f, fc, &elsif.else_if, depth+1)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
x(self, program, f, fc, &ifs.else_if, 0)?;
|
||||
writeln!(f, ".C{cl}_end: ; if")?;
|
||||
}
|
||||
Expr::Group(grp) => {
|
||||
self.write_expr(program, f, fc, grp.inner())?;
|
||||
}
|
||||
Expr::Return(ret) => {
|
||||
if let Some(ret) = &**ret {
|
||||
self.write_expr(program, f, fc, ret.inner())?;
|
||||
}
|
||||
if !fc.is_last_item {
|
||||
writeln!(f, " ret")?;
|
||||
} else {
|
||||
fc.is_last_item = false;
|
||||
}
|
||||
}
|
||||
Expr::MethodCall { left, strct, params } => {
|
||||
let method_name;
|
||||
match left.inner() {
|
||||
Expr::FieldAccess { left, right, .. } |
|
||||
Expr::PtrFieldAccess { left, right, .. } => {
|
||||
self.write_expr(program, f, fc, left.clone().unwrap().inner())?;
|
||||
match right.inner() {
|
||||
Expr::Path(path) => {
|
||||
method_name = path.0.first().unwrap().clone();
|
||||
}
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
_ => unreachable!()
|
||||
}
|
||||
let reg = fc.register_id_to_str(0);
|
||||
writeln!(f, " mov {reg}, rax")?;
|
||||
for (i, param) in params.0.iter().enumerate() {
|
||||
self.write_expr(program, f, fc, param.inner())?;
|
||||
if i <= 5 {
|
||||
let reg = fc.register_id_to_str(i+1);
|
||||
writeln!(f, " mov {reg}, rax")?;
|
||||
} else {
|
||||
writeln!(f, " push rax")?;
|
||||
}
|
||||
}
|
||||
|
||||
writeln!(f, " call {}", Path(vec![strct.clone().unwrap(), method_name]).display_asm_compat())?;
|
||||
}
|
||||
Expr::Call { path, params } => {
|
||||
for (i, param) in params.0.iter().enumerate() {
|
||||
self.write_expr(program, f, fc, param.inner())?;
|
||||
if i <= 5 {
|
||||
let reg = fc.register_id_to_str(i);
|
||||
writeln!(f, " mov {reg}, rax")?;
|
||||
} else {
|
||||
writeln!(f, " push rax")?;
|
||||
}
|
||||
}
|
||||
|
||||
writeln!(f, " call {}", path.inner().unwrap_path().display_asm_compat())?;
|
||||
}
|
||||
Expr::UnOp { typ, right } => {
|
||||
self.write_expr(program, f, fc, right.inner())?;
|
||||
match typ {
|
||||
Punctuation::Not => {
|
||||
writeln!(f, " test rax, rax ; logical not")?;
|
||||
writeln!(f, " sete al")?;
|
||||
writeln!(f, " movzx rax, al")?;
|
||||
},
|
||||
Punctuation::Plus => {
|
||||
writeln!(f, " mov rdx, rax ; +x")?;
|
||||
writeln!(f, " sar rdx, 63")?;
|
||||
writeln!(f, " xor rax, rdx")?;
|
||||
writeln!(f, " sub rax, rdx")?;
|
||||
},
|
||||
Punctuation::Minus => {
|
||||
writeln!(f, " neg rax ; -x")?;
|
||||
},
|
||||
Punctuation::Ampersand => {
|
||||
writeln!(f, " ; noop?")?;
|
||||
},
|
||||
Punctuation::Star => {
|
||||
writeln!(f, " ; noop deref")?;
|
||||
},
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
Expr::BinOp { typ, left, right, signed } => {
|
||||
self.write_expr(program, f, fc, left.inner())?;
|
||||
writeln!(f, " mov r10, rax")?;
|
||||
self.write_expr(program, f, fc, right.inner())?;
|
||||
match typ {
|
||||
Punctuation::Plus => {
|
||||
writeln!(f, " add rax, r10")?;
|
||||
},
|
||||
Punctuation::Minus => {
|
||||
writeln!(f, " sub rax, r10")?;
|
||||
},
|
||||
Punctuation::Div => {
|
||||
writeln!(f, " mov rdi, rax")?;
|
||||
writeln!(f, " mov rax, r10 ")?;
|
||||
if *signed {
|
||||
writeln!(f, " cqo")?;
|
||||
writeln!(f, " idiv rdi")?;
|
||||
} else {
|
||||
writeln!(f, " xor rdx, rdx")?;
|
||||
writeln!(f, " div rdi")?;
|
||||
}
|
||||
},
|
||||
Punctuation::Star => {
|
||||
writeln!(f, " mov rdi, rax")?;
|
||||
writeln!(f, " mov rax, r10 ")?;
|
||||
|
||||
if *signed {
|
||||
writeln!(f, " imul r10 ")?;
|
||||
} else {
|
||||
writeln!(f, " mul r10 ")?;
|
||||
}
|
||||
},
|
||||
Punctuation::Mod => {
|
||||
writeln!(f, " mov rdi, rax")?;
|
||||
writeln!(f, " mov rax, r10 ")?;
|
||||
if *signed {
|
||||
writeln!(f, " cqo")?;
|
||||
writeln!(f, " idiv rdi")?;
|
||||
} else {
|
||||
writeln!(f, " xor rdx, rdx")?;
|
||||
writeln!(f, " div rdi")?;
|
||||
}
|
||||
writeln!(f, " mov rax, rdx")?;
|
||||
},
|
||||
Punctuation::Shl => {
|
||||
writeln!(f, " shl r10, rax")?;
|
||||
},
|
||||
Punctuation::Shr => {
|
||||
writeln!(f, " shr r10, rax")?;
|
||||
},
|
||||
Punctuation::AndAnd => {
|
||||
let l = fc.cmp_level();
|
||||
let should_emit = fc.emit_short_circuit_label;
|
||||
self.write_expr(program, f, fc, left.inner())?;
|
||||
writeln!(f, " test rax, rax")?;
|
||||
writeln!(f, " jz .M{l}_false")?;
|
||||
fc.emit_short_circuit_label = false;
|
||||
self.write_expr(program, f, fc, right.inner())?;
|
||||
fc.emit_short_circuit_label = should_emit;
|
||||
if !should_emit {
|
||||
writeln!(f, " setnz al")?;
|
||||
writeln!(f, " movzx rax, al")?;
|
||||
writeln!(f, " jmp .M{l}_sc")?;
|
||||
writeln!(f, ".M{l}_true:")?;
|
||||
writeln!(f, " mov rax, 1")?;
|
||||
writeln!(f, ".M{l}_false:")?;
|
||||
writeln!(f, " xor rax, rax")?;
|
||||
writeln!(f, ".M{l}_sc:")?;
|
||||
fc.inc_cmp_level();
|
||||
}
|
||||
},
|
||||
Punctuation::OrOr => {
|
||||
let l = fc.cmp_level();
|
||||
let should_emit = fc.emit_short_circuit_label;
|
||||
self.write_expr(program, f, fc, left.inner())?;
|
||||
writeln!(f, " cmp rax, rax")?;
|
||||
writeln!(f, " jnz .M{l}_true")?;
|
||||
fc.emit_short_circuit_label = false;
|
||||
self.write_expr(program, f, fc, right.inner())?;
|
||||
fc.emit_short_circuit_label = should_emit;
|
||||
if !should_emit {
|
||||
writeln!(f, " setnz al")?;
|
||||
writeln!(f, " movzx rax, al")?;
|
||||
writeln!(f, " jmp .M{l}_sc")?;
|
||||
writeln!(f, ".M{l}_true:")?;
|
||||
writeln!(f, " mov rax, 1")?;
|
||||
writeln!(f, ".M{l}_false:")?;
|
||||
writeln!(f, " xor rax, rax")?;
|
||||
writeln!(f, ".M{l}_sc:")?;
|
||||
fc.inc_cmp_level();
|
||||
}
|
||||
},
|
||||
Punctuation::Ampersand => {},
|
||||
Punctuation::Or => {},
|
||||
Punctuation::Xor => {},
|
||||
Punctuation::AddEq => {},
|
||||
Punctuation::SubEq => {},
|
||||
Punctuation::DivEq => {},
|
||||
Punctuation::MulEq => {},
|
||||
Punctuation::ModEq => {},
|
||||
Punctuation::ShlEq => {},
|
||||
Punctuation::ShrEq => {},
|
||||
Punctuation::AndEq => {},
|
||||
Punctuation::OrEq => {},
|
||||
Punctuation::XorEq => {},
|
||||
Punctuation::Eq => {
|
||||
self.write_expr(program, f, fc, right.inner())?;
|
||||
let VarMapT::Stack(offset, _) = fc.get(&left.inner().unwrap_path().0.clone()[0]).unwrap();
|
||||
writeln!(f, "mov [rbx+{offset}], rax")?;
|
||||
},
|
||||
Punctuation::EqEq => {},
|
||||
Punctuation::Neq => {},
|
||||
Punctuation::Lt => {},
|
||||
Punctuation::Gt => {},
|
||||
Punctuation::Le => {},
|
||||
Punctuation::Ge => {},
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
Expr::Path(path) => {
|
||||
let ident = path.0.last().unwrap().clone();
|
||||
if let Some(var) = fc.get(&ident) {
|
||||
match var {
|
||||
VarMapT::Stack(offset, typ) => {
|
||||
match typ {
|
||||
/*Type::Builtin { .. } => {
|
||||
writeln!(f, " mov rax, [rsp+{offset}] ; var {ident}")?;
|
||||
writeln!(f, " mov rax, [rax] ; var {ident}")?;
|
||||
}*/
|
||||
_ => writeln!(f, " mov rax, [rsp+{offset}] ; var {ident}")?,
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let Some(_) = program.get_const_var(&ident) {
|
||||
writeln!(f, " lea rax, [rel {ident}] ; const {ident}")?;
|
||||
} else if let Some(_) = program.functions.get(&ident) {
|
||||
writeln!(f, " lea rax, [rel {ident}] ; fn ptr {ident}")?;
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
|
||||
}
|
||||
Expr::Self_ { strct: _ } => {
|
||||
if let Some(var) = fc.get(&Ident::new("self")) {
|
||||
match var {
|
||||
VarMapT::Stack(offset, _) => {
|
||||
writeln!(f, " mov rax, [rsp+{offset}] ; self")?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
v => unreachable!("{v:?}")
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn write_stat(&self, program: &Program, f: &mut impl Write, fc: &mut FunctionCtx, stat: &Statement) -> anyhow::Result<()> {
|
||||
match stat {
|
||||
Statement::Let(lt) => {
|
||||
let typ = lt.typ.clone().unwrap();
|
||||
let loc = typ.loc().clone();
|
||||
let typ = typ.inner().clone();
|
||||
let offset = fc.insert_stack(<.name, typ.size_of(program)?, typ.clone());
|
||||
|
||||
if let Some(value) = <.val {
|
||||
self.write_expr(program, f, fc, value.inner())?;
|
||||
writeln!(f, " mov [rsp+{offset}], rax ; {loc} let {};", lt.name)?;
|
||||
}
|
||||
}
|
||||
_ => unreachable!()
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn write_constants(&self, program: &Program, f: &mut File) -> anyhow::Result<()> {
|
||||
for (name, constant) in &program.const_vars {
|
||||
writeln!(f, "{name}: ; const")?;
|
||||
let bytes = get_constant_data_as_bytes(program, constant.val.clone(), false, false)?;
|
||||
fn x(bytes: &ConstDataTyp, f: &mut File) -> anyhow::Result<()> {
|
||||
match bytes {
|
||||
ConstDataTyp::Array(arr) => {
|
||||
for v in arr {
|
||||
x(v, f)?;
|
||||
}
|
||||
}
|
||||
ConstDataTyp::Bytes(bytes) => {
|
||||
write!(f, " db ")?;
|
||||
for (i, b) in bytes.into_iter().enumerate() {
|
||||
if i != 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
write!(f, "0x{:02x}", b)?;
|
||||
}
|
||||
writeln!(f, "")?;
|
||||
}
|
||||
ConstDataTyp::AddrOfFunc(name) |
|
||||
ConstDataTyp::AddrOfConst(name) |
|
||||
ConstDataTyp::AddrOfStatic(name) => {
|
||||
writeln!(f, " dq {name}")?;
|
||||
},
|
||||
ConstDataTyp::Variable(..) => unreachable!(),
|
||||
}
|
||||
/*
|
||||
|
||||
*/
|
||||
Ok(())
|
||||
}
|
||||
x(&bytes, f)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn write_statics(&self, program: &Program, f: &mut File) -> anyhow::Result<()> {
|
||||
for (name, statc) in &program.static_vars {
|
||||
writeln!(f, "{name}: ; static")?;
|
||||
let bytes = get_constant_data_as_bytes(program, statc.val.clone(), false, false)?;
|
||||
fn x(bytes: &ConstDataTyp, f: &mut File) -> anyhow::Result<()> {
|
||||
match bytes {
|
||||
ConstDataTyp::Array(arr) => {
|
||||
for v in arr {
|
||||
x(v, f)?;
|
||||
}
|
||||
}
|
||||
ConstDataTyp::Bytes(bytes) => {
|
||||
write!(f, " db ")?;
|
||||
for (i, b) in bytes.into_iter().enumerate() {
|
||||
if i != 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
write!(f, "0x{:02x}", b)?;
|
||||
}
|
||||
writeln!(f, "")?;
|
||||
}
|
||||
ConstDataTyp::AddrOfFunc(name) |
|
||||
ConstDataTyp::AddrOfConst(name) |
|
||||
ConstDataTyp::AddrOfStatic(name) => {
|
||||
writeln!(f, " dq {name}")?;
|
||||
},
|
||||
ConstDataTyp::Variable(..) => unreachable!(),
|
||||
}
|
||||
/*
|
||||
|
||||
*/
|
||||
Ok(())
|
||||
}
|
||||
x(&bytes, f)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn write_literals(&self, program: &Program, f: &mut File) -> anyhow::Result<()> {
|
||||
fn w(bytes: ConstDataTyp, f: &mut File) -> anyhow::Result<()> {
|
||||
match bytes {
|
||||
ConstDataTyp::Array(arr) => {
|
||||
for v in arr {
|
||||
w(v, f)?;
|
||||
}
|
||||
}
|
||||
ConstDataTyp::Bytes(bytes) => {
|
||||
write!(f, " db ")?;
|
||||
for (i, b) in bytes.into_iter().enumerate() {
|
||||
if i != 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
write!(f, "0x{:02x}", b)?;
|
||||
}
|
||||
writeln!(f, "")?;
|
||||
}
|
||||
ConstDataTyp::AddrOfFunc(name) |
|
||||
ConstDataTyp::AddrOfConst(name) |
|
||||
ConstDataTyp::AddrOfStatic(name) => {
|
||||
writeln!(f, " dq {name}")?;
|
||||
},
|
||||
ConstDataTyp::Variable(name, size) => {
|
||||
writeln!(f, " resb {size} ; {name}")?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
for lit in &program.literals {
|
||||
writeln!(f, "mcl_lit_{}:", lit.0)?;
|
||||
w(get_constant_data_as_bytes(program, LocBox::new(&Loc::default(), Expr::Literal(lit.0.clone(), lit.1.clone())), false, false)?, f)?;
|
||||
}
|
||||
for lit in &program.struct_literals {
|
||||
writeln!(f, "mcl_lit_{}:", lit.0)?;
|
||||
w(get_constant_data_as_bytes(program, LocBox::new(&Loc::default(), Expr::Struct(lit.0.clone(), lit.1.clone())), false, false)?, f)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
}
|
||||
66
src/targets/x86_64/asmgen/linux/runtime.s
Normal file
66
src/targets/x86_64/asmgen/linux/runtime.s
Normal file
@@ -0,0 +1,66 @@
|
||||
|
||||
__INTERNAL_syscall0:
|
||||
mov rax, rdi
|
||||
syscall
|
||||
ret
|
||||
|
||||
__INTERNAL_syscall1:
|
||||
mov rax, rdi
|
||||
mov rdi, rsi
|
||||
syscall
|
||||
ret
|
||||
|
||||
__INTERNAL_syscall2:
|
||||
mov rax, rdi
|
||||
mov rdi, rsi
|
||||
mov rsi, rdx
|
||||
syscall
|
||||
ret
|
||||
|
||||
__INTERNAL_syscall3:
|
||||
mov rax, rdi
|
||||
mov rdi, rsi
|
||||
mov rsi, rdx
|
||||
mov rdx, rcx
|
||||
syscall
|
||||
ret
|
||||
|
||||
__INTERNAL_syscall4:
|
||||
mov rax, rdi
|
||||
mov rdi, rsi
|
||||
mov rsi, rdx
|
||||
mov rdx, rcx
|
||||
mov r10, r8
|
||||
syscall
|
||||
ret
|
||||
|
||||
__INTERNAL_syscall5:
|
||||
mov rax, rdi
|
||||
mov rdi, rsi
|
||||
mov rsi, rdx
|
||||
mov rdx, rcx
|
||||
mov r10, r8
|
||||
mov r8, r9
|
||||
syscall
|
||||
ret
|
||||
|
||||
__INTERNAL_syscall6:
|
||||
mov rax, rdi
|
||||
mov rdi, rsi
|
||||
mov rsi, rdx
|
||||
mov rdx, rcx
|
||||
mov r10, r8
|
||||
mov r8, r9
|
||||
mov r9, [rsp + 8]
|
||||
syscall
|
||||
ret
|
||||
|
||||
|
||||
global _start
|
||||
_start:
|
||||
xor rax, rax
|
||||
call main
|
||||
|
||||
mov rdi, rax
|
||||
mov rax, 60
|
||||
syscall
|
||||
1
src/targets/x86_64/asmgen/mod.rs
Normal file
1
src/targets/x86_64/asmgen/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod linux;
|
||||
50
src/targets/x86_64/cgen/linux/internals.c
Normal file
50
src/targets/x86_64/cgen/linux/internals.c
Normal file
@@ -0,0 +1,50 @@
|
||||
|
||||
|
||||
int main();
|
||||
|
||||
static char** __INTERNALS__ARGV_PTR = 0;
|
||||
static int __INTERNALS__ARGC = 0;
|
||||
|
||||
__attribute__((noreturn))
|
||||
void _start() {
|
||||
long *stack = (long *) __builtin_frame_address(0);
|
||||
__INTERNALS__ARGC = stack[0];
|
||||
__INTERNALS__ARGV_PTR = (char **) &stack[1];
|
||||
|
||||
int exit_code = main();
|
||||
__asm__ volatile (
|
||||
"syscall\n"
|
||||
:: "a" (60), "D" (exit_code)
|
||||
:
|
||||
);
|
||||
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
unsigned long long syscall(unsigned char arg_count, unsigned char sc_num, void *const *const args) {
|
||||
register long long ret;
|
||||
|
||||
switch (arg_count) {
|
||||
case 6:
|
||||
__asm__ volatile ("mov 40(%0), %%r9" :: "r"(args) : "r9");
|
||||
case 5:
|
||||
__asm__ volatile ("mov 32(%0), %%r8" :: "r"(args) : "r8");
|
||||
case 4:
|
||||
__asm__ volatile ("mov 24(%0), %%r10" :: "r"(args) : "r10");
|
||||
case 3:
|
||||
__asm__ volatile ("mov 16(%0), %%rdx" :: "r"(args) : "rdx");
|
||||
case 2:
|
||||
__asm__ volatile ("mov 8(%0), %%rsi" :: "r"(args) : "rsi");
|
||||
case 1:
|
||||
__asm__ volatile (
|
||||
"mov (%0), %%rdi\n"
|
||||
"syscall"
|
||||
: "=a"(ret)
|
||||
: "r"(args), "a"(sc_num)
|
||||
: "rdi", "rsi", "rdx", "r10", "r8", "r9", "rcx", "r11", "memory"
|
||||
);
|
||||
return ret;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
309
src/targets/x86_64/cgen/linux/mod.rs
Normal file
309
src/targets/x86_64/cgen/linux/mod.rs
Normal file
@@ -0,0 +1,309 @@
|
||||
use std::{fs::File, ops::Deref};
|
||||
use std::io::Write;
|
||||
use crate::{common::Loc, parser::ast::{expr::Expr, literal::Literal, statement::{Function, Statement, Struct}, typ::Type, Ast, Ident, Program, Punctuation}, targets::Target};
|
||||
|
||||
const INTERNAL_CODE: &'static str = include_str!("internals.c");
|
||||
|
||||
pub struct CGen;
|
||||
|
||||
impl Target for CGen {
|
||||
fn new() -> Self where Self: Sized {
|
||||
Self
|
||||
}
|
||||
fn get_target_triple() -> &'static str {
|
||||
"x86_64-cgen-linux"
|
||||
}
|
||||
fn get_int_ext(&self) -> &'static str {
|
||||
"c"
|
||||
}
|
||||
fn write_code(&mut self, program: &crate::parser::ast::Program, f: &mut File) -> anyhow::Result<()> {
|
||||
self.write_fat_comment(f, "Internal number types");
|
||||
for (name, typ) in &program.types {
|
||||
if let Type::Builtin { name: _, size: _, signed: _ } = typ.inner() {
|
||||
self.write_internal_type(f, program, name, typ.inner())?;
|
||||
}
|
||||
}
|
||||
|
||||
writeln!(f, "{}", INTERNAL_CODE)?;
|
||||
|
||||
self.write_fat_comment(f, "Struct forward declarations");
|
||||
for (name, _) in &program.structs {
|
||||
writeln!(f, "typedef struct {name} {name};")?;
|
||||
}
|
||||
|
||||
|
||||
self.write_fat_comment(f, "Type aliases");
|
||||
for (name, typ) in &program.types {
|
||||
self.write_typedef(f, program, typ.inner(), name)?;
|
||||
}
|
||||
|
||||
self.write_fat_comment(f, "Struct definitions");
|
||||
for (name, strct) in &program.structs {
|
||||
self.write_struct(f, program, strct.inner(), name)?;
|
||||
}
|
||||
|
||||
self.write_fat_comment(f, "Function forward declarations");
|
||||
fn write_fn_fwdclr(slf: &mut CGen, program: &Program, f: &mut File, _name: &Ident, func: &Function) -> anyhow::Result<()> {
|
||||
writeln!(f, "// {}", func.get_def_as_str())?;
|
||||
if let Some(rett) = &func.ret_type {
|
||||
slf.write_type(f, program, rett.inner())?;
|
||||
} else {
|
||||
write!(f, "void")?;
|
||||
}
|
||||
write!(f, " {}(", func.get_full_name())?;
|
||||
|
||||
let mut first = true;
|
||||
for (name, typ) in &func.params {
|
||||
if !first {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
slf.write_type(f, program, typ.inner())?;
|
||||
write!(f, " {name}")?;
|
||||
first = false;
|
||||
}
|
||||
writeln!(f, ");")?;
|
||||
Ok(())
|
||||
}
|
||||
for (name, func) in &program.functions {
|
||||
// skip the main function cause the declaration will be included in the internals
|
||||
if name.0 == "main" {
|
||||
continue;
|
||||
}
|
||||
write_fn_fwdclr(self, program, f, name, func.inner())?;
|
||||
}
|
||||
for (sname, funcs) in &program.member_functions {
|
||||
for (fname, func) in funcs {
|
||||
write_fn_fwdclr(self, program, f, &Ident(format!("{sname}::{fname}")), func.inner())?;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
for i in &program.ast.0 {
|
||||
match i {
|
||||
|
||||
Ast::Statement(stat) => {
|
||||
match stat.inner() {
|
||||
Statement::Fn(func) => self.write_fn(f, program, func, stat.loc())?,
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
Ast::Expr(_) => unreachable!(),
|
||||
}
|
||||
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn compile(&mut self, _from: &std::path::Path, _to: &std::path::Path) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn link(&mut self, _from: Vec<std::path::PathBuf>, _to: &std::path::Path) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl CGen {
|
||||
fn write_fat_comment(&mut self, f: &mut File, s: &str) {
|
||||
let _ = writeln!(f, "\n/**");
|
||||
let _ = writeln!(f, " * {s}");
|
||||
let _ = writeln!(f, " */\n");
|
||||
}
|
||||
fn write_expr(&mut self, f: &mut File, program: &Program, expr: &Expr, loc: &Loc) -> anyhow::Result<()> {
|
||||
// https://c9x.me/compile/doc/il.html#Memory
|
||||
match expr {
|
||||
Expr::Literal(lit) => {
|
||||
match lit {
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
Expr::If(_ifexpr) => {
|
||||
|
||||
}
|
||||
Expr::BinOp { typ, .. } => {
|
||||
match typ {
|
||||
Punctuation::Eq => {
|
||||
|
||||
}
|
||||
_ => todo!()
|
||||
}
|
||||
}
|
||||
Expr::Path(p) => {
|
||||
for (n, p) in p.0.iter().enumerate() {
|
||||
if n > 0 {
|
||||
write!(f, "::")?;
|
||||
}
|
||||
write!(f, "{p}")?;
|
||||
}
|
||||
},
|
||||
Expr::Return(ret) => {
|
||||
write!(f, "return ")?;
|
||||
if let Some(ret) = ret.deref() {
|
||||
self.write_expr(f, program, ret.inner(), loc)?;
|
||||
}
|
||||
writeln!(f, ";")?;
|
||||
},
|
||||
Expr::Struct { path, fields } => {
|
||||
writeln!(f, "({path}) {{")?;
|
||||
for (name, val) in fields {
|
||||
write!(f, " .{name} = ")?;
|
||||
self.write_expr(f, program, val.inner(), val.loc())?;
|
||||
writeln!(f, ",")?;
|
||||
}
|
||||
write!(f, "}}")?;
|
||||
}
|
||||
_ => todo!("{expr:?}")
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_typedef(&mut self, f: &mut File, program: &Program, typ: &Type, name: &Ident) -> anyhow::Result<()> {
|
||||
fn part(f: &mut File, program: &Program, typ: &Type) -> anyhow::Result<String> {
|
||||
let mut pf = String::new();
|
||||
match typ {
|
||||
Type::Ref { inner, mutable } => {
|
||||
pf.push_str(&part(f, program, inner)?);
|
||||
write!(f, "*")?;
|
||||
if !*mutable {
|
||||
write!(f, "const")?;
|
||||
}
|
||||
}
|
||||
Type::Owned(name) => {
|
||||
write!(f, "{name}")?;
|
||||
}
|
||||
Type::SizedArray { inner, count } => {
|
||||
pf.push_str(&part(f, program, inner)?);
|
||||
let Expr::Literal(cnt) = count.inner() else {panic!()};
|
||||
let Literal::Number(cnt) = cnt else {panic!()};
|
||||
pf.push_str(&format!("[{}]", cnt.val));
|
||||
}
|
||||
Type::UnsizedArray { inner } => {
|
||||
pf.push_str(&part(f, program, inner)?);
|
||||
pf.push_str("[]");
|
||||
}
|
||||
Type::Builtin { .. } => ()
|
||||
}
|
||||
Ok(pf)
|
||||
}
|
||||
|
||||
write!(f, "typedef ")?;
|
||||
let pf = part(f, program, typ)?;
|
||||
writeln!(f, " {name}{pf};")?;
|
||||
|
||||
Ok(())
|
||||
|
||||
}
|
||||
fn write_type(&mut self, f: &mut File, program: &Program, typ: &Type) -> anyhow::Result<()> {
|
||||
static mut WAS_REF: bool = false;
|
||||
match typ {
|
||||
Type::Ref { inner, mutable } => {
|
||||
self.write_type(f, program, inner)?;
|
||||
write!(f, " *")?;
|
||||
if !*mutable {
|
||||
write!(f, "const")?;
|
||||
}
|
||||
unsafe {
|
||||
WAS_REF = true;
|
||||
}
|
||||
}
|
||||
Type::Owned(name) => {
|
||||
write!(f, "{name}")?;
|
||||
}
|
||||
Type::SizedArray { inner, count } => {
|
||||
self.write_type(f, program, inner)?;
|
||||
let Expr::Literal(cnt) = count.inner() else {panic!()};
|
||||
let Literal::Number(cnt) = cnt else {panic!()};
|
||||
if !unsafe { WAS_REF } {
|
||||
write!(f, "[{}]", cnt.val)?;
|
||||
}
|
||||
}
|
||||
Type::UnsizedArray { inner } => {
|
||||
self.write_type(f, program, inner)?;
|
||||
if !unsafe { WAS_REF } {
|
||||
write!(f, "[]")?;
|
||||
}
|
||||
}
|
||||
Type::Builtin { .. } => ()
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_internal_type(&mut self, f: &mut File, _program: &Program, _name: &Ident, typ: &Type) -> anyhow::Result<()> {
|
||||
match typ {
|
||||
Type::Builtin{ name, size, signed } => {
|
||||
if *size == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
write!(f, "typedef ")?;
|
||||
if *signed {
|
||||
write!(f, "signed ")?;
|
||||
} else {
|
||||
write!(f, "unsigned ")?;
|
||||
}
|
||||
match size {
|
||||
9.. => unimplemented!(),
|
||||
0 => (),
|
||||
1 => write!(f, "char ")?,
|
||||
2 => write!(f, "short ")?,
|
||||
4 => write!(f, "int ")?,
|
||||
8 => write!(f, "long long ")?,
|
||||
3 | 5 | 6 | 7 => unreachable!(),
|
||||
}
|
||||
writeln!(f, "{};", name)?;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_struct(&mut self, f: &mut File, program: &Program, strct: &Struct, name: &Ident) -> anyhow::Result<()> {
|
||||
writeln!(f, "struct {name} {{")?;
|
||||
for (name, typ) in &strct.fields {
|
||||
write!(f, " ")?;
|
||||
self.write_type(f, program, typ.inner())?;
|
||||
writeln!(f, " {name};")?;
|
||||
}
|
||||
writeln!(f, "}};")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_fn(&mut self, f: &mut File, program: &Program, func: &Function, _loc: &Loc) -> anyhow::Result<()> {
|
||||
writeln!(f, "\n\n// {}", func.get_def_as_str())?;
|
||||
if let Some(rett) = &func.ret_type {
|
||||
self.write_type(f, program, rett.inner())?;
|
||||
} else {
|
||||
write!(f, "void")?;
|
||||
}
|
||||
write!(f, " {}(", func.get_full_name())?;
|
||||
|
||||
let mut first = true;
|
||||
for (name, typ) in &func.params {
|
||||
if !first {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
self.write_type(f, program, typ.inner())?;
|
||||
write!(f, " {name}")?;
|
||||
first = false;
|
||||
}
|
||||
write!(f, ")")?;
|
||||
|
||||
|
||||
if let Some(body) = &func.body {
|
||||
writeln!(f, " {{")?;
|
||||
for ast in &body.0 {
|
||||
match ast {
|
||||
Ast::Expr(expr) => {
|
||||
self.write_expr(f, program, expr.inner(), expr.loc())?
|
||||
}
|
||||
Ast::Statement(_) => {
|
||||
//todo!()
|
||||
}
|
||||
}
|
||||
}
|
||||
writeln!(f, "}}")?;
|
||||
} else {
|
||||
write!(f, ";")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
2
src/targets/x86_64/cgen/mod.rs
Normal file
2
src/targets/x86_64/cgen/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
|
||||
pub mod linux;
|
||||
36
src/targets/x86_64/llvmgen/linux/mod.rs
Normal file
36
src/targets/x86_64/llvmgen/linux/mod.rs
Normal file
@@ -0,0 +1,36 @@
|
||||
use std::fs::File;
|
||||
|
||||
|
||||
use llvm_lib::core::module;
|
||||
|
||||
use crate::targets::Target;
|
||||
|
||||
// https://github.com/TheDan64/inkwell/blob/master/examples/kaleidoscope/implementation_typed_pointers.rs
|
||||
pub struct LlvmGen {
|
||||
|
||||
}
|
||||
|
||||
impl Target for LlvmGen {
|
||||
fn new() -> Self where Self: Sized {
|
||||
Self {
|
||||
|
||||
}
|
||||
}
|
||||
fn get_target_triple() -> &'static str {
|
||||
"x86_64-llvm-linux"
|
||||
}
|
||||
fn get_int_ext(&self) -> &'static str {
|
||||
""
|
||||
}
|
||||
fn write_code(&mut self, _program: &crate::parser::ast::Program, _ofp: &mut File) -> anyhow::Result<()> {
|
||||
let mdl = module::ModuleRef::new("main");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
fn compile(&mut self, _from: &std::path::Path, _to: &std::path::Path) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn link(&mut self, _from: Vec<std::path::PathBuf>, _to: &std::path::Path) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
1
src/targets/x86_64/llvmgen/mod.rs
Normal file
1
src/targets/x86_64/llvmgen/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod linux;
|
||||
4
src/targets/x86_64/mod.rs
Normal file
4
src/targets/x86_64/mod.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
pub mod asmgen;
|
||||
// pub mod cgen;
|
||||
// pub mod qbegen;
|
||||
// pub mod llvmgen;
|
||||
149
src/targets/x86_64/qbegen/linux/mod.rs
Normal file
149
src/targets/x86_64/qbegen/linux/mod.rs
Normal file
@@ -0,0 +1,149 @@
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use crate::{common::Loc, parser::ast::{expr::Expr, statement::{Function, Statement}, typ::Type, Ast, Ident, Program, Punctuation}, targets::Target, validator::predefined::TypeType};
|
||||
|
||||
pub struct QbeGen {
|
||||
var_idx: usize,
|
||||
}
|
||||
|
||||
impl Target for QbeGen {
|
||||
fn new() -> Self where Self: Sized {
|
||||
Self {
|
||||
var_idx: 0
|
||||
}
|
||||
}
|
||||
fn get_target_triple() -> &'static str {
|
||||
"x86_64-qbegen-linux"
|
||||
}
|
||||
fn get_int_ext(&self) -> &'static str {
|
||||
"ssa"
|
||||
}
|
||||
fn write_code(&mut self, program: &Program, f: &mut File) -> anyhow::Result<()> {
|
||||
for (name, typ) in &program.types {
|
||||
self.write_typetype(f, program, name, typ)?;
|
||||
}
|
||||
for (name, strct) in &program.structs {
|
||||
self.write_type_with_size(f, program, name, strct.inner().get_size(program)?)?;
|
||||
}
|
||||
|
||||
for i in &program.ast.0 {
|
||||
match i {
|
||||
Ast::Statement(stat) => {
|
||||
match stat.inner() {
|
||||
Statement::Fn(func) => self.write_fn(f, program, func, stat.loc())?,
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
Ast::Expr(_) => unreachable!(),
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
fn compile(&mut self, _from: &std::path::Path, _to: &std::path::Path) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn link(&mut self, _from: Vec<std::path::PathBuf>, _to: &std::path::Path) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl QbeGen {
|
||||
fn write_expr(&mut self, f: &mut File, program: &Program, expr: &Expr, loc: &Loc) -> anyhow::Result<()> {
|
||||
// https://c9x.me/compile/doc/il.html#Memory
|
||||
match expr {
|
||||
Expr::If(_ifexpr) => {
|
||||
|
||||
}
|
||||
Expr::BinOp { typ, left, right } => {
|
||||
match typ {
|
||||
Punctuation::Eq => {
|
||||
|
||||
}
|
||||
_ => todo!()
|
||||
}
|
||||
}
|
||||
_ => todo!("{expr:?}")
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn write_typetype(&mut self, f: &mut File, program: &Program, name: &Ident, typ: &TypeType) -> anyhow::Result<()> {
|
||||
match typ {
|
||||
TypeType::Normal(typ) => {
|
||||
writeln!(f, "type :{:<16} = {{ z {} }}", name.0, typ.inner().size_of_allow_unsized_arrays(program)?)?;
|
||||
}
|
||||
TypeType::Builtin(size) => {
|
||||
writeln!(f, "type :{:<16} = {{ z {size} }}", name.0)?;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_type_with_size(&mut self, f: &mut File, program: &Program, name: &Ident, size: usize) -> anyhow::Result<()> {
|
||||
writeln!(f, "type :{:<16} = {{ z {size} }}", name.0)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn size_to_qbe_size(&self, size: usize) -> Option<&'static str> {
|
||||
match size {
|
||||
1 => Some("b"),
|
||||
2 => Some("h"),
|
||||
4 => Some("w"),
|
||||
8 => Some("l"),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
fn write_fn(&mut self, f: &mut File, program: &Program, func: &Function, loc: &Loc) -> anyhow::Result<()> {
|
||||
writeln!(f, "\n\n# {}", func.get_def_as_str())?;
|
||||
let mut ret = " ";
|
||||
if let Some(r) = &func.ret_type {
|
||||
if let Some(s) = self.size_to_qbe_size(r.inner().size_of(program)?) {
|
||||
ret = s;
|
||||
} else {
|
||||
match r.inner() {
|
||||
Type::Owned(name) => {
|
||||
ret = &name.0;
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut args = String::new();
|
||||
for (name, typ) in &func.params {
|
||||
if !args.is_empty() {
|
||||
args.push_str(", ");
|
||||
}
|
||||
if let Some(s) = self.size_to_qbe_size(typ.inner().size_of(program)?) {
|
||||
args.push_str(s);
|
||||
} else {
|
||||
match typ.inner() {
|
||||
Type::Owned(name) => {
|
||||
args.push_str(&name.0);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
args.push_str(&format!(" %{}", name.0));
|
||||
}
|
||||
writeln!(f, "function {ret} ${}({args}) {{", func.get_full_name())?;
|
||||
writeln!(f, "@start")?;
|
||||
if let Some(body) = &func.body {
|
||||
for ast in &body.0 {
|
||||
match ast {
|
||||
Ast::Expr(expr) => {
|
||||
self.write_expr(f, program, expr.inner(), expr.loc())?
|
||||
}
|
||||
Ast::Statement(stat) => {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
writeln!(f, "}}")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
1
src/targets/x86_64/qbegen/mod.rs
Normal file
1
src/targets/x86_64/qbegen/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod linux;
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::{collections::HashMap, fmt::Display};
|
||||
use anyhow::bail;
|
||||
use parse_int::parse;
|
||||
use crate::{common::{loc::LocIncr, Loc}, error, lerror};
|
||||
use crate::{common::{loc::LocIncr, Loc},lerror};
|
||||
|
||||
pub mod tokentype;
|
||||
use tokentype::*;
|
||||
@@ -30,6 +30,12 @@ impl Token {
|
||||
pub fn tt(&self) -> &TokenType {
|
||||
&self.tt
|
||||
}
|
||||
pub fn new_test(tt: TokenType) -> Self {
|
||||
Self {
|
||||
loc: Loc::default(),
|
||||
tt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -81,6 +87,9 @@ pub fn tokenise(s: &str, file_p: &str) -> anyhow::Result<Vec<Token>> {
|
||||
buf.push(*c);
|
||||
last = *c;
|
||||
}
|
||||
let buf = buf
|
||||
.replace("\\n", "\n")
|
||||
.replace("\\r", "\r");
|
||||
tokens.push(Token::new(TokenType::string(&buf, false), &loc));
|
||||
}
|
||||
'\'' => {
|
||||
@@ -251,6 +260,7 @@ lazy_static::lazy_static!(
|
||||
("return", TokenType::Keyword(Keyword::Return)),
|
||||
("loop", TokenType::Keyword(Keyword::Loop)),
|
||||
("as", TokenType::Keyword(Keyword::As)),
|
||||
("self", TokenType::Keyword(Keyword::Self_)),
|
||||
("{", TokenType::Delim(Delimiter::CurlyL)),
|
||||
("}", TokenType::Delim(Delimiter::CurlyR)),
|
||||
("[", TokenType::Delim(Delimiter::SquareL)),
|
||||
@@ -272,7 +282,6 @@ lazy_static::lazy_static!(
|
||||
("%", TokenType::Punct(Punctuation::Mod)),
|
||||
("<<", TokenType::Punct(Punctuation::Shl)),
|
||||
(">>", TokenType::Punct(Punctuation::Shr)),
|
||||
("&&", TokenType::Punct(Punctuation::AndAnd)),
|
||||
("||", TokenType::Punct(Punctuation::OrOr)),
|
||||
("|", TokenType::Punct(Punctuation::Or)),
|
||||
(">", TokenType::Punct(Punctuation::Gt)),
|
||||
|
||||
@@ -1,15 +1,27 @@
|
||||
use core::panic;
|
||||
use std::fmt::Display;
|
||||
|
||||
use crate::parser::ast::expr::Path;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Ident(pub String);
|
||||
|
||||
impl ToString for Ident {
|
||||
fn to_string(&self) -> String {
|
||||
self.0.clone()
|
||||
impl Display for Ident {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Ident {
|
||||
pub fn as_path(self) -> Path {
|
||||
Path(vec![self])
|
||||
}
|
||||
pub fn new(s: impl ToString) -> Self {
|
||||
Self(s.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Number {
|
||||
pub val: usize,
|
||||
@@ -17,6 +29,24 @@ pub struct Number {
|
||||
pub signed: bool,
|
||||
}
|
||||
|
||||
impl Display for Number {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self.base {
|
||||
2 => write!(f, "{:#b}", self.val),
|
||||
8 => write!(f, "{:#o}", self.val),
|
||||
10 => {
|
||||
if self.signed {
|
||||
write!(f, "{}", self.val as isize)
|
||||
} else {
|
||||
write!(f, "{}", self.val as usize)
|
||||
}
|
||||
}
|
||||
16 => write!(f, "{:#x}", self.val),
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
pub struct TString {
|
||||
pub val: String,
|
||||
@@ -24,7 +54,7 @@ pub struct TString {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Char(char);
|
||||
pub struct Char(pub char);
|
||||
|
||||
impl Into<char> for Char {
|
||||
fn into(self) -> char {
|
||||
@@ -44,7 +74,7 @@ pub enum Keyword {
|
||||
Type, While, For, Break, Continue,
|
||||
Let, Const, Mut, Static,
|
||||
True, False, Include, Extern, Return,
|
||||
As, Loop
|
||||
As, Loop, Self_
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
@@ -143,7 +173,7 @@ impl TokenType {
|
||||
_ => panic!("Expected {}, got {self}", Self::ident(""))
|
||||
}
|
||||
}
|
||||
pub fn ident(s: &str) -> Self {
|
||||
pub fn ident(s: impl ToString) -> Self {
|
||||
Self::Ident(Ident(s.to_string()))
|
||||
}
|
||||
pub fn number(val: usize, base: u8, signed: bool) -> Self {
|
||||
|
||||
@@ -1,8 +1,775 @@
|
||||
use crate::parser::ast::Program;
|
||||
use std::{collections::HashMap, panic};
|
||||
|
||||
use anyhow::bail;
|
||||
|
||||
use crate::{common::{Loc, loc::LocBox}, parser::ast::{Ast, Ident, Program, Punctuation, Scope, TokenType, expr::*, literal::Literal, statement::*, typ::Type}, validator::predefined::get_builtin_from_name};
|
||||
|
||||
pub fn validate_code(prog: &Program) -> anyhow::Result<()> {
|
||||
pub mod predefined;
|
||||
|
||||
pub fn validate_code(prog: &mut Program) -> anyhow::Result<()> {
|
||||
let Block(mut items) = prog.ast.clone();
|
||||
predefined::load_builtin(prog);
|
||||
collect_types_and_constants(prog, &mut items);
|
||||
check_that_types_exist_for_items(prog, &items)?;
|
||||
//dbg!(&prog.types);
|
||||
//dbg!(&prog.structs);
|
||||
//dbg!(&prog.enums);
|
||||
//dbg!(&prog.member_functions);
|
||||
//dbg!(&prog.functions);
|
||||
for item in items.iter_mut() {
|
||||
match item {
|
||||
Ast::Statement(stat) => validate_stat(prog, stat, CurrentState::Outside)?,
|
||||
Ast::Expr(_) => unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
prog.ast.0 = items;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
enum CurrentState {
|
||||
InFunc,
|
||||
Outside,
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn validate_stat(prog: &mut Program, stat: &mut LocBox<Statement>, current_state: CurrentState) -> anyhow::Result<()> {
|
||||
match stat.inner_mut() {
|
||||
Statement::Fn(func) => validate_fn(prog, func)?,
|
||||
Statement::Let(lt) if current_state == CurrentState::InFunc => validate_stat_let(prog, lt)?,
|
||||
Statement::Let(_) if current_state == CurrentState::Outside => {
|
||||
lerror!(stat.loc(), "Let statements are not allowed outside a function");
|
||||
bail!("")
|
||||
}
|
||||
Statement::ConstVar(var) => validate_const_var(prog, var)?,
|
||||
Statement::StaticVar(var) => validate_static_var(prog, var)?,
|
||||
Statement::Enum(enm) => validate_enum(prog, enm)?,
|
||||
Statement::Struct(strct) => validate_struct(prog, strct)?,
|
||||
Statement::TypeAlias(alias) => validate_type_alias(prog, alias)?,
|
||||
Statement::Let(_) => unreachable!(),
|
||||
Statement::Include => (),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_stat_let(prog: &mut Program, lt: &mut Let) -> anyhow::Result<()> {
|
||||
if let Some(val) = &mut lt.val && let Some(t) = &mut lt.typ {
|
||||
let val_t = validate_expr(prog, val)?.unwrap();
|
||||
if val_t != *t.inner_mut() {
|
||||
lerror!(t.loc(), "Cannot assign {val_t} to {}", t.inner_mut());
|
||||
bail!("")
|
||||
}
|
||||
}
|
||||
if let Some(val) = &mut lt.val && let None = &mut lt.typ {
|
||||
let Some(t) = validate_expr(prog, val)? else {
|
||||
lerror!(val.loc(), "Expected a type, go none");
|
||||
bail!("");
|
||||
};
|
||||
lt.typ = Some(LocBox::new(val.loc(), t));
|
||||
}
|
||||
if let Some(scope) = &mut prog.scope {
|
||||
scope.vars.insert(lt.name.clone(), LocBox::new(&Default::default(), lt.clone()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_ast(prog: &mut Program, ast: &mut Ast) -> anyhow::Result<Option<Type>> {
|
||||
match ast {
|
||||
Ast::Expr(expr) => {
|
||||
validate_expr(prog, expr)
|
||||
}
|
||||
Ast::Statement(stat) => {
|
||||
validate_stat(prog, stat, CurrentState::InFunc)?;
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub fn validate_expr(prog: &mut Program, expr: &mut LocBox<Expr>) -> anyhow::Result<Option<Type>> {
|
||||
match expr.inner_mut() {
|
||||
Expr::Self_ { strct } => {
|
||||
if let Some(name) = &prog.curr_struct {
|
||||
*strct = Some(name.clone());
|
||||
let mut t = Type::Owned(name.clone());
|
||||
t.convert_owned_to_real_type(prog);
|
||||
Ok(Some(t))
|
||||
} else {
|
||||
lerror!(expr.loc(), "");
|
||||
bail!("")
|
||||
}
|
||||
}
|
||||
Expr::Break | Expr::Continue => Ok(None),
|
||||
Expr::Return(ret) => {
|
||||
if let Some(expr) = &mut**ret {
|
||||
validate_expr(prog, expr)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
Expr::Struct(id, strct) => {
|
||||
// this is probably fucked so fix this later
|
||||
let name = strct.path.0.last().expect("Paths always have at least one part");
|
||||
let typ = Type::Owned(name.clone());
|
||||
// this is so ass, this checks if struct exists
|
||||
validate_type(prog, &LocBox::new(&Loc::default(), typ.clone()))?;
|
||||
|
||||
for field in strct.fields.iter_mut() {
|
||||
validate_expr(prog, field.1)?;
|
||||
}
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
let mut hasher = DefaultHasher::new();
|
||||
(*strct).hash(&mut hasher);
|
||||
let hash = hasher.finish();
|
||||
*id = hash.to_string();
|
||||
|
||||
prog.struct_literals.insert(id.clone(), strct.clone());
|
||||
|
||||
Ok(Some(typ))
|
||||
},
|
||||
Expr::If(ifs) => {
|
||||
validate_expr(prog, &mut ifs.test)?;
|
||||
for item in ifs.body.0.iter_mut() {
|
||||
validate_ast(prog, item)?;
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
Expr::Path(path) => {
|
||||
// TODO: Path hell TBD
|
||||
Ok(Some(Type::Owned(path.0.last().unwrap().clone())))
|
||||
}
|
||||
Expr::Group(group) => validate_expr(prog, group),
|
||||
Expr::Call { path, params } => {
|
||||
let loc = path.loc().clone();
|
||||
let Expr::Path(path) = path.inner_mut() else {
|
||||
panic!("fml");
|
||||
};
|
||||
|
||||
match path.0.len() {
|
||||
1 => {
|
||||
let f = prog.functions.get(&path.0[0]).cloned();
|
||||
match f {
|
||||
Some(func) => {
|
||||
for (i, param) in params.0.iter_mut().enumerate() {
|
||||
let mut ft = func.inner().params[i].1.inner().clone();
|
||||
let t = validate_expr(prog, param)?.clone();
|
||||
match t {
|
||||
Some(mut t) => {
|
||||
t.convert_owned_to_real_type(prog);
|
||||
ft.convert_owned_to_real_type(prog);
|
||||
let t = t.get_variable_type(prog)?;
|
||||
let ft = ft.get_variable_type(prog)?;
|
||||
if t != ft {
|
||||
lerror!(param.loc(), "expected {ft}, got {t}");
|
||||
bail!("owo")
|
||||
}
|
||||
}
|
||||
None => {
|
||||
lerror!(param.loc(), "expected {ft}, got Nothing");
|
||||
bail!("nya")
|
||||
},
|
||||
}
|
||||
}
|
||||
if let Some(t) = &func.inner().ret_type {
|
||||
Ok(Some(t.inner().clone()))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
},
|
||||
None => {
|
||||
lerror!(loc, "Could not find function {}", path);
|
||||
panic!("")
|
||||
}
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
let s = prog.member_functions.get(&path.0[0]).unwrap();
|
||||
let f = s.get(&path.0[1]).cloned();
|
||||
match f {
|
||||
Some(func) => {
|
||||
for (i, param) in params.0.iter_mut().enumerate() {
|
||||
let ft = func.inner().params[i].1.inner().clone();
|
||||
let t = validate_expr(prog, param)?.clone();
|
||||
match t {
|
||||
Some(t) => {
|
||||
if t.get_absolute_value(prog)? != ft.get_absolute_value(prog)? {
|
||||
lerror!(param.loc(), "expected {ft}, got {}", t);
|
||||
bail!("")
|
||||
}
|
||||
}
|
||||
None => {
|
||||
lerror!(param.loc(), "expected {ft}, got Nothing");
|
||||
bail!("")
|
||||
},
|
||||
}
|
||||
}
|
||||
if let Some(t) = &func.inner().ret_type {
|
||||
Ok(Some(t.inner().clone()))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
},
|
||||
None => {
|
||||
lerror!(loc, "Could not find function {}", path);
|
||||
panic!("")
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => panic!("")
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
Expr::MethodCall { left, strct, params } => {
|
||||
let var_t;
|
||||
let method_name;
|
||||
match left.inner_mut() {
|
||||
Expr::FieldAccess { left, right, .. } |
|
||||
Expr::PtrFieldAccess { left, right, .. } => {
|
||||
var_t = validate_expr(prog, &mut left.clone().unwrap())?;
|
||||
let name = validate_expr(prog, right)?;
|
||||
match name.unwrap() {
|
||||
Type::Owned(name) => method_name = name,
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
_ => unreachable!()
|
||||
}
|
||||
|
||||
let Type::Owned(struct_name) = var_t.unwrap().get_absolute_value(prog)? else {
|
||||
panic!("fml");
|
||||
};
|
||||
*strct = Some(struct_name.clone());
|
||||
let mut strct = prog.member_functions.get_mut(&struct_name).unwrap().clone();
|
||||
let func = strct.get_mut(&method_name).unwrap();
|
||||
|
||||
for (i, param) in params.0.iter_mut().enumerate() {
|
||||
let t = validate_expr(prog, param)?;
|
||||
let ft = func.inner_mut().params[i].1.inner_mut();
|
||||
if t.as_ref() != Some(ft) {
|
||||
lerror!(param.loc(), "expected {ft}, got {}", t.unwrap());
|
||||
bail!("")
|
||||
}
|
||||
}
|
||||
if let Some(t) = &mut func.inner_mut().ret_type {
|
||||
Ok(Some(t.inner_mut().clone()))
|
||||
} else {
|
||||
Ok(None)
|
||||
|
||||
}
|
||||
}
|
||||
Expr::ForLoop { init, test, on_loop, body } => {
|
||||
let _ = validate_ast(prog, init)?;
|
||||
let _ = validate_expr(prog, test)?;
|
||||
let _ = validate_expr(prog, on_loop)?;
|
||||
for item in body.0.iter_mut() {
|
||||
let _ = validate_ast(prog, item)?;
|
||||
}
|
||||
Ok(None)
|
||||
},
|
||||
Expr::Cast { left, right } => {
|
||||
validate_expr(prog, left)?;
|
||||
Ok(Some(right.inner_mut().clone()))
|
||||
}
|
||||
Expr::Literal(id, lit) => {
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
let mut hasher = DefaultHasher::new();
|
||||
(*lit).hash(&mut hasher);
|
||||
let hash = hasher.finish();
|
||||
*id = hash.to_string();
|
||||
match lit {
|
||||
Literal::Array { items, item_size } => {
|
||||
for item in items {
|
||||
let typ = validate_expr(prog, item)?;
|
||||
if item_size.is_none() {
|
||||
*item_size = Some(typ.unwrap().get_variable_type(prog)?.size_of(prog)?);
|
||||
}
|
||||
}
|
||||
},
|
||||
Literal::ArrayRepeat { val, item_size, count: _ } => {
|
||||
let typ = validate_expr(prog, val)?;
|
||||
*item_size = Some(typ.unwrap().size_of(prog)?);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
prog.literals.insert(id.clone(), lit.clone());
|
||||
|
||||
|
||||
|
||||
|
||||
Ok(Some(lit.to_type(prog)?))
|
||||
}
|
||||
Expr::UnOp { typ, right } => {
|
||||
match typ {
|
||||
Punctuation::Not => {
|
||||
let t = validate_expr(prog, right)?.unwrap().get_absolute_value(prog)?;
|
||||
if !t.is_bool(prog) {
|
||||
lerror!(right.loc(), "Expected bool, got {t}");
|
||||
}
|
||||
Ok(Some(t))
|
||||
}
|
||||
Punctuation::Minus |
|
||||
Punctuation::Plus => {
|
||||
let t = validate_expr(prog, right)?.unwrap().get_absolute_value(prog)?;
|
||||
if !t.is_numeric(prog) {
|
||||
lerror!(right.loc(), "Expected number, got {t}");
|
||||
}
|
||||
Ok(Some(t))
|
||||
}
|
||||
|
||||
Punctuation::Ampersand => {
|
||||
let t = validate_expr(prog, right)?.unwrap().get_absolute_value(prog)?;
|
||||
Ok(Some(Type::Ref { inner: Box::new(t), mutable: false }))
|
||||
}
|
||||
Punctuation::Star => {
|
||||
let t = validate_expr(prog, right)?.unwrap().get_absolute_value(prog)?;
|
||||
if !t.is_ptr(prog) {
|
||||
lerror!(right.loc(), "Expected pointer, got {t}");
|
||||
}
|
||||
|
||||
match t {
|
||||
Type::Ref { inner, .. } => Ok(Some(*inner)),
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
_ => unreachable!()
|
||||
}
|
||||
},
|
||||
Expr::BinOp { typ, left, right, signed } => {
|
||||
match typ {
|
||||
Punctuation::Ampersand |
|
||||
Punctuation::Or |
|
||||
Punctuation::Xor |
|
||||
Punctuation::Plus |
|
||||
Punctuation::Minus |
|
||||
Punctuation::Div |
|
||||
Punctuation::Star |
|
||||
Punctuation::Mod |
|
||||
Punctuation::Shl |
|
||||
Punctuation::Shr => {
|
||||
let t1_s;
|
||||
let t2_s;
|
||||
let t1 = validate_expr(prog, left)?.unwrap().get_absolute_value(prog)?;
|
||||
let t2 = validate_expr(prog, right)?.unwrap().get_absolute_value(prog)?;
|
||||
if !(t1.is_numeric(prog) || t1.is_ptr(prog)) {
|
||||
lerror!(right.loc(), "Expected number, got {t1}");
|
||||
}
|
||||
if !(t2.is_numeric(prog) || t2.is_ptr(prog)) {
|
||||
lerror!(right.loc(), "Expected number, got {t2}");
|
||||
}
|
||||
match &t1 {
|
||||
Type::Builtin { name: _, size, .. } => t1_s = *size,
|
||||
_ => unreachable!("1: {t1:?}\n2: {t2:?}")
|
||||
}
|
||||
|
||||
match &t2 {
|
||||
Type::Builtin { name: _, size, .. } => t2_s = *size,
|
||||
_ => unreachable!()
|
||||
}
|
||||
*signed = t1.is_signed(prog).expect("verified as numeric");
|
||||
if t2_s > t1_s {
|
||||
Ok(Some(t2))
|
||||
} else {
|
||||
Ok(Some(t1))
|
||||
}
|
||||
}
|
||||
Punctuation::EqEq |
|
||||
Punctuation::Lt |
|
||||
Punctuation::Gt |
|
||||
Punctuation::Le |
|
||||
Punctuation::Ge |
|
||||
Punctuation::AndAnd |
|
||||
Punctuation::OrOr => {
|
||||
let t1 = validate_expr(prog, left)?.unwrap().get_absolute_value(prog)?;
|
||||
let t2 = validate_expr(prog, right)?.unwrap().get_absolute_value(prog)?;
|
||||
if !(t1.is_numeric(prog) || t1.is_ptr(prog)) {
|
||||
lerror!(right.loc(), "Expected number or pointer, got {t1}");
|
||||
}
|
||||
if !(t2.is_numeric(prog) || t2.is_ptr(prog)) {
|
||||
lerror!(right.loc(), "Expected number or pointer, got {t2}");
|
||||
}
|
||||
*signed = t1.is_signed(prog).expect("verified as numeric");
|
||||
Ok(Some(t1))
|
||||
}
|
||||
|
||||
Punctuation::AddEq |
|
||||
Punctuation::SubEq |
|
||||
Punctuation::DivEq |
|
||||
Punctuation::MulEq |
|
||||
Punctuation::ModEq |
|
||||
Punctuation::ShlEq |
|
||||
Punctuation::ShrEq |
|
||||
Punctuation::AndEq |
|
||||
Punctuation::OrEq |
|
||||
Punctuation::XorEq => {
|
||||
let var = validate_expr(prog, left)?.unwrap();
|
||||
let var_t = var.get_absolute_value(prog)?;
|
||||
let val_t = validate_expr(prog, right)?.unwrap().get_absolute_value(prog)?;
|
||||
|
||||
if !((var_t.is_numeric(prog) || var_t.is_ptr(prog)) && (val_t.is_numeric(prog) || val_t.is_ptr(prog))) {
|
||||
lerror!(left.loc(), "Mismatched types, assigning {val_t} to {var_t}");
|
||||
bail!("");
|
||||
}
|
||||
|
||||
*signed = var_t.is_signed(prog).expect("verified as numeric");
|
||||
Ok(None)
|
||||
}
|
||||
Punctuation::Neq |
|
||||
Punctuation::Eq => {
|
||||
let var = validate_expr(prog, left)?.unwrap();
|
||||
let var_t = var.get_absolute_value(prog)?;
|
||||
let val_t = validate_expr(prog, right)?.unwrap().get_absolute_value(prog)?;
|
||||
|
||||
if !(var_t == val_t || var_t.is_numeric(prog) && val_t.is_numeric(prog)) {
|
||||
lerror!(left.loc(), "Mismatched types, assigning {val_t} to {var_t}");
|
||||
bail!("");
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
v => unreachable!("{v:?}")
|
||||
}
|
||||
},
|
||||
Expr::ArrayIndex { name, index } => {
|
||||
let left = validate_expr(prog, name)?;
|
||||
let Some(left) = left else {
|
||||
lerror!(name.loc(), "expected value, got nothing, cannot index nothing");
|
||||
bail!("")
|
||||
};
|
||||
fn f(prog: &mut Program, left: &Type, loc: &Loc, index: &LocBox<Expr>) -> anyhow::Result<Option<Type>> {
|
||||
match left {
|
||||
Type::SizedArray { inner, count } => {
|
||||
let val1 = count.inner().as_number(prog).unwrap();
|
||||
let val2 = index.inner().as_number(prog).unwrap();
|
||||
if val1.signed && (val1.val as isize) < 0 {
|
||||
lerror!(index.loc(), "Cannot index bellow 0");
|
||||
bail!("");
|
||||
}
|
||||
|
||||
if val1.val <= val2.val {
|
||||
lerror!(index.loc(), "Cannot index outside aray boundaries");
|
||||
bail!("");
|
||||
}
|
||||
Ok(Some(*inner.clone()))
|
||||
}
|
||||
Type::UnsizedArray { inner } => {
|
||||
Ok(Some(*inner.clone()))
|
||||
}
|
||||
Type::Owned(name) => {
|
||||
let t = prog.types.get(name).cloned();
|
||||
if let Some(t) = &t {
|
||||
f(prog, t.inner(), t.loc(), index)
|
||||
} else {
|
||||
lerror!(loc, "Unknown type {name}");
|
||||
bail!("")
|
||||
}
|
||||
}
|
||||
Type::Ref { .. } |
|
||||
Type::FnPtr(_) |
|
||||
Type::Builtin { .. } => {
|
||||
lerror!(loc, "Numeric, pointer, and void types are not indexable (TODO: Maybe allow indexing pointers?)");
|
||||
bail!("")
|
||||
}
|
||||
}
|
||||
}
|
||||
f(prog, &left, name.loc(), &index)
|
||||
},
|
||||
Expr::PtrFieldAccess { left, right, offset, l_typ, r_typ } => {
|
||||
let left = validate_expr(prog, &mut left.clone().unwrap())?;
|
||||
let right = validate_expr(prog, &mut right.clone())?.unwrap();
|
||||
let Type::Owned(right) = right else {
|
||||
panic!()
|
||||
};
|
||||
let mut left = left.unwrap();
|
||||
left.convert_owned_to_real_type(prog);
|
||||
dbg!(&prog.curr_fn_args);
|
||||
*l_typ = Some(Box::new(left.clone()));
|
||||
match left {
|
||||
Type::Ref { inner, mutable: _ } => {
|
||||
match *inner {
|
||||
Type::Owned(name) => {
|
||||
if let Some(strct) = prog.structs.get(&name) {
|
||||
for field in strct.inner().fields.iter() {
|
||||
if field.0 == right {
|
||||
*r_typ = Some(Box::new(field.1.inner().clone()));
|
||||
*offset = strct.inner().clone().get_offset_of(prog, &right)?;
|
||||
info!("Offset {right:?} = {offset}");
|
||||
return Ok(Some(field.1.inner().clone()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
panic!("couldnt find struct {name}");
|
||||
}
|
||||
}
|
||||
v => panic!("{v:?}"),
|
||||
}
|
||||
}
|
||||
v => panic!("{} {v:?}", expr.loc()),
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
},
|
||||
Expr::FieldAccess { left, right, offset, l_typ, r_typ } => {
|
||||
let left = validate_expr(prog, &mut left.clone().unwrap())?;
|
||||
let right = validate_expr(prog, &mut right.clone())?.unwrap();
|
||||
let Type::Owned(right) = right else {
|
||||
panic!()
|
||||
};
|
||||
let mut left = left.unwrap();
|
||||
left.convert_owned_to_real_type(prog);
|
||||
*l_typ = Some(Box::new(left.clone()));
|
||||
match left {
|
||||
Type::Owned(name) => {
|
||||
if let Some(strct) = prog.structs.get(&name) {
|
||||
for field in strct.inner().fields.iter() {
|
||||
if field.0 == right {
|
||||
*r_typ = Some(Box::new(field.1.inner().clone()));
|
||||
*offset = strct.inner().clone().get_offset_of(prog, &right)?;
|
||||
return Ok(Some(field.1.inner().clone()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
panic!("couldnt find struct {name}");
|
||||
}
|
||||
}
|
||||
v => panic!("{v:?}"),
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
},
|
||||
Expr::WhileLoop { test, body } => {
|
||||
let _ = validate_expr(prog, test.as_mut())?;
|
||||
for item in body.0.iter_mut() {
|
||||
let _ = validate_ast(prog, item)?;
|
||||
}
|
||||
Ok(None)
|
||||
|
||||
},
|
||||
Expr::InfLoop { body } => {
|
||||
for item in body.0.iter_mut() {
|
||||
let _ = validate_ast(prog, item)?;
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_fn(prog: &mut Program, func: &mut Function) -> anyhow::Result<()> {
|
||||
prog.scope = Some(Scope::default());
|
||||
prog.curr_struct = func.struct_name.clone();
|
||||
dbg!(&func.params);
|
||||
for param in &func.params {
|
||||
let t = validate_type(prog, ¶m.1)?;
|
||||
dbg!(&t);
|
||||
prog.curr_fn_args.insert(param.0.clone(), LocBox::new(&Loc::default(), t.clone()));
|
||||
}
|
||||
if let Some(body) = &mut func.body {
|
||||
for item in body.0.iter_mut() {
|
||||
validate_ast(prog, item)?;
|
||||
}
|
||||
}
|
||||
|
||||
prog.curr_struct = None;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_const_var(prog: &mut Program, var: &mut ConstVar) -> anyhow::Result<()> {
|
||||
validate_type(prog, &var.typ)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_static_var(prog: &mut Program, var: &mut StaticVar) -> anyhow::Result<()> {
|
||||
validate_type(prog, &var.typ)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_enum(_: &mut Program, _: &Enum) -> anyhow::Result<()> {
|
||||
// Enum has no actual types
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_struct(prog: &mut Program, strct: &mut Struct) -> anyhow::Result<()> {
|
||||
for field in &strct.fields {
|
||||
if let Err(e) = validate_type(prog, &field.1) {
|
||||
error!("Could not find type in field {}::{}", strct.name, field.0);
|
||||
anyhow::bail!(e);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_type_alias(prog: &mut Program, alias: &TypeAlias) -> anyhow::Result<()> {
|
||||
validate_type(prog, &alias.typ)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
fn check_that_types_exist_for_items(prog: &mut Program, items: &Vec<Ast>) -> anyhow::Result<()> {
|
||||
let mut errored = false;
|
||||
for item in items {
|
||||
match item {
|
||||
Ast::Statement(stat) => {
|
||||
match stat.inner() {
|
||||
Statement::Fn(func) => {
|
||||
for (name, t) in &func.params {
|
||||
if let Err(_) = validate_type(prog, t) {
|
||||
if let Some(sn) = &func.struct_name {
|
||||
lerror!(t.loc(), "Type '{}', of argument, '{name}' in function '{}::{}' does not exist", t.inner(), sn, func.get_full_name_pretty());
|
||||
} else {
|
||||
lerror!(t.loc(), "Type '{}', of argument, '{name}' in function '{}' does not exist", t.inner(), func.get_full_name_pretty());
|
||||
}
|
||||
errored = true;
|
||||
}
|
||||
}
|
||||
if let Some(ret_typ) = &func.ret_type {
|
||||
if let Err(_) = validate_type(prog, &ret_typ) {
|
||||
lerror!(ret_typ.loc(), "Return type '{}' of function '{}' does not exist", ret_typ.inner(), func.get_full_name_pretty());
|
||||
errored = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
Statement::Include |
|
||||
Statement::Let { .. } => (),
|
||||
Statement::ConstVar(var) => {
|
||||
validate_type(prog, &var.typ)?;
|
||||
}
|
||||
Statement::StaticVar(var) => {
|
||||
validate_type(prog, &var.typ)?;
|
||||
}
|
||||
Statement::Enum(_) => (),
|
||||
Statement::Struct(strct) => {
|
||||
for (name, t) in strct.fields.iter() {
|
||||
if let Err(_) = validate_type(prog, t) {
|
||||
lerror!(t.loc(), "Type '{}', of field, '{}.{name}' does not exist", t.inner(), strct.name);
|
||||
errored = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Statement::TypeAlias(alias) => {
|
||||
if let Err(_) = validate_type(prog, &alias.typ) {
|
||||
lerror!(alias.typ.loc(), "Type '{}' of type alias '{}' does not exist", alias.typ.inner(), alias.name);
|
||||
errored = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ast::Expr(_) => unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
if errored {
|
||||
bail!("")
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn validate_type(prog: &mut Program, typ: &LocBox<Type>) -> anyhow::Result<Type> {
|
||||
fn f(prog: &mut Program, typ: &Type, loc: &Loc) -> anyhow::Result<Type> {
|
||||
match typ {
|
||||
Type::SizedArray { .. } |
|
||||
Type::UnsizedArray { .. } |
|
||||
Type::FnPtr(_) |
|
||||
Type::Ref { .. } => Ok(typ.clone()),
|
||||
Type::Owned(ident) => {
|
||||
if let Some(builtin) = get_builtin_from_name(&ident.0) {
|
||||
Ok(builtin)
|
||||
} else
|
||||
if prog.enums.get(ident).is_some() ||
|
||||
prog.types.get(ident).is_some() ||
|
||||
prog.structs.get(ident).is_some() {
|
||||
Ok(typ.clone())
|
||||
} else {
|
||||
lerror!(loc, "Could not find type '{}'", ident.0);
|
||||
bail!("")
|
||||
}
|
||||
}
|
||||
Type::Builtin { .. } => Ok(typ.clone())
|
||||
}
|
||||
}
|
||||
f(prog, &typ.inner(), typ.loc())
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn collect_types_and_constants(prog: &mut Program, items: &mut Vec<Ast>) {
|
||||
for item in items.iter_mut() {
|
||||
match item {
|
||||
Ast::Statement(stat) => {
|
||||
let loc = stat.loc().clone();
|
||||
match stat.inner_mut() {
|
||||
Statement::Fn(func)=> {
|
||||
if let Some(name) = &func.name {
|
||||
if let Some(struct_name) = &func.struct_name {
|
||||
if let Some(v) = prog.member_functions.get_mut(&struct_name) {
|
||||
v.insert(name.clone(), LocBox::new(&loc, func.clone()));
|
||||
} else {
|
||||
let mut v = HashMap::new();
|
||||
v.insert(name.clone(), LocBox::new(&loc, func.clone()));
|
||||
prog.member_functions.insert(struct_name.clone(), v);
|
||||
}
|
||||
} else {
|
||||
prog.functions.insert(name.clone(), LocBox::new(&loc, func.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
Statement::Enum(enm) => {
|
||||
prog.enums.insert(enm.name.clone(), LocBox::new(&loc, enm.clone()));
|
||||
}
|
||||
Statement::Struct(strct) => {
|
||||
prog.structs.insert(strct.name.clone(), LocBox::new(&loc, strct.clone()));
|
||||
}
|
||||
Statement::TypeAlias(alias) => {
|
||||
let typ = alias.clone().typ.inner_mut().clone();
|
||||
prog.types.insert(alias.name.clone(), LocBox::new(stat.loc(), typ));
|
||||
}
|
||||
Statement::Include |
|
||||
Statement::Let { .. } => (),
|
||||
Statement::ConstVar(var) => {
|
||||
prog.const_vars.insert(var.name.clone(), var.clone());
|
||||
}
|
||||
Statement::StaticVar(var) => {
|
||||
prog.static_vars.insert(var.name.clone(), var.clone());
|
||||
},
|
||||
}
|
||||
}
|
||||
Ast::Expr(expr) => {
|
||||
match expr.inner_mut() {
|
||||
Expr::Literal(id, val) => {
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
let mut hasher = DefaultHasher::new();
|
||||
(*val).hash(&mut hasher);
|
||||
let hash = hasher.finish();
|
||||
*id = hash.to_string();
|
||||
|
||||
prog.literals.insert(id.clone(), val.clone());
|
||||
}
|
||||
Expr::Struct(id, strct) => {
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
let mut hasher = DefaultHasher::new();
|
||||
(*strct).hash(&mut hasher);
|
||||
let hash = hasher.finish();
|
||||
*id = hash.to_string();
|
||||
|
||||
prog.struct_literals.insert(id.clone(), strct.clone());
|
||||
}
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
use std::collections::HashMap;
|
||||
use lazy_static::lazy_static;
|
||||
use crate::common::Loc;
|
||||
use crate::parser::ast::expr::Path;
|
||||
use crate::parser::typ::parse_type;
|
||||
use crate::{common::loc::LocBox, parser::ast::{statement::Function, typ::Type, Ident, Program}};
|
||||
|
||||
#[cfg(target_arch="x86_64")]
|
||||
pub const SIZE: usize = 8;
|
||||
#[cfg(target_arch="x86")]
|
||||
pub const SIZE: usize = 4;
|
||||
|
||||
pub struct BuiltinType;
|
||||
|
||||
impl BuiltinType {
|
||||
pub fn void() -> Type {
|
||||
Type::new_builtin("void", 0, false)
|
||||
}
|
||||
pub fn usize() -> Type {
|
||||
Type::new_builtin("usize", SIZE as u8, false)
|
||||
}
|
||||
pub fn isize() -> Type {
|
||||
Type::new_builtin("isize", SIZE as u8, true)
|
||||
}
|
||||
pub fn u8() -> Type {
|
||||
Type::new_builtin("u8", 1, false)
|
||||
}
|
||||
pub fn u16() -> Type {
|
||||
Type::new_builtin("u16", 2, false)
|
||||
}
|
||||
pub fn u32() -> Type {
|
||||
Type::new_builtin("u32", 4, false)
|
||||
}
|
||||
pub fn u64() -> Type {
|
||||
Type::new_builtin("u64", 8, false)
|
||||
}
|
||||
pub fn i8() -> Type {
|
||||
Type::new_builtin("i8", 1, true)
|
||||
}
|
||||
pub fn i16() -> Type {
|
||||
Type::new_builtin("i16", 2, true)
|
||||
}
|
||||
pub fn i32() -> Type {
|
||||
Type::new_builtin("i32", 4, true)
|
||||
}
|
||||
pub fn i64() -> Type {
|
||||
Type::new_builtin("i64", 8, true)
|
||||
}
|
||||
pub fn bool() -> Type {
|
||||
Type::new_builtin("bool", 1, true)
|
||||
}
|
||||
pub fn char() -> Type {
|
||||
Type::new_builtin("char", 1, true)
|
||||
}
|
||||
}
|
||||
lazy_static!(
|
||||
pub static ref TYPES_RAW: Vec<Type> = [
|
||||
BuiltinType::void(),
|
||||
BuiltinType::usize(),
|
||||
BuiltinType::isize(),
|
||||
BuiltinType::u8(),
|
||||
BuiltinType::u16(),
|
||||
BuiltinType::u32(),
|
||||
BuiltinType::u64(),
|
||||
BuiltinType::i8(),
|
||||
BuiltinType::i16(),
|
||||
BuiltinType::i32(),
|
||||
BuiltinType::i64(),
|
||||
BuiltinType::bool(),
|
||||
BuiltinType::char()
|
||||
].to_vec();
|
||||
pub static ref FUNCTIONS: HashMap<&'static str, (Vec<(&'static str, Type)>, Type)> = [
|
||||
("__INTERNAL_syscall0", (vec![
|
||||
("sc_num", BuiltinType::usize()),
|
||||
], BuiltinType::usize())),
|
||||
("__INTERNAL_syscall1", (vec![
|
||||
("sc_num", BuiltinType::usize()),
|
||||
("arg0", BuiltinType::void().as_ref()),
|
||||
], BuiltinType::usize())),
|
||||
("__INTERNAL_syscall2", (vec![
|
||||
("sc_num", BuiltinType::usize()),
|
||||
("arg0", BuiltinType::void().as_ref()),
|
||||
("arg1", BuiltinType::void().as_ref()),
|
||||
], BuiltinType::usize())),
|
||||
("__INTERNAL_syscall3", (vec![
|
||||
("sc_num", BuiltinType::usize()),
|
||||
("arg0", BuiltinType::void().as_ref()),
|
||||
("arg1", BuiltinType::void().as_ref()),
|
||||
("arg2", BuiltinType::void().as_ref()),
|
||||
], BuiltinType::usize())),
|
||||
("__INTERNAL_syscall4", (vec![
|
||||
("sc_num", BuiltinType::usize()),
|
||||
("arg0", BuiltinType::void().as_ref()),
|
||||
("arg1", BuiltinType::void().as_ref()),
|
||||
("arg2", BuiltinType::void().as_ref()),
|
||||
("arg3", BuiltinType::void().as_ref()),
|
||||
], BuiltinType::usize())),
|
||||
("__INTERNAL_syscall5", (vec![
|
||||
("sc_num", BuiltinType::usize()),
|
||||
("arg0", BuiltinType::void().as_ref()),
|
||||
("arg1", BuiltinType::void().as_ref()),
|
||||
("arg2", BuiltinType::void().as_ref()),
|
||||
("arg3", BuiltinType::void().as_ref()),
|
||||
("arg4", BuiltinType::void().as_ref()),
|
||||
], BuiltinType::usize())),
|
||||
("__INTERNAL_syscall6", (vec![
|
||||
("sc_num", BuiltinType::usize()),
|
||||
("arg0", BuiltinType::void().as_ref()),
|
||||
("arg1", BuiltinType::void().as_ref()),
|
||||
("arg2", BuiltinType::void().as_ref()),
|
||||
("arg3", BuiltinType::void().as_ref()),
|
||||
("arg4", BuiltinType::void().as_ref()),
|
||||
("arg5", BuiltinType::void().as_ref()),
|
||||
], BuiltinType::usize())),
|
||||
].into();
|
||||
);
|
||||
|
||||
pub fn get_builtin_from_name(type_name: &str) -> Option<Type> {
|
||||
for t in TYPES_RAW.iter() {
|
||||
match t {
|
||||
Type::Builtin { name, .. } if name.as_str() == type_name => {
|
||||
return Some(t.clone());
|
||||
}
|
||||
Type::Builtin { .. } => (),
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn load_builtin(prog: &mut Program) {
|
||||
let loc = Loc::new("(internal)", 0, 0);
|
||||
for t in TYPES_RAW.iter() {
|
||||
let Type::Builtin { name, size, signed } = t else {unreachable!()};
|
||||
prog.types.insert(
|
||||
Ident(name.clone()),
|
||||
LocBox::new(&loc, Type::Builtin{ name: name.to_string(), size: *size as u8, signed: *signed })
|
||||
);
|
||||
}
|
||||
|
||||
for (name, (args, ret_typ)) in FUNCTIONS.iter() {
|
||||
let mut params = Vec::new();
|
||||
let mut ret_type = None;
|
||||
if !ret_typ.is_void() {
|
||||
|
||||
ret_type = Some(Box::new(LocBox::new(&Loc::default(), ret_typ.clone())));
|
||||
}
|
||||
for (name, typ) in args {
|
||||
params.push((Ident(name.to_string()), LocBox::new(&Loc::new("(internal)", 0, 0), typ.clone())));
|
||||
}
|
||||
|
||||
let f = Function {
|
||||
struct_name: None,
|
||||
name: Some(Ident(name.to_string())),
|
||||
params,
|
||||
ret_type,
|
||||
qual_const: false,
|
||||
qual_extern: None,
|
||||
body: None
|
||||
};
|
||||
prog.functions.insert(Ident(name.to_string()), LocBox::new(&Loc::new("(internal)", 0, 0), f));
|
||||
}
|
||||
}
|
||||
|
||||
2
std/core.mcl
Normal file
2
std/core.mcl
Normal file
@@ -0,0 +1,2 @@
|
||||
include "std/str.mcl";
|
||||
include "std/io/mod.mcl";
|
||||
12
std/io/mod.mcl
Normal file
12
std/io/mod.mcl
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
fn write_str(fd: usize, s: &str) -> usize {
|
||||
return __INTERNAL_syscall3(1, fd as &void, s->inner as &void, s->len as &void);
|
||||
}
|
||||
|
||||
fn puts(s: &str) {
|
||||
write_str(1, s);
|
||||
}
|
||||
|
||||
fn eputs(s: &str) {
|
||||
write_str(2, s);
|
||||
}
|
||||
107
std/lib/beaker.mcl
Normal file
107
std/lib/beaker.mcl
Normal file
@@ -0,0 +1,107 @@
|
||||
include "std/core.mcl";
|
||||
|
||||
const MAX_CONTEXT_VARS: usize = 32;
|
||||
const MAX_KEY_LEN: usize = 64;
|
||||
const MAX_VALUE_LEN: usize = 256;
|
||||
const MAX_PATH_LEN: usize = 256;
|
||||
const MAX_HANDLERS: usize = 32;
|
||||
const BUFFER_SIZE: usize = 4096;
|
||||
const MAX_URL_PARAMS: usize = 16;
|
||||
const MAX_COOKIES: usize = 10;
|
||||
const MAX_OUTER_ARRAY_ITEMS: usize = 100;
|
||||
const MAX_INNER_ARRAY_ITEMS: usize = 200;
|
||||
|
||||
const TEMPLATES_DIR: &str = "templates/";
|
||||
const STATIC_DIR: &str = "static/";
|
||||
/*
|
||||
enum ContextType {
|
||||
CONTEXT_TYPE_STRING,
|
||||
CONTEXT_TYPE_STRING_ARRAY,
|
||||
CONTEXT_TYPE_STRING_2D_ARRAY,
|
||||
};
|
||||
*/
|
||||
type ContextType = i32;
|
||||
struct CV_array_data {
|
||||
values: &&char,
|
||||
count: i32
|
||||
}
|
||||
|
||||
struct CV_2d_array_data {
|
||||
values: &&&char,
|
||||
count: i32
|
||||
}
|
||||
|
||||
|
||||
struct ContextVarStr {
|
||||
key: [char; MAX_KEY_LEN],
|
||||
typ: ContextType,
|
||||
value: [char; MAX_VALUE_LEN]
|
||||
}
|
||||
|
||||
struct ContextVarArr {
|
||||
key: [char; MAX_KEY_LEN],
|
||||
typ: ContextType,
|
||||
value: CV_array_data,
|
||||
}
|
||||
|
||||
struct ContextVar2dArr {
|
||||
key: [char; MAX_KEY_LEN],
|
||||
typ: ContextType,
|
||||
value: CV_2d_array_data,
|
||||
}
|
||||
|
||||
struct ContextVar {
|
||||
key: [char; MAX_KEY_LEN],
|
||||
typ: ContextType,
|
||||
value: [char; MAX_VALUE_LEN]
|
||||
}
|
||||
|
||||
struct TemplateContext {
|
||||
vars: [ContextVar; MAX_CONTEXT_VARS],
|
||||
count: i32,
|
||||
}
|
||||
|
||||
struct UrlParam {
|
||||
key: [char; MAX_KEY_LEN],
|
||||
value: [char; MAX_VALUE_LEN],
|
||||
}
|
||||
|
||||
struct UrlParams {
|
||||
params: [UrlParam; MAX_URL_PARAMS],
|
||||
count: i32
|
||||
}
|
||||
struct Cookie {
|
||||
name: [char; MAX_KEY_LEN],
|
||||
value: [char; MAX_VALUE_LEN],
|
||||
expires: [char; MAX_VALUE_LEN],
|
||||
path: [char; MAX_KEY_LEN],
|
||||
http_only: bool,
|
||||
secure: bool
|
||||
}
|
||||
|
||||
type RequestHandler = fn(params: &UrlParams) -> i32;
|
||||
|
||||
struct RouteHandler {
|
||||
path: [char; MAX_PATH_LEN],
|
||||
handler: RequestHandler
|
||||
}
|
||||
|
||||
extern fn new_context() -> TemplateContext;
|
||||
extern fn context_set(ctx: &mut TemplateContext, key: &cstr, value: &cstr);
|
||||
extern fn context_set_string_array(ctx: &mut TemplateContext, key: &cstr, values: &cstr, count: i32);
|
||||
extern fn context_set_array_of_arrays(ctx: &mut TemplateContext, key: &cstr, values: [[&cstr]], outer_count: i32, inner_count: i32);
|
||||
extern fn free_context(ctx: &mut TemplateContext);
|
||||
extern fn render_template(template_file: &cstr, ctx: &mut TemplateContext) -> &cstr;
|
||||
|
||||
extern fn send_response(html: &cstr);
|
||||
extern fn send_redirect(location: &cstr);
|
||||
extern fn set_cookie(name: &cstr, value: &cstr, expires: &cstr, path: &cstr, http_only: bool, secure: bool);
|
||||
extern fn get_cookie(name: &cstr) -> &cstr;
|
||||
|
||||
extern fn set_handler(path: &cstr, handler: RequestHandler);
|
||||
extern fn parse_request_url(request_buffer: &cstr, params: &UrlParams) -> &cstr;
|
||||
extern fn get_mime_type(file_path: &cstr) -> &cstr;
|
||||
extern fn serve_static_file(request_path_relative_to_static: &cstr) -> bool;
|
||||
|
||||
extern fn beaker_run(ip: &cstr, port: i32) -> i32;
|
||||
|
||||
14
std/str.mcl
Normal file
14
std/str.mcl
Normal file
@@ -0,0 +1,14 @@
|
||||
type cstr = [u8];
|
||||
|
||||
|
||||
struct str {
|
||||
len: usize,
|
||||
inner: &cstr
|
||||
}
|
||||
|
||||
fn str.len(&self) -> usize {
|
||||
return self.len;
|
||||
}
|
||||
|
||||
|
||||
|
||||
115
test.mcl
115
test.mcl
@@ -1,86 +1,47 @@
|
||||
type str = [u8];
|
||||
|
||||
enum Wah {
|
||||
A,
|
||||
B,
|
||||
C,
|
||||
D
|
||||
include "std.mcl";
|
||||
|
||||
struct Foo {
|
||||
a: usize,
|
||||
b: &str
|
||||
}
|
||||
|
||||
struct Baz {
|
||||
owo: i32,
|
||||
uwu: usize
|
||||
fn Foo.new(a1: usize, b: &str) -> &Foo {
|
||||
return &Foo {
|
||||
a: a1,
|
||||
b: b
|
||||
};
|
||||
}
|
||||
|
||||
/// Type definitions
|
||||
// type Rah = &Baz;
|
||||
|
||||
/// Different kinds of functions
|
||||
// Normal function
|
||||
// fn main(a: &Foo, b: Rah) -> Nya {
|
||||
/// General expressions (math)
|
||||
// Works
|
||||
// let a = 1 * 3 == 4;
|
||||
// let b = 3/4 == *a;
|
||||
// let c = (a->b.c->d) / 2;
|
||||
// let d = 2 / a->b.c->d;
|
||||
// let e = a->b.c->d / 2;
|
||||
// let f = a.b.c.d / 2;
|
||||
// let g = a.b[a.c] * 5;
|
||||
|
||||
// No worky
|
||||
// nothing! yay!
|
||||
|
||||
/// Struct literals
|
||||
// let a = Baz {
|
||||
// owo: a,
|
||||
// uwu: b + c / d
|
||||
// };
|
||||
|
||||
/// If statement
|
||||
// if 1 > 3 {
|
||||
// ";3"
|
||||
// } else
|
||||
// if *a == 3 {
|
||||
// ":0"
|
||||
// } else {
|
||||
// ">:("
|
||||
// }
|
||||
|
||||
|
||||
/// 3 kinds of loops all doing the same thing
|
||||
/// While loops
|
||||
// let iw = 0;
|
||||
// while iw < 10 {
|
||||
// println("Owo");
|
||||
// }
|
||||
|
||||
/// For loops
|
||||
// for let ifr = 0 ; ifr < 20 ; ifr += 1 {
|
||||
// println("nya");
|
||||
// }
|
||||
|
||||
/// Infinite loops
|
||||
// let il = 0;
|
||||
// loop {
|
||||
// if il > 10 {
|
||||
// break;
|
||||
// }
|
||||
// println("Rah");
|
||||
// }
|
||||
|
||||
/// Function Calls
|
||||
// println(":3");
|
||||
//}
|
||||
|
||||
// Struct member function with inner data
|
||||
//fn Baz.main(self: &mut Baz, a: &Foo, b: &mut Bar) -> &Nya;
|
||||
|
||||
// Struct member function without any data a.k.a a static member func
|
||||
//fn Baz.main(a: &Foo, b: &mut Bar) -> &Nya;
|
||||
|
||||
|
||||
|
||||
fn print(s: &str) {
|
||||
// do nothign for now
|
||||
}
|
||||
|
||||
fn mul(n: usize, n2: usize) -> usize {
|
||||
return n * n2;
|
||||
}
|
||||
|
||||
fn main() -> i32 {
|
||||
let obj = Foo::new(1, "owo");
|
||||
obj->b;
|
||||
let owo = "ahahaha";
|
||||
loop {
|
||||
print(owo);
|
||||
}
|
||||
|
||||
for (let i = 0; i < 10; i += 1) {
|
||||
puts("nyaaa");
|
||||
if (i > 7) {
|
||||
break;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
print("cant see me!");
|
||||
}
|
||||
while (true) {
|
||||
mul(1);
|
||||
}
|
||||
}
|
||||
|
||||
const FOO: usize = main;
|
||||
|
||||
18
test2.mcl
Normal file
18
test2.mcl
Normal file
@@ -0,0 +1,18 @@
|
||||
// include "std/core.mcl";
|
||||
include "std/lib/beaker.mcl";
|
||||
|
||||
|
||||
|
||||
fn hello_world_handler(params: &UrlParams) -> i32 {
|
||||
send_response(c"Hello, World!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn main() -> i32 {
|
||||
set_handler(c"/", hello_world_handler);
|
||||
let result = beaker_run(c"127.0.0.1", 0xff00000000000000 as i32);
|
||||
|
||||
if (!(result as bool)) {
|
||||
eputs("[APP] Error: Beaker server failed to start.\n");
|
||||
}
|
||||
}
|
||||
1
tests/mod.rs
Normal file
1
tests/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
mod parser;
|
||||
@@ -1,10 +0,0 @@
|
||||
Program {
|
||||
ast: Block(
|
||||
[],
|
||||
),
|
||||
structs: {},
|
||||
enums: {},
|
||||
types: {},
|
||||
functions: {},
|
||||
member_functions: {},
|
||||
}
|
||||
3
tests/parser/expr.rs
Normal file
3
tests/parser/expr.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
Program {
|
||||
ast: Block(
|
||||
[],
|
||||
),
|
||||
structs: {},
|
||||
enums: {},
|
||||
types: {},
|
||||
functions: {},
|
||||
member_functions: {},
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
Program {
|
||||
ast: Block(
|
||||
[],
|
||||
),
|
||||
structs: {},
|
||||
enums: {},
|
||||
types: {},
|
||||
functions: {},
|
||||
member_functions: {},
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
Program {
|
||||
ast: Block(
|
||||
[],
|
||||
),
|
||||
structs: {},
|
||||
enums: {},
|
||||
types: {},
|
||||
functions: {},
|
||||
member_functions: {},
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
Program {
|
||||
ast: Block(
|
||||
[],
|
||||
),
|
||||
structs: {},
|
||||
enums: {},
|
||||
types: {},
|
||||
functions: {},
|
||||
member_functions: {},
|
||||
}
|
||||
3
tests/parser/mod.rs
Normal file
3
tests/parser/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
mod expr;
|
||||
mod stat;
|
||||
mod typ;
|
||||
52
tests/parser/stat/constant.rs
Normal file
52
tests/parser/stat/constant.rs
Normal file
@@ -0,0 +1,52 @@
|
||||
use mclangc::{
|
||||
cli::CliArgs,
|
||||
common::{Loc, loc::LocBox},
|
||||
parser::{self, ast::{
|
||||
Ident, Keyword, Number, Punctuation, TokenType, expr::Expr, literal::Literal,
|
||||
statement::{Statement, ConstVar},
|
||||
}},
|
||||
tokeniser::Token, validator::predefined::BuiltinType
|
||||
};
|
||||
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_parse_const_stat() {
|
||||
let mut tokens = vec![
|
||||
Token::new_test(TokenType::Keyword(Keyword::Const)),
|
||||
Token::new_test(TokenType::ident("MyConstant")),
|
||||
Token::new_test(TokenType::Punct(Punctuation::Colon)),
|
||||
Token::new_test(TokenType::ident("usize")),
|
||||
Token::new_test(TokenType::Punct(Punctuation::Eq)),
|
||||
Token::new_test(TokenType::number(69, 10, false)),
|
||||
Token::new_test(TokenType::Punct(Punctuation::Semi)),
|
||||
Token::new_test(TokenType::ident("overflow")),
|
||||
Token::new_test(TokenType::ident("overflow")),
|
||||
Token::new_test(TokenType::ident("overflow")),
|
||||
];
|
||||
tokens.reverse();
|
||||
let res = parser::stat::parse_statement(&mut tokens, &CliArgs::default(), &mut super::get_prog());
|
||||
assert!(res.is_ok(), "{res:?}");
|
||||
|
||||
match res {
|
||||
Ok(res) => {
|
||||
assert!(res.is_some(), "{res:?}");
|
||||
match res {
|
||||
Some(res) => {
|
||||
assert_eq!(res.inner().clone(), Statement::ConstVar(ConstVar {
|
||||
name: Ident::new("MyConstant"),
|
||||
typ: LocBox::new(&Loc::default(), BuiltinType::usize()),
|
||||
val: LocBox::new(&Loc::default(), Expr::Literal(String::new(), Literal::Number(Number { val: 69, base: 10, signed: false })))
|
||||
}));
|
||||
}
|
||||
None => unreachable!()
|
||||
}
|
||||
|
||||
}
|
||||
Err(_) => unreachable!()
|
||||
}
|
||||
assert!(tokens.len() == 3, "leftover token count incorrect");
|
||||
}
|
||||
|
||||
|
||||
|
||||
45
tests/parser/stat/mod.rs
Normal file
45
tests/parser/stat/mod.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
use mclangc::{
|
||||
common::{Loc, loc::LocBox},
|
||||
parser::{
|
||||
ast::{
|
||||
Ident, Program,
|
||||
statement::{Enum, Struct},
|
||||
typ::Type
|
||||
}
|
||||
},
|
||||
validator::predefined::{BuiltinType, load_builtin}
|
||||
};
|
||||
|
||||
|
||||
mod constant;
|
||||
mod statc;
|
||||
mod type_alias;
|
||||
|
||||
pub fn get_prog() -> Program {
|
||||
let mut prog = Program::default();
|
||||
load_builtin(&mut prog);
|
||||
let loc = Loc::default();
|
||||
prog.structs.insert(Ident::new("MyStruct"), LocBox::new(&loc, Struct {
|
||||
name: Ident::new("MyStruct"),
|
||||
fields: vec![
|
||||
(Ident::new("foo"), LocBox::new(&loc, BuiltinType::usize())),
|
||||
(Ident::new("bar"), LocBox::new(&loc, BuiltinType::bool())),
|
||||
(Ident::new("baz"), LocBox::new(&loc, Type::Owned(Ident::new("str")).as_ref())),
|
||||
]
|
||||
}));
|
||||
prog.enums.insert(Ident::new("MyEnum"), LocBox::new(&loc, Enum {
|
||||
name: Ident::new("MyEnum"),
|
||||
fields: vec![],
|
||||
}));
|
||||
prog.types.insert(Ident::new("MyType"), LocBox::new(&loc, Type::Owned(Ident::new("MyStruct"))));
|
||||
prog.types.insert(Ident::new("MyType2"), LocBox::new(&loc, Type::Owned(Ident::new("MyType"))));
|
||||
prog.types.insert(Ident::new("MyType3"), LocBox::new(&loc, Type::Owned(Ident::new("MyType2")).as_ref()));
|
||||
prog.types.insert(Ident::new("MyTypeInf"), LocBox::new(&loc, Type::Owned(Ident::new("MyTypeInf"))));
|
||||
prog.types.insert(Ident::new("MyTypeInfIndirect"), LocBox::new(&loc, Type::Owned(Ident::new("MyType4"))));
|
||||
prog.types.insert(Ident::new("MyType4"), LocBox::new(&loc, Type::Owned(Ident::new("MyType5"))));
|
||||
prog.types.insert(Ident::new("MyType5"), LocBox::new(&loc, Type::Owned(Ident::new("MyType4"))));
|
||||
|
||||
prog
|
||||
}
|
||||
|
||||
|
||||
48
tests/parser/stat/statc.rs
Normal file
48
tests/parser/stat/statc.rs
Normal file
@@ -0,0 +1,48 @@
|
||||
use mclangc::{
|
||||
cli::CliArgs,
|
||||
common::{Loc, loc::LocBox},
|
||||
parser::{self, ast::{
|
||||
Ident, Keyword, Number, Punctuation, TokenType, expr::Expr, literal::Literal,
|
||||
statement::{Statement, StaticVar},
|
||||
}},
|
||||
tokeniser::Token, validator::predefined::BuiltinType
|
||||
};
|
||||
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_parse_static_stat() {
|
||||
let mut tokens = vec![
|
||||
Token::new_test(TokenType::Keyword(Keyword::Static)),
|
||||
Token::new_test(TokenType::ident("MyStatic")),
|
||||
Token::new_test(TokenType::Punct(Punctuation::Colon)),
|
||||
Token::new_test(TokenType::ident("usize")),
|
||||
Token::new_test(TokenType::Punct(Punctuation::Eq)),
|
||||
Token::new_test(TokenType::number(69, 10, false)),
|
||||
Token::new_test(TokenType::Punct(Punctuation::Semi)),
|
||||
Token::new_test(TokenType::ident("overflow")),
|
||||
Token::new_test(TokenType::ident("overflow")),
|
||||
Token::new_test(TokenType::ident("overflow")),
|
||||
];
|
||||
tokens.reverse();
|
||||
let res = parser::stat::parse_statement(&mut tokens, &CliArgs::default(), &mut super::get_prog());
|
||||
assert!(res.is_ok(), "{res:?}");
|
||||
match res {
|
||||
Ok(res) => {
|
||||
assert!(res.is_some(), "{res:?}");
|
||||
match res {
|
||||
Some(res) => {
|
||||
assert_eq!(res.inner().clone(), Statement::StaticVar(StaticVar {
|
||||
name: Ident::new("MyStatic"),
|
||||
typ: LocBox::new(&Loc::default(), BuiltinType::usize()),
|
||||
val: LocBox::new(&Loc::default(), Expr::Literal(String::new(), Literal::Number(Number { val: 69, base: 10, signed: false })))
|
||||
}));
|
||||
}
|
||||
None => unreachable!()
|
||||
}
|
||||
|
||||
}
|
||||
Err(_) => unreachable!()
|
||||
}
|
||||
assert!(tokens.len() == 3, "leftover token count incorrect");
|
||||
}
|
||||
48
tests/parser/stat/type_alias.rs
Normal file
48
tests/parser/stat/type_alias.rs
Normal file
@@ -0,0 +1,48 @@
|
||||
use mclangc::{
|
||||
cli::CliArgs,
|
||||
common::{Loc, loc::LocBox},
|
||||
parser::{self, ast::{
|
||||
Ident, Keyword, Number, Punctuation, TokenType, expr::Expr, literal::Literal,
|
||||
statement::{Statement, StaticVar},
|
||||
}},
|
||||
tokeniser::Token, validator::predefined::BuiltinType
|
||||
};
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_parse_alias_stat() {
|
||||
let mut tokens = vec![
|
||||
Token::new_test(TokenType::Keyword(Keyword::Static)),
|
||||
Token::new_test(TokenType::ident("MyStatic")),
|
||||
Token::new_test(TokenType::Punct(Punctuation::Colon)),
|
||||
Token::new_test(TokenType::ident("usize")),
|
||||
Token::new_test(TokenType::Punct(Punctuation::Eq)),
|
||||
Token::new_test(TokenType::number(69, 10, false)),
|
||||
Token::new_test(TokenType::Punct(Punctuation::Semi)),
|
||||
Token::new_test(TokenType::ident("overflow")),
|
||||
Token::new_test(TokenType::ident("overflow")),
|
||||
Token::new_test(TokenType::ident("overflow")),
|
||||
];
|
||||
tokens.reverse();
|
||||
let res = parser::stat::parse_statement(&mut tokens, &CliArgs::default(), &mut super::get_prog());
|
||||
assert!(res.is_ok(), "{res:?}");
|
||||
match res {
|
||||
Ok(res) => {
|
||||
assert!(res.is_some(), "{res:?}");
|
||||
match res {
|
||||
Some(res) => {
|
||||
assert_eq!(res.inner().clone(), Statement::StaticVar(StaticVar {
|
||||
name: Ident::new("MyStatic"),
|
||||
typ: LocBox::new(&Loc::default(), BuiltinType::usize()),
|
||||
val: LocBox::new(&Loc::default(), Expr::Literal(String::new(), Literal::Number(Number { val: 69, base: 10, signed: false })))
|
||||
}));
|
||||
}
|
||||
None => unreachable!()
|
||||
}
|
||||
|
||||
}
|
||||
Err(_) => unreachable!()
|
||||
}
|
||||
assert!(tokens.len() == 3, "leftover token count incorrect");
|
||||
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
Program {
|
||||
ast: Block(
|
||||
[],
|
||||
),
|
||||
structs: {},
|
||||
enums: {},
|
||||
types: {},
|
||||
functions: {},
|
||||
member_functions: {},
|
||||
}
|
||||
245
tests/parser/typ.rs
Normal file
245
tests/parser/typ.rs
Normal file
@@ -0,0 +1,245 @@
|
||||
use std::vec;
|
||||
|
||||
use anyhow::bail;
|
||||
use mclangc::{common::{Loc, loc::LocBox}, parser::{self, ast::{Ident, Keyword, Program, Punctuation, TokenType, statement::{Enum, Struct}, typ::Type}}, tokeniser::Token, validator::{self, predefined::{BuiltinType, get_builtin_from_name, load_builtin}}};
|
||||
|
||||
|
||||
|
||||
fn get_prog() -> Program {
|
||||
let mut prog = Program::default();
|
||||
load_builtin(&mut prog);
|
||||
let loc = Loc::default();
|
||||
prog.structs.insert(Ident::new("MyStruct"), LocBox::new(&loc, Struct {
|
||||
name: Ident::new("MyStruct"),
|
||||
fields: vec![
|
||||
(Ident::new("foo"), LocBox::new(&loc, BuiltinType::usize())),
|
||||
(Ident::new("bar"), LocBox::new(&loc, BuiltinType::bool())),
|
||||
(Ident::new("baz"), LocBox::new(&loc, Type::Owned(Ident::new("str")).as_ref())),
|
||||
]
|
||||
}));
|
||||
prog.enums.insert(Ident::new("MyEnum"), LocBox::new(&loc, Enum {
|
||||
name: Ident::new("MyEnum"),
|
||||
fields: vec![],
|
||||
}));
|
||||
prog.types.insert(Ident::new("MyType"), LocBox::new(&loc, Type::Owned(Ident::new("MyStruct"))));
|
||||
prog.types.insert(Ident::new("MyType2"), LocBox::new(&loc, Type::Owned(Ident::new("MyType"))));
|
||||
prog.types.insert(Ident::new("MyType3"), LocBox::new(&loc, Type::Owned(Ident::new("MyType2")).as_ref()));
|
||||
prog.types.insert(Ident::new("MyTypeInf"), LocBox::new(&loc, Type::Owned(Ident::new("MyTypeInf"))));
|
||||
prog.types.insert(Ident::new("MyTypeInfIndirect"), LocBox::new(&loc, Type::Owned(Ident::new("MyType4"))));
|
||||
prog.types.insert(Ident::new("MyType4"), LocBox::new(&loc, Type::Owned(Ident::new("MyType5"))));
|
||||
prog.types.insert(Ident::new("MyType5"), LocBox::new(&loc, Type::Owned(Ident::new("MyType4"))));
|
||||
|
||||
prog
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_type_alias_simple() {
|
||||
let mut tokens = vec![
|
||||
Token::new_test(TokenType::Ident(Ident::new("MyType")))
|
||||
];
|
||||
let ret = parser::typ::parse_type(&mut tokens);
|
||||
let mut prog = get_prog();
|
||||
let ret = validator::validate_type(&mut prog, &ret.unwrap());
|
||||
assert!(ret.is_ok());
|
||||
assert_eq!(ret.unwrap(), Type::Owned(Ident::new("MyStruct")))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_type_alias_deep() {
|
||||
let mut tokens = vec![
|
||||
Token::new_test(TokenType::Ident(Ident::new("MyType3")))
|
||||
];
|
||||
let ret = parser::typ::parse_type(&mut tokens);
|
||||
let mut prog = get_prog();
|
||||
let ret = validator::validate_type(&mut prog, &ret.unwrap());
|
||||
assert!(ret.is_ok());
|
||||
assert_eq!(ret.unwrap(), Type::Owned(Ident::new("MyStruct")))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_type_alias_recursive_simple() {
|
||||
let mut tokens = vec![
|
||||
Token::new_test(TokenType::Ident(Ident::new("MyTypeInf")))
|
||||
];
|
||||
let ret = parser::typ::parse_type(&mut tokens);
|
||||
let mut prog = get_prog();
|
||||
let ret = validator::validate_type(&mut prog, &ret.unwrap());
|
||||
assert!(ret.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_type_alias_recursive_indirect() {
|
||||
let mut tokens = vec![
|
||||
Token::new_test(TokenType::Ident(Ident::new("MyType4")))
|
||||
];
|
||||
let ret = parser::typ::parse_type(&mut tokens);
|
||||
let mut prog = get_prog();
|
||||
let ret = validator::validate_type(&mut prog, &ret.unwrap());
|
||||
assert!(ret.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_type_named_struct() {
|
||||
let mut tokens = vec![
|
||||
Token::new_test(TokenType::Ident(Ident::new("MyStruct")))
|
||||
];
|
||||
let ret = parser::typ::parse_type(&mut tokens);
|
||||
assert!(ret.is_ok());
|
||||
assert_eq!(ret.unwrap(), LocBox::new(&Loc::default(), Type::Owned(Ident::new("MyStruct"))))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_type_named_enum() {
|
||||
let mut tokens = vec![
|
||||
Token::new_test(TokenType::Ident(Ident::new("MyEnum")))
|
||||
];
|
||||
let ret = parser::typ::parse_type(&mut tokens);
|
||||
assert!(ret.is_ok());
|
||||
assert_eq!(ret.unwrap(), LocBox::new(&Loc::default(), Type::Owned(Ident::new("MyEnum"))))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_type_ref() {
|
||||
let mut tokens = vec![
|
||||
Token::new_test(TokenType::Punct(Punctuation::Ampersand)),
|
||||
Token::new_test(TokenType::Ident(Ident::new("MyStruct")))
|
||||
];
|
||||
tokens.reverse();
|
||||
let ret = parser::typ::parse_type(&mut tokens);
|
||||
assert!(ret.is_ok());
|
||||
assert_eq!(ret.unwrap(), LocBox::new(&Loc::default(), Type::Owned(Ident::new("MyStruct")).as_ref()))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_type_ref2() {
|
||||
let mut tokens = vec![
|
||||
Token::new_test(TokenType::Punct(Punctuation::Ampersand)),
|
||||
Token::new_test(TokenType::Punct(Punctuation::Ampersand)),
|
||||
Token::new_test(TokenType::Ident(Ident::new("MyStruct")))
|
||||
];
|
||||
tokens.reverse();
|
||||
let ret = parser::typ::parse_type(&mut tokens);
|
||||
assert!(ret.is_ok());
|
||||
assert_eq!(ret.unwrap(), LocBox::new(&Loc::default(), Type::Owned(Ident::new("MyStruct")).as_ref().as_ref()))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_type_ref3() {
|
||||
let mut tokens = vec![
|
||||
Token::new_test(TokenType::Punct(Punctuation::Ampersand)),
|
||||
Token::new_test(TokenType::Punct(Punctuation::Ampersand)),
|
||||
Token::new_test(TokenType::Punct(Punctuation::Ampersand)),
|
||||
Token::new_test(TokenType::Ident(Ident::new("MyStruct")))
|
||||
];
|
||||
tokens.reverse();
|
||||
let ret = parser::typ::parse_type(&mut tokens);
|
||||
assert!(ret.is_ok());
|
||||
assert_eq!(ret.unwrap(), LocBox::new(&Loc::default(), Type::Owned(Ident::new("MyStruct")).as_ref().as_ref().as_ref()))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_type_mut_ref() {
|
||||
let mut tokens = vec![
|
||||
Token::new_test(TokenType::Punct(Punctuation::Ampersand)),
|
||||
Token::new_test(TokenType::Keyword(Keyword::Mut)),
|
||||
Token::new_test(TokenType::Ident(Ident::new("MyStruct")))
|
||||
];
|
||||
tokens.reverse();
|
||||
let ret = parser::typ::parse_type(&mut tokens);
|
||||
assert!(ret.is_ok());
|
||||
assert_eq!(ret.unwrap(), LocBox::new(&Loc::default(), Type::Owned(Ident::new("MyStruct")).as_ref_mut()))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_type_mut_ref2() {
|
||||
let mut tokens = vec![
|
||||
Token::new_test(TokenType::Punct(Punctuation::Ampersand)),
|
||||
Token::new_test(TokenType::Keyword(Keyword::Mut)),
|
||||
Token::new_test(TokenType::Punct(Punctuation::Ampersand)),
|
||||
Token::new_test(TokenType::Keyword(Keyword::Mut)),
|
||||
Token::new_test(TokenType::Ident(Ident::new("MyStruct")))
|
||||
];
|
||||
tokens.reverse();
|
||||
let ret = parser::typ::parse_type(&mut tokens);
|
||||
assert!(ret.is_ok());
|
||||
assert_eq!(ret.unwrap(), LocBox::new(&Loc::default(), Type::Owned(Ident::new("MyStruct")).as_ref_mut().as_ref_mut()))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_type_mut_ref3() {
|
||||
let mut tokens = vec![
|
||||
Token::new_test(TokenType::Punct(Punctuation::Ampersand)),
|
||||
Token::new_test(TokenType::Keyword(Keyword::Mut)),
|
||||
Token::new_test(TokenType::Punct(Punctuation::Ampersand)),
|
||||
Token::new_test(TokenType::Keyword(Keyword::Mut)),
|
||||
Token::new_test(TokenType::Punct(Punctuation::Ampersand)),
|
||||
Token::new_test(TokenType::Keyword(Keyword::Mut)),
|
||||
Token::new_test(TokenType::Ident(Ident::new("MyStruct")))
|
||||
];
|
||||
tokens.reverse();
|
||||
let ret = parser::typ::parse_type(&mut tokens);
|
||||
assert!(ret.is_ok());
|
||||
assert_eq!(ret.unwrap(), LocBox::new(&Loc::default(), Type::Owned(Ident::new("MyStruct")).as_ref_mut().as_ref_mut().as_ref_mut()))
|
||||
}
|
||||
|
||||
fn test_builtin(s: &str) {
|
||||
let mut tokens = vec![
|
||||
Token::new_test(TokenType::Ident(Ident::new(s)))
|
||||
];
|
||||
let ret = parser::typ::parse_type(&mut tokens);
|
||||
assert!(ret.is_ok());
|
||||
assert_eq!(ret.unwrap(), LocBox::new(&Loc::default(), get_builtin_from_name(s).unwrap()))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_type_builtin_usize() {
|
||||
test_builtin("usize");
|
||||
}
|
||||
#[test]
|
||||
fn parse_type_builtin_isize() {
|
||||
test_builtin("isize");
|
||||
}
|
||||
#[test]
|
||||
fn parse_type_builtin_u8() {
|
||||
test_builtin("u8");
|
||||
}
|
||||
#[test]
|
||||
fn parse_type_builtin_i8() {
|
||||
test_builtin("i8");
|
||||
}
|
||||
#[test]
|
||||
fn parse_type_builtin_u16() {
|
||||
test_builtin("u16");
|
||||
}
|
||||
#[test]
|
||||
fn parse_type_builtin_i16() {
|
||||
test_builtin("i16");
|
||||
}
|
||||
#[test]
|
||||
fn parse_type_builtin_u32() {
|
||||
test_builtin("u32");
|
||||
}
|
||||
#[test]
|
||||
fn parse_type_builtin_i32() {
|
||||
test_builtin("i32");
|
||||
}
|
||||
#[test]
|
||||
fn parse_type_builtin_u64() {
|
||||
test_builtin("u64");
|
||||
}
|
||||
#[test]
|
||||
fn parse_type_builtin_i64() {
|
||||
test_builtin("i64");
|
||||
}
|
||||
#[test]
|
||||
fn parse_type_builtin_void() {
|
||||
test_builtin("void");
|
||||
}
|
||||
#[test]
|
||||
fn parse_type_builtin_char() {
|
||||
test_builtin("char");
|
||||
}
|
||||
#[test]
|
||||
fn parse_type_builtin_bool() {
|
||||
test_builtin("bool");
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
[]
|
||||
Reference in New Issue
Block a user