Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 867f9e51bd |
Generated
+1
-1
@@ -3,5 +3,5 @@
|
|||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lang"
|
name = "v2"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|||||||
+4
-2
@@ -1,4 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "lang"
|
name = "v2"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
# the
|
|
||||||
|
|
||||||
my child (programming language)
|
|
||||||
|
|
||||||
everything is subject to change rn, and this probably isn't up to date
|
|
||||||
also nothing is super well tested right now so I'm sure there are bugs I don't think exist
|
|
||||||
|
|
||||||
`cargo run -- data/test.lang`
|
|
||||||
|
|
||||||
currently working!!:
|
|
||||||
- functions (arguments, returning)
|
|
||||||
- assembly blocks (input, output for expression)
|
|
||||||
- structs (construction, field access, modifying, nesting)
|
|
||||||
|
|
||||||
todo:
|
|
||||||
- generics (groundwork is there I think)
|
|
||||||
- traits (unsure exactly how I wanna do this, either way need number ones)
|
|
||||||
- actually handle jumps & LIs that are too large
|
|
||||||
- iterators?
|
|
||||||
- borrow checking
|
|
||||||
- basic optimization: use registers, remove temp var moves
|
|
||||||
- Cow str
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
fn main() {
|
|
||||||
// let x = 3;
|
|
||||||
let y = 4 + 4 + 5;
|
|
||||||
let z = 1 * 2 - 3 / test * 4;
|
|
||||||
let r = 1-2.5 + 3;
|
|
||||||
let w = 1 * (2 - 3) / "test" - 7
|
|
||||||
let a = test('3');
|
|
||||||
let c = '3' ;
|
|
||||||
test(5);
|
|
||||||
return 5 +
|
|
||||||
|
|
||||||
a;
|
|
||||||
r = )!!;
|
|
||||||
r = !3;
|
|
||||||
r = 3 + !;
|
|
||||||
let b = (test2.func)(3 + 4)(8)("a");
|
|
||||||
let x = {
|
|
||||||
return 5;
|
|
||||||
let a = 3;
|
|
||||||
b
|
|
||||||
};
|
|
||||||
exit(3, let, "hello");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test() {
|
|
||||||
let r = 3;
|
|
||||||
let a = }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test2() {
|
|
||||||
let a anerit;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test3() {
|
|
||||||
let x = 3
|
|
||||||
-273
@@ -1,273 +0,0 @@
|
|||||||
println("testy");
|
|
||||||
let x = 3;
|
|
||||||
print_dec(x);
|
|
||||||
subtest();
|
|
||||||
start();
|
|
||||||
|
|
||||||
fn subtest() {
|
|
||||||
fn el() {
|
|
||||||
println("helo el");
|
|
||||||
}
|
|
||||||
el();
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Test {
|
|
||||||
a: 64,
|
|
||||||
b: 64,
|
|
||||||
c: 64,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Test2 {
|
|
||||||
a: 64,
|
|
||||||
b: Test,
|
|
||||||
c: Test,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn start() {
|
|
||||||
println("Helld!");
|
|
||||||
print_hex(rem(10, 7));
|
|
||||||
println("");
|
|
||||||
println("Hello World!!!!!");
|
|
||||||
thinger();
|
|
||||||
let x = 3;
|
|
||||||
if not(not(lt(x, 5))) {
|
|
||||||
println("tada!");
|
|
||||||
};
|
|
||||||
println("before:");
|
|
||||||
x = 0;
|
|
||||||
loop {
|
|
||||||
if not(lt(x, 10)) {
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
println("RAAAAA");
|
|
||||||
x = add(x, 1);
|
|
||||||
};
|
|
||||||
println("after");
|
|
||||||
let infer_me: slice<_> = "hello";
|
|
||||||
print(tester());
|
|
||||||
let test: Test = Test {
|
|
||||||
a: 10,
|
|
||||||
b: 4,
|
|
||||||
c: 0,
|
|
||||||
};
|
|
||||||
structer(test);
|
|
||||||
arger("a", "b", "c");
|
|
||||||
let z = sub(test.a, 10);
|
|
||||||
print_hex(add(mul(sub(add(10, test.b), 1), 3), z));
|
|
||||||
print("test: 0x");
|
|
||||||
print_hex(31);
|
|
||||||
println("");
|
|
||||||
generic();
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn structer(test: Test) {
|
|
||||||
print("test {\n a: ");
|
|
||||||
print_dec(test.a);
|
|
||||||
print("\n b: ");
|
|
||||||
print_dec(test.b);
|
|
||||||
print("\n c: ");
|
|
||||||
print_dec(test.c);
|
|
||||||
println("\n}");
|
|
||||||
print("update c: ");
|
|
||||||
test.c = add(test.a, test.b);
|
|
||||||
print_dec(test.c);
|
|
||||||
println("");
|
|
||||||
|
|
||||||
let test2: Test2 = Test2 {
|
|
||||||
a: 3,
|
|
||||||
b: test,
|
|
||||||
c: test,
|
|
||||||
};
|
|
||||||
test2.c.c = 20;
|
|
||||||
print("test2.b.c: ");
|
|
||||||
print_dec(test2.b.c);
|
|
||||||
println("");
|
|
||||||
print("test2.c.c: ");
|
|
||||||
print_dec(test2.c.c);
|
|
||||||
println("");
|
|
||||||
}
|
|
||||||
|
|
||||||
struct GTest<T, U, V> {
|
|
||||||
a: T,
|
|
||||||
b: U,
|
|
||||||
c: V,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generic() {
|
|
||||||
let gt = GTest {
|
|
||||||
a: 39,
|
|
||||||
b: "hello",
|
|
||||||
c: 40,
|
|
||||||
};
|
|
||||||
print("generic: ");
|
|
||||||
print_dec(gt.a);
|
|
||||||
print(", ");
|
|
||||||
print(gt.b);
|
|
||||||
print_dec(gt.c);
|
|
||||||
println("");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn thinger() {
|
|
||||||
print("estamos jugando\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unused() {
|
|
||||||
print("el unused\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn println(msg: slice<8>) {
|
|
||||||
print(msg);
|
|
||||||
print("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn print(msg: slice<8>) {
|
|
||||||
asm (a1 = msg@) {
|
|
||||||
ld a2, 8, a1
|
|
||||||
ld a1, 0, a1
|
|
||||||
li a0, 1
|
|
||||||
li a7, 64
|
|
||||||
ecall
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn print_hex(x: 64) {
|
|
||||||
let i = 64;
|
|
||||||
loop {
|
|
||||||
i = sub(i, 4);
|
|
||||||
let c = and(shr(x, i), 15);
|
|
||||||
if gt(c, 9) {
|
|
||||||
c = add(c, 7);
|
|
||||||
};
|
|
||||||
c = add(c, 48);
|
|
||||||
asm (a1 = c@) {
|
|
||||||
li a2, 1
|
|
||||||
li a0, 1
|
|
||||||
li a7, 64
|
|
||||||
ecall
|
|
||||||
};
|
|
||||||
if lt(i, 1) {
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn print_dec(x: 64) {
|
|
||||||
let i = 1;
|
|
||||||
loop {
|
|
||||||
if gt(i, x) {
|
|
||||||
if lt(i, 2) {
|
|
||||||
print("0");
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
i = mul(i, 10);
|
|
||||||
};
|
|
||||||
let found = 0;
|
|
||||||
loop {
|
|
||||||
i = div(i, 10);
|
|
||||||
let c = rem(div(x, i), 10);
|
|
||||||
if and(lt(c, 1), not(found)) {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
found = 1;
|
|
||||||
if gt(c, 9) {
|
|
||||||
c = add(c, 7);
|
|
||||||
};
|
|
||||||
c = add(c, 48);
|
|
||||||
asm (a1 = c@) {
|
|
||||||
li a2, 1
|
|
||||||
li a0, 1
|
|
||||||
li a7, 64
|
|
||||||
ecall
|
|
||||||
};
|
|
||||||
if lt(i, 2) {
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
if not(found) {
|
|
||||||
print("0");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add(a: 64, b: 64) -> 64 {
|
|
||||||
asm (t0 = a, t1 = b, out = t0) {
|
|
||||||
add t0, t0, t1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mul(a: 64, b: 64) -> 64 {
|
|
||||||
asm (t0 = a, t1 = b, out = t0) {
|
|
||||||
mul t0, t0, t1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn div(a: 64, b: 64) -> 64 {
|
|
||||||
asm (t0 = a, t1 = b, out = t0) {
|
|
||||||
div t0, t0, t1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sub(a: 64, b: 64) -> 64 {
|
|
||||||
asm (t0 = a, t1 = b, out = t0) {
|
|
||||||
sub t0, t0, t1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rem(a: 64, b: 64) -> 64 {
|
|
||||||
asm (t0 = a, t1 = b, out = t0) {
|
|
||||||
rem t0, t0, t1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn shr(a: 64, b: 64) -> 64 {
|
|
||||||
asm (t0 = a, t1 = b, out = t0) {
|
|
||||||
srl t0, t0, t1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn shl(a: 64, b: 64) -> 64 {
|
|
||||||
asm (t0 = a, t1 = b, out = t0) {
|
|
||||||
sll t0, t0, t1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lt(a: 64, b: 64) -> 64 {
|
|
||||||
asm (t0 = a, t1 = b, out = t0) {
|
|
||||||
slt t0, t0, t1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn gt(a: 64, b: 64) -> 64 {
|
|
||||||
lt(b, a)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn and(a: 64, b: 64) -> 64 {
|
|
||||||
asm (t0 = a, t1 = b, out = t0) {
|
|
||||||
and t0, t0, t1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn not(a: 64) -> 64 {
|
|
||||||
asm (t0 = a, out = t0) {
|
|
||||||
xori t0, t0, 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn arger(a: slice<8>, b: slice<8>, c: slice<8>) {
|
|
||||||
print(a);
|
|
||||||
print(b);
|
|
||||||
println(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn exit(status: 64) {
|
|
||||||
asm (a0 = status) {
|
|
||||||
li a7, 93
|
|
||||||
ecall
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tester() -> slice<8> {
|
|
||||||
"hola\n"
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import util;
|
|
||||||
|
|
||||||
util.println("hello!");
|
|
||||||
let x = 39;
|
|
||||||
util.exit(x);
|
|
||||||
|
|
||||||
-146
@@ -1,146 +0,0 @@
|
|||||||
fn exit(status: 64) {
|
|
||||||
asm (a0 = status) {
|
|
||||||
li a7, 93
|
|
||||||
ecall
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn println(msg: slice<8>) {
|
|
||||||
print(msg);
|
|
||||||
print("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn print(msg: slice<8>) {
|
|
||||||
asm (a1 = msg@) {
|
|
||||||
ld a2, 8, a1
|
|
||||||
ld a1, 0, a1
|
|
||||||
li a0, 1
|
|
||||||
li a7, 64
|
|
||||||
ecall
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn print_hex(x: 64) {
|
|
||||||
let i = 64;
|
|
||||||
loop {
|
|
||||||
i = sub(i, 4);
|
|
||||||
let c = and(shr(x, i), 15);
|
|
||||||
if gt(c, 9) {
|
|
||||||
c = add(c, 7);
|
|
||||||
};
|
|
||||||
c = add(c, 48);
|
|
||||||
asm (a1 = c@) {
|
|
||||||
li a2, 1
|
|
||||||
li a0, 1
|
|
||||||
li a7, 64
|
|
||||||
ecall
|
|
||||||
};
|
|
||||||
if lt(i, 1) {
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn print_dec(x: 64) {
|
|
||||||
let i = 1;
|
|
||||||
loop {
|
|
||||||
if gt(i, x) {
|
|
||||||
if lt(i, 2) {
|
|
||||||
print("0");
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
i = mul(i, 10);
|
|
||||||
};
|
|
||||||
let found = 0;
|
|
||||||
loop {
|
|
||||||
i = div(i, 10);
|
|
||||||
let c = rem(div(x, i), 10);
|
|
||||||
if and(lt(c, 1), not(found)) {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
found = 1;
|
|
||||||
if gt(c, 9) {
|
|
||||||
c = add(c, 7);
|
|
||||||
};
|
|
||||||
c = add(c, 48);
|
|
||||||
asm (a1 = c@) {
|
|
||||||
li a2, 1
|
|
||||||
li a0, 1
|
|
||||||
li a7, 64
|
|
||||||
ecall
|
|
||||||
};
|
|
||||||
if lt(i, 2) {
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
if not(found) {
|
|
||||||
print("0");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add(a: 64, b: 64) -> 64 {
|
|
||||||
asm (t0 = a, t1 = b, out = t0) {
|
|
||||||
add t0, t0, t1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mul(a: 64, b: 64) -> 64 {
|
|
||||||
asm (t0 = a, t1 = b, out = t0) {
|
|
||||||
mul t0, t0, t1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn div(a: 64, b: 64) -> 64 {
|
|
||||||
asm (t0 = a, t1 = b, out = t0) {
|
|
||||||
div t0, t0, t1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sub(a: 64, b: 64) -> 64 {
|
|
||||||
asm (t0 = a, t1 = b, out = t0) {
|
|
||||||
sub t0, t0, t1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rem(a: 64, b: 64) -> 64 {
|
|
||||||
asm (t0 = a, t1 = b, out = t0) {
|
|
||||||
rem t0, t0, t1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn shr(a: 64, b: 64) -> 64 {
|
|
||||||
asm (t0 = a, t1 = b, out = t0) {
|
|
||||||
srl t0, t0, t1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn shl(a: 64, b: 64) -> 64 {
|
|
||||||
asm (t0 = a, t1 = b, out = t0) {
|
|
||||||
sll t0, t0, t1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lt(a: 64, b: 64) -> 64 {
|
|
||||||
asm (t0 = a, t1 = b, out = t0) {
|
|
||||||
slt t0, t0, t1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn gt(a: 64, b: 64) -> 64 {
|
|
||||||
lt(b, a)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn and(a: 64, b: 64) -> 64 {
|
|
||||||
asm (t0 = a, t1 = b, out = t0) {
|
|
||||||
and t0, t0, t1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn not(a: 64) -> 64 {
|
|
||||||
asm (t0 = a, out = t0) {
|
|
||||||
xori t0, t0, 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
resolution overview
|
|
||||||
loop {
|
|
||||||
resolve idents
|
|
||||||
resolve + type check / match instructions
|
|
||||||
URes.resolve(errs) can return: failed, ok(id), waiting
|
|
||||||
each instruction keeps track of progress
|
|
||||||
eg. fns: match each arg
|
|
||||||
updates to whether it's waiting or finished: ok or err
|
|
||||||
only finish if no sub tasks are waiting
|
|
||||||
finished = "macro ready"
|
|
||||||
run macros / code modification on "macro ready" (fns, structs)
|
|
||||||
eg. insert instructions
|
|
||||||
hygienic; only take in from scope
|
|
||||||
add inserted instructions to unresolved list
|
|
||||||
finished = "analysis ready"
|
|
||||||
analysis on "analysis ready" fns
|
|
||||||
eg. does this return in all code paths
|
|
||||||
finished + all correct = "ready to lower"
|
|
||||||
lower "ready to lower" fns
|
|
||||||
run lowered const fns / var expressions
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
move names into separate vec with origins?
|
|
||||||
make struct fields a vec, resolve to index?
|
|
||||||
|
|
||||||
inner values that auto generate map function:
|
|
||||||
enum Thing<inner T> {
|
|
||||||
A(T),
|
|
||||||
B(T, T),
|
|
||||||
C
|
|
||||||
}
|
|
||||||
or
|
|
||||||
#[derive(Map(T))]
|
|
||||||
enum Thing<T> { ... }
|
|
||||||
// scoping here is bad :woozy:
|
|
||||||
|
|
||||||
|
|
||||||
{([<
|
|
||||||
std::Option:(u32)::Some(3)
|
|
||||||
func:(u32)("hello", test, 3);
|
|
||||||
std::Option:[u32]::Some(3)
|
|
||||||
func:[T]("hello", test, 3);
|
|
||||||
std::Option::<u32>::Some(3)
|
|
||||||
func::<u32>(3)
|
|
||||||
std.Option.[u32].Some(3)
|
|
||||||
func.[T]("hello", test, 3);
|
|
||||||
std::Option:<u32>::Some(3)
|
|
||||||
func:<u32>(3)
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
use std::{collections::HashMap, path::PathBuf};
|
|
||||||
|
|
||||||
pub type FileID = usize;
|
|
||||||
pub type FileMap = HashMap<FileID, SrcFile>;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct SrcFile {
|
|
||||||
pub path: PathBuf,
|
|
||||||
pub text: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
||||||
pub struct FilePos {
|
|
||||||
pub file: FileID,
|
|
||||||
pub line: usize,
|
|
||||||
pub col: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
|
||||||
pub struct FileSpan {
|
|
||||||
pub file: FileID,
|
|
||||||
pub start: FilePos,
|
|
||||||
pub end: FilePos,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FilePos {
|
|
||||||
pub fn start(file: FileID) -> Self {
|
|
||||||
Self {
|
|
||||||
line: 0,
|
|
||||||
col: 0,
|
|
||||||
file,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FilePos {
|
|
||||||
pub fn to(self, end: FilePos) -> FileSpan {
|
|
||||||
FileSpan {
|
|
||||||
start: self,
|
|
||||||
end,
|
|
||||||
file: self.file,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn char_span(self) -> FileSpan {
|
|
||||||
FileSpan::at(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const BEFORE: usize = 1;
|
|
||||||
const AFTER: usize = 0;
|
|
||||||
|
|
||||||
impl FileSpan {
|
|
||||||
const BUILTIN_FILE: usize = usize::MAX;
|
|
||||||
pub fn at(pos: FilePos) -> Self {
|
|
||||||
Self {
|
|
||||||
start: pos,
|
|
||||||
end: pos,
|
|
||||||
file: pos.file,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn builtin() -> Self {
|
|
||||||
let pos = FilePos {
|
|
||||||
file: Self::BUILTIN_FILE,
|
|
||||||
line: 0,
|
|
||||||
col: 0,
|
|
||||||
};
|
|
||||||
Self::at(pos)
|
|
||||||
}
|
|
||||||
pub fn is_builtin(&self) -> bool {
|
|
||||||
self.file == Self::BUILTIN_FILE
|
|
||||||
}
|
|
||||||
pub fn write_for(&self, writer: &mut impl std::io::Write, file: &str) -> std::io::Result<()> {
|
|
||||||
if self.is_builtin() {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
let start = self.start.line.saturating_sub(BEFORE);
|
|
||||||
let num_before = self.start.line - start;
|
|
||||||
let mut lines = file.lines().skip(start);
|
|
||||||
let width = format!("{}", self.end.line + AFTER).len();
|
|
||||||
let same_line = self.start.line == self.end.line;
|
|
||||||
for i in 0..num_before {
|
|
||||||
writeln!(writer, "{:>width$} | {}", start + i, lines.next().unwrap())?;
|
|
||||||
}
|
|
||||||
let line = lines.next().unwrap();
|
|
||||||
writeln!(writer, "{:>width$} | {}", self.start.line, line)?;
|
|
||||||
let len = if same_line {
|
|
||||||
self.end.col - self.start.col + 1
|
|
||||||
} else {
|
|
||||||
line.len() - self.start.col
|
|
||||||
};
|
|
||||||
writeln!(
|
|
||||||
writer,
|
|
||||||
"{} | {}",
|
|
||||||
" ".repeat(width),
|
|
||||||
" ".repeat(self.start.col) + &"^".repeat(len)
|
|
||||||
)?;
|
|
||||||
if !same_line {
|
|
||||||
for _ in 0..self.end.line - self.start.line - 1 {
|
|
||||||
lines.next();
|
|
||||||
}
|
|
||||||
let line = lines.next().unwrap();
|
|
||||||
writeln!(writer, "{:>width$} | {}", self.end.line, line)?;
|
|
||||||
writeln!(
|
|
||||||
writer,
|
|
||||||
"{} | {}",
|
|
||||||
" ".repeat(width),
|
|
||||||
"^".repeat(self.end.col + 1)
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
// for i in 0..AFTER {
|
|
||||||
// if let Some(next) = lines.next() {
|
|
||||||
// writeln!(writer, "{:>width$} | {}", self.end.line + i + 1, next)?;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
mod output;
|
|
||||||
mod file;
|
|
||||||
|
|
||||||
pub use output::*;
|
|
||||||
pub use file::*;
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
use super::{FileMap, FilePos, FileSpan};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct CompilerMsg {
|
|
||||||
pub msg: String,
|
|
||||||
pub spans: Vec<FileSpan>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct CompilerOutput {
|
|
||||||
pub file_map: FileMap,
|
|
||||||
pub errs: Vec<CompilerMsg>,
|
|
||||||
pub hints: Vec<CompilerMsg>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CompilerMsg {
|
|
||||||
pub fn from_msg(msg: String) -> Self {
|
|
||||||
Self {
|
|
||||||
msg,
|
|
||||||
spans: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn new(msg: String, span: FileSpan) -> Self {
|
|
||||||
Self {
|
|
||||||
msg,
|
|
||||||
spans: vec![span],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn at(pos: FilePos, msg: String) -> Self {
|
|
||||||
Self {
|
|
||||||
msg,
|
|
||||||
spans: vec![FileSpan::at(pos)],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn write_to(
|
|
||||||
&self,
|
|
||||||
ty: &str,
|
|
||||||
writer: &mut impl std::io::Write,
|
|
||||||
map: &FileMap,
|
|
||||||
) -> std::io::Result<()> {
|
|
||||||
let after = if self.spans.is_empty() { "" } else { ":" };
|
|
||||||
writeln!(writer, "{}: {}{}", ty, self.msg, after)?;
|
|
||||||
for span in &self.spans {
|
|
||||||
let file = map.get(&span.file).expect("unknown file id");
|
|
||||||
writeln!(writer, "{:?}", &file.path)?;
|
|
||||||
span.write_for(writer, &file.text)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CompilerOutput {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
errs: Vec::new(),
|
|
||||||
hints: Vec::new(),
|
|
||||||
file_map: FileMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn err(&mut self, msg: CompilerMsg) {
|
|
||||||
self.errs.push(msg);
|
|
||||||
}
|
|
||||||
pub fn hint(&mut self, msg: CompilerMsg) {
|
|
||||||
self.hints.push(msg);
|
|
||||||
}
|
|
||||||
pub fn write_to(&self, out: &mut impl std::io::Write) {
|
|
||||||
for err in &self.errs {
|
|
||||||
err.write_to("error", out, &self.file_map).unwrap();
|
|
||||||
}
|
|
||||||
for hint in &self.hints {
|
|
||||||
hint.write_to("hint", out, &self.file_map).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
pub mod riscv;
|
|
||||||
@@ -1,357 +0,0 @@
|
|||||||
use crate::{
|
|
||||||
compiler::program::{Addr, Instr, SymTable},
|
|
||||||
ir::Symbol,
|
|
||||||
util::{Bits32, LabeledFmt},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum LinkerInstruction<R = Reg, S = Symbol> {
|
|
||||||
Op {
|
|
||||||
op: Funct3,
|
|
||||||
funct: Funct7,
|
|
||||||
dest: R,
|
|
||||||
src1: R,
|
|
||||||
src2: R,
|
|
||||||
},
|
|
||||||
OpImm {
|
|
||||||
op: Funct3,
|
|
||||||
dest: R,
|
|
||||||
src: R,
|
|
||||||
imm: i32,
|
|
||||||
},
|
|
||||||
OpImmF7 {
|
|
||||||
op: Funct3,
|
|
||||||
funct: Funct7,
|
|
||||||
dest: R,
|
|
||||||
src: R,
|
|
||||||
imm: i32,
|
|
||||||
},
|
|
||||||
Store {
|
|
||||||
width: Funct3,
|
|
||||||
src: R,
|
|
||||||
offset: i32,
|
|
||||||
base: R,
|
|
||||||
},
|
|
||||||
Load {
|
|
||||||
width: Funct3,
|
|
||||||
dest: R,
|
|
||||||
offset: i32,
|
|
||||||
base: R,
|
|
||||||
},
|
|
||||||
Mv {
|
|
||||||
dest: R,
|
|
||||||
src: R,
|
|
||||||
},
|
|
||||||
La {
|
|
||||||
dest: R,
|
|
||||||
src: S,
|
|
||||||
},
|
|
||||||
Jal {
|
|
||||||
dest: R,
|
|
||||||
offset: i32,
|
|
||||||
},
|
|
||||||
Call(S),
|
|
||||||
J(S),
|
|
||||||
Branch {
|
|
||||||
to: S,
|
|
||||||
typ: Funct3,
|
|
||||||
left: R,
|
|
||||||
right: R,
|
|
||||||
},
|
|
||||||
Ret,
|
|
||||||
ECall,
|
|
||||||
EBreak,
|
|
||||||
Li {
|
|
||||||
dest: R,
|
|
||||||
imm: i32,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
impl<R, S> LinkerInstruction<R, S> {
|
|
||||||
pub fn map<R2, S2>(&self, r: impl Fn(&R) -> R2) -> LinkerInstruction<R2, S2> {
|
|
||||||
self.try_map(|v| Some(r(v))).unwrap()
|
|
||||||
}
|
|
||||||
pub fn try_map<R2, S2>(&self, r: impl Fn(&R) -> Option<R2>) -> Option<LinkerInstruction<R2, S2>> {
|
|
||||||
use LinkerInstruction as I;
|
|
||||||
Some(match self {
|
|
||||||
Self::ECall => I::ECall,
|
|
||||||
Self::EBreak => I::EBreak,
|
|
||||||
&Self::Li { ref dest, imm } => I::Li { dest: r(dest)?, imm },
|
|
||||||
Self::Mv { ref dest, src } => I::Mv {
|
|
||||||
dest: r(dest)?,
|
|
||||||
src: r(src)?,
|
|
||||||
},
|
|
||||||
Self::La { .. } => todo!(),
|
|
||||||
&Self::Load {
|
|
||||||
width,
|
|
||||||
ref dest,
|
|
||||||
ref base,
|
|
||||||
offset,
|
|
||||||
} => I::Load {
|
|
||||||
width,
|
|
||||||
dest: r(dest)?,
|
|
||||||
offset,
|
|
||||||
base: r(base)?,
|
|
||||||
},
|
|
||||||
&Self::Store {
|
|
||||||
width,
|
|
||||||
ref src,
|
|
||||||
ref base,
|
|
||||||
offset,
|
|
||||||
} => I::Store {
|
|
||||||
width,
|
|
||||||
src: r(src)?,
|
|
||||||
offset,
|
|
||||||
base: r(base)?,
|
|
||||||
},
|
|
||||||
&Self::Op {
|
|
||||||
op,
|
|
||||||
funct,
|
|
||||||
ref dest,
|
|
||||||
ref src1,
|
|
||||||
ref src2,
|
|
||||||
} => I::Op {
|
|
||||||
op,
|
|
||||||
funct,
|
|
||||||
dest: r(dest)?,
|
|
||||||
src1: r(src1)?,
|
|
||||||
src2: r(src2)?,
|
|
||||||
},
|
|
||||||
&Self::OpImm { op, ref dest, ref src, imm } => I::OpImm {
|
|
||||||
op,
|
|
||||||
dest: r(dest)?,
|
|
||||||
src: r(src)?,
|
|
||||||
imm,
|
|
||||||
},
|
|
||||||
&Self::OpImmF7 {
|
|
||||||
op,
|
|
||||||
funct,
|
|
||||||
ref dest,
|
|
||||||
ref src,
|
|
||||||
imm,
|
|
||||||
} => I::OpImmF7 {
|
|
||||||
op,
|
|
||||||
funct,
|
|
||||||
dest: r(dest)?,
|
|
||||||
src: r(src)?,
|
|
||||||
imm,
|
|
||||||
},
|
|
||||||
Self::Ret => I::Ret,
|
|
||||||
Self::Call(..) => todo!(),
|
|
||||||
Self::Jal { .. } => todo!(),
|
|
||||||
Self::J(..) => todo!(),
|
|
||||||
Self::Branch { .. } => todo!(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn addi(dest: Reg, src: Reg, imm: BitsI32<11, 0>) -> RawInstruction {
|
|
||||||
opi(op32i::ADD, dest, src, imm.to_u())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ori(dest: Reg, src: Reg, imm: Bits32<11, 0>) -> RawInstruction {
|
|
||||||
opi(op32i::OR, dest, src, imm)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Instr for LinkerInstruction {
|
|
||||||
fn push_to(
|
|
||||||
&self,
|
|
||||||
data: &mut Vec<u8>,
|
|
||||||
sym_map: &mut SymTable,
|
|
||||||
pos: Addr,
|
|
||||||
missing: bool,
|
|
||||||
) -> Option<Symbol> {
|
|
||||||
let last = match self {
|
|
||||||
Self::Op {
|
|
||||||
op,
|
|
||||||
funct,
|
|
||||||
dest,
|
|
||||||
src1,
|
|
||||||
src2,
|
|
||||||
} => opr(*op, *funct, *dest, *src1, *src2),
|
|
||||||
Self::OpImm { op, dest, src, imm } => opi(*op, *dest, *src, BitsI32::new(*imm).to_u()),
|
|
||||||
Self::OpImmF7 {
|
|
||||||
op,
|
|
||||||
funct,
|
|
||||||
dest,
|
|
||||||
src,
|
|
||||||
imm,
|
|
||||||
} => opif7(*op, *funct, *dest, *src, BitsI32::new(*imm)),
|
|
||||||
Self::Store {
|
|
||||||
width,
|
|
||||||
src,
|
|
||||||
offset,
|
|
||||||
base,
|
|
||||||
} => store(*width, *src, BitsI32::new(*offset), *base),
|
|
||||||
Self::Load {
|
|
||||||
width,
|
|
||||||
dest,
|
|
||||||
offset,
|
|
||||||
base,
|
|
||||||
} => load(*width, *dest, BitsI32::new(*offset), *base),
|
|
||||||
Self::Mv { dest, src } => addi(*dest, *src, BitsI32::new(0)),
|
|
||||||
Self::La { dest, src } => {
|
|
||||||
if let Some(addr) = sym_map.get(*src) {
|
|
||||||
let offset = addr.val() as i32 - pos.val() as i32;
|
|
||||||
let sign = offset.signum();
|
|
||||||
let mut lower = offset % 0x1000;
|
|
||||||
let mut upper = offset - lower;
|
|
||||||
if (((lower >> 11) & 1) == 1) ^ (sign == -1) {
|
|
||||||
let add = sign << 12;
|
|
||||||
upper += add;
|
|
||||||
lower = offset - upper;
|
|
||||||
}
|
|
||||||
assert!(upper + (lower << 20 >> 20) == offset);
|
|
||||||
data.extend(auipc(*dest, BitsI32::new(upper)).to_le_bytes());
|
|
||||||
addi(*dest, *dest, BitsI32::new(lower))
|
|
||||||
} else {
|
|
||||||
data.extend_from_slice(&[0; 2 * 4]);
|
|
||||||
return Some(*src);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Self::Jal { dest, offset } => jal(*dest, BitsI32::new(*offset)),
|
|
||||||
Self::J(sym) => {
|
|
||||||
if let Some(addr) = sym_map.get(*sym) {
|
|
||||||
let offset = addr.val() as i32 - pos.val() as i32;
|
|
||||||
j(BitsI32::new(offset))
|
|
||||||
} else {
|
|
||||||
data.extend_from_slice(&[0; 4]);
|
|
||||||
return Some(*sym);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Self::Call(sym) => {
|
|
||||||
if let Some(addr) = sym_map.get(*sym) {
|
|
||||||
let offset = addr.val() as i32 - pos.val() as i32;
|
|
||||||
jal(ra, BitsI32::new(offset))
|
|
||||||
} else {
|
|
||||||
data.extend_from_slice(&[0; 4]);
|
|
||||||
return Some(*sym);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Self::Ret => ret(),
|
|
||||||
Self::ECall => ecall(),
|
|
||||||
Self::EBreak => ebreak(),
|
|
||||||
Self::Li { dest, imm } => addi(*dest, zero, BitsI32::new(*imm)),
|
|
||||||
Self::Branch {
|
|
||||||
to,
|
|
||||||
typ,
|
|
||||||
left,
|
|
||||||
right,
|
|
||||||
} => {
|
|
||||||
if let Some(addr) = sym_map.get(*to) {
|
|
||||||
let offset = addr.val() as i32 - pos.val() as i32;
|
|
||||||
branch(*typ, *left, *right, BitsI32::new(offset))
|
|
||||||
} else {
|
|
||||||
data.extend_from_slice(&[0; 4]);
|
|
||||||
return Some(*to);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
data.extend(last.to_le_bytes());
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LinkerInstruction {
|
|
||||||
pub fn addi(dest: Reg, src: Reg, imm: i32) -> Self {
|
|
||||||
Self::OpImm {
|
|
||||||
op: op32i::ADD,
|
|
||||||
dest,
|
|
||||||
src,
|
|
||||||
imm,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn sd(src: Reg, offset: i32, base: Reg) -> Self {
|
|
||||||
Self::Store {
|
|
||||||
width: width::D,
|
|
||||||
src,
|
|
||||||
offset,
|
|
||||||
base,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn ld(dest: Reg, offset: i32, base: Reg) -> Self {
|
|
||||||
Self::Load {
|
|
||||||
width: width::D,
|
|
||||||
dest,
|
|
||||||
offset,
|
|
||||||
base,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is not even remotely worth it but technically it doesn't use the heap I think xdddddddddd
|
|
||||||
impl<R: std::fmt::Debug, S: std::fmt::Debug> std::fmt::Debug for LinkerInstruction<R, S> {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
self.fmt_label(f, &|f, s| write!(f, "{s:?}"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct DebugInstr<'a, R, S, L: Fn(&mut std::fmt::Formatter<'_>, &S) -> std::fmt::Result> {
|
|
||||||
instr: &'a LinkerInstruction<R, S>,
|
|
||||||
label: &'a L,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<R: std::fmt::Debug, S: std::fmt::Debug> LabeledFmt<S> for LinkerInstruction<R, S> {
|
|
||||||
fn fmt_label(
|
|
||||||
&self,
|
|
||||||
f: &mut std::fmt::Formatter<'_>,
|
|
||||||
label: &dyn crate::util::Labeler<S>,
|
|
||||||
) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::ECall => write!(f, "ecall"),
|
|
||||||
Self::EBreak => write!(f, "ebreak"),
|
|
||||||
Self::Li { dest, imm } => write!(f, "li {dest:?}, {imm:?}"),
|
|
||||||
Self::Mv { dest, src } => write!(f, "mv {dest:?}, {src:?}"),
|
|
||||||
Self::La { dest, src } => {
|
|
||||||
write!(f, "la {dest:?}, @")?;
|
|
||||||
label(f, src)
|
|
||||||
}
|
|
||||||
Self::Load {
|
|
||||||
width,
|
|
||||||
dest,
|
|
||||||
offset,
|
|
||||||
base,
|
|
||||||
} => write!(f, "l{} {dest:?}, {offset}({base:?})", width::str(*width)),
|
|
||||||
Self::Store {
|
|
||||||
width,
|
|
||||||
src,
|
|
||||||
offset,
|
|
||||||
base,
|
|
||||||
} => write!(f, "s{} {src:?}, {offset}({base:?})", width::str(*width)),
|
|
||||||
Self::Op {
|
|
||||||
op,
|
|
||||||
funct,
|
|
||||||
dest,
|
|
||||||
src1,
|
|
||||||
src2,
|
|
||||||
} => write!(f, "{} {dest:?}, {src1:?}, {src2:?}", opstr(*op, *funct)),
|
|
||||||
Self::OpImm { op, dest, src, imm } => {
|
|
||||||
write!(f, "{}i {dest:?}, {src:?}, {imm}", opstr(*op, op32i::FUNCT7))
|
|
||||||
}
|
|
||||||
Self::OpImmF7 {
|
|
||||||
op,
|
|
||||||
funct,
|
|
||||||
dest,
|
|
||||||
src,
|
|
||||||
imm,
|
|
||||||
} => write!(f, "{}i {dest:?}, {src:?}, {imm}", opstr(*op, *funct)),
|
|
||||||
Self::Jal { dest, offset } => write!(f, "jal {dest:?}, {offset:?}"),
|
|
||||||
Self::Call(s) => {
|
|
||||||
write!(f, "call ")?;
|
|
||||||
label(f, s)
|
|
||||||
}
|
|
||||||
Self::J(s) => {
|
|
||||||
write!(f, "j ")?;
|
|
||||||
label(f, s)
|
|
||||||
}
|
|
||||||
Self::Branch {
|
|
||||||
to,
|
|
||||||
typ,
|
|
||||||
left,
|
|
||||||
right,
|
|
||||||
} => write!(f, "b{} {left:?} {right:?} {to:?}", branch::str(*typ)),
|
|
||||||
Self::Ret => write!(f, "ret"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,224 +0,0 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
compiler::{arch::riscv::Reg, debug::DebugInfo, UnlinkedFunction, UnlinkedProgram},
|
|
||||||
ir::{arch::riscv64::RegRef, LInstruction as IRI, LProgram, Len, Size, VarID},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{LinkerInstruction as LI, *};
|
|
||||||
|
|
||||||
fn align(s: &Size) -> i32 {
|
|
||||||
(*s as i32 - 1).div_euclid(8) + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mov_mem(
|
|
||||||
v: &mut Vec<LI>,
|
|
||||||
src: Reg,
|
|
||||||
src_offset: i32,
|
|
||||||
dest: Reg,
|
|
||||||
dest_offset: i32,
|
|
||||||
temp: Reg,
|
|
||||||
mut len: Len,
|
|
||||||
) {
|
|
||||||
let mut off = 0;
|
|
||||||
for width in width::MAIN.iter().rev().copied() {
|
|
||||||
let wl = width::len(width);
|
|
||||||
while len >= wl {
|
|
||||||
v.extend([
|
|
||||||
LI::Load {
|
|
||||||
width,
|
|
||||||
dest: temp,
|
|
||||||
offset: src_offset + off,
|
|
||||||
base: src,
|
|
||||||
},
|
|
||||||
LI::Store {
|
|
||||||
width,
|
|
||||||
src: temp,
|
|
||||||
offset: dest_offset + off,
|
|
||||||
base: dest,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
len -= wl;
|
|
||||||
off += wl as i32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn compile(program: &LProgram) -> UnlinkedProgram<LI> {
|
|
||||||
let mut fns = Vec::new();
|
|
||||||
let mut data = Vec::new();
|
|
||||||
let mut dbg = DebugInfo::new(program.labels().to_vec());
|
|
||||||
for (sym, d) in program.ro_data() {
|
|
||||||
data.push((d.clone(), *sym));
|
|
||||||
}
|
|
||||||
for (sym, f) in program.fns() {
|
|
||||||
let mut v = Vec::new();
|
|
||||||
let mut stack = HashMap::new();
|
|
||||||
let mut stack_len = 0;
|
|
||||||
let mut stack_ra = None;
|
|
||||||
let mut stack_rva = None;
|
|
||||||
if f.makes_call {
|
|
||||||
// return addr
|
|
||||||
stack_ra = Some(stack_len);
|
|
||||||
stack_len += 8;
|
|
||||||
}
|
|
||||||
for (id, s) in &f.stack {
|
|
||||||
stack.insert(id, stack_len);
|
|
||||||
stack_len += align(s);
|
|
||||||
}
|
|
||||||
for (id, s) in f.args.iter().rev() {
|
|
||||||
stack.insert(id, stack_len);
|
|
||||||
stack_len += align(s);
|
|
||||||
}
|
|
||||||
if f.ret_size > 0 {
|
|
||||||
stack_rva = Some(stack_len);
|
|
||||||
stack_len += align(&f.ret_size);
|
|
||||||
}
|
|
||||||
v.push(LI::addi(sp, sp, -stack_len));
|
|
||||||
for (id, var) in &f.subvar_map {
|
|
||||||
// TODO: ALIGN DOES NOT MAKE SENSE HERE!!! need to choose to decide in lower or asm
|
|
||||||
stack.insert(id, stack[&var.id] + align(&var.offset));
|
|
||||||
}
|
|
||||||
let has_stack = stack_len > 0;
|
|
||||||
if has_stack {
|
|
||||||
if let Some(stack_ra) = stack_ra {
|
|
||||||
v.push(LI::sd(ra, stack_ra, sp));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut locations = HashMap::new();
|
|
||||||
let mut irli = Vec::new();
|
|
||||||
let mut ret = Vec::new();
|
|
||||||
if has_stack {
|
|
||||||
if let Some(stack_ra) = stack_ra {
|
|
||||||
ret.push(LI::ld(ra, stack_ra, sp));
|
|
||||||
}
|
|
||||||
ret.push(LI::addi(sp, sp, stack_len));
|
|
||||||
}
|
|
||||||
ret.push(LI::Ret);
|
|
||||||
|
|
||||||
for i in &f.instructions {
|
|
||||||
irli.push((v.len(), format!("{i:?}")));
|
|
||||||
match i {
|
|
||||||
IRI::Mv {
|
|
||||||
dst: dest,
|
|
||||||
dst_offset: dest_offset,
|
|
||||||
src,
|
|
||||||
src_offset,
|
|
||||||
} => {
|
|
||||||
let s = align(&f.stack[src]) as u32;
|
|
||||||
mov_mem(
|
|
||||||
&mut v,
|
|
||||||
sp,
|
|
||||||
stack[src] + align(src_offset),
|
|
||||||
sp,
|
|
||||||
stack[dest] + align(dest_offset),
|
|
||||||
t0,
|
|
||||||
s,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
IRI::Ref { dst: dest, src } => {
|
|
||||||
v.push(LI::addi(t0, sp, stack[src]));
|
|
||||||
v.push(LI::sd(t0, stack[dest], sp));
|
|
||||||
}
|
|
||||||
IRI::LoadAddr {
|
|
||||||
dst: dest,
|
|
||||||
offset,
|
|
||||||
src,
|
|
||||||
} => {
|
|
||||||
v.extend([
|
|
||||||
LI::La {
|
|
||||||
dest: t0,
|
|
||||||
src: *src,
|
|
||||||
},
|
|
||||||
LI::sd(t0, stack[dest] + *offset as i32, sp),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
IRI::LoadData {
|
|
||||||
dst: dest,
|
|
||||||
offset,
|
|
||||||
src,
|
|
||||||
len,
|
|
||||||
} => {
|
|
||||||
v.push(LI::La {
|
|
||||||
dest: t0,
|
|
||||||
src: *src,
|
|
||||||
});
|
|
||||||
mov_mem(&mut v, t0, 0, sp, stack[dest] + *offset as i32, t1, *len);
|
|
||||||
}
|
|
||||||
IRI::Call { dst: dest, f, args } => {
|
|
||||||
let mut offset = 0;
|
|
||||||
if let Some((dest, s)) = dest {
|
|
||||||
offset -= align(s);
|
|
||||||
v.push(LI::addi(t0, sp, stack[&dest]));
|
|
||||||
v.push(LI::sd(t0, offset, sp))
|
|
||||||
}
|
|
||||||
for (arg, s) in args {
|
|
||||||
let bs = align(s);
|
|
||||||
offset -= bs;
|
|
||||||
mov_mem(&mut v, sp, stack[arg], sp, offset, t0, bs as Len);
|
|
||||||
}
|
|
||||||
v.push(LI::Call(*f));
|
|
||||||
}
|
|
||||||
IRI::AsmBlock {
|
|
||||||
inputs,
|
|
||||||
outputs,
|
|
||||||
instructions,
|
|
||||||
} => {
|
|
||||||
for (reg, var) in inputs {
|
|
||||||
v.push(LI::ld(*reg, stack[var], sp));
|
|
||||||
}
|
|
||||||
fn r(rr: &RegRef<VarID>) -> Reg {
|
|
||||||
match rr {
|
|
||||||
RegRef::Var(..) => todo!(),
|
|
||||||
RegRef::Reg(reg) => *reg,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i in instructions {
|
|
||||||
v.push(i.map(|v| r(v)));
|
|
||||||
}
|
|
||||||
for (reg, var) in outputs {
|
|
||||||
v.push(LI::sd(*reg, stack[var], sp));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
IRI::Ret { src } => {
|
|
||||||
if let Some(src) = src {
|
|
||||||
let Some(rva) = stack_rva else {
|
|
||||||
panic!("no return value address on stack!")
|
|
||||||
};
|
|
||||||
v.push(LI::ld(t0, rva, sp));
|
|
||||||
mov_mem(&mut v, sp, stack[src], t0, 0, t1, align(&f.ret_size) as u32);
|
|
||||||
}
|
|
||||||
v.extend(&ret);
|
|
||||||
}
|
|
||||||
IRI::Jump(location) => {
|
|
||||||
v.push(LI::J(*location));
|
|
||||||
}
|
|
||||||
IRI::Branch { to, cond } => {
|
|
||||||
v.push(LI::ld(t0, stack[cond], sp));
|
|
||||||
v.push(LI::Branch {
|
|
||||||
to: *to,
|
|
||||||
typ: branch::EQ,
|
|
||||||
left: t0,
|
|
||||||
right: zero,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
IRI::Mark(location) => {
|
|
||||||
locations.insert(v.len(), *location);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dbg.push_fn(irli);
|
|
||||||
fns.push(UnlinkedFunction {
|
|
||||||
instrs: v,
|
|
||||||
sym: *sym,
|
|
||||||
locations,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
UnlinkedProgram {
|
|
||||||
fns,
|
|
||||||
ro_data: data,
|
|
||||||
start: Some(program.entry()),
|
|
||||||
dbg,
|
|
||||||
sym_count: program.len(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
use crate::{
|
|
||||||
compiler::arch::riscv::Reg,
|
|
||||||
util::{Bits32, BitsI32},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct RawInstruction(u32);
|
|
||||||
|
|
||||||
impl RawInstruction {
|
|
||||||
pub fn to_le_bytes(&self) -> impl IntoIterator<Item = u8> {
|
|
||||||
self.0.to_le_bytes().into_iter()
|
|
||||||
}
|
|
||||||
pub fn to_be_bytes(&self) -> impl IntoIterator<Item = u8> {
|
|
||||||
self.0.to_be_bytes().into_iter()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const SYSTEM: u32 = 0b1110011;
|
|
||||||
pub const LOAD: u32 = 0b0000011;
|
|
||||||
pub const STORE: u32 = 0b0100011;
|
|
||||||
pub const AUIPC: u32 = 0b0010111;
|
|
||||||
pub const IMM_OP: u32 = 0b0010011;
|
|
||||||
pub const OP: u32 = 0b0110011;
|
|
||||||
pub const JAL: u32 = 0b1101111;
|
|
||||||
pub const JALR: u32 = 0b1100111;
|
|
||||||
pub const BRANCH: u32 = 0b1100011;
|
|
||||||
|
|
||||||
pub type Funct3 = Bits32<2, 0>;
|
|
||||||
pub type Funct7 = Bits32<6, 0>;
|
|
||||||
|
|
||||||
use RawInstruction as I;
|
|
||||||
|
|
||||||
pub const fn r_type(
|
|
||||||
funct7: Bits32<6, 0>,
|
|
||||||
rs2: Reg,
|
|
||||||
rs1: Reg,
|
|
||||||
funct3: Bits32<2, 0>,
|
|
||||||
rd: Reg,
|
|
||||||
opcode: u32,
|
|
||||||
) -> I {
|
|
||||||
I((funct7.val() << 25)
|
|
||||||
+ (rs2.val() << 20)
|
|
||||||
+ (rs1.val() << 15)
|
|
||||||
+ (funct3.val() << 12)
|
|
||||||
+ (rd.val() << 7)
|
|
||||||
+ opcode)
|
|
||||||
}
|
|
||||||
pub const fn i_type(imm: Bits32<11, 0>, rs1: Reg, funct: Funct3, rd: Reg, opcode: u32) -> I {
|
|
||||||
I((imm.val() << 20) + (rs1.val() << 15) + (funct.val() << 12) + (rd.val() << 7) + opcode)
|
|
||||||
}
|
|
||||||
pub const fn s_type(rs2: Reg, rs1: Reg, funct3: Funct3, imm: Bits32<11, 0>, opcode: u32) -> I {
|
|
||||||
I((imm.bits(11, 5) << 25)
|
|
||||||
+ (rs2.val() << 20)
|
|
||||||
+ (rs1.val() << 15)
|
|
||||||
+ (funct3.val() << 12)
|
|
||||||
+ (imm.bits(4, 0) << 7)
|
|
||||||
+ opcode)
|
|
||||||
}
|
|
||||||
pub const fn b_type(rs2: Reg, rs1: Reg, funct3: Funct3, imm: Bits32<12, 1>, opcode: u32) -> I {
|
|
||||||
I((imm.bit(12) << 31)
|
|
||||||
+ (imm.bits(10, 5) << 25)
|
|
||||||
+ (rs2.val() << 20)
|
|
||||||
+ (rs1.val() << 15)
|
|
||||||
+ (funct3.val() << 12)
|
|
||||||
+ (imm.bits(4, 1) << 8)
|
|
||||||
+ (imm.bit(11) << 7)
|
|
||||||
+ opcode)
|
|
||||||
}
|
|
||||||
pub const fn u_type(imm: Bits32<31, 12>, rd: Reg, opcode: u32) -> I {
|
|
||||||
I((imm.bits(31, 12) << 12) + (rd.val() << 7) + opcode)
|
|
||||||
}
|
|
||||||
pub const fn j_type(imm: Bits32<20, 1>, rd: Reg, opcode: u32) -> I {
|
|
||||||
I((imm.bit(20) << 31)
|
|
||||||
+ (imm.bits(10, 1) << 21)
|
|
||||||
+ (imm.bit(11) << 20)
|
|
||||||
+ (imm.bits(19, 12) << 12)
|
|
||||||
+ (rd.val() << 7)
|
|
||||||
+ opcode)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn opr(op: Funct3, funct: Funct7, dest: Reg, src1: Reg, src2: Reg) -> I {
|
|
||||||
r_type(funct, src2, src1, op, dest, OP)
|
|
||||||
}
|
|
||||||
pub fn opi(op: Funct3, dest: Reg, src: Reg, imm: Bits32<11, 0>) -> RawInstruction {
|
|
||||||
i_type(imm, src, op, dest, IMM_OP)
|
|
||||||
}
|
|
||||||
pub fn opif7(op: Funct3, funct: Funct7, dest: Reg, src: Reg, imm: BitsI32<4, 0>) -> I {
|
|
||||||
i_type(
|
|
||||||
Bits32::new(imm.to_u().val() + (funct.val() << 5)),
|
|
||||||
src,
|
|
||||||
op,
|
|
||||||
dest,
|
|
||||||
IMM_OP,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
use super::*;
|
|
||||||
|
|
||||||
mod base;
|
|
||||||
mod rv32i;
|
|
||||||
mod rv32m;
|
|
||||||
mod string;
|
|
||||||
pub use base::*;
|
|
||||||
pub use rv32i::*;
|
|
||||||
pub use rv32m::*;
|
|
||||||
pub use string::*;
|
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
use crate::{compiler::arch::riscv::Reg, util::Bits32};
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
pub mod op32i {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
pub const ADD: Funct3 = Funct3::new(0b000);
|
|
||||||
pub const SL: Funct3 = Funct3::new(0b001);
|
|
||||||
pub const SLT: Funct3 = Funct3::new(0b010);
|
|
||||||
pub const SLTU: Funct3 = Funct3::new(0b011);
|
|
||||||
pub const XOR: Funct3 = Funct3::new(0b100);
|
|
||||||
pub const SR: Funct3 = Funct3::new(0b101);
|
|
||||||
pub const OR: Funct3 = Funct3::new(0b110);
|
|
||||||
pub const AND: Funct3 = Funct3::new(0b111);
|
|
||||||
|
|
||||||
pub const LOGICAL: Funct7 = Funct7::new(0b0000000);
|
|
||||||
pub const ARITHMETIC: Funct7 = Funct7::new(0b0100000);
|
|
||||||
pub const F7ADD: Funct7 = Funct7::new(0b0000000);
|
|
||||||
pub const F7SUB: Funct7 = Funct7::new(0b0100000);
|
|
||||||
|
|
||||||
pub const FUNCT7: Funct7 = Funct7::new(0b0000000);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod width {
|
|
||||||
use crate::ir::Len;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
pub const MAIN: [Funct3; 4] = [B, H, W, D];
|
|
||||||
|
|
||||||
pub const B: Funct3 = Funct3::new(0b000);
|
|
||||||
pub const H: Funct3 = Funct3::new(0b001);
|
|
||||||
pub const W: Funct3 = Funct3::new(0b010);
|
|
||||||
pub const D: Funct3 = Funct3::new(0b011);
|
|
||||||
pub const BU: Funct3 = Funct3::new(0b100);
|
|
||||||
pub const HU: Funct3 = Funct3::new(0b101);
|
|
||||||
pub const WU: Funct3 = Funct3::new(0b110);
|
|
||||||
|
|
||||||
pub const fn str(w: Funct3) -> &'static str {
|
|
||||||
match w {
|
|
||||||
B => "b",
|
|
||||||
H => "h",
|
|
||||||
W => "w",
|
|
||||||
D => "d",
|
|
||||||
BU => "bu",
|
|
||||||
HU => "hu",
|
|
||||||
WU => "wu",
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn len(w: Funct3) -> Len {
|
|
||||||
match w {
|
|
||||||
B => 1,
|
|
||||||
H => 2,
|
|
||||||
W => 4,
|
|
||||||
D => 8,
|
|
||||||
BU => 1,
|
|
||||||
HU => 2,
|
|
||||||
WU => 4,
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod branch {
|
|
||||||
use super::*;
|
|
||||||
pub const EQ: Funct3 = Funct3::new(0b000);
|
|
||||||
pub const NE: Funct3 = Funct3::new(0b001);
|
|
||||||
pub const LT: Funct3 = Funct3::new(0b100);
|
|
||||||
pub const GE: Funct3 = Funct3::new(0b101);
|
|
||||||
pub const LTU: Funct3 = Funct3::new(0b110);
|
|
||||||
pub const GEU: Funct3 = Funct3::new(0b111);
|
|
||||||
|
|
||||||
pub fn str(f: Funct3) -> &'static str {
|
|
||||||
match f {
|
|
||||||
EQ => "eq",
|
|
||||||
NE => "ne",
|
|
||||||
LT => "lt",
|
|
||||||
GE => "ge",
|
|
||||||
LTU => "ltu",
|
|
||||||
GEU => "geu",
|
|
||||||
_ => "?",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn ecall() -> RawInstruction {
|
|
||||||
i_type(Bits32::new(0), zero, Bits32::new(0), zero, SYSTEM)
|
|
||||||
}
|
|
||||||
pub const fn ebreak() -> RawInstruction {
|
|
||||||
i_type(Bits32::new(1), zero, Bits32::new(0), zero, SYSTEM)
|
|
||||||
}
|
|
||||||
pub const fn auipc(dest: Reg, imm: BitsI32<31, 12>) -> RawInstruction {
|
|
||||||
u_type(imm.to_u(), dest, AUIPC)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn load(width: Funct3, dest: Reg, offset: BitsI32<11, 0>, base: Reg) -> RawInstruction {
|
|
||||||
i_type(offset.to_u(), base, width, dest, LOAD)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn store(width: Funct3, src: Reg, offset: BitsI32<11, 0>, base: Reg) -> RawInstruction {
|
|
||||||
s_type(src, base, width, offset.to_u(), STORE)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn jal(dest: Reg, offset: BitsI32<20, 1>) -> RawInstruction {
|
|
||||||
j_type(offset.to_u(), dest, JAL)
|
|
||||||
}
|
|
||||||
pub const fn jalr(dest: Reg, offset: BitsI32<11, 0>, base: Reg) -> RawInstruction {
|
|
||||||
i_type(offset.to_u(), base, Bits32::new(0), dest, JALR)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn j(offset: BitsI32<20, 1>) -> RawInstruction {
|
|
||||||
jal(zero, offset)
|
|
||||||
}
|
|
||||||
pub const fn ret() -> RawInstruction {
|
|
||||||
jalr(zero, BitsI32::new(0), ra)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn branch(typ: Funct3, left: Reg, right: Reg, offset: BitsI32<12, 1>) -> RawInstruction {
|
|
||||||
b_type(right, left, typ, offset.to_u(), BRANCH)
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
use super::{Funct3, Funct7};
|
|
||||||
|
|
||||||
pub mod op32m {
|
|
||||||
use super::*;
|
|
||||||
pub const MUL: Funct3 = Funct3::new(0b000);
|
|
||||||
pub const MULH: Funct3 = Funct3::new(0b001);
|
|
||||||
pub const MULHSU: Funct3 = Funct3::new(0b010);
|
|
||||||
pub const MULHU: Funct3 = Funct3::new(0b011);
|
|
||||||
pub const DIV: Funct3 = Funct3::new(0b100);
|
|
||||||
pub const DIVU: Funct3 = Funct3::new(0b101);
|
|
||||||
pub const REM: Funct3 = Funct3::new(0b110);
|
|
||||||
pub const REMU: Funct3 = Funct3::new(0b111);
|
|
||||||
|
|
||||||
pub const FUNCT7: Funct7 = Funct7::new(0b0000001);
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
use super::*;
|
|
||||||
|
|
||||||
pub fn opstr(op: Funct3, funct: Funct7) -> &'static str {
|
|
||||||
match (op, funct) {
|
|
||||||
(op32i::SLT, op32i::FUNCT7) => "slt",
|
|
||||||
(op32i::SLTU, op32i::FUNCT7) => "sltu",
|
|
||||||
(op32i::XOR, op32i::FUNCT7) => "xor",
|
|
||||||
(op32i::OR, op32i::FUNCT7) => "or",
|
|
||||||
(op32i::AND, op32i::FUNCT7) => "and",
|
|
||||||
|
|
||||||
(op32i::ADD, op32i::F7ADD) => "add",
|
|
||||||
(op32i::ADD, op32i::F7SUB) => "sub",
|
|
||||||
(op32i::SL, op32i::LOGICAL) => "sll",
|
|
||||||
(op32i::SR, op32i::LOGICAL) => "srl",
|
|
||||||
(op32i::SR, op32i::ARITHMETIC) => "sra",
|
|
||||||
|
|
||||||
(op32m::MUL, op32m::FUNCT7) => "mul",
|
|
||||||
(op32m::MULH, op32m::FUNCT7) => "mulh",
|
|
||||||
(op32m::MULHSU, op32m::FUNCT7) => "mulhsu",
|
|
||||||
(op32m::MULHU, op32m::FUNCT7) => "mulhu",
|
|
||||||
(op32m::DIV, op32m::FUNCT7) => "div",
|
|
||||||
(op32m::DIVU, op32m::FUNCT7) => "divu",
|
|
||||||
(op32m::REM, op32m::FUNCT7) => "rem",
|
|
||||||
(op32m::REMU, op32m::FUNCT7) => "remu",
|
|
||||||
_ => "unknown",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
mod asm;
|
|
||||||
mod compile;
|
|
||||||
mod reg;
|
|
||||||
mod instr;
|
|
||||||
|
|
||||||
use crate::util::BitsI32;
|
|
||||||
|
|
||||||
pub use asm::*;
|
|
||||||
pub use compile::*;
|
|
||||||
pub use reg::*;
|
|
||||||
pub use instr::*;
|
|
||||||
@@ -1,180 +0,0 @@
|
|||||||
#![allow(non_upper_case_globals)]
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct Reg(u8);
|
|
||||||
|
|
||||||
/// hard wired 0
|
|
||||||
pub const zero: Reg = Reg(0);
|
|
||||||
/// return address
|
|
||||||
pub const ra: Reg = Reg(1);
|
|
||||||
/// stack pointer
|
|
||||||
pub const sp: Reg = Reg(2);
|
|
||||||
/// global pointer
|
|
||||||
pub const gp: Reg = Reg(3);
|
|
||||||
/// thread pointer
|
|
||||||
pub const tp: Reg = Reg(4);
|
|
||||||
/// temp / alternate link
|
|
||||||
pub const t0: Reg = Reg(5);
|
|
||||||
pub const t1: Reg = Reg(6);
|
|
||||||
pub const t2: Reg = Reg(7);
|
|
||||||
|
|
||||||
pub const fp: Reg = Reg(8);
|
|
||||||
pub const s0: Reg = Reg(8);
|
|
||||||
pub const s1: Reg = Reg(9);
|
|
||||||
|
|
||||||
pub const a0: Reg = Reg(10);
|
|
||||||
pub const a1: Reg = Reg(11);
|
|
||||||
pub const a2: Reg = Reg(12);
|
|
||||||
pub const a3: Reg = Reg(13);
|
|
||||||
pub const a4: Reg = Reg(14);
|
|
||||||
pub const a5: Reg = Reg(15);
|
|
||||||
pub const a6: Reg = Reg(16);
|
|
||||||
pub const a7: Reg = Reg(17);
|
|
||||||
|
|
||||||
pub const s2: Reg = Reg(18);
|
|
||||||
pub const s3: Reg = Reg(19);
|
|
||||||
pub const s4: Reg = Reg(20);
|
|
||||||
pub const s5: Reg = Reg(21);
|
|
||||||
pub const s6: Reg = Reg(22);
|
|
||||||
pub const s7: Reg = Reg(23);
|
|
||||||
pub const s8: Reg = Reg(24);
|
|
||||||
pub const s9: Reg = Reg(25);
|
|
||||||
pub const s10: Reg = Reg(26);
|
|
||||||
pub const s11: Reg = Reg(27);
|
|
||||||
|
|
||||||
pub const t3: Reg = Reg(28);
|
|
||||||
pub const t4: Reg = Reg(29);
|
|
||||||
pub const t5: Reg = Reg(30);
|
|
||||||
pub const t6: Reg = Reg(31);
|
|
||||||
|
|
||||||
impl Reg {
|
|
||||||
#[inline]
|
|
||||||
pub const fn val(&self) -> u32 {
|
|
||||||
self.0 as u32
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Reg {
|
|
||||||
pub fn from_str(str: &str) -> Option<Self> {
|
|
||||||
Some(match str {
|
|
||||||
"zero" => zero,
|
|
||||||
"ra" => ra,
|
|
||||||
"sp" => sp,
|
|
||||||
"gp" => gp,
|
|
||||||
"tp" => tp,
|
|
||||||
"t0" => t0,
|
|
||||||
"t1" => t1,
|
|
||||||
"t2" => t2,
|
|
||||||
"fp" => fp,
|
|
||||||
"s0" => s0,
|
|
||||||
"s1" => s1,
|
|
||||||
"a0" => a0,
|
|
||||||
"a1" => a1,
|
|
||||||
"a2" => a2,
|
|
||||||
"a3" => a3,
|
|
||||||
"a4" => a4,
|
|
||||||
"a5" => a5,
|
|
||||||
"a6" => a6,
|
|
||||||
"a7" => a7,
|
|
||||||
"s2" => s2,
|
|
||||||
"s3" => s3,
|
|
||||||
"s4" => s4,
|
|
||||||
"s5" => s5,
|
|
||||||
"s6" => s6,
|
|
||||||
"s7" => s7,
|
|
||||||
"s8" => s8,
|
|
||||||
"s9" => s9,
|
|
||||||
"s10" => s10,
|
|
||||||
"s11" => s11,
|
|
||||||
"t3" => t3,
|
|
||||||
"t4" => t4,
|
|
||||||
"t5" => t5,
|
|
||||||
"t6" => t6,
|
|
||||||
_ => {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Debug for Reg {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"{}",
|
|
||||||
match self.0 {
|
|
||||||
0 => "zero",
|
|
||||||
1 => "ra",
|
|
||||||
2 => "sp",
|
|
||||||
3 => "gp",
|
|
||||||
4 => "tp",
|
|
||||||
5 => "t0",
|
|
||||||
6 => "t1",
|
|
||||||
7 => "t2",
|
|
||||||
8 => "fp",
|
|
||||||
9 => "s1",
|
|
||||||
10 => "a0",
|
|
||||||
11 => "a1",
|
|
||||||
12 => "a2",
|
|
||||||
13 => "a3",
|
|
||||||
14 => "a4",
|
|
||||||
15 => "a5",
|
|
||||||
16 => "a6",
|
|
||||||
17 => "a7",
|
|
||||||
18 => "s2",
|
|
||||||
19 => "s3",
|
|
||||||
20 => "s4",
|
|
||||||
21 => "s5",
|
|
||||||
22 => "s6",
|
|
||||||
23 => "s7",
|
|
||||||
24 => "s8",
|
|
||||||
25 => "s9",
|
|
||||||
26 => "s10",
|
|
||||||
27 => "s11",
|
|
||||||
28 => "t3",
|
|
||||||
29 => "t4",
|
|
||||||
30 => "t5",
|
|
||||||
31 => "t6",
|
|
||||||
_ => "unknown",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// pub const ft0: Reg = Reg(0);
|
|
||||||
// pub const ft1: Reg = Reg(1);
|
|
||||||
// pub const ft2: Reg = Reg(2);
|
|
||||||
// pub const ft3: Reg = Reg(3);
|
|
||||||
// pub const ft4: Reg = Reg(4);
|
|
||||||
// pub const ft5: Reg = Reg(5);
|
|
||||||
// pub const ft6: Reg = Reg(6);
|
|
||||||
// pub const ft7: Reg = Reg(7);
|
|
||||||
//
|
|
||||||
// pub const fs0: Reg = Reg(8);
|
|
||||||
// pub const fs1: Reg = Reg(9);
|
|
||||||
//
|
|
||||||
// pub const fa0: Reg = Reg(10);
|
|
||||||
// pub const fa1: Reg = Reg(11);
|
|
||||||
// pub const fa2: Reg = Reg(12);
|
|
||||||
// pub const fa3: Reg = Reg(13);
|
|
||||||
// pub const fa4: Reg = Reg(14);
|
|
||||||
// pub const fa5: Reg = Reg(15);
|
|
||||||
// pub const fa6: Reg = Reg(16);
|
|
||||||
// pub const fa7: Reg = Reg(17);
|
|
||||||
//
|
|
||||||
// pub const fs2: Reg = Reg(18);
|
|
||||||
// pub const fs3: Reg = Reg(19);
|
|
||||||
// pub const fs4: Reg = Reg(20);
|
|
||||||
// pub const fs5: Reg = Reg(21);
|
|
||||||
// pub const fs6: Reg = Reg(22);
|
|
||||||
// pub const fs7: Reg = Reg(23);
|
|
||||||
// pub const fs8: Reg = Reg(24);
|
|
||||||
// pub const fs9: Reg = Reg(25);
|
|
||||||
// pub const fs10: Reg = Reg(26);
|
|
||||||
// pub const fs11: Reg = Reg(27);
|
|
||||||
//
|
|
||||||
// pub const ft8: Reg = Reg(28);
|
|
||||||
// pub const ft9: Reg = Reg(29);
|
|
||||||
// pub const ft10: Reg = Reg(30);
|
|
||||||
// pub const ft11: Reg = Reg(31);
|
|
||||||
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
use crate::ir::Symbol;
|
|
||||||
|
|
||||||
pub struct DebugInfo {
|
|
||||||
pub sym_labels: Vec<Option<String>>,
|
|
||||||
pub ir_lower: Vec<Vec<(usize, String)>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DebugInfo {
|
|
||||||
pub fn new(sym_labels: Vec<Option<String>>) -> Self {
|
|
||||||
Self {
|
|
||||||
ir_lower: Vec::new(),
|
|
||||||
sym_labels,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn push_fn(&mut self, instrs: Vec<(usize, String)>) {
|
|
||||||
self.ir_lower.push(instrs);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sym_label(&self, s: Symbol) -> Option<&String> {
|
|
||||||
self.sym_labels[*s].as_ref()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,111 +0,0 @@
|
|||||||
use super::{program::Addr, LinkedProgram};
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct ELF64Header {
|
|
||||||
magic: u32,
|
|
||||||
class: u8,
|
|
||||||
endianness: u8,
|
|
||||||
ei_version: u8,
|
|
||||||
os_abi: u8,
|
|
||||||
os_abi_ver: u8,
|
|
||||||
pad: [u8; 7],
|
|
||||||
ty: u16,
|
|
||||||
machine: u16,
|
|
||||||
e_version: u32,
|
|
||||||
entry: u64,
|
|
||||||
program_header_offset: u64,
|
|
||||||
section_header_offset: u64,
|
|
||||||
flags: u32,
|
|
||||||
header_size: u16,
|
|
||||||
program_header_entry_size: u16,
|
|
||||||
program_header_num: u16,
|
|
||||||
section_header_entry_size: u16,
|
|
||||||
section_header_num: u16,
|
|
||||||
section_header_str_idx: u16,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct ProgramHeader {
|
|
||||||
ty: u32,
|
|
||||||
flags: u32,
|
|
||||||
offset: u64,
|
|
||||||
vaddr: u64,
|
|
||||||
paddr: u64,
|
|
||||||
filesz: u64,
|
|
||||||
memsz: u64,
|
|
||||||
align: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct SectionHeader {
|
|
||||||
name_idx: u32,
|
|
||||||
ty: u32,
|
|
||||||
flags: u64,
|
|
||||||
addr: u64,
|
|
||||||
offset: u64,
|
|
||||||
size: u64,
|
|
||||||
link: u32,
|
|
||||||
info: u32,
|
|
||||||
addr_align: u64,
|
|
||||||
entry_size: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is currently specialized for riscv64; obviously add params later
|
|
||||||
pub fn create(program: &[u8], start_offset: Addr) -> Vec<u8> {
|
|
||||||
let addr_start = 0x1000;
|
|
||||||
let page_size = 0x1000;
|
|
||||||
// I don't know if I have to add addr_start here, idk how it maps the memory
|
|
||||||
let program_size = std::mem::size_of_val(program) as u64 + addr_start;
|
|
||||||
let program_header = ProgramHeader {
|
|
||||||
ty: 0x1, // LOAD
|
|
||||||
flags: 0b101, // executable, readable
|
|
||||||
offset: 0x0,
|
|
||||||
vaddr: addr_start,
|
|
||||||
paddr: addr_start,
|
|
||||||
filesz: program_size,
|
|
||||||
memsz: program_size,
|
|
||||||
align: page_size,
|
|
||||||
};
|
|
||||||
let header_len = (size_of::<ELF64Header>() + size_of::<ProgramHeader>()) as u64;
|
|
||||||
let program_pos = header_len;
|
|
||||||
let header = ELF64Header {
|
|
||||||
magic: 0x7f_45_4c_46u32.swap_bytes(),
|
|
||||||
class: 0x2, // 64 bit
|
|
||||||
endianness: 0x1, // little endian
|
|
||||||
ei_version: 0x1,
|
|
||||||
os_abi: 0x0, // system-v
|
|
||||||
os_abi_ver: 0x0,
|
|
||||||
pad: [0x0; 7],
|
|
||||||
ty: 0x2, // executable
|
|
||||||
machine: 0xf3, // risc-v
|
|
||||||
e_version: 0x1,
|
|
||||||
entry: addr_start + program_pos + start_offset.val(),
|
|
||||||
program_header_offset: size_of::<ELF64Header>() as u64,
|
|
||||||
section_header_offset: 0x0,
|
|
||||||
// C ABI (16 bit instruction align) + double precision floats
|
|
||||||
flags: 0x1 | 0x4,
|
|
||||||
header_size: size_of::<ELF64Header>() as u16,
|
|
||||||
program_header_entry_size: size_of::<ProgramHeader>() as u16,
|
|
||||||
program_header_num: 0x1,
|
|
||||||
section_header_entry_size: size_of::<SectionHeader>() as u16,
|
|
||||||
section_header_num: 0x0,
|
|
||||||
section_header_str_idx: 0x0,
|
|
||||||
};
|
|
||||||
let mut bytes: Vec<u8> = Vec::new();
|
|
||||||
unsafe {
|
|
||||||
bytes.extend(as_u8_slice(&header));
|
|
||||||
bytes.extend(as_u8_slice(&program_header));
|
|
||||||
bytes.extend(program);
|
|
||||||
}
|
|
||||||
bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn as_u8_slice<T: Sized>(p: &T) -> &[u8] {
|
|
||||||
core::slice::from_raw_parts((p as *const T) as *const u8, size_of::<T>())
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LinkedProgram {
|
|
||||||
pub fn to_elf(&self) -> Vec<u8> {
|
|
||||||
create(&self.code, self.start.expect("no start found"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
pub enum Instruction {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
pub mod arch;
|
|
||||||
mod debug;
|
|
||||||
mod elf;
|
|
||||||
mod program;
|
|
||||||
mod target;
|
|
||||||
|
|
||||||
use arch::riscv;
|
|
||||||
pub use program::*;
|
|
||||||
|
|
||||||
use crate::ir::LProgram;
|
|
||||||
|
|
||||||
pub fn compile(program: &LProgram) -> UnlinkedProgram<riscv::LinkerInstruction> {
|
|
||||||
arch::riscv::compile(program)
|
|
||||||
}
|
|
||||||
@@ -1,138 +0,0 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
ir::Symbol,
|
|
||||||
util::{Labelable, LabeledFmt},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::debug::DebugInfo;
|
|
||||||
|
|
||||||
pub struct LinkedProgram {
|
|
||||||
pub code: Vec<u8>,
|
|
||||||
pub start: Option<Addr>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct UnlinkedProgram<I: Instr> {
|
|
||||||
pub fns: Vec<UnlinkedFunction<I>>,
|
|
||||||
pub ro_data: Vec<(Vec<u8>, Symbol)>,
|
|
||||||
pub sym_count: usize,
|
|
||||||
pub start: Option<Symbol>,
|
|
||||||
pub dbg: DebugInfo,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct UnlinkedFunction<I: Instr> {
|
|
||||||
pub instrs: Vec<I>,
|
|
||||||
pub sym: Symbol,
|
|
||||||
pub locations: HashMap<usize, Symbol>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I: Instr + std::fmt::Debug> UnlinkedProgram<I> {
|
|
||||||
pub fn link(self) -> LinkedProgram {
|
|
||||||
let mut data = Vec::new();
|
|
||||||
let mut sym_table = SymTable::new(self.sym_count);
|
|
||||||
let mut missing = HashMap::<Symbol, Vec<(Addr, I)>>::new();
|
|
||||||
for (val, id) in self.ro_data {
|
|
||||||
sym_table.insert(id, Addr(data.len() as u64));
|
|
||||||
data.extend(val);
|
|
||||||
}
|
|
||||||
data.resize(data.len() + (4 - data.len() % 4), 0);
|
|
||||||
for f in self.fns {
|
|
||||||
let mut added = vec![f.sym];
|
|
||||||
sym_table.insert(f.sym, Addr(data.len() as u64));
|
|
||||||
for (i, instr) in f.instrs.into_iter().enumerate() {
|
|
||||||
let i_pos = Addr(data.len() as u64);
|
|
||||||
if let Some(sym) = f.locations.get(&i) {
|
|
||||||
sym_table.insert(*sym, i_pos);
|
|
||||||
added.push(*sym);
|
|
||||||
}
|
|
||||||
if let Some(sym) = instr.push_to(&mut data, &mut sym_table, i_pos, false) {
|
|
||||||
if let Some(vec) = missing.get_mut(&sym) {
|
|
||||||
vec.push((i_pos, instr));
|
|
||||||
} else {
|
|
||||||
missing.insert(sym, vec![(i_pos, instr)]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for add in added {
|
|
||||||
if let Some(vec) = missing.remove(&add) {
|
|
||||||
for (addr, i) in vec {
|
|
||||||
let mut replace = Vec::new();
|
|
||||||
i.push_to(&mut replace, &mut sym_table, addr, true);
|
|
||||||
let pos = addr.val() as usize;
|
|
||||||
data[pos..pos + replace.len()].copy_from_slice(&replace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert!(missing.is_empty());
|
|
||||||
LinkedProgram {
|
|
||||||
code: data,
|
|
||||||
start: self
|
|
||||||
.start
|
|
||||||
.map(|s| sym_table.get(s).expect("start symbol doesn't exist")),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Instr {
|
|
||||||
fn push_to(
|
|
||||||
&self,
|
|
||||||
data: &mut Vec<u8>,
|
|
||||||
syms: &mut SymTable,
|
|
||||||
pos: Addr,
|
|
||||||
missing: bool,
|
|
||||||
) -> Option<Symbol>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
||||||
pub struct Addr(u64);
|
|
||||||
impl Addr {
|
|
||||||
const NONE: Self = Self(!0);
|
|
||||||
pub fn val(&self) -> u64 {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SymTable(Vec<Addr>);
|
|
||||||
impl SymTable {
|
|
||||||
pub fn new(len: usize) -> Self {
|
|
||||||
Self(vec![Addr::NONE; len])
|
|
||||||
}
|
|
||||||
pub fn insert(&mut self, sym: Symbol, addr: Addr) {
|
|
||||||
self.0[*sym] = addr;
|
|
||||||
}
|
|
||||||
pub fn get(&self, sym: Symbol) -> Option<Addr> {
|
|
||||||
match self.0[*sym] {
|
|
||||||
Addr::NONE => None,
|
|
||||||
addr => Some(addr),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I: Instr + Labelable<Symbol> + LabeledFmt<Symbol>> std::fmt::Debug for UnlinkedProgram<I> {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
for (fun, irli) in self.fns.iter().zip(&self.dbg.ir_lower) {
|
|
||||||
writeln!(f, "{}:", self.dbg.sym_label(fun.sym).unwrap())?;
|
|
||||||
let mut liter = irli.iter();
|
|
||||||
let mut cur = liter.next();
|
|
||||||
for (i, instr) in fun.instrs.iter().enumerate() {
|
|
||||||
while let Some(c) = cur
|
|
||||||
&& i == c.0
|
|
||||||
{
|
|
||||||
writeln!(f, " {}:", c.1)?;
|
|
||||||
cur = liter.next();
|
|
||||||
}
|
|
||||||
writeln!(
|
|
||||||
f,
|
|
||||||
" {:?}",
|
|
||||||
instr.labeled(&|f: &mut std::fmt::Formatter, s: &Symbol| write!(
|
|
||||||
f,
|
|
||||||
"{}",
|
|
||||||
self.dbg.sym_label(*s).unwrap_or(&format!("{:?}", *s))
|
|
||||||
))
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
pub trait Target {
|
|
||||||
type Reg;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait RegType {
|
|
||||||
type Size;
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
pub mod riscv64;
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
use std::fmt::Debug;
|
|
||||||
|
|
||||||
use crate::{compiler::arch::riscv::*, ir::IdentID};
|
|
||||||
|
|
||||||
pub type RV64Instruction<V = IdentID> = LinkerInstruction<RegRef<V>, V>;
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub enum RegRef<V = IdentID, R = Reg> {
|
|
||||||
Var(V),
|
|
||||||
Reg(R),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: Debug, R: Debug> Debug for RegRef<V, R> {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::Var(v) => write!(f, "{{{:?}}}", v),
|
|
||||||
Self::Reg(r) => r.fmt(f),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
use super::{arch::riscv64::RegRef, IdentID};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct IRAsmInstruction {
|
|
||||||
op: String,
|
|
||||||
args: Vec<RegRef<IdentID, String>>,
|
|
||||||
}
|
|
||||||
-136
@@ -1,136 +0,0 @@
|
|||||||
use std::{
|
|
||||||
fmt::Debug,
|
|
||||||
marker::PhantomData,
|
|
||||||
ops::{Index, IndexMut},
|
|
||||||
};
|
|
||||||
|
|
||||||
// I had an idea for why these were different... now I don't
|
|
||||||
pub type Size = u32;
|
|
||||||
pub type Len = u32;
|
|
||||||
|
|
||||||
pub struct ID<T>(pub usize, PhantomData<T>);
|
|
||||||
|
|
||||||
impl<T> ID<T> {
|
|
||||||
pub fn new(i: usize) -> Self {
|
|
||||||
Self(i, PhantomData)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> From<usize> for ID<T> {
|
|
||||||
fn from(value: usize) -> Self {
|
|
||||||
Self(value, PhantomData)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Debug for ID<T> {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(f, "{{{}}}", self.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> PartialEq for ID<T> {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.0 == other.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Eq for ID<T> {}
|
|
||||||
|
|
||||||
impl<T> std::hash::Hash for ID<T> {
|
|
||||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
|
||||||
self.0.hash(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Clone for ID<T> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self(self.0.clone(), PhantomData)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Copy for ID<T> {}
|
|
||||||
|
|
||||||
// :fear:
|
|
||||||
impl<T> Index<ID<T>> for Vec<T> {
|
|
||||||
type Output = T;
|
|
||||||
|
|
||||||
fn index(&self, i: ID<T>) -> &Self::Output {
|
|
||||||
&self[i.0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> IndexMut<ID<T>> for Vec<T> {
|
|
||||||
fn index_mut(&mut self, i: ID<T>) -> &mut Self::Output {
|
|
||||||
&mut self[i.0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Index<&ID<T>> for Vec<T> {
|
|
||||||
type Output = T;
|
|
||||||
|
|
||||||
fn index(&self, i: &ID<T>) -> &Self::Output {
|
|
||||||
&self[i.0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> IndexMut<&ID<T>> for Vec<T> {
|
|
||||||
fn index_mut(&mut self, i: &ID<T>) -> &mut Self::Output {
|
|
||||||
&mut self[i.0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Index<&mut ID<T>> for Vec<T> {
|
|
||||||
type Output = T;
|
|
||||||
|
|
||||||
fn index(&self, i: &mut ID<T>) -> &Self::Output {
|
|
||||||
&self[i.0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> IndexMut<&mut ID<T>> for Vec<T> {
|
|
||||||
fn index_mut(&mut self, i: &mut ID<T>) -> &mut Self::Output {
|
|
||||||
&mut self[i.0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Index<ID<T>> for [T] {
|
|
||||||
type Output = T;
|
|
||||||
|
|
||||||
fn index(&self, i: ID<T>) -> &Self::Output {
|
|
||||||
&self[i.0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> IndexMut<ID<T>> for [T] {
|
|
||||||
fn index_mut(&mut self, i: ID<T>) -> &mut Self::Output {
|
|
||||||
&mut self[i.0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Index<&ID<T>> for [T] {
|
|
||||||
type Output = T;
|
|
||||||
|
|
||||||
fn index(&self, i: &ID<T>) -> &Self::Output {
|
|
||||||
&self[i.0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> IndexMut<&ID<T>> for [T] {
|
|
||||||
fn index_mut(&mut self, i: &ID<T>) -> &mut Self::Output {
|
|
||||||
&mut self[i.0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Index<&mut ID<T>> for [T] {
|
|
||||||
type Output = T;
|
|
||||||
|
|
||||||
fn index(&self, i: &mut ID<T>) -> &Self::Output {
|
|
||||||
&self[i.0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> IndexMut<&mut ID<T>> for [T] {
|
|
||||||
fn index_mut(&mut self, i: &mut ID<T>) -> &mut Self::Output {
|
|
||||||
&mut self[i.0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
use super::*;
|
|
||||||
use crate::{compiler::arch::riscv::Reg, ir::arch::riscv64::RegRef};
|
|
||||||
use arch::riscv64::RV64Instruction;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct IRLFunction {
|
|
||||||
pub instructions: Vec<LInstruction>,
|
|
||||||
pub stack: HashMap<VarID, Size>,
|
|
||||||
pub subvar_map: HashMap<VarID, VarOffset>,
|
|
||||||
pub args: Vec<(VarID, Size)>,
|
|
||||||
pub ret_size: Size,
|
|
||||||
pub makes_call: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum LInstruction {
|
|
||||||
Mv {
|
|
||||||
dst: VarID,
|
|
||||||
dst_offset: Size,
|
|
||||||
src: VarID,
|
|
||||||
src_offset: Size,
|
|
||||||
},
|
|
||||||
Ref {
|
|
||||||
dst: VarID,
|
|
||||||
src: VarID,
|
|
||||||
},
|
|
||||||
LoadAddr {
|
|
||||||
dst: VarID,
|
|
||||||
offset: Size,
|
|
||||||
src: Symbol,
|
|
||||||
},
|
|
||||||
LoadData {
|
|
||||||
dst: VarID,
|
|
||||||
offset: Size,
|
|
||||||
src: Symbol,
|
|
||||||
len: Len,
|
|
||||||
},
|
|
||||||
Call {
|
|
||||||
dst: Option<(VarID, Size)>,
|
|
||||||
f: Symbol,
|
|
||||||
args: Vec<(VarID, Size)>,
|
|
||||||
},
|
|
||||||
AsmBlock {
|
|
||||||
instructions: Vec<RV64Instruction<VarID>>,
|
|
||||||
inputs: Vec<(Reg, VarID)>,
|
|
||||||
outputs: Vec<(Reg, VarID)>,
|
|
||||||
},
|
|
||||||
Ret {
|
|
||||||
src: Option<VarID>,
|
|
||||||
},
|
|
||||||
// TODO I feel like this should be turned into control flow instructions, maybe...
|
|
||||||
// not sure but LLVM has them so might be right play; seems optimal for optimization
|
|
||||||
Jump(Symbol),
|
|
||||||
Branch {
|
|
||||||
to: Symbol,
|
|
||||||
cond: VarID,
|
|
||||||
},
|
|
||||||
Mark(Symbol),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LInstruction {
|
|
||||||
pub fn is_ret(&self) -> bool {
|
|
||||||
matches!(self, Self::Ret { .. })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
mod func;
|
|
||||||
mod program;
|
|
||||||
mod symbol;
|
|
||||||
mod res;
|
|
||||||
|
|
||||||
pub use func::*;
|
|
||||||
pub use program::*;
|
|
||||||
pub use symbol::*;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
@@ -1,465 +0,0 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use super::{
|
|
||||||
IRLFunction, LInstruction, Len, Symbol, SymbolSpaceBuilder, UInstruction, UProgram, VarID,
|
|
||||||
};
|
|
||||||
use crate::ir::{
|
|
||||||
AsmBlockArgType, Size, StructInst, SymbolSpace, Type, TypeID, UFunc, UInstrInst, VarOffset,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct LProgram {
|
|
||||||
sym_space: SymbolSpace,
|
|
||||||
entry: Symbol,
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: there are THREE places here where I specify size (8)
|
|
||||||
|
|
||||||
impl LProgram {
|
|
||||||
pub fn create(p: &UProgram) -> Result<Self, String> {
|
|
||||||
let start = p
|
|
||||||
.names
|
|
||||||
.id::<UFunc>(&[], "crate")
|
|
||||||
.ok_or("no start method found")?;
|
|
||||||
let mut ssbuilder = SymbolSpaceBuilder::with_entries(&[start]);
|
|
||||||
let entry = ssbuilder.func(&start);
|
|
||||||
while let Some((sym, i)) = ssbuilder.pop_fn() {
|
|
||||||
let f = &p.fns[i.0];
|
|
||||||
let mut fbuilder = LFunctionBuilder::new(p, &mut ssbuilder);
|
|
||||||
for i in &f.instructions {
|
|
||||||
fbuilder.insert_instr(i);
|
|
||||||
}
|
|
||||||
if fbuilder.instrs.last().is_none_or(|i| !i.is_ret()) {
|
|
||||||
fbuilder.instrs.push(LInstruction::Ret { src: None });
|
|
||||||
}
|
|
||||||
let res = fbuilder.finish(f);
|
|
||||||
ssbuilder.write_fn(sym, res, Some(f.name.clone()));
|
|
||||||
}
|
|
||||||
let sym_space = ssbuilder.finish().expect("we failed the mission");
|
|
||||||
Ok(Self { sym_space, entry })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn entry(&self) -> Symbol {
|
|
||||||
self.entry
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct LStructInst {
|
|
||||||
offsets: Vec<Len>,
|
|
||||||
types: Vec<Type>,
|
|
||||||
order: HashMap<String, usize>,
|
|
||||||
size: Size,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LStructInst {
|
|
||||||
pub fn offset(&self, name: &str) -> Option<Len> {
|
|
||||||
Some(self.offsets[*self.order.get(name)?])
|
|
||||||
}
|
|
||||||
pub fn ty(&self, name: &str) -> Option<&Type> {
|
|
||||||
Some(&self.types[*self.order.get(name)?])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct LFunctionBuilder<'a> {
|
|
||||||
data: LFunctionBuilderData<'a>,
|
|
||||||
program: &'a UProgram,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> LFunctionBuilderData<'a> {
|
|
||||||
pub fn new(builder: &'a mut SymbolSpaceBuilder) -> Self {
|
|
||||||
Self {
|
|
||||||
instrs: Vec::new(),
|
|
||||||
struct_insts: HashMap::new(),
|
|
||||||
stack: HashMap::new(),
|
|
||||||
subvar_map: HashMap::new(),
|
|
||||||
makes_call: false,
|
|
||||||
builder,
|
|
||||||
loopp: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct LFunctionBuilderData<'a> {
|
|
||||||
builder: &'a mut SymbolSpaceBuilder,
|
|
||||||
instrs: Vec<LInstruction>,
|
|
||||||
stack: HashMap<VarID, Size>,
|
|
||||||
subvar_map: HashMap<VarID, VarOffset>,
|
|
||||||
struct_insts: HashMap<StructInst, LStructInst>,
|
|
||||||
makes_call: bool,
|
|
||||||
loopp: Option<LoopCtx>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct LoopCtx {
|
|
||||||
top: Symbol,
|
|
||||||
bot: Symbol,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> LFunctionBuilder<'a> {
|
|
||||||
pub fn new(program: &'a UProgram, builder: &'a mut SymbolSpaceBuilder) -> Self {
|
|
||||||
Self {
|
|
||||||
data: LFunctionBuilderData::new(builder),
|
|
||||||
program,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn alloc_stack(&mut self, i: VarID) -> Option<()> {
|
|
||||||
if self
|
|
||||||
.data
|
|
||||||
.size_of_var(self.program, i)
|
|
||||||
.expect("unsized type")
|
|
||||||
== 0
|
|
||||||
{
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
self.map_subvar(i);
|
|
||||||
let var = self.data.var_offset(self.program, i).expect("var offset");
|
|
||||||
if !self.stack.contains_key(&var.id) {
|
|
||||||
let size = self
|
|
||||||
.data
|
|
||||||
.size_of_var(self.program, var.id)
|
|
||||||
.expect("unsized type");
|
|
||||||
self.data.stack.insert(var.id, size);
|
|
||||||
}
|
|
||||||
Some(())
|
|
||||||
}
|
|
||||||
pub fn map_subvar(&mut self, i: VarID) {
|
|
||||||
let off = self.data.var_offset(self.program, i).expect("var offset");
|
|
||||||
if off.id != i {
|
|
||||||
self.subvar_map.insert(i, off);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn insert_instr(&mut self, i: &UInstrInst) -> Option<Option<String>> {
|
|
||||||
match i
|
|
||||||
.i
|
|
||||||
.resolve(self.program)
|
|
||||||
.expect("failed to resolve during lowering")
|
|
||||||
{
|
|
||||||
UInstruction::Mv { dst, src } => {
|
|
||||||
self.alloc_stack(dst)?;
|
|
||||||
self.map_subvar(src);
|
|
||||||
self.instrs.push(LInstruction::Mv {
|
|
||||||
dst,
|
|
||||||
dst_offset: 0,
|
|
||||||
src,
|
|
||||||
src_offset: 0,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
UInstruction::Ref { dst, src } => {
|
|
||||||
self.alloc_stack(dst)?;
|
|
||||||
self.map_subvar(src);
|
|
||||||
self.instrs.push(LInstruction::Ref { dst, src });
|
|
||||||
}
|
|
||||||
UInstruction::Deref { dst, src } => {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
UInstruction::LoadData { dst, src } => {
|
|
||||||
self.alloc_stack(dst)?;
|
|
||||||
let data = &self.program.data[src];
|
|
||||||
let sym = self.data.builder.ro_data(
|
|
||||||
src,
|
|
||||||
&data.content,
|
|
||||||
Some(&self.program.data[src].name),
|
|
||||||
);
|
|
||||||
self.instrs.push(LInstruction::LoadData {
|
|
||||||
dst,
|
|
||||||
offset: 0,
|
|
||||||
len: data.content.len() as Len,
|
|
||||||
src: sym,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
UInstruction::LoadSlice { dst, src } => {
|
|
||||||
self.alloc_stack(dst)?;
|
|
||||||
let data = &self.program.data[src];
|
|
||||||
let Type::Array(_, len) = &self.program.types[data.ty] else {
|
|
||||||
return Some(Some(format!(
|
|
||||||
"tried to load {} as slice",
|
|
||||||
self.program.type_name(&data.ty)
|
|
||||||
)));
|
|
||||||
};
|
|
||||||
let sym = self.data.builder.ro_data(
|
|
||||||
src,
|
|
||||||
&data.content,
|
|
||||||
Some(&self.program.data[src].name),
|
|
||||||
);
|
|
||||||
self.instrs.push(LInstruction::LoadAddr {
|
|
||||||
dst,
|
|
||||||
offset: 0,
|
|
||||||
src: sym,
|
|
||||||
});
|
|
||||||
|
|
||||||
let sym = self
|
|
||||||
.builder
|
|
||||||
.anon_ro_data(&(*len as u64).to_le_bytes(), Some(format!("len: {}", len)));
|
|
||||||
self.instrs.push(LInstruction::LoadData {
|
|
||||||
dst,
|
|
||||||
offset: 8,
|
|
||||||
len: 8,
|
|
||||||
src: sym,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
UInstruction::Call { dst, f, args } => {
|
|
||||||
self.alloc_stack(dst);
|
|
||||||
self.makes_call = true;
|
|
||||||
let sym = self.builder.func(f.id);
|
|
||||||
let ret_size = self
|
|
||||||
.data
|
|
||||||
.size_of_var(self.program, dst)
|
|
||||||
.expect("unsized type");
|
|
||||||
let dst = if ret_size > 0 {
|
|
||||||
Some((dst, ret_size))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
let call = LInstruction::Call {
|
|
||||||
dst,
|
|
||||||
f: sym,
|
|
||||||
args: args
|
|
||||||
.into_iter()
|
|
||||||
.map(|id| {
|
|
||||||
self.map_subvar(id);
|
|
||||||
(
|
|
||||||
id,
|
|
||||||
self.data
|
|
||||||
.size_of_var(self.program, id)
|
|
||||||
.expect("unsized type"),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
};
|
|
||||||
self.instrs.push(call);
|
|
||||||
}
|
|
||||||
UInstruction::AsmBlock { instructions, args } => {
|
|
||||||
let mut inputs = Vec::new();
|
|
||||||
let mut outputs = Vec::new();
|
|
||||||
for a in args {
|
|
||||||
match a.ty {
|
|
||||||
AsmBlockArgType::In => {
|
|
||||||
self.map_subvar(a.var);
|
|
||||||
inputs.push((a.reg, a.var))
|
|
||||||
}
|
|
||||||
AsmBlockArgType::Out => {
|
|
||||||
self.alloc_stack(a.var)?;
|
|
||||||
outputs.push((a.reg, a.var));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.instrs.push(LInstruction::AsmBlock {
|
|
||||||
instructions: instructions.clone(),
|
|
||||||
inputs,
|
|
||||||
outputs,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
UInstruction::Ret { src } => {
|
|
||||||
self.map_subvar(src);
|
|
||||||
let src = if self
|
|
||||||
.data
|
|
||||||
.size_of_var(self.program, src)
|
|
||||||
.expect("unsized var")
|
|
||||||
== 0
|
|
||||||
{
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(src)
|
|
||||||
};
|
|
||||||
self.data.instrs.push(LInstruction::Ret { src })
|
|
||||||
}
|
|
||||||
UInstruction::Construct {
|
|
||||||
dst,
|
|
||||||
ref struc,
|
|
||||||
ref fields,
|
|
||||||
} => {
|
|
||||||
self.alloc_stack(dst)?;
|
|
||||||
for (field, &src) in fields {
|
|
||||||
self.map_subvar(src);
|
|
||||||
let i = LInstruction::Mv {
|
|
||||||
dst,
|
|
||||||
src,
|
|
||||||
dst_offset: self
|
|
||||||
.data
|
|
||||||
.field_offset(self.program, struc, field)
|
|
||||||
.expect("field offset"),
|
|
||||||
src_offset: 0,
|
|
||||||
};
|
|
||||||
self.instrs.push(i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
UInstruction::If { cond, body } => {
|
|
||||||
self.map_subvar(cond);
|
|
||||||
let sym = self.builder.reserve();
|
|
||||||
self.instrs.push(LInstruction::Branch { to: *sym, cond });
|
|
||||||
for i in body {
|
|
||||||
self.insert_instr(&i);
|
|
||||||
}
|
|
||||||
self.instrs.push(LInstruction::Mark(*sym));
|
|
||||||
}
|
|
||||||
UInstruction::Loop { body } => {
|
|
||||||
let top = self.builder.reserve();
|
|
||||||
let bot = self.builder.reserve();
|
|
||||||
let old = self.loopp;
|
|
||||||
self.loopp = Some(LoopCtx {
|
|
||||||
bot: *bot,
|
|
||||||
top: *top,
|
|
||||||
});
|
|
||||||
self.instrs.push(LInstruction::Mark(*top));
|
|
||||||
for i in body {
|
|
||||||
self.insert_instr(i);
|
|
||||||
}
|
|
||||||
self.instrs.push(LInstruction::Jump(*top));
|
|
||||||
self.instrs.push(LInstruction::Mark(*bot));
|
|
||||||
self.loopp = old;
|
|
||||||
}
|
|
||||||
UInstruction::Break => {
|
|
||||||
self.data.instrs.push(LInstruction::Jump(
|
|
||||||
self.data.loopp.expect("Tried to break outside of loop").bot,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
UInstruction::Continue => {
|
|
||||||
self.data.instrs.push(LInstruction::Jump(
|
|
||||||
self.data.loopp.expect("Tried to break outside of loop").top,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Some(None)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn finish(mut self, f: &UFunc) -> IRLFunction {
|
|
||||||
IRLFunction {
|
|
||||||
args: f
|
|
||||||
.args
|
|
||||||
.iter()
|
|
||||||
.map(|a| {
|
|
||||||
(
|
|
||||||
*a,
|
|
||||||
self.data
|
|
||||||
.size_of_var(self.program, *a)
|
|
||||||
.expect("unsized type"),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
ret_size: self
|
|
||||||
.data
|
|
||||||
.size_of_type(self.program, &f.ret)
|
|
||||||
.expect("unsized type"),
|
|
||||||
instructions: self.data.instrs,
|
|
||||||
makes_call: self.data.makes_call,
|
|
||||||
stack: self.data.stack,
|
|
||||||
subvar_map: self.data.subvar_map,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LFunctionBuilderData<'_> {
|
|
||||||
pub fn var_offset(&mut self, p: &UProgram, mut var: VarID) -> Option<VarOffset> {
|
|
||||||
let mut path = Vec::new();
|
|
||||||
while let Type::Field(parent) = &p.get(var)?.ty {
|
|
||||||
var = parent.parent;
|
|
||||||
path.push(&parent.name);
|
|
||||||
}
|
|
||||||
let mut ty = &p.get(var)?.ty;
|
|
||||||
let mut offset = 0;
|
|
||||||
while let Type::Struct(sty) = ty {
|
|
||||||
let Some(name) = path.pop() else {
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
offset += self.field_offset(p, sty, &name)?;
|
|
||||||
ty = p.struct_field_type(sty, name).expect("bad field");
|
|
||||||
}
|
|
||||||
Some(VarOffset { id: var, offset })
|
|
||||||
}
|
|
||||||
pub fn addr_size(&self) -> Size {
|
|
||||||
64
|
|
||||||
}
|
|
||||||
pub fn struct_inst(&mut self, p: &UProgram, ty: &StructInst) -> &LStructInst {
|
|
||||||
// normally I'd let Some(..) here and return, but polonius does not exist :grief:
|
|
||||||
if self.struct_insts.get(ty).is_none() {
|
|
||||||
let LStructInst { id, args } = ty;
|
|
||||||
let struc = p.expect(*id);
|
|
||||||
let mut types = Vec::new();
|
|
||||||
let mut sizes = struc
|
|
||||||
.fields
|
|
||||||
.iter()
|
|
||||||
.map(|(n, f)| {
|
|
||||||
let ty = if let Type::Generic { id } = &f.ty {
|
|
||||||
struc
|
|
||||||
.generics
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.find_map(|(i, g)| if *g == *id { args.get(i) } else { None })
|
|
||||||
.unwrap_or(&f.ty)
|
|
||||||
} else {
|
|
||||||
&f.ty
|
|
||||||
};
|
|
||||||
types.push(ty.clone());
|
|
||||||
(n, self.size_of_type(p, ty).expect("unsized type"))
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
sizes.sort_by(|(n1, s1, ..), (n2, s2, ..)| s1.cmp(s2).then_with(|| n1.cmp(n2)));
|
|
||||||
let mut offset = 0;
|
|
||||||
let mut offsets = Vec::new();
|
|
||||||
let mut order = HashMap::new();
|
|
||||||
for (i, (name, size)) in sizes.iter().rev().enumerate() {
|
|
||||||
// TODO: alignment!!!
|
|
||||||
order.insert(name.to_string(), i);
|
|
||||||
offsets.push(offset);
|
|
||||||
offset += size;
|
|
||||||
}
|
|
||||||
self.struct_insts.insert(
|
|
||||||
ty.clone(),
|
|
||||||
LStructInst {
|
|
||||||
offsets,
|
|
||||||
order,
|
|
||||||
types,
|
|
||||||
size: offset,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
self.struct_insts.get(ty).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn field_offset(&mut self, p: &UProgram, sty: &StructInst, field: &str) -> Option<Len> {
|
|
||||||
let inst = self.struct_inst(p, sty);
|
|
||||||
Some(inst.offset(field)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn size_of_type(&mut self, p: &UProgram, ty: &TypeID) -> Option<Size> {
|
|
||||||
// TODO: target matters
|
|
||||||
Some(match &p.types[ty] {
|
|
||||||
Type::Bits(b) => *b,
|
|
||||||
Type::Struct(ty) => self.struct_inst(p, ty).size,
|
|
||||||
Type::Generic(id) => return None,
|
|
||||||
// function references are resolved at compile time into direct calls,
|
|
||||||
// so they don't have any size as arguments
|
|
||||||
Type::FnInst(fi) => 0,
|
|
||||||
Type::Ref(_) => self.addr_size(),
|
|
||||||
Type::Array(ty, len) => self.size_of_type(p, ty)? * len,
|
|
||||||
Type::Slice(_) => self.addr_size() * 2,
|
|
||||||
Type::Unit => 0,
|
|
||||||
_ => return None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn size_of_var(&mut self, p: &UProgram, var: VarID) -> Option<Size> {
|
|
||||||
self.size_of_type(p, &p.get(var)?.ty)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> std::ops::Deref for LFunctionBuilder<'a> {
|
|
||||||
type Target = LFunctionBuilderData<'a>;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> std::ops::DerefMut for LFunctionBuilder<'a> {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
&mut self.data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::ops::Deref for LProgram {
|
|
||||||
type Target = SymbolSpace;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.sym_space
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
use crate::ir::{
|
|
||||||
arch::riscv64::{RV64Instruction, RegRef},
|
|
||||||
AsmBlockArg, Resolved, UInstrInst, UInstruction, UProgram, VarID,
|
|
||||||
};
|
|
||||||
|
|
||||||
impl UInstrInst {
|
|
||||||
pub fn resolve<'a>(&'a self, p: &'a UProgram) -> Option<UInstrInst<Resolved>> {
|
|
||||||
Some(UInstrInst {
|
|
||||||
i: self.i.resolve(p)?,
|
|
||||||
origin: self.origin,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UInstruction {
|
|
||||||
pub fn resolve<'a>(&'a self, p: &'a UProgram) -> Option<UInstruction<Resolved>> {
|
|
||||||
use UInstruction as I;
|
|
||||||
Some(match self {
|
|
||||||
I::Mv { dst, src } => I::Mv {
|
|
||||||
dst: dst.var(p)?,
|
|
||||||
src: src.var(p)?,
|
|
||||||
},
|
|
||||||
I::Ref { dst, src } => I::Ref {
|
|
||||||
dst: dst.var(p)?,
|
|
||||||
src: src.var(p)?,
|
|
||||||
},
|
|
||||||
I::Deref { dst, src } => I::Deref {
|
|
||||||
dst: dst.var(p)?,
|
|
||||||
src: src.var(p)?,
|
|
||||||
},
|
|
||||||
I::LoadData { dst, src } => I::LoadData {
|
|
||||||
dst: dst.var(p)?,
|
|
||||||
src: *src,
|
|
||||||
},
|
|
||||||
I::LoadSlice { dst, src } => I::LoadSlice {
|
|
||||||
dst: dst.var(p)?,
|
|
||||||
src: *src,
|
|
||||||
},
|
|
||||||
I::Call { dst, f, args } => I::Call {
|
|
||||||
dst: dst.var(p)?,
|
|
||||||
f: f.fun(p)?.clone(),
|
|
||||||
args: args.iter().map(|i| i.var(p)).try_collect()?,
|
|
||||||
},
|
|
||||||
I::AsmBlock { instructions, args } => I::AsmBlock {
|
|
||||||
instructions: instructions
|
|
||||||
.iter()
|
|
||||||
.map(|i| i.resolve(p))
|
|
||||||
.collect::<Option<_>>()?,
|
|
||||||
args: args.iter().map(|a| a.resolve(p)).try_collect()?,
|
|
||||||
},
|
|
||||||
I::Ret { src } => I::Ret { src: src.var(p)? },
|
|
||||||
I::Construct { dst, struc, fields } => I::Construct {
|
|
||||||
dst: dst.var(p)?,
|
|
||||||
struc: struc.struc(p)?.clone(),
|
|
||||||
fields: fields
|
|
||||||
.iter()
|
|
||||||
.map(|(name, ident)| ident.var(p).map(|i| (name.clone(), i)))
|
|
||||||
.collect::<Option<_>>()?,
|
|
||||||
},
|
|
||||||
I::If { cond, body } => I::If {
|
|
||||||
cond: cond.var(p)?,
|
|
||||||
body: body.iter().map(|i| i.resolve(p)).try_collect()?,
|
|
||||||
},
|
|
||||||
I::Loop { body } => I::Loop {
|
|
||||||
body: body.iter().map(|i| i.resolve(p)).try_collect()?,
|
|
||||||
},
|
|
||||||
I::Break => I::Break,
|
|
||||||
I::Continue => I::Continue,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsmBlockArg {
|
|
||||||
pub fn resolve(&self, p: &UProgram) -> Option<AsmBlockArg<VarID>> {
|
|
||||||
Some(AsmBlockArg {
|
|
||||||
var: self.var.var(p)?,
|
|
||||||
reg: self.reg,
|
|
||||||
ty: self.ty,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RV64Instruction {
|
|
||||||
pub fn resolve(&self, p: &UProgram) -> Option<RV64Instruction<VarID>> {
|
|
||||||
self.try_map(|i| {
|
|
||||||
Some(match i {
|
|
||||||
RegRef::Var(v) => RegRef::Var(v.var(p)?),
|
|
||||||
RegRef::Reg(r) => RegRef::Reg(*r),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,153 +0,0 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use super::{DataID, FnID, IRLFunction};
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
|
|
||||||
pub struct Symbol(usize);
|
|
||||||
/// intentionally does not have copy or clone;
|
|
||||||
/// this should only be consumed once
|
|
||||||
pub struct WritableSymbol(Symbol);
|
|
||||||
|
|
||||||
impl std::ops::Deref for WritableSymbol {
|
|
||||||
type Target = Symbol;
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SymbolSpace {
|
|
||||||
ro_data: Vec<(Symbol, Vec<u8>)>,
|
|
||||||
fns: Vec<(Symbol, IRLFunction)>,
|
|
||||||
len: usize,
|
|
||||||
labels: Vec<Option<String>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SymbolSpaceBuilder {
|
|
||||||
symbols: usize,
|
|
||||||
unwritten_fns: Vec<(WritableSymbol, FnID)>,
|
|
||||||
fn_map: HashMap<FnID, Symbol>,
|
|
||||||
data_map: HashMap<DataID, Symbol>,
|
|
||||||
ro_data: Vec<(Symbol, Vec<u8>)>,
|
|
||||||
fns: Vec<(Symbol, IRLFunction)>,
|
|
||||||
labels: Vec<Option<String>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SymbolSpace {
|
|
||||||
pub fn ro_data(&self) -> &[(Symbol, Vec<u8>)] {
|
|
||||||
&self.ro_data
|
|
||||||
}
|
|
||||||
pub fn fns(&self) -> &[(Symbol, IRLFunction)] {
|
|
||||||
&self.fns
|
|
||||||
}
|
|
||||||
pub fn labels(&self) -> &[Option<String>] {
|
|
||||||
&self.labels
|
|
||||||
}
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
self.len
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SymbolSpaceBuilder {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
symbols: 0,
|
|
||||||
unwritten_fns: Vec::new(),
|
|
||||||
fn_map: HashMap::new(),
|
|
||||||
data_map: HashMap::new(),
|
|
||||||
ro_data: Vec::new(),
|
|
||||||
fns: Vec::new(),
|
|
||||||
labels: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn with_entries(entries: &[FnID]) -> SymbolSpaceBuilder {
|
|
||||||
let mut s = Self::new();
|
|
||||||
for e in entries {
|
|
||||||
s.func(*e);
|
|
||||||
}
|
|
||||||
s
|
|
||||||
}
|
|
||||||
pub fn pop_fn(&mut self) -> Option<(WritableSymbol, FnID)> {
|
|
||||||
self.unwritten_fns.pop()
|
|
||||||
}
|
|
||||||
pub fn anon_ro_data(&mut self, data: &[u8], label: Option<String>) -> Symbol {
|
|
||||||
let sym = self.reserve();
|
|
||||||
self.write_ro_data(sym, data.to_vec(), label)
|
|
||||||
}
|
|
||||||
pub fn ro_data(&mut self, id: DataID, data: &[u8], label: Option<&str>) -> Symbol {
|
|
||||||
match self.data_map.get(&id) {
|
|
||||||
Some(s) => *s,
|
|
||||||
None => {
|
|
||||||
let sym = self.reserve();
|
|
||||||
self.data_map.insert(id, *sym);
|
|
||||||
self.write_ro_data(sym, data.to_vec(), label.map(|l| l.to_string()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn func(&mut self, id: FnID) -> Symbol {
|
|
||||||
match self.fn_map.get(&id) {
|
|
||||||
Some(s) => *s,
|
|
||||||
None => {
|
|
||||||
let wsym = self.reserve();
|
|
||||||
let sym = *wsym;
|
|
||||||
self.unwritten_fns.push((wsym, id));
|
|
||||||
self.fn_map.insert(id, sym);
|
|
||||||
sym
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn write_ro_data(
|
|
||||||
&mut self,
|
|
||||||
sym: WritableSymbol,
|
|
||||||
data: Vec<u8>,
|
|
||||||
name: Option<String>,
|
|
||||||
) -> Symbol {
|
|
||||||
self.ro_data.push((*sym, data));
|
|
||||||
self.labels[sym.0 .0] = name;
|
|
||||||
*sym
|
|
||||||
}
|
|
||||||
pub fn write_fn(
|
|
||||||
&mut self,
|
|
||||||
sym: WritableSymbol,
|
|
||||||
func: IRLFunction,
|
|
||||||
name: Option<String>,
|
|
||||||
) -> Symbol {
|
|
||||||
self.fns.push((*sym, func));
|
|
||||||
self.labels[sym.0 .0] = name;
|
|
||||||
*sym
|
|
||||||
}
|
|
||||||
pub fn reserve(&mut self) -> WritableSymbol {
|
|
||||||
let val = self.symbols;
|
|
||||||
self.symbols += 1;
|
|
||||||
self.labels.push(None);
|
|
||||||
WritableSymbol(Symbol(val))
|
|
||||||
}
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
self.symbols
|
|
||||||
}
|
|
||||||
pub fn finish(self) -> Option<SymbolSpace> {
|
|
||||||
if self.unwritten_fns.is_empty() {
|
|
||||||
Some(SymbolSpace {
|
|
||||||
len: self.symbols,
|
|
||||||
fns: self.fns,
|
|
||||||
ro_data: self.ro_data,
|
|
||||||
labels: self.labels,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Debug for Symbol {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(f, "@{}", self.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::ops::Deref for Symbol {
|
|
||||||
type Target = usize;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
//! the IR is split into 2 layers: upper and lower
|
|
||||||
//! upper handles all of the main language features like types,
|
|
||||||
//! and the lower is a very concrete format that can be easily
|
|
||||||
//! translated to assembly and will probably also include
|
|
||||||
//! the majority of optimization, but not sure
|
|
||||||
|
|
||||||
mod upper;
|
|
||||||
mod lower;
|
|
||||||
mod id;
|
|
||||||
mod asm;
|
|
||||||
pub mod arch;
|
|
||||||
|
|
||||||
pub use upper::*;
|
|
||||||
pub use lower::*;
|
|
||||||
pub use id::*;
|
|
||||||
|
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
use std::fmt::Display;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
/// a generic identifier for all (identifiable) kinds
|
|
||||||
/// eg. a::b::c.d.e
|
|
||||||
/// or a::Result<T,_>
|
|
||||||
pub struct UIdent {
|
|
||||||
pub status: IdentStatus,
|
|
||||||
pub origin: Origin,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum IdentStatus {
|
|
||||||
Res(Res),
|
|
||||||
// lets you do things like import and then specialize in multiple places
|
|
||||||
// eg. import SomeStruct ...... f() -> SomeStruct // type ....... SomeStruct {} // struct
|
|
||||||
// and then have correct errors like "expected struct, found type Bla"
|
|
||||||
Ref(IdentID),
|
|
||||||
Unres {
|
|
||||||
base: ResBase,
|
|
||||||
path: Vec<MemberIdent>,
|
|
||||||
},
|
|
||||||
Failed(Option<ResErr>),
|
|
||||||
Cooked,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct MemberIdent {
|
|
||||||
pub ty: MemberTy,
|
|
||||||
pub name: String,
|
|
||||||
pub gargs: Vec<TypeID>,
|
|
||||||
pub origin: Origin,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum MemberTy {
|
|
||||||
Member,
|
|
||||||
Field,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MemberTy {
|
|
||||||
pub fn sep(&self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
MemberTy::Member => "::",
|
|
||||||
MemberTy::Field => ".",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for MemberTy {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
f.write_str(match self {
|
|
||||||
MemberTy::Member => "member",
|
|
||||||
MemberTy::Field => "field",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum Res {
|
|
||||||
Var(VarID),
|
|
||||||
Fn(FnInst),
|
|
||||||
Struct(StructInst),
|
|
||||||
Type(TypeID),
|
|
||||||
Generic(GenericID),
|
|
||||||
Module(ModID),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Res {
|
|
||||||
pub fn kind(&self) -> KindTy {
|
|
||||||
match self {
|
|
||||||
Res::Var(..) => KindTy::Var,
|
|
||||||
Res::Fn(..) => KindTy::Fn,
|
|
||||||
Res::Struct(..) => KindTy::Struct,
|
|
||||||
Res::Type(..) => KindTy::Type,
|
|
||||||
Res::Module(..) => KindTy::Module,
|
|
||||||
Res::Generic(..) => KindTy::Generic,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn display_str(&self, p: &UProgram) -> String {
|
|
||||||
let name = match self {
|
|
||||||
Res::Var(id) => &p.vars[id].name,
|
|
||||||
Res::Fn(fi) => &p.fns[fi.id].name,
|
|
||||||
Res::Struct(si) => &p.structs[si.id].name,
|
|
||||||
Res::Type(id) => &p.type_name(id),
|
|
||||||
Res::Generic(id) => &p.generics[id].name,
|
|
||||||
Res::Module(id) => &p.modules[id].name,
|
|
||||||
};
|
|
||||||
format!("{} '{}'", self.kind(), name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub enum ResBase {
|
|
||||||
Unvalidated(MemRes),
|
|
||||||
Validated(Res),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ResBase {
|
|
||||||
pub fn display_str(&self, p: &UProgram) -> String {
|
|
||||||
match self {
|
|
||||||
ResBase::Unvalidated(uv) => uv.display_str(p),
|
|
||||||
ResBase::Validated(res) => res.display_str(p),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct MemRes {
|
|
||||||
pub mem: Member,
|
|
||||||
pub origin: Origin,
|
|
||||||
pub gargs: Vec<TypeID>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MemRes {
|
|
||||||
pub fn display_str(&self, p: &UProgram) -> String {
|
|
||||||
self.mem.id.display_str(p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IdentID {
|
|
||||||
pub fn var(&self, p: &UProgram) -> Option<VarID> {
|
|
||||||
match p.idents[self].status {
|
|
||||||
IdentStatus::Res(Res::Var(id)) => Some(id),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn fun<'a>(&self, p: &'a UProgram) -> Option<&'a FnInst> {
|
|
||||||
match &p.idents[self].status {
|
|
||||||
IdentStatus::Res(Res::Fn(i)) => Some(&i),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn struc<'a>(&self, p: &'a UProgram) -> Option<&'a StructInst> {
|
|
||||||
match &p.idents[self].status {
|
|
||||||
IdentStatus::Res(Res::Struct(i)) => Some(&i),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use super::{arch::riscv64::RV64Instruction, *};
|
|
||||||
use crate::compiler::arch::riscv::Reg;
|
|
||||||
|
|
||||||
pub trait ResStage {
|
|
||||||
type Var;
|
|
||||||
type Func;
|
|
||||||
type Struct;
|
|
||||||
type Type;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Unresolved;
|
|
||||||
impl ResStage for Unresolved {
|
|
||||||
type Var = VarRes;
|
|
||||||
type Func = IdentID;
|
|
||||||
type Struct = IdentID;
|
|
||||||
type Type = TypeRes;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Resolved;
|
|
||||||
impl ResStage for Resolved {
|
|
||||||
type Var = VarID;
|
|
||||||
type Func = FnInst;
|
|
||||||
type Struct = StructInst;
|
|
||||||
type Type = TypeID;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum UInstruction<S: ResStage = Unresolved> {
|
|
||||||
Mv {
|
|
||||||
dst: S::Var,
|
|
||||||
src: S::Var,
|
|
||||||
},
|
|
||||||
Ref {
|
|
||||||
dst: S::Var,
|
|
||||||
src: S::Var,
|
|
||||||
},
|
|
||||||
Deref {
|
|
||||||
dst: S::Var,
|
|
||||||
src: S::Var,
|
|
||||||
},
|
|
||||||
LoadData {
|
|
||||||
dst: S::Var,
|
|
||||||
src: DataID,
|
|
||||||
},
|
|
||||||
LoadSlice {
|
|
||||||
dst: S::Var,
|
|
||||||
src: DataID,
|
|
||||||
},
|
|
||||||
Call {
|
|
||||||
dst: S::Var,
|
|
||||||
f: S::Func,
|
|
||||||
args: Vec<S::Var>,
|
|
||||||
},
|
|
||||||
AsmBlock {
|
|
||||||
instructions: Vec<RV64Instruction<S::Var>>,
|
|
||||||
args: Vec<AsmBlockArg<S::Var>>,
|
|
||||||
},
|
|
||||||
Ret {
|
|
||||||
src: S::Var,
|
|
||||||
},
|
|
||||||
Construct {
|
|
||||||
dst: S::Var,
|
|
||||||
struc: S::Struct,
|
|
||||||
fields: HashMap<String, S::Var>,
|
|
||||||
},
|
|
||||||
If {
|
|
||||||
cond: S::Var,
|
|
||||||
body: Vec<InstrID>,
|
|
||||||
},
|
|
||||||
Loop {
|
|
||||||
body: Vec<InstrID>,
|
|
||||||
},
|
|
||||||
Break,
|
|
||||||
Continue,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct UInstrInst<S: ResStage = Unresolved> {
|
|
||||||
pub i: UInstruction<S>,
|
|
||||||
pub origin: Origin,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct AsmBlockArg<V = IdentID> {
|
|
||||||
pub var: V,
|
|
||||||
pub reg: Reg,
|
|
||||||
pub ty: AsmBlockArgType,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub enum AsmBlockArgType {
|
|
||||||
In,
|
|
||||||
Out,
|
|
||||||
}
|
|
||||||
@@ -1,161 +0,0 @@
|
|||||||
//! all main IR Upper data structures stored in UProgram
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
use crate::{
|
|
||||||
common::FileSpan,
|
|
||||||
ir::{Len, ID},
|
|
||||||
};
|
|
||||||
use std::{
|
|
||||||
collections::HashMap,
|
|
||||||
fmt::{Debug, Display},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub type NamePath = Vec<String>;
|
|
||||||
|
|
||||||
pub type FnID = ID<UFunc>;
|
|
||||||
pub type VarID = ID<UVar>;
|
|
||||||
pub type IdentID = ID<UIdent>;
|
|
||||||
pub type TypeID = ID<Type>;
|
|
||||||
pub type GenericID = ID<UGeneric>;
|
|
||||||
pub type StructID = ID<UStruct>;
|
|
||||||
pub type DataID = ID<UData>;
|
|
||||||
pub type ModID = ID<UModule>;
|
|
||||||
pub type InstrID = ID<UInstrInst>;
|
|
||||||
|
|
||||||
pub type VarRes = URes<VarID>;
|
|
||||||
pub type TypeRes = URes<VarID>;
|
|
||||||
|
|
||||||
pub struct UFunc {
|
|
||||||
pub name: String,
|
|
||||||
pub origin: Origin,
|
|
||||||
pub args: Vec<VarID>,
|
|
||||||
pub gargs: Vec<GenericID>,
|
|
||||||
pub ret: TypeRes,
|
|
||||||
pub instructions: Vec<InstrID>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct StructField {
|
|
||||||
pub ty: TypeRes,
|
|
||||||
pub origin: Origin,
|
|
||||||
// pub vis: Visibility
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct UStruct {
|
|
||||||
pub name: String,
|
|
||||||
pub origin: Origin,
|
|
||||||
pub fields: HashMap<String, StructField>,
|
|
||||||
pub gargs: Vec<GenericID>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct UGeneric {
|
|
||||||
pub name: String,
|
|
||||||
pub origin: Origin,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct UVar {
|
|
||||||
pub name: String,
|
|
||||||
pub origin: Origin,
|
|
||||||
pub ty: TypeRes,
|
|
||||||
pub parent: Option<VarID>,
|
|
||||||
pub children: HashMap<String, VarID>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum VarTy {
|
|
||||||
Ident(IdentID),
|
|
||||||
Res(TypeID),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
|
|
||||||
pub struct VarOffset {
|
|
||||||
pub id: VarID,
|
|
||||||
pub offset: Len,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct UData {
|
|
||||||
pub name: String,
|
|
||||||
pub ty: TypeID,
|
|
||||||
pub content: Vec<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct UModule {
|
|
||||||
pub name: String,
|
|
||||||
pub members: HashMap<String, Member>,
|
|
||||||
pub parent: Option<ModID>,
|
|
||||||
pub func: FnID,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Member {
|
|
||||||
pub id: MemberID,
|
|
||||||
// pub visibility: Visibility
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub enum MemberID {
|
|
||||||
Fn(FnID),
|
|
||||||
Struct(StructID),
|
|
||||||
Var(VarID),
|
|
||||||
Module(ModID),
|
|
||||||
Type(TypeDef),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct TypeDef {
|
|
||||||
pub gargs: Vec<GenericID>,
|
|
||||||
pub ty: TypeID,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MemberID {
|
|
||||||
pub fn kind(&self) -> KindTy {
|
|
||||||
match self {
|
|
||||||
MemberID::Fn(_) => KindTy::Fn,
|
|
||||||
MemberID::Struct(_) => KindTy::Struct,
|
|
||||||
MemberID::Var(_) => KindTy::Var,
|
|
||||||
MemberID::Module(_) => KindTy::Module,
|
|
||||||
MemberID::Type(_) => KindTy::Type,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn display_str(&self, p: &UProgram) -> String {
|
|
||||||
let name = match self {
|
|
||||||
MemberID::Var(id) => &p.vars[id].name,
|
|
||||||
MemberID::Fn(id) => &p.fns[id].name,
|
|
||||||
MemberID::Struct(id) => &p.structs[id].name,
|
|
||||||
MemberID::Module(id) => &p.modules[id].name,
|
|
||||||
MemberID::Type(def) => &p.type_name(def.ty),
|
|
||||||
};
|
|
||||||
format!("{} '{}'", self.kind(), name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum URes<T> {
|
|
||||||
Res(T),
|
|
||||||
Unres(IdentID),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Origin = FileSpan;
|
|
||||||
|
|
||||||
// "effective" (externally visible) kinds
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub enum KindTy {
|
|
||||||
Type,
|
|
||||||
Var,
|
|
||||||
Struct,
|
|
||||||
Fn,
|
|
||||||
Module,
|
|
||||||
Generic,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for KindTy {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
f.write_str(match self {
|
|
||||||
KindTy::Type => "type",
|
|
||||||
KindTy::Var => "variable",
|
|
||||||
KindTy::Fn => "function",
|
|
||||||
KindTy::Struct => "struct",
|
|
||||||
KindTy::Module => "module",
|
|
||||||
KindTy::Generic => "generic",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
mod instr;
|
|
||||||
mod kind;
|
|
||||||
mod program;
|
|
||||||
mod ty;
|
|
||||||
mod resolve;
|
|
||||||
mod error;
|
|
||||||
mod ident;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
pub use instr::*;
|
|
||||||
pub use kind::*;
|
|
||||||
pub use program::*;
|
|
||||||
pub use ty::*;
|
|
||||||
pub use error::*;
|
|
||||||
pub use resolve::*;
|
|
||||||
pub use ident::*;
|
|
||||||
@@ -1,178 +0,0 @@
|
|||||||
use super::*;
|
|
||||||
|
|
||||||
pub struct UProgram {
|
|
||||||
pub fns: Vec<UFunc>,
|
|
||||||
pub structs: Vec<UStruct>,
|
|
||||||
pub modules: Vec<UModule>,
|
|
||||||
pub data: Vec<UData>,
|
|
||||||
pub generics: Vec<UGeneric>,
|
|
||||||
pub vars: Vec<UVar>,
|
|
||||||
pub idents: Vec<UIdent>,
|
|
||||||
pub types: Vec<Type>,
|
|
||||||
pub instrs: Vec<UInstrInst>,
|
|
||||||
|
|
||||||
pub unres_idents: Vec<IdentID>,
|
|
||||||
pub unres_instrs: Vec<(FnID, InstrID)>,
|
|
||||||
pub tc: TypeCache,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TypeCache {
|
|
||||||
pub unit: TypeID,
|
|
||||||
pub error: TypeID,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UProgram {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
let mut types = Vec::new();
|
|
||||||
let tc = TypeCache {
|
|
||||||
unit: push_id(&mut types, Type::Unit),
|
|
||||||
error: push_id(&mut types, Type::Error),
|
|
||||||
};
|
|
||||||
Self {
|
|
||||||
fns: Vec::new(),
|
|
||||||
vars: Vec::new(),
|
|
||||||
idents: Vec::new(),
|
|
||||||
structs: Vec::new(),
|
|
||||||
types: Vec::new(),
|
|
||||||
generics: Vec::new(),
|
|
||||||
data: Vec::new(),
|
|
||||||
modules: Vec::new(),
|
|
||||||
instrs: Vec::new(),
|
|
||||||
unres_idents: Vec::new(),
|
|
||||||
unres_instrs: Vec::new(),
|
|
||||||
tc,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn infer(&mut self) -> TypeID {
|
|
||||||
self.def_ty(Type::Infer)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn def_var(&mut self, v: UVar) -> VarID {
|
|
||||||
push_id(&mut self.vars, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn def_fn(&mut self, f: UFunc) -> FnID {
|
|
||||||
push_id(&mut self.fns, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn def_ty(&mut self, t: Type) -> TypeID {
|
|
||||||
push_id(&mut self.types, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn def_ident(&mut self, i: UIdent) -> IdentID {
|
|
||||||
let id = push_id(&mut self.idents, i);
|
|
||||||
if let IdentStatus::Unres { .. } = self.idents[id].status {
|
|
||||||
self.unres_idents.push(id);
|
|
||||||
}
|
|
||||||
id
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn def_generic(&mut self, g: UGeneric) -> GenericID {
|
|
||||||
push_id(&mut self.generics, g)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn def_data(&mut self, d: UData) -> DataID {
|
|
||||||
push_id(&mut self.data, d)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn def_struct(&mut self, s: UStruct) -> StructID {
|
|
||||||
push_id(&mut self.structs, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn def_module(&mut self, m: UModule) -> ModID {
|
|
||||||
push_id(&mut self.modules, m)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn res_ty(&self, i: IdentID) -> Option<TypeID> {
|
|
||||||
self.idents[i].status;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn type_name(&self, ty: impl Typed) -> String {
|
|
||||||
match ty.ty(self) {
|
|
||||||
Type::Struct(ty) => {
|
|
||||||
format!(
|
|
||||||
"{}{}",
|
|
||||||
self.structs[ty.id].name,
|
|
||||||
self.gparams_str(&ty.gargs)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Type::FnInst(ty) => {
|
|
||||||
format!(
|
|
||||||
"fn{}({}) -> {}",
|
|
||||||
&self.gparams_str(&ty.gargs),
|
|
||||||
&self.type_list_str(self.fns[ty.id].args.iter().map(|v| self.vars[v].ty)),
|
|
||||||
&self.type_name(self.fns[ty.id].ret)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Type::Ref(t) => format!("{}&", self.type_name(t)),
|
|
||||||
Type::Bits(size) => format!("b{}", size),
|
|
||||||
Type::Array(t, len) => format!("[{}; {len}]", self.type_name(t)),
|
|
||||||
Type::Unit => "()".to_string(),
|
|
||||||
Type::Slice(t) => format!("&[{}]", self.type_name(t)),
|
|
||||||
Type::Infer => "{inferred}".to_string(),
|
|
||||||
Type::Generic(id) => self.generics[id].name.clone(),
|
|
||||||
Type::Deref(t) => format!("{}^", self.type_name(t)),
|
|
||||||
Type::Error => "{error}".to_string(),
|
|
||||||
Type::Ptr(id) => self.type_name(id),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn type_list_str(&self, mut args: impl Iterator<Item = TypeID>) -> String {
|
|
||||||
let mut str = String::new();
|
|
||||||
if let Some(arg) = args.next() {
|
|
||||||
str += &self.type_name(arg);
|
|
||||||
}
|
|
||||||
for arg in args {
|
|
||||||
str = str + ", " + &self.type_name(arg);
|
|
||||||
}
|
|
||||||
str
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn gparams_str(&self, args: &[TypeID]) -> String {
|
|
||||||
let mut str = String::new();
|
|
||||||
if !args.is_empty() {
|
|
||||||
str += "<";
|
|
||||||
}
|
|
||||||
str += &self.type_list_str(args.iter().cloned());
|
|
||||||
if !args.is_empty() {
|
|
||||||
str += ">";
|
|
||||||
}
|
|
||||||
str
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn push_id<T>(v: &mut Vec<T>, t: T) -> ID<T> {
|
|
||||||
let id = ID::new(v.len());
|
|
||||||
v.push(t);
|
|
||||||
id
|
|
||||||
}
|
|
||||||
|
|
||||||
// I'm done with names...
|
|
||||||
pub trait Typed {
|
|
||||||
fn ty<'a>(&'a self, p: &'a UProgram) -> &'a Type;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Typed for &Type {
|
|
||||||
fn ty(&self, _: &UProgram) -> &Type {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Typed for TypeID {
|
|
||||||
fn ty<'a>(&'a self, p: &'a UProgram) -> &'a Type {
|
|
||||||
&p.types[self]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Typed for &TypeID {
|
|
||||||
fn ty<'a>(&'a self, p: &'a UProgram) -> &'a Type {
|
|
||||||
&p.types[*self]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Typed for &Box<Type> {
|
|
||||||
fn ty<'a>(&'a self, _: &'a UProgram) -> &'a Type {
|
|
||||||
&**self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,213 +0,0 @@
|
|||||||
use crate::common::{CompilerMsg, CompilerOutput};
|
|
||||||
|
|
||||||
use super::{
|
|
||||||
IdentStatus, KindTy, MemberTy, Origin, Res, ResBase, StructID, Type, TypeID, UProgram,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn report_errs(p: &UProgram, output: &mut CompilerOutput, mut errs: Vec<ResErr>) {
|
|
||||||
for ident in &p.idents {
|
|
||||||
match &ident.status {
|
|
||||||
IdentStatus::Unres { path, base } => {
|
|
||||||
let mem = path.last().unwrap();
|
|
||||||
errs.push(ResErr::UnknownMember {
|
|
||||||
ty: mem.ty,
|
|
||||||
name: mem.name.clone(),
|
|
||||||
origin: mem.origin,
|
|
||||||
parent: base.clone(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
IdentStatus::Failed(err) => {
|
|
||||||
if let Some(err) = err {
|
|
||||||
errs.push(err.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for err in errs {
|
|
||||||
match err {
|
|
||||||
ResErr::Type {
|
|
||||||
dst,
|
|
||||||
src,
|
|
||||||
errs,
|
|
||||||
origin,
|
|
||||||
} => {
|
|
||||||
let mut msg = type_assign_err(p, dst, src);
|
|
||||||
for inner in errs {
|
|
||||||
if inner.dst != dst && inner.src != src {
|
|
||||||
msg.push_str("\n ");
|
|
||||||
msg.push_str(&type_assign_err(p, inner.dst, inner.src));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
output.err(CompilerMsg::new(msg, origin));
|
|
||||||
}
|
|
||||||
ResErr::NotCallable { origin, ty } => {
|
|
||||||
output.err(CompilerMsg::new(
|
|
||||||
format!("Cannot call type '{}'", p.type_name(ty)),
|
|
||||||
origin,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
ResErr::CannotDeref { origin, ty } => {
|
|
||||||
output.err(CompilerMsg::new(
|
|
||||||
format!("Cannot dereference type '{}'", p.type_name(ty)),
|
|
||||||
origin,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
ResErr::CondType { origin, ty } => {
|
|
||||||
output.err(CompilerMsg::new(
|
|
||||||
format!("Condition types must be '64'; found '{}'", p.type_name(ty)),
|
|
||||||
origin,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
ResErr::BadControlFlow { origin, op } => {
|
|
||||||
output.err(CompilerMsg::new(
|
|
||||||
format!("Cannot {} here (outside of loop)", op.str()),
|
|
||||||
origin,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
ResErr::MissingField { origin, id, name } => {
|
|
||||||
output.err(CompilerMsg::new(
|
|
||||||
format!(
|
|
||||||
"Missing field '{name}' in creation of struct '{}'",
|
|
||||||
p.structs[id].name
|
|
||||||
),
|
|
||||||
origin,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
ResErr::UnknownStructField { origin, id, name } => {
|
|
||||||
output.err(CompilerMsg::new(
|
|
||||||
format!("Unknown field '{name}' in struct '{}'", p.structs[id].name),
|
|
||||||
origin,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
ResErr::NoReturn { fid } => output.err(CompilerMsg::new(
|
|
||||||
format!("Function must return a value"),
|
|
||||||
p.fns[fid].origin,
|
|
||||||
)),
|
|
||||||
ResErr::GenericCount {
|
|
||||||
origin,
|
|
||||||
expected,
|
|
||||||
found,
|
|
||||||
} => output.err(CompilerMsg::new(
|
|
||||||
if expected == 0 {
|
|
||||||
format!("No generic arguments expected")
|
|
||||||
} else {
|
|
||||||
format!("Expected {expected} generic arguments, found {found}")
|
|
||||||
},
|
|
||||||
origin,
|
|
||||||
)),
|
|
||||||
ResErr::KindMismatch {
|
|
||||||
origin,
|
|
||||||
found,
|
|
||||||
expected,
|
|
||||||
} => output.err(CompilerMsg::new(
|
|
||||||
format!("Expected {expected}, found {}", found.display_str(p)),
|
|
||||||
origin,
|
|
||||||
)),
|
|
||||||
ResErr::UnknownMember {
|
|
||||||
origin,
|
|
||||||
ty,
|
|
||||||
name,
|
|
||||||
parent,
|
|
||||||
} => output.err(CompilerMsg::new(
|
|
||||||
format!("Unknown {ty} {name} of {}", parent.display_str(p)),
|
|
||||||
origin,
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for var in &p.vars {
|
|
||||||
if let Some(ty) = var.ty() {
|
|
||||||
match &p.types[ty] {
|
|
||||||
Type::Infer => output.err(CompilerMsg::new(
|
|
||||||
format!("Type of {:?} cannot be inferred", var.name),
|
|
||||||
var.origin,
|
|
||||||
)),
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub enum ResErr {
|
|
||||||
UnknownMember {
|
|
||||||
origin: Origin,
|
|
||||||
ty: MemberTy,
|
|
||||||
name: String,
|
|
||||||
parent: ResBase,
|
|
||||||
},
|
|
||||||
KindMismatch {
|
|
||||||
origin: Origin,
|
|
||||||
expected: KindTy,
|
|
||||||
found: Res,
|
|
||||||
},
|
|
||||||
GenericCount {
|
|
||||||
origin: Origin,
|
|
||||||
expected: usize,
|
|
||||||
found: usize,
|
|
||||||
},
|
|
||||||
NotCallable {
|
|
||||||
origin: Origin,
|
|
||||||
ty: TypeID,
|
|
||||||
},
|
|
||||||
CannotDeref {
|
|
||||||
origin: Origin,
|
|
||||||
ty: TypeID,
|
|
||||||
},
|
|
||||||
CondType {
|
|
||||||
origin: Origin,
|
|
||||||
ty: TypeID,
|
|
||||||
},
|
|
||||||
NoReturn {
|
|
||||||
fid: usize,
|
|
||||||
},
|
|
||||||
BadControlFlow {
|
|
||||||
op: ControlFlowOp,
|
|
||||||
origin: Origin,
|
|
||||||
},
|
|
||||||
MissingField {
|
|
||||||
origin: Origin,
|
|
||||||
id: StructID,
|
|
||||||
name: String,
|
|
||||||
},
|
|
||||||
UnknownStructField {
|
|
||||||
origin: Origin,
|
|
||||||
id: StructID,
|
|
||||||
name: String,
|
|
||||||
},
|
|
||||||
Type {
|
|
||||||
dst: TypeID,
|
|
||||||
src: TypeID,
|
|
||||||
errs: Vec<TypeMismatch>,
|
|
||||||
origin: Origin,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum ControlFlowOp {
|
|
||||||
Break,
|
|
||||||
Continue,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ControlFlowOp {
|
|
||||||
pub fn str(&self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
ControlFlowOp::Break => "break",
|
|
||||||
ControlFlowOp::Continue => "continue",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct TypeMismatch {
|
|
||||||
pub dst: TypeID,
|
|
||||||
pub src: TypeID,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn type_assign_err(p: &UProgram, dst: TypeID, src: TypeID) -> String {
|
|
||||||
format!(
|
|
||||||
"Cannot assign type {} to {}",
|
|
||||||
p.type_name(src),
|
|
||||||
p.type_name(dst)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,195 +0,0 @@
|
|||||||
use super::*;
|
|
||||||
|
|
||||||
impl UProgram {
|
|
||||||
pub fn resolve_idents(&mut self, errs: &mut Vec<ResErr>) -> ResolveRes {
|
|
||||||
let mut resolve_res = ResolveRes::Finished;
|
|
||||||
'main: for i in std::mem::take(&mut self.unres_idents) {
|
|
||||||
let mut j = i;
|
|
||||||
// take from ref if possible
|
|
||||||
while let IdentStatus::Ref(other) = &self.idents[j].status {
|
|
||||||
match &self.idents[other].status {
|
|
||||||
IdentStatus::Res(res) => self.idents[i].status = IdentStatus::Res(res.clone()),
|
|
||||||
&IdentStatus::Ref(id) => j = id,
|
|
||||||
IdentStatus::Unres { .. } => {
|
|
||||||
self.unres_idents.push(i);
|
|
||||||
continue 'main;
|
|
||||||
}
|
|
||||||
IdentStatus::Failed(..) => self.idents[i].status = IdentStatus::Cooked,
|
|
||||||
IdentStatus::Cooked => self.idents[i].status = IdentStatus::Cooked,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let status = &mut self.idents[i].status;
|
|
||||||
// TOOD: there are some clones here that shouldn't be needed
|
|
||||||
let IdentStatus::Unres { path, base } = status else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
while let Some(mem) = path.pop() {
|
|
||||||
let res = match base {
|
|
||||||
ResBase::Unvalidated(u) => {
|
|
||||||
match u.validate(
|
|
||||||
&self.fns,
|
|
||||||
&self.structs,
|
|
||||||
&self.generics,
|
|
||||||
&mut self.types,
|
|
||||||
errs,
|
|
||||||
) {
|
|
||||||
Ok(res) => res,
|
|
||||||
Err(err) => {
|
|
||||||
*status = IdentStatus::Failed(err);
|
|
||||||
continue 'main;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ResBase::Validated(res) => res.clone(),
|
|
||||||
};
|
|
||||||
*base = match (res, mem.ty) {
|
|
||||||
(Res::Module(id), MemberTy::Member) => {
|
|
||||||
let Some(m) = self.modules[id].members.get(&mem.name) else {
|
|
||||||
self.unres_idents.push(i);
|
|
||||||
continue 'main;
|
|
||||||
};
|
|
||||||
ResBase::Unvalidated(MemRes {
|
|
||||||
mem: m.clone(),
|
|
||||||
origin: mem.origin,
|
|
||||||
gargs: mem.gargs,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
(Res::Var(id), MemberTy::Field) => {
|
|
||||||
// trait resolution here
|
|
||||||
let Some(&child) = self.vars[id].children.get(&mem.name) else {
|
|
||||||
self.unres_idents.push(i);
|
|
||||||
continue 'main;
|
|
||||||
};
|
|
||||||
ResBase::Unvalidated(MemRes {
|
|
||||||
mem: Member {
|
|
||||||
id: MemberID::Var(child),
|
|
||||||
},
|
|
||||||
origin: mem.origin,
|
|
||||||
gargs: mem.gargs,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
*status = IdentStatus::Failed(Some(ResErr::UnknownMember {
|
|
||||||
origin: mem.origin,
|
|
||||||
ty: mem.ty,
|
|
||||||
name: mem.name.clone(),
|
|
||||||
parent: base.clone(),
|
|
||||||
}));
|
|
||||||
continue 'main;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
let res = match base {
|
|
||||||
ResBase::Unvalidated(u) => {
|
|
||||||
match u.validate(
|
|
||||||
&self.fns,
|
|
||||||
&self.structs,
|
|
||||||
&self.generics,
|
|
||||||
&mut self.types,
|
|
||||||
errs,
|
|
||||||
) {
|
|
||||||
Ok(res) => res,
|
|
||||||
Err(err) => {
|
|
||||||
*status = IdentStatus::Failed(err);
|
|
||||||
continue 'main;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ResBase::Validated(res) => res.clone(),
|
|
||||||
};
|
|
||||||
*status = IdentStatus::Res(res);
|
|
||||||
resolve_res = ResolveRes::Unfinished;
|
|
||||||
}
|
|
||||||
resolve_res
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MemRes {
|
|
||||||
pub fn validate(
|
|
||||||
&self,
|
|
||||||
fns: &[UFunc],
|
|
||||||
structs: &[UStruct],
|
|
||||||
generics: &[UGeneric],
|
|
||||||
types: &mut Vec<Type>,
|
|
||||||
errs: &mut Vec<ResErr>,
|
|
||||||
) -> Result<Res, Option<ResErr>> {
|
|
||||||
let no_gargs = || {
|
|
||||||
if self.gargs.len() > 0 {
|
|
||||||
Err(ResErr::GenericCount {
|
|
||||||
origin: self.origin,
|
|
||||||
expected: 0,
|
|
||||||
found: self.gargs.len(),
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Ok(match &self.mem.id {
|
|
||||||
&MemberID::Fn(id) => {
|
|
||||||
validate_gargs(
|
|
||||||
&fns[id].gargs,
|
|
||||||
&self.gargs,
|
|
||||||
generics,
|
|
||||||
types,
|
|
||||||
errs,
|
|
||||||
self.origin,
|
|
||||||
)?;
|
|
||||||
Res::Fn(FnInst {
|
|
||||||
id,
|
|
||||||
gargs: self.gargs.clone(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
&MemberID::Struct(id) => {
|
|
||||||
validate_gargs(
|
|
||||||
&structs[id].gargs,
|
|
||||||
&self.gargs,
|
|
||||||
generics,
|
|
||||||
types,
|
|
||||||
errs,
|
|
||||||
self.origin,
|
|
||||||
)?;
|
|
||||||
Res::Struct(StructInst {
|
|
||||||
id,
|
|
||||||
gargs: self.gargs.clone(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
&MemberID::Var(id) => {
|
|
||||||
no_gargs()?;
|
|
||||||
Res::Var(id)
|
|
||||||
}
|
|
||||||
&MemberID::Module(id) => {
|
|
||||||
no_gargs()?;
|
|
||||||
Res::Module(id)
|
|
||||||
}
|
|
||||||
MemberID::Type(def) => {
|
|
||||||
validate_gargs(&def.gargs, &self.gargs, generics, types, errs, self.origin)?;
|
|
||||||
inst_typedef(def, &self.gargs, types);
|
|
||||||
Res::Type(def.ty)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn validate_gargs(
|
|
||||||
dst: &[GenericID],
|
|
||||||
src: &[TypeID],
|
|
||||||
generics: &[UGeneric],
|
|
||||||
types: &[Type],
|
|
||||||
errs: &mut Vec<ResErr>,
|
|
||||||
origin: Origin,
|
|
||||||
) -> Result<(), Option<ResErr>> {
|
|
||||||
if dst.len() != src.len() {
|
|
||||||
return Err(Some(ResErr::GenericCount {
|
|
||||||
origin,
|
|
||||||
expected: dst.len(),
|
|
||||||
found: src.len(),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
for (dst, src) in dst.iter().zip(src.iter()) {
|
|
||||||
let g = &generics[dst];
|
|
||||||
let t = &types[src];
|
|
||||||
// TODO: validate trait constraints
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
pub fn inst_fn_var(
|
|
||||||
fi: FnInst,
|
|
||||||
fns: &[UFunc],
|
|
||||||
origin: Origin,
|
|
||||||
vars: &mut Vec<UVar>,
|
|
||||||
types: &mut Vec<Type>,
|
|
||||||
) -> VarID {
|
|
||||||
let name = fns[fi.id].name.clone();
|
|
||||||
let ty = push_id(types, Type::FnInst(fi));
|
|
||||||
push_id(
|
|
||||||
vars,
|
|
||||||
UVar {
|
|
||||||
name,
|
|
||||||
origin,
|
|
||||||
ty: VarTy::Res(ty),
|
|
||||||
parent: None,
|
|
||||||
children: HashMap::new(),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn inst_struct_var(
|
|
||||||
si: StructInst,
|
|
||||||
structs: &[UStruct],
|
|
||||||
origin: Origin,
|
|
||||||
vars: &mut Vec<UVar>,
|
|
||||||
types: &mut Vec<Type>,
|
|
||||||
) -> VarID {
|
|
||||||
let name = structs[si.id].name.clone();
|
|
||||||
let ty = push_id(types, Type::Struct(si));
|
|
||||||
let id = push_id(
|
|
||||||
vars,
|
|
||||||
UVar {
|
|
||||||
name,
|
|
||||||
origin,
|
|
||||||
ty: VarTy::Res(ty),
|
|
||||||
parent: None,
|
|
||||||
children: HashMap::new(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
id
|
|
||||||
}
|
|
||||||
|
|
||||||
/// gargs assumed to be valid
|
|
||||||
pub fn inst_typedef(def: &TypeDef, gargs: &[TypeID], types: &mut Vec<Type>) -> TypeID {
|
|
||||||
let gmap = inst_gmap(&def.gargs, &gargs);
|
|
||||||
inst_type(def.ty, types, &gmap)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn inst_gmap(dst: &[GenericID], src: &[TypeID]) -> HashMap<GenericID, TypeID> {
|
|
||||||
let mut gmap = HashMap::new();
|
|
||||||
for (&gid, &tid) in dst.iter().zip(src) {
|
|
||||||
gmap.insert(gid, tid);
|
|
||||||
}
|
|
||||||
gmap
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn inst_type(id: TypeID, types: &mut Vec<Type>, gmap: &HashMap<GenericID, TypeID>) -> TypeID {
|
|
||||||
if gmap.len() == 0 {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
match inst_type_(id, types, gmap) {
|
|
||||||
Some(new) => new,
|
|
||||||
None => id,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn inst_type_(
|
|
||||||
id: TypeID,
|
|
||||||
types: &mut Vec<Type>,
|
|
||||||
gmap: &HashMap<GenericID, TypeID>,
|
|
||||||
) -> Option<TypeID> {
|
|
||||||
let ty = match types[id].clone() {
|
|
||||||
Type::Bits(_) => return None,
|
|
||||||
Type::Struct(struct_ty) => Type::Struct(StructInst {
|
|
||||||
id: struct_ty.id,
|
|
||||||
gargs: inst_all(&struct_ty.gargs, types, gmap)?,
|
|
||||||
}),
|
|
||||||
Type::FnInst(fn_ty) => Type::FnInst(FnInst {
|
|
||||||
id: fn_ty.id,
|
|
||||||
gargs: inst_all(&fn_ty.gargs, types, gmap)?,
|
|
||||||
}),
|
|
||||||
Type::Ref(id) => Type::Ref(inst_type_(id, types, gmap)?),
|
|
||||||
Type::Slice(id) => Type::Slice(inst_type_(id, types, gmap)?),
|
|
||||||
Type::Array(id, len) => Type::Array(inst_type_(id, types, gmap)?, len),
|
|
||||||
Type::Unit => return None,
|
|
||||||
Type::Generic(gid) => return gmap.get(&gid).map(|id| Some(*id)).unwrap_or_else(|| None),
|
|
||||||
Type::Infer => Type::Infer,
|
|
||||||
Type::Deref(id) => Type::Deref(inst_type_(id, types, gmap)?),
|
|
||||||
Type::Ptr(id) => Type::Ptr(inst_type_(id, types, gmap)?),
|
|
||||||
Type::Error => return None,
|
|
||||||
};
|
|
||||||
Some(push_id(types, ty))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn inst_all(
|
|
||||||
ids: &[TypeID],
|
|
||||||
types: &mut Vec<Type>,
|
|
||||||
gmap: &HashMap<GenericID, TypeID>,
|
|
||||||
) -> Option<Vec<TypeID>> {
|
|
||||||
let mut vec = None;
|
|
||||||
for (i, &id) in ids.iter().enumerate() {
|
|
||||||
if let Some(id) = inst_type_(id, types, gmap) {
|
|
||||||
vec.get_or_insert_with(|| ids.iter().take(i).cloned().collect::<Vec<_>>())
|
|
||||||
.push(id);
|
|
||||||
} else if let Some(vec) = &mut vec {
|
|
||||||
vec.push(id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
vec
|
|
||||||
}
|
|
||||||
@@ -1,182 +0,0 @@
|
|||||||
use std::collections::HashSet;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
pub enum UResEvent {
|
|
||||||
VarUse(VarID),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UProgram {
|
|
||||||
pub fn resolve_instrs(&mut self, errs: &mut Vec<ResErr>) -> ResolveRes {
|
|
||||||
let mut data = ResData {
|
|
||||||
changed: false,
|
|
||||||
types: &mut self.types,
|
|
||||||
s: Sources {
|
|
||||||
idents: &mut self.idents,
|
|
||||||
vars: &mut self.vars,
|
|
||||||
fns: &self.fns,
|
|
||||||
structs: &self.structs,
|
|
||||||
generics: &self.generics,
|
|
||||||
data: &self.data,
|
|
||||||
modules: &self.modules,
|
|
||||||
},
|
|
||||||
errs,
|
|
||||||
};
|
|
||||||
for ids in std::mem::take(&mut self.unres_instrs) {
|
|
||||||
if let ResolveRes::Unfinished = resolve_instr(ids, &mut self.instrs, &mut data) {
|
|
||||||
self.unres_instrs.push(ids);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
ResolveRes::Finished
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
struct ResolveCtx {
|
|
||||||
ret: IdentID,
|
|
||||||
breakable: bool,
|
|
||||||
i: InstrID,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn resolve_instr<'a>(
|
|
||||||
(fi, ii): (FnID, InstrID),
|
|
||||||
instrs: &mut Vec<UInstrInst>,
|
|
||||||
data: &mut ResData<'a>,
|
|
||||||
) -> ResolveRes {
|
|
||||||
let instr = &mut instrs[ii];
|
|
||||||
match &mut instr.i {
|
|
||||||
UInstruction::Call { dst, f, args } => {
|
|
||||||
let fi = data.res::<UFunc>(*f);
|
|
||||||
for &a in args {
|
|
||||||
data.res::<UVar>(a);
|
|
||||||
}
|
|
||||||
data.res::<UVar>(dst);
|
|
||||||
match fi {
|
|
||||||
Ok(fi) => {
|
|
||||||
let f = &data.s.fns[fi.id];
|
|
||||||
for (&src, &dst) in args.iter().zip(&f.args) {
|
|
||||||
data.s.constraints.push(UResEvent::AssignVVI { dst, src });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(r) => return r,
|
|
||||||
}
|
|
||||||
ResolveRes::Finished
|
|
||||||
}
|
|
||||||
UInstruction::Mv { dst, src } => {
|
|
||||||
res |= data.match_types::<UVar, UVar>(dst, src, src);
|
|
||||||
}
|
|
||||||
UInstruction::Ref { dst, src } => {
|
|
||||||
let dstty = &data.types[data.res_var_ty(dst)?];
|
|
||||||
let &Type::Ref(dest_ty) = dstty else {
|
|
||||||
compiler_error()
|
|
||||||
};
|
|
||||||
res |= data.match_types::<Type, UVar>(dest_ty, src, src);
|
|
||||||
}
|
|
||||||
UInstruction::Deref { dst, src } => {
|
|
||||||
let srcid = data.res_var_ty(src)?;
|
|
||||||
let &Type::Ref(src_ty) = data.types[srcid] else {
|
|
||||||
let origin = src.origin(data);
|
|
||||||
data.errs.push(ResErr::CannotDeref { origin, ty: srcid });
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
res |= data.match_types::<UVar, Type>(dst, src_ty, src);
|
|
||||||
}
|
|
||||||
UInstruction::LoadData { dst, src } => {
|
|
||||||
let srcid = src.type_id(&data.s);
|
|
||||||
res |= data.match_types::<UVar, Type>(dst, srcid, dst);
|
|
||||||
}
|
|
||||||
UInstruction::LoadSlice { dst, src } => {
|
|
||||||
let (dstty, dstid) = data.res_var_ty(dst, ctx)?;
|
|
||||||
let &Type::Slice(dstty) = dstty else {
|
|
||||||
compiler_error()
|
|
||||||
};
|
|
||||||
let srcid = src.type_id(&data.s);
|
|
||||||
let Type::Array(srcty, _) = data.types[srcid] else {
|
|
||||||
compiler_error()
|
|
||||||
};
|
|
||||||
res |= data.match_types(dstty, srcty, dst);
|
|
||||||
}
|
|
||||||
UInstruction::AsmBlock { instructions, args } => {
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
UInstruction::Ret { src } => {
|
|
||||||
res |= data.match_types::<Type, UVar>(ctx.ret, src, src);
|
|
||||||
}
|
|
||||||
UInstruction::Construct { dst, struc, fields } => {
|
|
||||||
let si = data.res::<UStruct>(dst, ctx)?;
|
|
||||||
let sid = si.id;
|
|
||||||
let st = &data.s.structs[sid];
|
|
||||||
let mut used = HashSet::new();
|
|
||||||
for (name, field) in &st.fields {
|
|
||||||
if let Some(src) = fields.get(name) {
|
|
||||||
used.insert(name);
|
|
||||||
res |= data.match_types::<Type, UVar>(field.ty, src, src);
|
|
||||||
} else {
|
|
||||||
let origin = dst.origin(data);
|
|
||||||
data.errs.push(ResErr::MissingField {
|
|
||||||
origin,
|
|
||||||
id: sid,
|
|
||||||
name: name.clone(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (name, _) in fields {
|
|
||||||
if !used.contains(name) {
|
|
||||||
let origin = dst.origin(data);
|
|
||||||
data.errs.push(ResErr::UnknownStructField {
|
|
||||||
origin,
|
|
||||||
id: sid,
|
|
||||||
name: name.clone(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
UInstruction::If { cond, body } => {
|
|
||||||
if let Some(ty) = data.res_var_ty(cond, ctx) {
|
|
||||||
if !matches!(ty.0, RType::Bits(64)) {
|
|
||||||
let id = ty.1;
|
|
||||||
let origin = cond.origin(data);
|
|
||||||
data.errs.push(ResErr::CondType { origin, ty: id });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i in body {
|
|
||||||
resolve_instr(
|
|
||||||
data,
|
|
||||||
ResolveCtx {
|
|
||||||
ret: ctx.ret,
|
|
||||||
breakable: ctx.breakable,
|
|
||||||
i,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
UInstruction::Loop { body } => {
|
|
||||||
for i in body {
|
|
||||||
resolve_instr(
|
|
||||||
data,
|
|
||||||
ResolveCtx {
|
|
||||||
ret: ctx.ret,
|
|
||||||
breakable: true,
|
|
||||||
i,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
UInstruction::Break => {
|
|
||||||
if !ctx.breakable {
|
|
||||||
data.errs.push(ResErr::BadControlFlow {
|
|
||||||
op: ControlFlowOp::Break,
|
|
||||||
origin: ctx.i.origin,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
UInstruction::Continue => {
|
|
||||||
if !ctx.breakable {
|
|
||||||
data.errs.push(ResErr::BadControlFlow {
|
|
||||||
op: ControlFlowOp::Continue,
|
|
||||||
origin: ctx.i.origin,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,146 +0,0 @@
|
|||||||
use super::*;
|
|
||||||
|
|
||||||
pub fn match_types(data: &mut ResData, dst: TypeID, src: TypeID) -> MatchRes {
|
|
||||||
let Some(dst) = clean_type(data.types, dst) else {
|
|
||||||
return MatchRes::Finished;
|
|
||||||
};
|
|
||||||
let Some(src) = clean_type(data.types, src) else {
|
|
||||||
return MatchRes::Finished;
|
|
||||||
};
|
|
||||||
// prevents this from blowing up I think:
|
|
||||||
// let mut x, y;
|
|
||||||
// x = y;
|
|
||||||
// y = x;
|
|
||||||
if dst == src {
|
|
||||||
return MatchRes::Finished;
|
|
||||||
}
|
|
||||||
let error = || MatchRes::Error(vec![TypeMismatch { dst, src }]);
|
|
||||||
match (data.types[dst].clone(), data.types[src].clone()) {
|
|
||||||
// prefer changing dst over src
|
|
||||||
(Type::Infer, _) => {
|
|
||||||
data.changed = true;
|
|
||||||
data.types[dst] = Type::Ptr(src);
|
|
||||||
MatchRes::Finished
|
|
||||||
}
|
|
||||||
(_, Type::Infer) => {
|
|
||||||
data.changed = true;
|
|
||||||
data.types[src] = Type::Ptr(dst);
|
|
||||||
MatchRes::Finished
|
|
||||||
}
|
|
||||||
(Type::Struct(dest), Type::Struct(src)) => {
|
|
||||||
if dest.id != src.id {
|
|
||||||
return error();
|
|
||||||
}
|
|
||||||
match_all(data, dest.gargs.iter().cloned(), src.gargs.iter().cloned())
|
|
||||||
}
|
|
||||||
// (
|
|
||||||
// Type::Fn {
|
|
||||||
// args: dst_args,
|
|
||||||
// ret: dst_ret,
|
|
||||||
// },
|
|
||||||
// Type::Fn {
|
|
||||||
// args: src_args,
|
|
||||||
// ret: src_ret,
|
|
||||||
// },
|
|
||||||
// ) => {
|
|
||||||
// let dst = dst_args.into_iter().chain(once(dst_ret));
|
|
||||||
// let src = src_args.into_iter().chain(once(src_ret));
|
|
||||||
// match_all(data, dst, src)
|
|
||||||
// }
|
|
||||||
(Type::Ref(dest), Type::Ref(src)) => match_types(data, dest, src),
|
|
||||||
(Type::Slice(dest), Type::Slice(src)) => match_types(data, dest, src),
|
|
||||||
(Type::Array(dest, dlen), Type::Array(src, slen)) => {
|
|
||||||
if dlen == slen {
|
|
||||||
match_types(data, dest, src)
|
|
||||||
} else {
|
|
||||||
error()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => error(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn match_all(
|
|
||||||
data: &mut ResData,
|
|
||||||
dst: impl Iterator<Item = TypeID>,
|
|
||||||
src: impl Iterator<Item = TypeID>,
|
|
||||||
) -> MatchRes {
|
|
||||||
let mut finished = true;
|
|
||||||
let mut errors = Vec::new();
|
|
||||||
for (dst, src) in dst.zip(src) {
|
|
||||||
match match_types(data, dst, src) {
|
|
||||||
MatchRes::Unfinished => finished = false,
|
|
||||||
MatchRes::Error(errs) => errors.extend(errs),
|
|
||||||
MatchRes::Finished => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if finished {
|
|
||||||
if errors.is_empty() {
|
|
||||||
MatchRes::Finished
|
|
||||||
} else {
|
|
||||||
MatchRes::Error(errors)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
MatchRes::Unfinished
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ResData<'a> {
|
|
||||||
pub fn match_types(
|
|
||||||
&mut self,
|
|
||||||
dst: impl MaybeTypeID,
|
|
||||||
src: impl MaybeTypeID,
|
|
||||||
origin: impl HasOrigin,
|
|
||||||
) -> ResolveRes {
|
|
||||||
let dst = dst.type_id(&self.s)?;
|
|
||||||
let src = src.type_id(&self.s)?;
|
|
||||||
let res = match_types(self, dst, src);
|
|
||||||
match res {
|
|
||||||
MatchRes::Unfinished => ResolveRes::Unfinished,
|
|
||||||
MatchRes::Finished => ResolveRes::Finished,
|
|
||||||
MatchRes::Error(es) => {
|
|
||||||
self.errs.push(ResErr::Type {
|
|
||||||
errs: es,
|
|
||||||
origin: origin.origin(self),
|
|
||||||
dst,
|
|
||||||
src,
|
|
||||||
});
|
|
||||||
ResolveRes::Finished
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum MatchRes {
|
|
||||||
Unfinished,
|
|
||||||
Finished,
|
|
||||||
Error(Vec<TypeMismatch>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromResidual<Result<Infallible, MatchRes>> for MatchRes {
|
|
||||||
fn from_residual(residual: Result<Infallible, MatchRes>) -> Self {
|
|
||||||
match residual {
|
|
||||||
Ok(_) => unreachable!(),
|
|
||||||
Err(r) => r,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait MaybeTypeID {
|
|
||||||
fn type_id(&self, s: &Sources) -> Result<TypeID, ResolveRes>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: TypeIDed> MaybeTypeID for T {
|
|
||||||
fn type_id(&self, s: &Sources) -> Result<TypeID, ResolveRes> {
|
|
||||||
Ok(self.type_id(s))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MaybeTypeID for VarID {
|
|
||||||
fn type_id(&self, s: &Sources) -> Result<TypeID, ResolveRes> {
|
|
||||||
match s.vars[self].ty {
|
|
||||||
VarTy::Ident(id) => todo!(),
|
|
||||||
VarTy::Res(id) => Ok(id),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,293 +0,0 @@
|
|||||||
use super::*;
|
|
||||||
use crate::{
|
|
||||||
common::CompilerOutput,
|
|
||||||
ir::{MemRes, Member},
|
|
||||||
};
|
|
||||||
use std::{
|
|
||||||
convert::Infallible,
|
|
||||||
ops::{BitOrAssign, FromResidual},
|
|
||||||
};
|
|
||||||
|
|
||||||
mod error;
|
|
||||||
mod ident;
|
|
||||||
mod instantiate;
|
|
||||||
mod instr;
|
|
||||||
mod matc;
|
|
||||||
|
|
||||||
pub use error::*;
|
|
||||||
use instantiate::*;
|
|
||||||
|
|
||||||
impl UProgram {
|
|
||||||
pub fn resolve(&mut self, output: &mut CompilerOutput) {
|
|
||||||
self.unres_instrs = (0..self.instrs.len()).map(|i| InstrID::from(i)).collect();
|
|
||||||
let mut res = ResolveRes::Unfinished;
|
|
||||||
let mut errs = Vec::new();
|
|
||||||
while res == ResolveRes::Unfinished {
|
|
||||||
res = ResolveRes::Finished;
|
|
||||||
res |= self.resolve_idents(&mut errs);
|
|
||||||
res |= self.resolve_instrs(&mut errs);
|
|
||||||
}
|
|
||||||
for (fid, f) in self.fns.iter().enumerate() {
|
|
||||||
// this currently works bc expressions create temporary variables
|
|
||||||
// although you can't do things like loop {return 3} (need to analyze control flow)
|
|
||||||
if let Some(ty) = self.res_ty(f.ret)
|
|
||||||
&& self.types[ty] != Type::Unit
|
|
||||||
&& f.instructions
|
|
||||||
.last()
|
|
||||||
.is_none_or(|i| !matches!(self.instrs[i].i, UInstruction::Ret { .. }))
|
|
||||||
{
|
|
||||||
errs.push(ResErr::NoReturn { fid });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
report_errs(self, output, errs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compiler_error() -> ! {
|
|
||||||
// TODO: this is probably a compiler error / should never happen
|
|
||||||
panic!("how could this happen to me (you)");
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Sources<'a> {
|
|
||||||
idents: &'a mut [UIdent],
|
|
||||||
vars: &'a mut Vec<UVar>,
|
|
||||||
fns: &'a [UFunc],
|
|
||||||
structs: &'a [UStruct],
|
|
||||||
generics: &'a [UGeneric],
|
|
||||||
data: &'a [UData],
|
|
||||||
modules: &'a [UModule],
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ResData<'a> {
|
|
||||||
changed: bool,
|
|
||||||
types: &'a mut Vec<Type>,
|
|
||||||
s: Sources<'a>,
|
|
||||||
errs: &'a mut Vec<ResErr>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ResData<'a> {
|
|
||||||
pub fn res<K: ResKind>(&mut self, i: IdentID) -> Result<K::Res, ResolveRes> {
|
|
||||||
i.res_as::<K>(&mut self.s, &mut self.types)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn res_ty(&mut self, x: impl Resolvable<Type>) -> Result<TypeID, ResolveRes> {
|
|
||||||
let id = Resolvable::<Type>::try_res(&x, &mut self.s, self.types, self.errs)?;
|
|
||||||
resolved_type(self.types, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn res_var_ty(&mut self, i: IdentID) -> Result<TypeID, ResolveRes> {
|
|
||||||
let id = self.res::<UVar>(i)?;
|
|
||||||
let id = match self.s.vars[id].ty {
|
|
||||||
VarTy::Res(t) => Ok(t),
|
|
||||||
VarTy::Ident(i) => i.res_as::<Type>(&mut self.s, self.types),
|
|
||||||
}?;
|
|
||||||
resolved_type(self.types, id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
||||||
pub enum ResolveRes {
|
|
||||||
Finished,
|
|
||||||
Unfinished,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BitOrAssign for ResolveRes {
|
|
||||||
fn bitor_assign(&mut self, rhs: Self) {
|
|
||||||
match rhs {
|
|
||||||
ResolveRes::Finished => (),
|
|
||||||
ResolveRes::Unfinished => *self = ResolveRes::Unfinished,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromResidual<Option<Infallible>> for ResolveRes {
|
|
||||||
fn from_residual(_: Option<Infallible>) -> Self {
|
|
||||||
Self::Unfinished
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
trait Resolvable<K: ResKind> {
|
|
||||||
fn try_res(
|
|
||||||
&self,
|
|
||||||
s: &mut Sources,
|
|
||||||
types: &mut Vec<Type>,
|
|
||||||
errs: &mut Vec<ResErr>,
|
|
||||||
) -> Result<K::Res, ResolveRes>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IdentID {
|
|
||||||
fn res_as<K: ResKind>(
|
|
||||||
&self,
|
|
||||||
s: &mut Sources,
|
|
||||||
types: &mut Vec<Type>,
|
|
||||||
) -> Result<K::Res, ResolveRes> {
|
|
||||||
let origin = s.idents[self].origin;
|
|
||||||
let res = match &s.idents[self].status {
|
|
||||||
IdentStatus::Res(res) => res.clone(),
|
|
||||||
IdentStatus::Ref { .. } => return Err(ResolveRes::Unfinished),
|
|
||||||
IdentStatus::Unres { .. } => return Err(ResolveRes::Unfinished),
|
|
||||||
IdentStatus::Failed(..) => return Err(ResolveRes::Finished),
|
|
||||||
IdentStatus::Cooked => return Err(ResolveRes::Finished),
|
|
||||||
};
|
|
||||||
match K::from_res(res, types, s, origin) {
|
|
||||||
Ok(res) => Ok(res),
|
|
||||||
Err(res) => {
|
|
||||||
s.idents[self].status = IdentStatus::Failed(Some(ResErr::KindMismatch {
|
|
||||||
origin,
|
|
||||||
expected: K::ty(),
|
|
||||||
found: res,
|
|
||||||
}));
|
|
||||||
Err(ResolveRes::Finished)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K: ResKind> Resolvable<K> for &IdentID {
|
|
||||||
fn try_res(
|
|
||||||
&self,
|
|
||||||
s: &mut Sources,
|
|
||||||
types: &mut Vec<Type>,
|
|
||||||
errs: &mut Vec<ResErr>,
|
|
||||||
) -> Result<K::Res, ResolveRes> {
|
|
||||||
Resolvable::<K>::try_res(*self, s, types, errs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Resolvable<UVar> for VarID {
|
|
||||||
fn try_res(
|
|
||||||
&self,
|
|
||||||
s: &mut Sources,
|
|
||||||
types: &mut Vec<Type>,
|
|
||||||
errs: &mut Vec<ResErr>,
|
|
||||||
) -> Result<<UVar as ResKind>::Res, ResolveRes> {
|
|
||||||
Ok(*self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Resolvable<Type> for TypeID {
|
|
||||||
fn try_res(
|
|
||||||
&self,
|
|
||||||
s: &mut Sources,
|
|
||||||
types: &mut Vec<Type>,
|
|
||||||
errs: &mut Vec<ResErr>,
|
|
||||||
) -> Result<<Type as ResKind>::Res, ResolveRes> {
|
|
||||||
Ok(*self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ResKind {
|
|
||||||
type Res;
|
|
||||||
fn ty() -> KindTy;
|
|
||||||
fn from_res(
|
|
||||||
res: Res,
|
|
||||||
types: &mut Vec<Type>,
|
|
||||||
s: &mut Sources,
|
|
||||||
origin: Origin,
|
|
||||||
) -> Result<Self::Res, Res>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ResKind for UFunc {
|
|
||||||
type Res = FnInst;
|
|
||||||
fn ty() -> KindTy {
|
|
||||||
KindTy::Fn
|
|
||||||
}
|
|
||||||
fn from_res(res: Res, _: &mut Vec<Type>, _: &mut Sources, _: Origin) -> Result<Self::Res, Res> {
|
|
||||||
match res {
|
|
||||||
Res::Fn(fi) => Ok(fi),
|
|
||||||
_ => Err(res),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ResKind for UVar {
|
|
||||||
type Res = VarID;
|
|
||||||
fn ty() -> KindTy {
|
|
||||||
KindTy::Var
|
|
||||||
}
|
|
||||||
fn from_res(
|
|
||||||
res: Res,
|
|
||||||
types: &mut Vec<Type>,
|
|
||||||
s: &mut Sources,
|
|
||||||
origin: Origin,
|
|
||||||
) -> Result<Self::Res, Res> {
|
|
||||||
Ok(match res {
|
|
||||||
Res::Fn(fty) => inst_fn_var(fty, s.fns, origin, s.vars, types),
|
|
||||||
Res::Var(id) => id,
|
|
||||||
_ => return Err(res),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ResKind for UStruct {
|
|
||||||
type Res = StructInst;
|
|
||||||
fn ty() -> KindTy {
|
|
||||||
KindTy::Struct
|
|
||||||
}
|
|
||||||
fn from_res(res: Res, _: &mut Vec<Type>, _: &mut Sources, _: Origin) -> Result<Self::Res, Res> {
|
|
||||||
match res {
|
|
||||||
Res::Struct(si) => Ok(si),
|
|
||||||
_ => Err(res),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ResKind for Type {
|
|
||||||
type Res = TypeID;
|
|
||||||
fn ty() -> KindTy {
|
|
||||||
KindTy::Type
|
|
||||||
}
|
|
||||||
fn from_res(
|
|
||||||
res: Res,
|
|
||||||
types: &mut Vec<Type>,
|
|
||||||
s: &mut Sources,
|
|
||||||
_: Origin,
|
|
||||||
) -> Result<Self::Res, Res> {
|
|
||||||
Ok(match res {
|
|
||||||
Res::Struct(si) => push_id(types, Type::Struct(si)),
|
|
||||||
Res::Type(id) => id,
|
|
||||||
_ => return Err(res),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait TypeIDed {
|
|
||||||
fn type_id(&self, s: &Sources) -> TypeID;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TypeIDed for TypeID {
|
|
||||||
fn type_id(&self, _: &Sources) -> TypeID {
|
|
||||||
*self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TypeIDed for DataID {
|
|
||||||
fn type_id(&self, s: &Sources) -> TypeID {
|
|
||||||
s.data[self].ty
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: TypeIDed> TypeIDed for &T {
|
|
||||||
fn type_id(&self, s: &Sources) -> TypeID {
|
|
||||||
(*self).type_id(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromResidual<Result<Infallible, ResolveRes>> for ResolveRes {
|
|
||||||
fn from_residual(residual: Result<Infallible, ResolveRes>) -> Self {
|
|
||||||
match residual {
|
|
||||||
Ok(_) => unreachable!(),
|
|
||||||
Err(r) => r,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
trait HasOrigin {
|
|
||||||
fn origin(&self, data: &ResData) -> Origin;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HasOrigin for &IdentID {
|
|
||||||
fn origin(&self, data: &ResData) -> Origin {
|
|
||||||
data.s.idents[*self].origin
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
use super::{FnID, GenericID, Len, ResolveRes, StructID, TypeID, UProgram, VarID};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
|
||||||
pub struct FieldRef {
|
|
||||||
pub parent: VarID,
|
|
||||||
pub name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
|
||||||
pub struct StructInst {
|
|
||||||
pub id: StructID,
|
|
||||||
/// assumed to be valid
|
|
||||||
pub gargs: Vec<TypeID>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
|
||||||
pub struct FnInst {
|
|
||||||
pub id: FnID,
|
|
||||||
/// assumed to be valid
|
|
||||||
pub gargs: Vec<TypeID>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq)]
|
|
||||||
pub enum Type {
|
|
||||||
Bits(u32),
|
|
||||||
Struct(StructInst),
|
|
||||||
// this can be added for constraints later (F: fn(...) -> ...)
|
|
||||||
// Fn { args: Vec<TypeID>, ret: TypeID },
|
|
||||||
// "fake" types
|
|
||||||
FnInst(FnInst),
|
|
||||||
Ref(TypeID),
|
|
||||||
Slice(TypeID),
|
|
||||||
Array(TypeID, Len),
|
|
||||||
Unit,
|
|
||||||
Infer,
|
|
||||||
Generic(GenericID),
|
|
||||||
Deref(TypeID),
|
|
||||||
Ptr(TypeID),
|
|
||||||
Error,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Type {
|
|
||||||
pub fn rf(self, p: &mut UProgram) -> Self {
|
|
||||||
p.def_ty(self).rf()
|
|
||||||
}
|
|
||||||
pub fn derf(self, p: &mut UProgram) -> Self {
|
|
||||||
p.def_ty(self).derf()
|
|
||||||
}
|
|
||||||
pub fn arr(self, p: &mut UProgram, len: Len) -> Self {
|
|
||||||
p.def_ty(self).arr(len)
|
|
||||||
}
|
|
||||||
pub fn slice(self, p: &mut UProgram) -> Self {
|
|
||||||
p.def_ty(self).slice()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TypeID {
|
|
||||||
pub fn rf(self) -> Type {
|
|
||||||
Type::Ref(self)
|
|
||||||
}
|
|
||||||
pub fn derf(self) -> Type {
|
|
||||||
Type::Deref(self)
|
|
||||||
}
|
|
||||||
pub fn arr(self, len: Len) -> Type {
|
|
||||||
Type::Array(self, len)
|
|
||||||
}
|
|
||||||
pub fn slice(self) -> Type {
|
|
||||||
Type::Slice(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Type {
|
|
||||||
pub fn bx(self) -> Box<Self> {
|
|
||||||
Box::new(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clean_type(types: &[Type], id: TypeID) -> Option<TypeID> {
|
|
||||||
match &types[id] {
|
|
||||||
&Type::Ptr(id) => clean_type(types, id),
|
|
||||||
&Type::Deref(did) => match &types[clean_type(types, did)?] {
|
|
||||||
&Type::Ref(id) => clean_type(types, id),
|
|
||||||
_ => Some(id),
|
|
||||||
},
|
|
||||||
Type::Error => None,
|
|
||||||
_ => Some(id),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn resolved_type(types: &[Type], id: TypeID) -> Result<TypeID, ResolveRes> {
|
|
||||||
match &types[id] {
|
|
||||||
&Type::Ptr(id) => resolved_type(types, id),
|
|
||||||
&Type::Deref(id) => match &types[resolved_type(types, id)?] {
|
|
||||||
&Type::Ref(id) => resolved_type(types, id),
|
|
||||||
Type::Infer => Err(ResolveRes::Unfinished),
|
|
||||||
_ => Err(ResolveRes::Finished),
|
|
||||||
},
|
|
||||||
Type::Error => Err(ResolveRes::Finished),
|
|
||||||
_ => Ok(id),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+2
-172
@@ -1,178 +1,8 @@
|
|||||||
#![feature(box_patterns)]
|
|
||||||
#![feature(try_trait_v2)]
|
#![feature(try_trait_v2)]
|
||||||
|
#![feature(associated_type_defaults)]
|
||||||
#![feature(trait_alias)]
|
#![feature(trait_alias)]
|
||||||
#![feature(let_chains)]
|
|
||||||
#![feature(iterator_try_collect)]
|
|
||||||
// dawg what
|
|
||||||
#![feature(str_as_str)]
|
|
||||||
|
|
||||||
pub const FILE_EXT: &str = "lang";
|
|
||||||
|
|
||||||
use common::{CompilerOutput, SrcFile};
|
|
||||||
use ir::{LProgram, UProgram};
|
|
||||||
use parser::{Import, Imports, PModule, ParserCtx};
|
|
||||||
use std::{
|
|
||||||
collections::HashSet,
|
|
||||||
fs::{create_dir_all, OpenOptions},
|
|
||||||
io::stdout,
|
|
||||||
os::unix::fs::OpenOptionsExt,
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
process::Command,
|
|
||||||
};
|
|
||||||
|
|
||||||
mod common;
|
|
||||||
mod compiler;
|
|
||||||
mod ir;
|
|
||||||
mod parser;
|
mod parser;
|
||||||
mod util;
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let file = std::env::args_os().nth(1);
|
parser::parse(include_str!("test.lang"));
|
||||||
// TODO: professional arg parsing
|
|
||||||
let gdb = std::env::args().nth(2).is_some_and(|a| a == "--debug");
|
|
||||||
let asm = std::env::args().nth(2).is_some_and(|a| a == "--asm");
|
|
||||||
if let Some(path) = file {
|
|
||||||
let path = PathBuf::from(path);
|
|
||||||
run_file(&path, gdb, asm);
|
|
||||||
} else {
|
|
||||||
run_stdin();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UProgram {
|
|
||||||
pub fn from_path(path: &Path) -> (Self, CompilerOutput) {
|
|
||||||
let parent = path.parent().expect("bruh");
|
|
||||||
let mut program = Self::new();
|
|
||||||
let mut output = CompilerOutput::new();
|
|
||||||
|
|
||||||
let mut imports = Imports::new();
|
|
||||||
imports.insert(Import(vec![path
|
|
||||||
.file_name()
|
|
||||||
.expect("bruh")
|
|
||||||
.to_str()
|
|
||||||
.expect("bruh")
|
|
||||||
.to_string()]));
|
|
||||||
let mut imported = HashSet::new();
|
|
||||||
let mut fid = 0;
|
|
||||||
|
|
||||||
while !imports.is_empty() {
|
|
||||||
let iter = std::mem::take(&mut imports);
|
|
||||||
for i in iter {
|
|
||||||
let import_path = &i.0;
|
|
||||||
if imported.contains(&i) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let mut file_path = parent.to_path_buf();
|
|
||||||
file_path.extend(import_path);
|
|
||||||
file_path.set_extension(FILE_EXT);
|
|
||||||
let text = std::fs::read_to_string(&file_path).expect("failed to read file");
|
|
||||||
output.file_map.insert(
|
|
||||||
fid,
|
|
||||||
SrcFile {
|
|
||||||
path: file_path,
|
|
||||||
text: text.clone(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
let mut ctx = ParserCtx::new(fid, text.as_str(), &mut output);
|
|
||||||
fid += 1;
|
|
||||||
let res = PModule::parse(&mut ctx);
|
|
||||||
// println!("Parsed:");
|
|
||||||
// println!("{:#?}", res.node);
|
|
||||||
res.lower(import_path.clone(), &mut program, &mut imports, &mut output);
|
|
||||||
imported.insert(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(program, output)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_file(path: &Path, gdb: bool, asm: bool) {
|
|
||||||
let (mut program, mut output) = UProgram::from_path(path);
|
|
||||||
program.resolve(&mut output);
|
|
||||||
// println!("vars:");
|
|
||||||
// for (id, def) in program.iter_vars() {
|
|
||||||
// println!(" {id:?} = {}: {}", program.names.path(id), program.type_name(&def.ty));
|
|
||||||
// }
|
|
||||||
// for (id, f) in program.iter_fns() {
|
|
||||||
// println!("{}:{id:?} = {:#?}", program.names.path(id), f);
|
|
||||||
// }
|
|
||||||
if !output.errs.is_empty() {
|
|
||||||
output.write_to(&mut stdout());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let program = LProgram::create(&program).expect("morir");
|
|
||||||
let unlinked = compiler::compile(&program);
|
|
||||||
if asm {
|
|
||||||
println!("{:?}", unlinked);
|
|
||||||
} else {
|
|
||||||
let bin = unlinked.link().to_elf();
|
|
||||||
println!("compiled");
|
|
||||||
save_run(&bin, gdb);
|
|
||||||
}
|
|
||||||
output.write_to(&mut stdout());
|
|
||||||
}
|
|
||||||
|
|
||||||
fn save_run(binary: &[u8], run_gdb: bool) {
|
|
||||||
use std::io::prelude::*;
|
|
||||||
let dir = Path::new("./build");
|
|
||||||
create_dir_all(dir).expect("Failed to create or confirm build directory");
|
|
||||||
let name = Path::new("test");
|
|
||||||
let path = dir.join(name);
|
|
||||||
let path = path.as_os_str();
|
|
||||||
let mut file = OpenOptions::new()
|
|
||||||
.create(true)
|
|
||||||
.write(true)
|
|
||||||
.truncate(true)
|
|
||||||
.mode(0o750)
|
|
||||||
.open(path)
|
|
||||||
.expect("Failed to create file");
|
|
||||||
file.write_all(binary).expect("Failed to write to file");
|
|
||||||
file.sync_all().expect("Failed to sync file");
|
|
||||||
println!("running...");
|
|
||||||
let mut p = Command::new("qemu-riscv64");
|
|
||||||
let proc = if run_gdb {
|
|
||||||
p.arg("-g").arg("1234").arg(path).spawn()
|
|
||||||
} else {
|
|
||||||
p.arg(path).spawn()
|
|
||||||
};
|
|
||||||
if let Ok(mut process) = proc {
|
|
||||||
if run_gdb {
|
|
||||||
match Command::new("gdb")
|
|
||||||
.arg("-q")
|
|
||||||
.arg("-ex")
|
|
||||||
.arg("target remote :1234")
|
|
||||||
.arg(path)
|
|
||||||
.spawn()
|
|
||||||
{
|
|
||||||
Ok(mut gdb) => {
|
|
||||||
gdb.wait().expect("xd");
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
println!("gdb error: {e:?}");
|
|
||||||
process.kill().expect("uh oh");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Ok(status) = process.wait() {
|
|
||||||
if let Some(code) = status.code() {
|
|
||||||
std::process::exit(code);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run_stdin() {
|
|
||||||
println!("todo");
|
|
||||||
// for line in BufReader::new(std::io::stdin()).lines() {
|
|
||||||
// let str = &line.expect("failed to read line");
|
|
||||||
// let mut ctx = ParserCtx::from(&str[..]);
|
|
||||||
// if let Some(expr) = PStatement::parse_node(&mut ctx).node.as_ref() {
|
|
||||||
// if ctx.next().is_none() {
|
|
||||||
// println!("{:?}", expr);
|
|
||||||
// } else {
|
|
||||||
// println!("uhhhh ehehe");
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// ctx.output.write_for(&mut stdout(), str);
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,108 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub struct ParserCtx<'a> {
|
||||||
|
pub cursor: TokenCursor<'a>,
|
||||||
|
pub msgs: &'a mut Vec<CompilerMsg>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ParserCtx<'a> {
|
||||||
|
pub fn new(cursor: impl Into<TokenCursor<'a>>, msgs: &'a mut Vec<CompilerMsg>) -> Self {
|
||||||
|
Self {
|
||||||
|
cursor: cursor.into(),
|
||||||
|
msgs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse<T: Parsable<Data = ()>>(&mut self) -> Option<Node<T>> {
|
||||||
|
self.parse_with(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_with<T: Parsable>(&mut self, data: T::Data) -> Option<Node<T>> {
|
||||||
|
let data = match T::parse(self, data) {
|
||||||
|
ParseResult::Ok(t) => Some(t),
|
||||||
|
ParseResult::Node(n) => return Some(n),
|
||||||
|
ParseResult::Break(msg) => {
|
||||||
|
self.msgs.push(msg);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
ParseResult::Continue(msg) => {
|
||||||
|
self.msgs.push(msg);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
ParseResult::SubErr => {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Some(Node { data })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn seek(&mut self, token: impl Into<Token>) -> bool {
|
||||||
|
let token = token.into();
|
||||||
|
while let Some(next) = self.next() {
|
||||||
|
if next == token {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn expect_next(&mut self) -> Option<Token> {
|
||||||
|
let res = self.cursor.next();
|
||||||
|
if res.is_none() {
|
||||||
|
self.msgs.push(CompilerMsg::new(
|
||||||
|
"Unexpected end of input",
|
||||||
|
self.cursor.prev_end(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn expect_peek(&self) -> Result<&Token, CompilerMsg> {
|
||||||
|
match self.cursor.peek() {
|
||||||
|
Some(t) => Ok(t),
|
||||||
|
None => Err(self.unexpected_end()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn expect(&mut self, token: impl Into<Token>) -> Result<(), CompilerMsg> {
|
||||||
|
let token = token.into();
|
||||||
|
if self.next_is_ref(&token) {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(self.unexpected(format!("token {:?}", token)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unexpected_end(&self) -> CompilerMsg {
|
||||||
|
CompilerMsg::new("Unexpected end of input", self.next_start())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn peek(&self) -> Option<&Token> {
|
||||||
|
self.cursor.peek()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unexpected<'b>(&self, expected: impl std::fmt::Display) -> CompilerMsg {
|
||||||
|
if let Some((next, span)) = self.peek_span() {
|
||||||
|
CompilerMsg::new(
|
||||||
|
format!("Unexpected token {:?}, expected {}", next, expected),
|
||||||
|
span,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
self.unexpected_end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> std::ops::Deref for ParserCtx<'a> {
|
||||||
|
type Target = TokenCursor<'a>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.cursor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> std::ops::DerefMut for ParserCtx<'a> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.cursor
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct CompilerMsg {
|
||||||
|
msg: String,
|
||||||
|
span: CharSpan,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct CharSpan {
|
||||||
|
start: CharPos,
|
||||||
|
end: CharPos,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CharPos {
|
||||||
|
pub fn to(self, end: CharPos) -> CharSpan {
|
||||||
|
CharSpan { start: self, end }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CompilerMsg {
|
||||||
|
pub fn new(msg: impl Into<String>, span: impl Into<CharSpan>) -> Self {
|
||||||
|
Self {
|
||||||
|
msg: msg.into(),
|
||||||
|
span: span.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CharPos> for CharSpan {
|
||||||
|
fn from(value: CharPos) -> Self {
|
||||||
|
Self {
|
||||||
|
start: value,
|
||||||
|
end: value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+18
-4
@@ -1,5 +1,19 @@
|
|||||||
// mod v1;
|
mod ctx;
|
||||||
// mod v2;
|
mod token;
|
||||||
mod v3;
|
mod tree;
|
||||||
|
mod io;
|
||||||
|
|
||||||
pub use v3::*;
|
pub use ctx::*;
|
||||||
|
use token::*;
|
||||||
|
pub use tree::*;
|
||||||
|
pub use io::*;
|
||||||
|
|
||||||
|
pub fn parse(file: &str) {
|
||||||
|
let mut msgs = Vec::new();
|
||||||
|
let mut parser = ParserCtx::new(file, &mut msgs);
|
||||||
|
if let Some(block) = parser.parse_with::<PBlock>(false) {
|
||||||
|
println!("{block:#?}");
|
||||||
|
} else {
|
||||||
|
println!("{msgs:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
use std::{iter::Peekable, str::Chars};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct CharIter<'a> {
|
||||||
|
iter: Peekable<Chars<'a>>,
|
||||||
|
pos: CharPos,
|
||||||
|
next_pos: CharPos,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
pub struct CharPos {
|
||||||
|
line: usize,
|
||||||
|
col: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> CharIter<'a> {
|
||||||
|
pub fn new(text: &'a str) -> Self {
|
||||||
|
Self {
|
||||||
|
iter: text.chars().peekable(),
|
||||||
|
pos: CharPos::default(),
|
||||||
|
next_pos: CharPos::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next(&mut self) -> Option<char> {
|
||||||
|
let next = self.iter.next();
|
||||||
|
self.advance(next)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn advance(&mut self, c: Option<char>) -> Option<char> {
|
||||||
|
self.pos = self.next_pos;
|
||||||
|
if let Some(c) = c {
|
||||||
|
if c == '\n' {
|
||||||
|
self.next_pos.line += 1;
|
||||||
|
self.next_pos.col = 0;
|
||||||
|
} else {
|
||||||
|
self.next_pos.col += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn peek(&mut self) -> Option<char> {
|
||||||
|
self.iter.peek().copied()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next_if(&mut self, f: impl FnOnce(&char) -> bool) -> Option<char> {
|
||||||
|
let next = self.iter.next_if(f);
|
||||||
|
self.advance(next)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pos(&self) -> CharPos {
|
||||||
|
self.pos
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn until(&mut self, until: char) -> Option<String> {
|
||||||
|
let mut str = String::new();
|
||||||
|
let mut next = self.next()?;
|
||||||
|
while next != until {
|
||||||
|
str.push(next);
|
||||||
|
next = self.next()?;
|
||||||
|
}
|
||||||
|
Some(str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a str> for CharIter<'a> {
|
||||||
|
fn from(value: &'a str) -> Self {
|
||||||
|
Self::new(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum Keyword {
|
||||||
|
Let,
|
||||||
|
Fn,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Keyword {
|
||||||
|
pub fn parse(ident: &str) -> Option<Self> {
|
||||||
|
Some(match ident {
|
||||||
|
"let" => Self::Let,
|
||||||
|
"fn" => Self::Fn,
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Keyword> for Token {
|
||||||
|
fn from(value: Keyword) -> Self {
|
||||||
|
Token::Keyword(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum Literal {
|
||||||
|
String(String),
|
||||||
|
}
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
use super::io::*;
|
||||||
|
|
||||||
|
mod chr;
|
||||||
|
mod kw;
|
||||||
|
mod lit;
|
||||||
|
mod symbol;
|
||||||
|
|
||||||
|
pub use chr::*;
|
||||||
|
pub use kw::*;
|
||||||
|
pub use lit::*;
|
||||||
|
pub use symbol::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum Token {
|
||||||
|
Lit(Literal),
|
||||||
|
Keyword(Keyword),
|
||||||
|
Ident(String),
|
||||||
|
Symbol(Symbol),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct TokenCursor<'a> {
|
||||||
|
iter: CharIter<'a>,
|
||||||
|
prev_sym: Option<Symbol>,
|
||||||
|
next: Option<(Token, CharSpan)>,
|
||||||
|
next_start: CharPos,
|
||||||
|
prev_end: CharPos,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TokenCursor<'a> {
|
||||||
|
pub fn new(iter: impl Into<CharIter<'a>>) -> Self {
|
||||||
|
let mut s = Self {
|
||||||
|
next: None,
|
||||||
|
prev_sym: None,
|
||||||
|
iter: iter.into(),
|
||||||
|
next_start: CharPos::default(),
|
||||||
|
prev_end: CharPos::default(),
|
||||||
|
};
|
||||||
|
s.next();
|
||||||
|
s
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next(&mut self) -> Option<Token> {
|
||||||
|
self.next_span().map(|n| n.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next_span(&mut self) -> Option<(Token, CharSpan)> {
|
||||||
|
self.prev_end = self.iter.pos();
|
||||||
|
self.prev_sym = self.next.as_ref().and_then(|n| match n.0 {
|
||||||
|
Token::Symbol(s) => Some(s),
|
||||||
|
_ => None,
|
||||||
|
});
|
||||||
|
while self.iter.next_if(|c| c.is_whitespace()).is_some() {}
|
||||||
|
self.next_start = self.iter.pos();
|
||||||
|
std::mem::replace(&mut self.next, Self::get_next(&mut self.iter))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_next(iter: &mut CharIter) -> Option<(Token, CharSpan)> {
|
||||||
|
while iter.next_if(|c| c.is_whitespace()).is_some() {}
|
||||||
|
if let Some(c) = iter.next() {
|
||||||
|
let start = iter.pos();
|
||||||
|
let val = Self::get_next_inner(iter, c);
|
||||||
|
let span = start.to(iter.pos());
|
||||||
|
val.map(|v| (v, span))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_next_inner(iter: &mut CharIter, c: char) -> Option<Token> {
|
||||||
|
if c == '"' {
|
||||||
|
return iter.until('"').map(|s| Token::Lit(Literal::String(s)));
|
||||||
|
}
|
||||||
|
if let Some(sym) = Symbol::parse(c, iter) {
|
||||||
|
return Some(Token::Symbol(sym));
|
||||||
|
}
|
||||||
|
let mut ident = c.to_string();
|
||||||
|
while let Some(c) = iter.next_if(|c| !c.is_whitespace() && Symbol::parse_char(*c).is_none())
|
||||||
|
{
|
||||||
|
ident.push(c);
|
||||||
|
}
|
||||||
|
Some(if let Some(kw) = Keyword::parse(&ident) {
|
||||||
|
Token::Keyword(kw)
|
||||||
|
} else {
|
||||||
|
Token::Ident(ident)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn peek(&self) -> Option<&Token> {
|
||||||
|
self.peek_span().map(|v| v.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn peek_span(&self) -> Option<(&Token, CharSpan)> {
|
||||||
|
self.next.as_ref().map(|(t, s)| (t, *s))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next_if(&mut self, f: impl FnOnce(&Token) -> bool) -> Option<Token> {
|
||||||
|
if self.peek().is_some_and(f) {
|
||||||
|
self.next()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next_is(&mut self, token: impl Into<Token>) -> bool {
|
||||||
|
self.next_is_ref(&token.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn peek_is(&mut self, token: impl Into<Token>) -> bool {
|
||||||
|
self.peek().is_some_and(|t| *t == token.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next_is_ref(&mut self, token: &Token) -> bool {
|
||||||
|
self.next_if(|t| t == token).is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next_start(&self) -> CharPos {
|
||||||
|
self.next_start
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prev_end(&self) -> CharPos {
|
||||||
|
self.prev_end
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prev_sym(&self) -> Option<Symbol> {
|
||||||
|
self.prev_sym
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: Into<CharIter<'a>>> From<T> for TokenCursor<'a> {
|
||||||
|
fn from(value: T) -> Self {
|
||||||
|
Self::new(value.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub enum Symbol {
|
||||||
|
// 1 char
|
||||||
|
OpenParen,
|
||||||
|
CloseParen,
|
||||||
|
OpenCurly,
|
||||||
|
CloseCurly,
|
||||||
|
Plus,
|
||||||
|
Minus,
|
||||||
|
Slash,
|
||||||
|
Asterisk,
|
||||||
|
Equal,
|
||||||
|
Colon,
|
||||||
|
Semicolon,
|
||||||
|
SingleQuote,
|
||||||
|
Comma,
|
||||||
|
// 2 chars
|
||||||
|
Arrow,
|
||||||
|
DoubleArrow,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Symbol {
|
||||||
|
pub fn parse(c: char, iter: &mut CharIter) -> Option<Self> {
|
||||||
|
Self::parse_char(c).map(|s| s.parse_rest(iter))
|
||||||
|
}
|
||||||
|
pub fn parse_char(c: char) -> Option<Self> {
|
||||||
|
Some(match c {
|
||||||
|
'(' => Symbol::OpenParen,
|
||||||
|
')' => Symbol::CloseParen,
|
||||||
|
'{' => Symbol::OpenCurly,
|
||||||
|
'}' => Symbol::CloseCurly,
|
||||||
|
'+' => Symbol::Plus,
|
||||||
|
'-' => Symbol::Minus,
|
||||||
|
'/' => Symbol::Slash,
|
||||||
|
'*' => Symbol::Asterisk,
|
||||||
|
'=' => Symbol::Equal,
|
||||||
|
':' => Symbol::Colon,
|
||||||
|
';' => Symbol::Semicolon,
|
||||||
|
'\'' => Symbol::SingleQuote,
|
||||||
|
',' => Symbol::Comma,
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn parse_rest(mut self, iter: &mut CharIter) -> Self {
|
||||||
|
let Some(next) = iter.peek() else {
|
||||||
|
return self;
|
||||||
|
};
|
||||||
|
match (self, next) {
|
||||||
|
(Symbol::Minus, '>') => self = Symbol::Arrow,
|
||||||
|
(Symbol::Equal, '>') => self = Symbol::DoubleArrow,
|
||||||
|
_ => return self,
|
||||||
|
}
|
||||||
|
iter.next();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn str(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Symbol::OpenParen => "(",
|
||||||
|
Symbol::CloseParen => ")",
|
||||||
|
Symbol::OpenCurly => "{",
|
||||||
|
Symbol::CloseCurly => "}",
|
||||||
|
Symbol::Plus => "+",
|
||||||
|
Symbol::Minus => "-",
|
||||||
|
Symbol::Slash => "/",
|
||||||
|
Symbol::Asterisk => "*",
|
||||||
|
Symbol::Equal => "=",
|
||||||
|
Symbol::Colon => ":",
|
||||||
|
Symbol::Semicolon => ";",
|
||||||
|
Symbol::SingleQuote => "'",
|
||||||
|
Symbol::Arrow => "->",
|
||||||
|
Symbol::DoubleArrow => "=>",
|
||||||
|
Symbol::Comma => ",",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Symbol> for Token {
|
||||||
|
fn from(value: Symbol) -> Self {
|
||||||
|
Token::Symbol(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PBlock {
|
||||||
|
statements: Vec<Node<PStatement>>,
|
||||||
|
return_last: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parsable for PBlock {
|
||||||
|
type Data = bool;
|
||||||
|
|
||||||
|
fn parse(ctx: &mut ParserCtx, curlies: bool) -> ParseResult<Self> {
|
||||||
|
let end = if curlies {
|
||||||
|
ctx.expect(Symbol::OpenCurly)?;
|
||||||
|
Some(Symbol::CloseCurly)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let res = ctx.parse_list(
|
||||||
|
end,
|
||||||
|
SepCheck::new(Symbol::Semicolon)
|
||||||
|
.dup(true)
|
||||||
|
.skip_if(|ctx, _| ctx.prev_sym().is_some_and(|s| s == Symbol::CloseCurly)),
|
||||||
|
)?;
|
||||||
|
ParseResult::Ok(Self {
|
||||||
|
statements: res.nodes,
|
||||||
|
return_last: res.last_sep,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum PExpr {
|
||||||
|
Ident(String),
|
||||||
|
Lit(Literal),
|
||||||
|
Block(Node<PBlock>),
|
||||||
|
Group(BNode<PExpr>),
|
||||||
|
Unit,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parsable for PExpr {
|
||||||
|
fn parse(ctx: &mut ParserCtx, _: ()) -> ParseResult<Self> {
|
||||||
|
Self::parse_unit(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PExpr {
|
||||||
|
fn parse_unit(ctx: &mut ParserCtx) -> ParseResult<Self> {
|
||||||
|
ParseResult::Ok(match ctx.expect_peek()? {
|
||||||
|
Token::Lit(lit) => {
|
||||||
|
let res = PExpr::Lit(lit.clone());
|
||||||
|
ctx.next();
|
||||||
|
res
|
||||||
|
}
|
||||||
|
Token::Ident(ident) => {
|
||||||
|
let res = PExpr::Ident(ident.to_string());
|
||||||
|
ctx.next();
|
||||||
|
res
|
||||||
|
}
|
||||||
|
Token::Symbol(symbol) => match symbol {
|
||||||
|
Symbol::OpenParen => {
|
||||||
|
ctx.expect_next()?;
|
||||||
|
if ctx.next_is(Symbol::CloseParen) {
|
||||||
|
PExpr::Unit
|
||||||
|
} else {
|
||||||
|
let inner = ctx.parse();
|
||||||
|
let Some(inner) = inner else {
|
||||||
|
ctx.seek(Symbol::CloseParen);
|
||||||
|
return ParseResult::SubErr;
|
||||||
|
};
|
||||||
|
ctx.expect(Symbol::CloseParen)?;
|
||||||
|
PExpr::Group(inner.bx())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Symbol::OpenCurly => PExpr::Block(ctx.parse_with(true)?),
|
||||||
|
_ => return ctx.unexpected("expression").res(),
|
||||||
|
},
|
||||||
|
_ => return ctx.unexpected("expression").res(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PFunc {
|
||||||
|
name: Node<PIdent>,
|
||||||
|
args: Vec<Node<PVarDef>>,
|
||||||
|
body: Node<PExpr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parsable for PFunc {
|
||||||
|
type Data = ();
|
||||||
|
|
||||||
|
fn parse(ctx: &mut ParserCtx, _: Self::Data) -> ParseResult<Self> {
|
||||||
|
ctx.expect(Keyword::Fn)?;
|
||||||
|
let name = ctx.parse()?;
|
||||||
|
ctx.expect(Symbol::OpenParen)?;
|
||||||
|
let args = ctx.parse_list(Some(Symbol::CloseParen), SepCheck::new(Symbol::Comma))?;
|
||||||
|
let body = ctx.parse()?;
|
||||||
|
ParseResult::Ok(Self {
|
||||||
|
name,
|
||||||
|
args: args.nodes,
|
||||||
|
body,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
use super::*;
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PIdent(String);
|
||||||
|
|
||||||
|
impl Deref for PIdent {
|
||||||
|
type Target = String;
|
||||||
|
|
||||||
|
fn deref(&self) -> &String {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parsable for PIdent {
|
||||||
|
fn parse(ctx: &mut ParserCtx, _: ()) -> ParseResult<Self> {
|
||||||
|
if let Token::Ident(ident) = ctx.expect_peek()? {
|
||||||
|
let ident = ident.clone();
|
||||||
|
ctx.next();
|
||||||
|
ParseResult::Ok(Self(ident))
|
||||||
|
} else {
|
||||||
|
ctx.unexpected("identifier").res()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub struct ListRes<T> {
|
||||||
|
pub nodes: Vec<Node<T>>,
|
||||||
|
pub last_sep: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait BetweenFn<T> {
|
||||||
|
fn run(&mut self, ctx: &mut ParserCtx, prev: &Node<T>) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ParserCtx<'_> {
|
||||||
|
pub fn parse_list<T: Parsable<Data = ()>>(
|
||||||
|
&mut self,
|
||||||
|
end: Option<impl Into<Token>>,
|
||||||
|
mut between: impl BetweenFn<T>,
|
||||||
|
) -> Option<ListRes<T>> {
|
||||||
|
let end = end.map(|t| t.into());
|
||||||
|
let mut nodes = Vec::new();
|
||||||
|
let mut last_sep = false;
|
||||||
|
macro_rules! abort {
|
||||||
|
() => {
|
||||||
|
if end.is_some_and(|t| self.seek(t)) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
macro_rules! check_end {
|
||||||
|
() => {
|
||||||
|
if end.as_ref().is_some_and(|t| self.next_is_ref(t))
|
||||||
|
|| (end.is_none() && self.peek().is_none())
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
loop {
|
||||||
|
check_end!();
|
||||||
|
last_sep = false;
|
||||||
|
nodes.push(match self.parse() {
|
||||||
|
Some(node) => node,
|
||||||
|
None => abort!(),
|
||||||
|
});
|
||||||
|
check_end!();
|
||||||
|
if between.run(self, nodes.last().unwrap()) {
|
||||||
|
abort!();
|
||||||
|
}
|
||||||
|
last_sep = true;
|
||||||
|
}
|
||||||
|
Some(ListRes { nodes, last_sep })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SepCheck {
|
||||||
|
pub sep: Token,
|
||||||
|
pub dup: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> BetweenFn<T> for SepCheck {
|
||||||
|
fn run(&mut self, ctx: &mut ParserCtx, prev: &Node<T>) -> bool {
|
||||||
|
let Some(next) = ctx.expect_next() else {
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
if next != self.sep {
|
||||||
|
ctx.msgs
|
||||||
|
.push(ctx.unexpected(format!("Expected {:?}", self.sep)));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if self.dup {
|
||||||
|
while ctx.next_is_ref(&self.sep) {}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SepCheck {
|
||||||
|
pub fn new(sep: impl Into<Token>) -> Self {
|
||||||
|
Self {
|
||||||
|
sep: sep.into(),
|
||||||
|
dup: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn dup(mut self, dup: bool) -> Self {
|
||||||
|
self.dup = dup;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait SkipFn<T> = Fn(&mut ParserCtx, &Node<T>) -> bool;
|
||||||
|
|
||||||
|
// I hate everything. sepcheck is fine, this is not
|
||||||
|
pub struct SkipIf<T, F: SkipFn<T>, B: BetweenFn<T>> {
|
||||||
|
f: F,
|
||||||
|
inner: B,
|
||||||
|
_pd: std::marker::PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, F: SkipFn<T>, B: BetweenFn<T>> SkipIf<T, F, B> {
|
||||||
|
pub fn new(f: F, run: B) -> Self {
|
||||||
|
Self {
|
||||||
|
f,
|
||||||
|
inner: run,
|
||||||
|
_pd: std::marker::PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, F: SkipFn<T>, B: BetweenFn<T>> BetweenFn<T> for SkipIf<T, F, B> {
|
||||||
|
fn run(&mut self, ctx: &mut ParserCtx, prev: &Node<T>) -> bool {
|
||||||
|
if (self.f)(ctx, prev) {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
self.inner.run(ctx, prev)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait BetweenFnUtil<T>: BetweenFn<T> + Sized {
|
||||||
|
fn skip_if<F: SkipFn<T>>(self, f: F) -> SkipIf<T, F, Self>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: BetweenFn<T>, T> BetweenFnUtil<T> for B {
|
||||||
|
fn skip_if<F: SkipFn<T>>(self, f: F) -> SkipIf<T, F, Self> {
|
||||||
|
SkipIf::new(f, self)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
mod block;
|
||||||
|
mod expr;
|
||||||
|
mod func;
|
||||||
|
mod ident;
|
||||||
|
mod list;
|
||||||
|
mod node;
|
||||||
|
mod statement;
|
||||||
|
mod ty;
|
||||||
|
mod vardef;
|
||||||
|
|
||||||
|
pub use block::*;
|
||||||
|
pub use expr::*;
|
||||||
|
pub use func::*;
|
||||||
|
pub use ident::*;
|
||||||
|
pub use list::*;
|
||||||
|
pub use node::*;
|
||||||
|
pub use statement::*;
|
||||||
|
pub use ty::*;
|
||||||
|
pub use vardef::*;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub struct Node<T> {
|
||||||
|
pub data: Option<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type BNode<T> = Box<Node<T>>;
|
||||||
|
|
||||||
|
pub enum ParseResult<T> {
|
||||||
|
Ok(T),
|
||||||
|
Node(Node<T>),
|
||||||
|
Continue(CompilerMsg),
|
||||||
|
Break(CompilerMsg),
|
||||||
|
SubErr,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Parsable: Sized {
|
||||||
|
type Data = ();
|
||||||
|
fn parse(ctx: &mut ParserCtx, data: Self::Data) -> ParseResult<Self>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Node<T> {
|
||||||
|
pub fn bx(self) -> Box<Self> {
|
||||||
|
Box::new(self)
|
||||||
|
}
|
||||||
|
pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Node<U> {
|
||||||
|
Node {
|
||||||
|
data: self.data.map(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
use std::convert::Infallible;
|
||||||
|
impl<T> std::ops::FromResidual<Option<Infallible>> for ParseResult<T> {
|
||||||
|
fn from_residual(residual: Option<Infallible>) -> Self {
|
||||||
|
match residual {
|
||||||
|
None => ParseResult::SubErr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T> std::ops::FromResidual<Result<Infallible, CompilerMsg>> for ParseResult<T> {
|
||||||
|
fn from_residual(residual: Result<Infallible, CompilerMsg>) -> Self {
|
||||||
|
match residual {
|
||||||
|
Err(msg) => ParseResult::Break(msg),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CompilerMsg {
|
||||||
|
pub fn res<T>(self) -> ParseResult<T> {
|
||||||
|
ParseResult::Break(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: std::fmt::Debug> std::fmt::Debug for Node<T> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
if let Some(d) = &self.data {
|
||||||
|
d.fmt(f)
|
||||||
|
} else {
|
||||||
|
f.write_str("{error}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum PStatement {
|
||||||
|
Expr(PExpr),
|
||||||
|
Let(Node<PVarDef>, Node<PExpr>),
|
||||||
|
Fn(PFunc),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parsable for PStatement {
|
||||||
|
fn parse(ctx: &mut ParserCtx, _: ()) -> ParseResult<Self> {
|
||||||
|
let res = match ctx.expect_peek()? {
|
||||||
|
Token::Keyword(kw) => match kw {
|
||||||
|
Keyword::Let => {
|
||||||
|
ctx.next();
|
||||||
|
let name = ctx.parse()?;
|
||||||
|
ctx.expect(Symbol::Equal)?;
|
||||||
|
let body = ctx.parse()?;
|
||||||
|
Self::Let(name, body)
|
||||||
|
}
|
||||||
|
Keyword::Fn => return ParseResult::Node(ctx.parse()?.map(PStatement::Fn)),
|
||||||
|
},
|
||||||
|
_ => return ParseResult::Node(ctx.parse()?.map(PStatement::Expr)),
|
||||||
|
};
|
||||||
|
ParseResult::Ok(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PType {
|
||||||
|
name: Node<PIdent>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parsable for PType {
|
||||||
|
fn parse(ctx: &mut ParserCtx, _: Self::Data) -> ParseResult<Self> {
|
||||||
|
ParseResult::Ok(Self { name: ctx.parse()? })
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PVarDef {
|
||||||
|
name: Node<PIdent>,
|
||||||
|
ty: Option<Node<PType>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parsable for PVarDef {
|
||||||
|
fn parse(ctx: &mut ParserCtx, _: Self::Data) -> ParseResult<Self> {
|
||||||
|
let name = ctx.parse()?;
|
||||||
|
let mut ty = None;
|
||||||
|
if ctx.next_is(Symbol::Colon) {
|
||||||
|
ty = Some(ctx.parse()?);
|
||||||
|
}
|
||||||
|
ParseResult::Ok(Self { name, ty })
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
use super::error::ParserError;
|
|
||||||
use super::token::{CharCursor, Keyword, Symbol, Token, TokenInstance};
|
|
||||||
use super::FilePos;
|
|
||||||
|
|
||||||
pub struct TokenCursor<'a> {
|
|
||||||
cursor: CharCursor<'a>,
|
|
||||||
next: Option<TokenInstance>,
|
|
||||||
next_pos: FilePos,
|
|
||||||
prev_end: FilePos,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> TokenCursor<'a> {
|
|
||||||
pub fn next(&mut self) -> Option<TokenInstance> {
|
|
||||||
self.prev_end = self.cursor.prev_pos();
|
|
||||||
self.next_pos = self.cursor.next_pos();
|
|
||||||
std::mem::replace(&mut self.next, TokenInstance::parse(&mut self.cursor))
|
|
||||||
}
|
|
||||||
pub fn expect_next(&mut self) -> Result<TokenInstance, ParserError> {
|
|
||||||
self.peek().ok_or(ParserError::unexpected_end())?;
|
|
||||||
Ok(self.next().unwrap())
|
|
||||||
}
|
|
||||||
pub fn expect_token(&mut self, t: Token) -> Result<(), ParserError> {
|
|
||||||
let next = self.expect_next()?;
|
|
||||||
if t == next.token {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(ParserError::unexpected_token(&next, &format!("{t:?}")))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn expect_sym(&mut self, symbol: Symbol) -> Result<(), ParserError> {
|
|
||||||
self.expect_token(Token::Symbol(symbol))
|
|
||||||
}
|
|
||||||
pub fn seek_sym(&mut self, symbol: Symbol) {
|
|
||||||
while self
|
|
||||||
.next()
|
|
||||||
.is_some_and(|n| n.token != Token::Symbol(symbol))
|
|
||||||
{}
|
|
||||||
}
|
|
||||||
pub fn seek_syms(&mut self, syms: &[Symbol]) {
|
|
||||||
while self
|
|
||||||
.peek()
|
|
||||||
.is_some_and(|n| !syms.iter().any(|s| n.is_symbol(*s)))
|
|
||||||
{
|
|
||||||
self.next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn seek(&mut self, f: impl Fn(&TokenInstance) -> bool) -> Option<&TokenInstance> {
|
|
||||||
loop {
|
|
||||||
if f(self.peek()?) {
|
|
||||||
return self.peek();
|
|
||||||
}
|
|
||||||
self.next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn expect_kw(&mut self, kw: Keyword) -> Result<(), ParserError> {
|
|
||||||
self.expect_token(Token::Keyword(kw))
|
|
||||||
}
|
|
||||||
pub fn peek(&self) -> Option<&TokenInstance> {
|
|
||||||
self.next.as_ref()
|
|
||||||
}
|
|
||||||
pub fn expect_peek(&mut self) -> Result<&TokenInstance, ParserError> {
|
|
||||||
self.peek().ok_or(ParserError::unexpected_end())
|
|
||||||
}
|
|
||||||
pub fn chars(&mut self) -> &mut CharCursor<'a> {
|
|
||||||
&mut self.cursor
|
|
||||||
}
|
|
||||||
pub fn prev_end(&self) -> FilePos {
|
|
||||||
self.prev_end
|
|
||||||
}
|
|
||||||
pub fn next_pos(&self) -> FilePos {
|
|
||||||
self.next_pos
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<&'a str> for TokenCursor<'a> {
|
|
||||||
fn from(string: &'a str) -> Self {
|
|
||||||
Self::from(CharCursor::from(string))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<CharCursor<'a>> for TokenCursor<'a> {
|
|
||||||
fn from(mut cursor: CharCursor<'a>) -> Self {
|
|
||||||
let cur = TokenInstance::parse(&mut cursor);
|
|
||||||
Self {
|
|
||||||
cursor,
|
|
||||||
next: cur,
|
|
||||||
next_pos: FilePos::start(),
|
|
||||||
prev_end: FilePos::start(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
use super::{
|
|
||||||
token::{FileSpan, TokenInstance},
|
|
||||||
FilePos,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct ParserError {
|
|
||||||
pub msg: String,
|
|
||||||
pub spans: Vec<FileSpan>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ParserErrors {
|
|
||||||
pub errs: Vec<ParserError>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ParserError {
|
|
||||||
pub fn from_instances(instances: &[&TokenInstance], msg: String) -> Self {
|
|
||||||
ParserError {
|
|
||||||
msg,
|
|
||||||
spans: instances.iter().map(|i| i.span).collect(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn from_msg(msg: String) -> Self {
|
|
||||||
Self {
|
|
||||||
msg,
|
|
||||||
spans: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn at(pos: FilePos, msg: String) -> Self {
|
|
||||||
Self {
|
|
||||||
msg,
|
|
||||||
spans: vec![FileSpan::at(pos)],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn unexpected_end() -> Self {
|
|
||||||
Self::from_msg("unexpected end of input".to_string())
|
|
||||||
}
|
|
||||||
pub fn unexpected_token(inst: &TokenInstance, expected: &str) -> Self {
|
|
||||||
let t = &inst.token;
|
|
||||||
ParserError::from_instances(
|
|
||||||
&[inst],
|
|
||||||
format!("unexpected token {t:?}; expected {expected}"),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
pub fn write_for(&self, writer: &mut impl std::io::Write, file: &str) -> std::io::Result<()> {
|
|
||||||
let after = if self.spans.is_empty() { "" } else { ":" };
|
|
||||||
writeln!(writer, "error: {}{}", self.msg, after)?;
|
|
||||||
for span in &self.spans {
|
|
||||||
span.write_for(writer, file)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ParserErrors {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self { errs: Vec::new() }
|
|
||||||
}
|
|
||||||
pub fn add(&mut self, err: ParserError) {
|
|
||||||
self.errs.push(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
use std::io::{stdout, BufRead, BufReader};
|
|
||||||
|
|
||||||
mod cursor;
|
|
||||||
mod error;
|
|
||||||
mod node;
|
|
||||||
mod nodes;
|
|
||||||
mod parse;
|
|
||||||
mod token;
|
|
||||||
|
|
||||||
pub use cursor::*;
|
|
||||||
pub use error::*;
|
|
||||||
pub use node::*;
|
|
||||||
pub use nodes::*;
|
|
||||||
pub use parse::*;
|
|
||||||
use token::*;
|
|
||||||
|
|
||||||
pub fn parse_file(file: &str) {
|
|
||||||
let mut errors = ParserErrors::new();
|
|
||||||
let res = Module::parse_node(&mut TokenCursor::from(file), &mut errors);
|
|
||||||
println!("{:?}", res.node);
|
|
||||||
if errors.errs.is_empty() {
|
|
||||||
let module = res.node.resolve().expect("what");
|
|
||||||
}
|
|
||||||
let out = &mut stdout();
|
|
||||||
for err in errors.errs {
|
|
||||||
err.write_for(out, file).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run_stdin() {
|
|
||||||
for line in BufReader::new(std::io::stdin()).lines() {
|
|
||||||
let mut errors = ParserErrors::new();
|
|
||||||
let str = &line.expect("failed to read line");
|
|
||||||
let mut cursor = TokenCursor::from(&str[..]);
|
|
||||||
if let Ok(expr) = Statement::parse_node(&mut cursor, &mut errors).node.as_ref() {
|
|
||||||
if cursor.next().is_none() {
|
|
||||||
println!("{:?}", expr);
|
|
||||||
} else {
|
|
||||||
println!("uhhhh ehehe");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let out = &mut stdout();
|
|
||||||
for err in errors.errs {
|
|
||||||
err.write_for(out, str).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
use std::{
|
|
||||||
fmt::Debug,
|
|
||||||
ops::{Deref, DerefMut},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::FileSpan;
|
|
||||||
|
|
||||||
pub trait MaybeResolved {
|
|
||||||
type Inner<T>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Resolved;
|
|
||||||
impl MaybeResolved for Resolved {
|
|
||||||
type Inner<T> = T;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Unresolved;
|
|
||||||
impl MaybeResolved for Unresolved {
|
|
||||||
type Inner<T> = Result<T, ()>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Node<T, R: MaybeResolved> {
|
|
||||||
pub inner: <R as MaybeResolved>::Inner<T>,
|
|
||||||
pub span: FileSpan,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Node<T, Unresolved> {
|
|
||||||
pub fn new(inner: T, span: FileSpan) -> Self {
|
|
||||||
Self {
|
|
||||||
inner: Ok(inner),
|
|
||||||
span,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn bx(self) -> Node<Box<T>, Unresolved> {
|
|
||||||
Node {
|
|
||||||
inner: self.inner.map(|v| Box::new(v)),
|
|
||||||
span: self.span,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, R: MaybeResolved> Deref for Node<T, R> {
|
|
||||||
type Target = <R as MaybeResolved>::Inner<T>;
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.inner
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, R: MaybeResolved> DerefMut for Node<T, R> {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
&mut self.inner
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Debug> Debug for Node<T, Unresolved> {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match &self.inner {
|
|
||||||
Ok(v) => v.fmt(f),
|
|
||||||
Err(_) => f.write_str("{error}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Debug> Debug for Node<T, Resolved> {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
self.inner.fmt(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Resolvable<Res> {
|
|
||||||
fn resolve(self) -> Result<Res, ()>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Resolvable<Res>, Res> Resolvable<Node<Res, Resolved>> for Node<T, Unresolved> {
|
|
||||||
fn resolve(self) -> Result<Node<Res, Resolved>, ()> {
|
|
||||||
if let Ok(inner) = self.inner {
|
|
||||||
return Ok(Node {
|
|
||||||
inner: inner.resolve()?,
|
|
||||||
span: self.span,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Resolvable<Res>, Res> Resolvable<Box<Res>> for Box<T> {
|
|
||||||
fn resolve(self) -> Result<Box<Res>, ()> {
|
|
||||||
Ok(Box::new((*self).resolve()?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
use std::fmt::{Debug, Write};
|
|
||||||
|
|
||||||
use super::{
|
|
||||||
token::Symbol, MaybeResolved, Node, NodeParsable, Parsable, ParseResult, ParserError,
|
|
||||||
ParserErrors, Resolvable, Resolved, Statement, TokenCursor, Unresolved,
|
|
||||||
};
|
|
||||||
use crate::util::Padder;
|
|
||||||
|
|
||||||
pub struct Body<R: MaybeResolved> {
|
|
||||||
pub statements: Vec<Node<Statement<R>, R>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Parsable for Body<Unresolved> {
|
|
||||||
fn parse(cursor: &mut TokenCursor, errors: &mut ParserErrors) -> ParseResult<Self> {
|
|
||||||
let mut statements = Vec::new();
|
|
||||||
cursor.expect_sym(Symbol::OpenCurly)?;
|
|
||||||
if cursor.expect_peek()?.is_symbol(Symbol::CloseCurly) {
|
|
||||||
cursor.next();
|
|
||||||
return ParseResult::Ok(Self { statements });
|
|
||||||
}
|
|
||||||
let mut expect_semi = false;
|
|
||||||
let mut recover = false;
|
|
||||||
loop {
|
|
||||||
let Some(next) = cursor.peek() else {
|
|
||||||
recover = true;
|
|
||||||
errors.add(ParserError::unexpected_end());
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
if next.is_symbol(Symbol::CloseCurly) {
|
|
||||||
cursor.next();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if next.is_symbol(Symbol::Semicolon) {
|
|
||||||
cursor.next();
|
|
||||||
expect_semi = false;
|
|
||||||
continue;
|
|
||||||
} else if expect_semi {
|
|
||||||
errors.add(ParserError {
|
|
||||||
msg: "expected ';'".to_string(),
|
|
||||||
spans: vec![cursor.next_pos().char_span()],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
let res = Statement::parse_node(cursor, errors);
|
|
||||||
statements.push(res.node);
|
|
||||||
expect_semi = true;
|
|
||||||
if res.recover {
|
|
||||||
cursor.seek_syms(&[Symbol::Semicolon, Symbol::CloseCurly]);
|
|
||||||
if cursor.peek().is_none() {
|
|
||||||
recover = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ParseResult::from_recover(Self { statements }, recover)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Resolvable<Body<Resolved>> for Body<Unresolved> {
|
|
||||||
fn resolve(self) -> Result<Body<Resolved>, ()> {
|
|
||||||
Ok(Body {
|
|
||||||
statements: self
|
|
||||||
.statements
|
|
||||||
.into_iter()
|
|
||||||
.map(|s| s.resolve())
|
|
||||||
.collect::<Result<_, _>>()?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for Body<Unresolved> {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
if self.statements.first().is_some() {
|
|
||||||
f.write_str("{\n ")?;
|
|
||||||
let mut padder = Padder::new(f);
|
|
||||||
for s in &self.statements {
|
|
||||||
// they don't expose wrap_buf :grief:
|
|
||||||
padder.write_str(&format!("{s:?}\n"))?;
|
|
||||||
}
|
|
||||||
f.write_char('}')?;
|
|
||||||
} else {
|
|
||||||
f.write_str("{}")?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,176 +0,0 @@
|
|||||||
use std::fmt::{Debug, Write};
|
|
||||||
|
|
||||||
use super::{
|
|
||||||
BinaryOperator, Body, Ident, Literal, MaybeResolved, Node, NodeParsable, Parsable, ParseResult,
|
|
||||||
ParserError, ParserErrors, Resolvable, Resolved, Symbol, TokenCursor, UnaryOperator,
|
|
||||||
Unresolved,
|
|
||||||
};
|
|
||||||
|
|
||||||
type BoxNode<R> = Node<Box<Expr<R>>, R>;
|
|
||||||
|
|
||||||
pub enum Expr<R: MaybeResolved> {
|
|
||||||
Lit(Node<Literal, R>),
|
|
||||||
Ident(Node<Ident, R>),
|
|
||||||
BinaryOp(BinaryOperator, BoxNode<R>, BoxNode<R>),
|
|
||||||
UnaryOp(UnaryOperator, BoxNode<R>),
|
|
||||||
Block(Node<Body<R>, R>),
|
|
||||||
Call(BoxNode<R>, Vec<Node<Expr<R>, R>>),
|
|
||||||
Group(BoxNode<R>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Parsable for Expr<Unresolved> {
|
|
||||||
fn parse(cursor: &mut TokenCursor, errors: &mut ParserErrors) -> ParseResult<Self> {
|
|
||||||
let start = cursor.next_pos();
|
|
||||||
let next = cursor.expect_peek()?;
|
|
||||||
let mut e1 = if next.is_symbol(Symbol::OpenParen) {
|
|
||||||
cursor.next();
|
|
||||||
if cursor.expect_peek()?.is_symbol(Symbol::CloseParen) {
|
|
||||||
cursor.next();
|
|
||||||
return ParseResult::Ok(Expr::Lit(Node::new(
|
|
||||||
Literal::Unit,
|
|
||||||
cursor.next_pos().char_span(),
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
let res = Node::parse(cursor, errors);
|
|
||||||
if res.recover {
|
|
||||||
cursor.seek_sym(Symbol::CloseParen);
|
|
||||||
}
|
|
||||||
cursor.expect_sym(Symbol::CloseParen)?;
|
|
||||||
Self::Group(res.node.bx())
|
|
||||||
} else if next.is_symbol(Symbol::OpenCurly) {
|
|
||||||
Self::Block(Body::parse_node(cursor, errors)?)
|
|
||||||
} else if let Some(op) = UnaryOperator::from_token(next) {
|
|
||||||
cursor.next();
|
|
||||||
return Node::parse(cursor, errors).map(|n| {
|
|
||||||
let n = n.bx();
|
|
||||||
if let Ok(box Self::BinaryOp(op2, n1, n2)) = n.inner {
|
|
||||||
let span = start.to(n1.span.end);
|
|
||||||
Self::BinaryOp(op2, Node::new(Self::UnaryOp(op, n1), span).bx(), n2)
|
|
||||||
} else {
|
|
||||||
Self::UnaryOp(op, n)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if let Some(val) = Node::maybe_parse(cursor, errors) {
|
|
||||||
Self::Lit(val)
|
|
||||||
} else {
|
|
||||||
let res = Node::parse(cursor, &mut ParserErrors::new());
|
|
||||||
if res.node.is_ok() {
|
|
||||||
Self::Ident(res.node)
|
|
||||||
} else {
|
|
||||||
let next = cursor.expect_peek()?;
|
|
||||||
return ParseResult::Err(ParserError::unexpected_token(next, "an expression"));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let Some(mut next) = cursor.peek() else {
|
|
||||||
return ParseResult::Ok(e1);
|
|
||||||
};
|
|
||||||
while next.is_symbol(Symbol::OpenParen) {
|
|
||||||
cursor.next();
|
|
||||||
let mut args = Vec::new();
|
|
||||||
loop {
|
|
||||||
let next = cursor.expect_peek()?;
|
|
||||||
if next.is_symbol(Symbol::CloseParen) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
let res = Node::<Expr<Unresolved>, Unresolved>::parse(cursor, errors);
|
|
||||||
args.push(res.node);
|
|
||||||
if res.recover {
|
|
||||||
cursor.seek_syms(&[Symbol::CloseParen, Symbol::Comma]);
|
|
||||||
}
|
|
||||||
let next = cursor.expect_peek()?;
|
|
||||||
if !next.is_symbol(Symbol::Comma) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
cursor.next();
|
|
||||||
}
|
|
||||||
cursor.expect_sym(Symbol::CloseParen)?;
|
|
||||||
let end = cursor.prev_end();
|
|
||||||
e1 = Self::Call(Node::new(Box::new(e1), start.to(end)), args);
|
|
||||||
let Some(next2) = cursor.peek() else {
|
|
||||||
return ParseResult::Ok(e1);
|
|
||||||
};
|
|
||||||
next = next2
|
|
||||||
}
|
|
||||||
let end = cursor.prev_end();
|
|
||||||
let mut recover = false;
|
|
||||||
let res = if let Some(mut op) = BinaryOperator::from_token(&next.token) {
|
|
||||||
cursor.next();
|
|
||||||
let mut n1 = Node::new(e1, start.to(end)).bx();
|
|
||||||
let res = Node::parse(cursor, errors);
|
|
||||||
let mut n2 = res.node.bx();
|
|
||||||
recover = res.recover;
|
|
||||||
if let Ok(box Self::BinaryOp(op2, _, _)) = n2.as_ref() {
|
|
||||||
if op.presedence() > op2.presedence() {
|
|
||||||
let Ok(box Self::BinaryOp(op2, n21, n22)) = n2.inner else {
|
|
||||||
unreachable!();
|
|
||||||
};
|
|
||||||
let end = n21.span.end;
|
|
||||||
n1 = Node::new(Self::BinaryOp(op, n1, n21), start.to(end)).bx();
|
|
||||||
op = op2;
|
|
||||||
n2 = n22;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Self::BinaryOp(op, n1, n2)
|
|
||||||
} else {
|
|
||||||
e1
|
|
||||||
};
|
|
||||||
ParseResult::from_recover(res, recover)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Resolvable<Expr<Resolved>> for Expr<Unresolved> {
|
|
||||||
fn resolve(self) -> Result<Expr<Resolved>, ()> {
|
|
||||||
Ok(match self {
|
|
||||||
Expr::Lit(l) => Expr::Lit(l.resolve()?),
|
|
||||||
Expr::Ident(n) => Expr::Ident(n.resolve()?),
|
|
||||||
Expr::BinaryOp(o, e1, e2) => Expr::BinaryOp(o, e1.resolve()?, e2.resolve()?),
|
|
||||||
Expr::UnaryOp(o, e) => Expr::UnaryOp(o, e.resolve()?),
|
|
||||||
Expr::Block(b) => Expr::Block(b.resolve()?),
|
|
||||||
Expr::Call(f, args) => Expr::Call(
|
|
||||||
f.resolve()?,
|
|
||||||
args.into_iter()
|
|
||||||
.map(|arg| arg.resolve())
|
|
||||||
.collect::<Result<_, ()>>()?,
|
|
||||||
),
|
|
||||||
Expr::Group(e) => Expr::Group(e.resolve()?),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for Expr<Unresolved> {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
Expr::Lit(c) => c.fmt(f)?,
|
|
||||||
Expr::Ident(n) => n.fmt(f)?,
|
|
||||||
Expr::Block(b) => b.fmt(f)?,
|
|
||||||
Expr::BinaryOp(op, e1, e2) => {
|
|
||||||
write!(f, "({:?}", *e1)?;
|
|
||||||
if op.pad() {
|
|
||||||
write!(f, " {} ", op.str())?;
|
|
||||||
} else {
|
|
||||||
write!(f, "{}", op.str())?;
|
|
||||||
}
|
|
||||||
write!(f, "{:?})", *e2)?;
|
|
||||||
}
|
|
||||||
Expr::Call(n, args) => {
|
|
||||||
n.fmt(f)?;
|
|
||||||
f.write_char('(')?;
|
|
||||||
if let Some(a) = args.first() {
|
|
||||||
a.fmt(f)?;
|
|
||||||
}
|
|
||||||
for arg in args.iter().skip(1) {
|
|
||||||
f.write_str(", ")?;
|
|
||||||
arg.fmt(f)?;
|
|
||||||
}
|
|
||||||
f.write_char(')')?;
|
|
||||||
}
|
|
||||||
Expr::UnaryOp(op, e) => {
|
|
||||||
write!(f, "(")?;
|
|
||||||
write!(f, "{}", op.str())?;
|
|
||||||
write!(f, "{:?})", *e)?;
|
|
||||||
}
|
|
||||||
Expr::Group(inner) => inner.fmt(f)?,
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
use super::{
|
|
||||||
Body, Ident, Keyword, MaybeResolved, Node, Parsable, ParseResult, ParserErrors, Resolvable,
|
|
||||||
Resolved, Symbol, TokenCursor, Unresolved,
|
|
||||||
};
|
|
||||||
use std::fmt::Debug;
|
|
||||||
|
|
||||||
pub struct Function<R: MaybeResolved> {
|
|
||||||
pub name: Node<Ident, R>,
|
|
||||||
pub body: Node<Body<R>, R>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Parsable for Function<Unresolved> {
|
|
||||||
fn parse(cursor: &mut TokenCursor, errors: &mut ParserErrors) -> ParseResult<Self> {
|
|
||||||
cursor.expect_kw(Keyword::Fn)?;
|
|
||||||
let name = Node::parse(cursor, errors)?;
|
|
||||||
cursor.expect_sym(Symbol::OpenParen)?;
|
|
||||||
cursor.expect_sym(Symbol::CloseParen)?;
|
|
||||||
Node::parse(cursor, errors).map(|body| Self { name, body })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for Function<Unresolved> {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
f.write_str("fn ")?;
|
|
||||||
self.name.fmt(f)?;
|
|
||||||
f.write_str("() ")?;
|
|
||||||
self.body.fmt(f)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Resolvable<Function<Resolved>> for Function<Unresolved> {
|
|
||||||
fn resolve(self) -> Result<Function<Resolved>, ()> {
|
|
||||||
Ok(Function {
|
|
||||||
name: self.name.resolve()?,
|
|
||||||
body: self.body.resolve()?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
use std::fmt::Debug;
|
|
||||||
use super::{Parsable, ParseResult, ParserError, Resolvable, Token};
|
|
||||||
|
|
||||||
pub struct Ident(String);
|
|
||||||
|
|
||||||
impl Ident {
|
|
||||||
pub fn val(&self) -> &String {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Parsable for Ident {
|
|
||||||
fn parse(cursor: &mut super::TokenCursor, errors: &mut super::ParserErrors) -> ParseResult<Self> {
|
|
||||||
let next = cursor.expect_peek()?;
|
|
||||||
let Token::Ident(name) = &next.token else {
|
|
||||||
return ParseResult::Err(ParserError::unexpected_token(next, "an identifier"));
|
|
||||||
};
|
|
||||||
let name = name.to_string();
|
|
||||||
cursor.next();
|
|
||||||
ParseResult::Ok(Self(name))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for Ident {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(f, "{}", self.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Resolvable<Ident> for Ident {
|
|
||||||
fn resolve(self) -> Result<Ident, ()> {
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,120 +0,0 @@
|
|||||||
use super::{
|
|
||||||
CharCursor, MaybeParsable, ParserError, ParserErrors, Resolvable, Symbol, Token, TokenCursor,
|
|
||||||
};
|
|
||||||
use std::fmt::Debug;
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq)]
|
|
||||||
pub enum Literal {
|
|
||||||
String(String),
|
|
||||||
Char(char),
|
|
||||||
Number(Number),
|
|
||||||
Unit,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq)]
|
|
||||||
pub struct Number {
|
|
||||||
pub whole: String,
|
|
||||||
pub decimal: Option<String>,
|
|
||||||
pub ty: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MaybeParsable for Literal {
|
|
||||||
fn maybe_parse(
|
|
||||||
cursor: &mut TokenCursor,
|
|
||||||
_: &mut ParserErrors,
|
|
||||||
) -> Result<Option<Self>, ParserError> {
|
|
||||||
let inst = cursor.expect_peek()?;
|
|
||||||
let mut res = match &inst.token {
|
|
||||||
Token::Symbol(Symbol::SingleQuote) => {
|
|
||||||
let chars = cursor.chars();
|
|
||||||
let c = chars.expect_next()?;
|
|
||||||
chars.expect('\'')?;
|
|
||||||
Self::Char(c)
|
|
||||||
}
|
|
||||||
Token::Symbol(Symbol::DoubleQuote) => Self::String(string_from(cursor.chars())?),
|
|
||||||
Token::Ident(text) => {
|
|
||||||
let first = text.chars().next().unwrap();
|
|
||||||
if first.is_ascii_digit() {
|
|
||||||
Self::Number(Number {
|
|
||||||
whole: text.to_string(),
|
|
||||||
decimal: None,
|
|
||||||
ty: None,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => return Ok(None),
|
|
||||||
};
|
|
||||||
cursor.next();
|
|
||||||
if let (Some(next), Self::Number(num)) = (cursor.peek(), &mut res) {
|
|
||||||
if next.token.is_symbol(Symbol::Dot) {
|
|
||||||
cursor.next();
|
|
||||||
if let Some(next) = cursor.peek() {
|
|
||||||
if let Token::Ident(i) = &next.token {
|
|
||||||
if i.chars().next().unwrap().is_ascii_digit() {
|
|
||||||
num.decimal = Some(i.to_string());
|
|
||||||
cursor.next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(Some(res))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn string_from(cursor: &mut CharCursor) -> Result<String, ParserError> {
|
|
||||||
let mut str = String::new();
|
|
||||||
loop {
|
|
||||||
let c = cursor.expect_next()?;
|
|
||||||
if c == '"' {
|
|
||||||
return Ok(str);
|
|
||||||
}
|
|
||||||
str.push(match c {
|
|
||||||
'\\' => {
|
|
||||||
let next = cursor.expect_next()?;
|
|
||||||
match next {
|
|
||||||
'"' => '"',
|
|
||||||
'\'' => '\'',
|
|
||||||
't' => '\t',
|
|
||||||
'n' => '\n',
|
|
||||||
'0' => '\0',
|
|
||||||
_ => {
|
|
||||||
todo!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => c,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Resolvable<Literal> for Literal {
|
|
||||||
fn resolve(self) -> Result<Literal, ()> {
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for Literal {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::String(str) => str.fmt(f),
|
|
||||||
Self::Char(c) => c.fmt(f),
|
|
||||||
Self::Number(n) => n.fmt(f),
|
|
||||||
Self::Unit => f.write_str("()"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for Number {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(f, "{}", self.whole)?;
|
|
||||||
if let Some(d) = &self.decimal {
|
|
||||||
write!(f, ".{}", d)?;
|
|
||||||
}
|
|
||||||
if let Some(ty) = &self.ty {
|
|
||||||
write!(f, "T{}", ty)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
mod body;
|
|
||||||
mod expr;
|
|
||||||
mod func;
|
|
||||||
mod module;
|
|
||||||
mod op;
|
|
||||||
mod statement;
|
|
||||||
mod lit;
|
|
||||||
mod ident;
|
|
||||||
|
|
||||||
pub use body::*;
|
|
||||||
pub use expr::*;
|
|
||||||
pub use func::*;
|
|
||||||
pub use module::*;
|
|
||||||
pub use op::*;
|
|
||||||
pub use statement::*;
|
|
||||||
pub use lit::*;
|
|
||||||
pub use ident::*;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
use super::{
|
|
||||||
Function, Keyword, MaybeResolved, Node, Parsable, ParseResult, ParserError, ParserErrors,
|
|
||||||
Resolvable, Resolved, TokenCursor, Unresolved,
|
|
||||||
};
|
|
||||||
use std::fmt::Debug;
|
|
||||||
|
|
||||||
pub struct Module<R: MaybeResolved> {
|
|
||||||
pub functions: Vec<Node<Function<R>, R>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Parsable for Module<Unresolved> {
|
|
||||||
fn parse(cursor: &mut TokenCursor, errors: &mut ParserErrors) -> ParseResult<Self> {
|
|
||||||
let mut functions = Vec::new();
|
|
||||||
loop {
|
|
||||||
let Some(next) = cursor.peek() else {
|
|
||||||
return ParseResult::Ok(Self { functions });
|
|
||||||
};
|
|
||||||
if next.is_keyword(Keyword::Fn) {
|
|
||||||
let res = Node::parse(cursor, errors);
|
|
||||||
functions.push(res.node);
|
|
||||||
if res.recover {
|
|
||||||
return ParseResult::Recover(Self { functions });
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
errors.add(ParserError::unexpected_token(next, "fn"));
|
|
||||||
cursor.next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for Module<Unresolved> {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
self.functions.fmt(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Resolvable<Module<Resolved>> for Module<Unresolved> {
|
|
||||||
fn resolve(self) -> Result<Module<Resolved>, ()> {
|
|
||||||
Ok(Module {
|
|
||||||
functions: self
|
|
||||||
.functions
|
|
||||||
.into_iter()
|
|
||||||
.map(|f| f.resolve())
|
|
||||||
.collect::<Result<_, _>>()?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
use super::{Symbol, Token};
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
|
||||||
pub enum BinaryOperator {
|
|
||||||
Add,
|
|
||||||
Sub,
|
|
||||||
Mul,
|
|
||||||
Div,
|
|
||||||
LessThan,
|
|
||||||
GreaterThan,
|
|
||||||
Access,
|
|
||||||
Assign,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BinaryOperator {
|
|
||||||
pub fn presedence(&self) -> u32 {
|
|
||||||
match self {
|
|
||||||
Self::Assign => 0,
|
|
||||||
Self::LessThan => 1,
|
|
||||||
Self::GreaterThan => 1,
|
|
||||||
Self::Add => 2,
|
|
||||||
Self::Sub => 3,
|
|
||||||
Self::Mul => 4,
|
|
||||||
Self::Div => 5,
|
|
||||||
Self::Access => 6,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn str(&self) -> &str {
|
|
||||||
match self {
|
|
||||||
Self::Add => "+",
|
|
||||||
Self::Sub => "-",
|
|
||||||
Self::Mul => "*",
|
|
||||||
Self::Div => "/",
|
|
||||||
Self::LessThan => "<",
|
|
||||||
Self::GreaterThan => ">",
|
|
||||||
Self::Access => ".",
|
|
||||||
Self::Assign => "=",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn from_token(token: &Token) -> Option<Self> {
|
|
||||||
let Token::Symbol(symbol) = token else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
Some(match symbol {
|
|
||||||
Symbol::OpenAngle => Self::LessThan,
|
|
||||||
Symbol::CloseAngle => Self::GreaterThan,
|
|
||||||
Symbol::Plus => Self::Add,
|
|
||||||
Symbol::Minus => Self::Sub,
|
|
||||||
Symbol::Asterisk => Self::Mul,
|
|
||||||
Symbol::Slash => Self::Div,
|
|
||||||
Symbol::Dot => Self::Access,
|
|
||||||
Symbol::Equals => Self::Assign,
|
|
||||||
_ => {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
pub fn pad(&self) -> bool {
|
|
||||||
match self {
|
|
||||||
Self::Add => true,
|
|
||||||
Self::Sub => true,
|
|
||||||
Self::Mul => true,
|
|
||||||
Self::Div => true,
|
|
||||||
Self::LessThan => true,
|
|
||||||
Self::GreaterThan => true,
|
|
||||||
Self::Access => false,
|
|
||||||
Self::Assign => true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum UnaryOperator {
|
|
||||||
Not,
|
|
||||||
Ref,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UnaryOperator {
|
|
||||||
pub fn str(&self) -> &str {
|
|
||||||
match self {
|
|
||||||
Self::Not => "!",
|
|
||||||
Self::Ref => "&",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn from_token(token: &Token) -> Option<Self> {
|
|
||||||
let Token::Symbol(symbol) = token else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
Some(match symbol {
|
|
||||||
Symbol::Ampersand => Self::Ref,
|
|
||||||
Symbol::Bang => Self::Not,
|
|
||||||
_ => {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
use std::fmt::{Debug, Write};
|
|
||||||
use super::{
|
|
||||||
Expr, Ident, Keyword, MaybeResolved, Node, Parsable, ParseResult, ParserErrors, Resolvable, Resolved, Symbol, Token, TokenCursor, Unresolved
|
|
||||||
};
|
|
||||||
|
|
||||||
pub enum Statement<R: MaybeResolved> {
|
|
||||||
Let(Node<Ident, R>, Node<Expr<R>, R>),
|
|
||||||
Return(Node<Expr<R>, R>),
|
|
||||||
Expr(Node<Expr<R>, R>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Parsable for Statement<Unresolved> {
|
|
||||||
fn parse(cursor: &mut TokenCursor, errors: &mut ParserErrors) -> ParseResult<Self> {
|
|
||||||
let next = cursor.expect_peek()?;
|
|
||||||
match next.token {
|
|
||||||
Token::Keyword(Keyword::Let) => {
|
|
||||||
cursor.next();
|
|
||||||
let name = Node::parse(cursor, errors)?;
|
|
||||||
cursor.expect_sym(Symbol::Equals)?;
|
|
||||||
Node::parse(cursor, errors).map(|expr| Self::Let(name, expr))
|
|
||||||
}
|
|
||||||
Token::Keyword(Keyword::Return) => {
|
|
||||||
cursor.next();
|
|
||||||
Node::parse(cursor, errors).map(Self::Return)
|
|
||||||
}
|
|
||||||
_ => Node::parse(cursor, errors).map(Self::Expr),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Resolvable<Statement<Resolved>> for Statement<Unresolved> {
|
|
||||||
fn resolve(self) -> Result<Statement<Resolved>, ()> {
|
|
||||||
Ok(match self {
|
|
||||||
Self::Let(i, e) => Statement::Let(i.resolve()?, e.resolve()?),
|
|
||||||
Self::Return(e) => Statement::Return(e.resolve()?),
|
|
||||||
Self::Expr(e) => Statement::Expr(e.resolve()?),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for Statement<Unresolved> {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
Statement::Let(n, e) => {
|
|
||||||
f.write_str("let ")?;
|
|
||||||
n.fmt(f);
|
|
||||||
f.write_str(" = ")?;
|
|
||||||
e.fmt(f)?;
|
|
||||||
f.write_char(';')?;
|
|
||||||
}
|
|
||||||
Statement::Return(e) => {
|
|
||||||
f.write_str("return ")?;
|
|
||||||
e.fmt(f)?;
|
|
||||||
f.write_char(';')?;
|
|
||||||
}
|
|
||||||
Statement::Expr(e) => {
|
|
||||||
e.fmt(f)?;
|
|
||||||
f.write_char(';')?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,178 +0,0 @@
|
|||||||
use std::{
|
|
||||||
convert::Infallible,
|
|
||||||
ops::{ControlFlow, FromResidual, Try},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{Node, ParserError, ParserErrors, TokenCursor, Unresolved};
|
|
||||||
|
|
||||||
pub enum ParseResult<T> {
|
|
||||||
Ok(T),
|
|
||||||
Recover(T),
|
|
||||||
Err(ParserError),
|
|
||||||
SubErr,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> ParseResult<T> {
|
|
||||||
pub fn from_recover(data: T, recover: bool) -> Self {
|
|
||||||
if recover {
|
|
||||||
Self::Recover(data)
|
|
||||||
} else {
|
|
||||||
Self::Ok(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Try for ParseResult<T> {
|
|
||||||
type Output = Result<T, T>;
|
|
||||||
type Residual = Option<ParserError>;
|
|
||||||
fn from_output(output: Self::Output) -> Self {
|
|
||||||
match output {
|
|
||||||
Ok(v) => Self::Ok(v),
|
|
||||||
Err(v) => Self::Recover(v),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
|
|
||||||
match self {
|
|
||||||
ParseResult::Ok(v) => ControlFlow::Continue(Ok(v)),
|
|
||||||
ParseResult::Recover(v) => ControlFlow::Continue(Err(v)),
|
|
||||||
ParseResult::Err(e) => ControlFlow::Break(Some(e)),
|
|
||||||
ParseResult::SubErr => ControlFlow::Break(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> FromResidual for ParseResult<T> {
|
|
||||||
fn from_residual(residual: <Self as Try>::Residual) -> Self {
|
|
||||||
match residual {
|
|
||||||
Some(err) => Self::Err(err),
|
|
||||||
None => Self::SubErr,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> FromResidual<Result<Infallible, ParserError>> for ParseResult<T> {
|
|
||||||
fn from_residual(residual: Result<Infallible, ParserError>) -> Self {
|
|
||||||
match residual {
|
|
||||||
Err(e) => Self::Err(e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, U> FromResidual<ParseResult<T>> for ParseResult<U> {
|
|
||||||
fn from_residual(residual: ParseResult<T>) -> Self {
|
|
||||||
match residual {
|
|
||||||
ParseResult::Err(e) => Self::Err(e),
|
|
||||||
ParseResult::SubErr => Self::SubErr,
|
|
||||||
_ => unreachable!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct NodeParseResult<T> {
|
|
||||||
pub node: Node<T, Unresolved>,
|
|
||||||
pub recover: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> NodeParseResult<T> {
|
|
||||||
pub fn map<F: FnOnce(Node<T, Unresolved>) -> U, U>(self, op: F) -> ParseResult<U> {
|
|
||||||
let res = op(self.node);
|
|
||||||
if self.recover {
|
|
||||||
ParseResult::Recover(res)
|
|
||||||
} else {
|
|
||||||
ParseResult::Ok(res)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Try for NodeParseResult<T> {
|
|
||||||
type Output = Node<T, Unresolved>;
|
|
||||||
type Residual = ParseResult<T>;
|
|
||||||
|
|
||||||
fn from_output(output: Self::Output) -> Self {
|
|
||||||
Self {
|
|
||||||
node: output,
|
|
||||||
recover: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
|
|
||||||
if self.recover {
|
|
||||||
ControlFlow::Break(ParseResult::SubErr)
|
|
||||||
} else {
|
|
||||||
ControlFlow::Continue(self.node)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> FromResidual for NodeParseResult<T> {
|
|
||||||
fn from_residual(_: <Self as Try>::Residual) -> Self {
|
|
||||||
// I hope this is unreachable ???
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Parsable: Sized {
|
|
||||||
fn parse(cursor: &mut TokenCursor, errors: &mut ParserErrors) -> ParseResult<Self>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait MaybeParsable: Sized {
|
|
||||||
fn maybe_parse(
|
|
||||||
cursor: &mut TokenCursor,
|
|
||||||
errors: &mut ParserErrors,
|
|
||||||
) -> Result<Option<Self>, ParserError>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Parsable> Node<T, Unresolved> {
|
|
||||||
pub fn parse(cursor: &mut TokenCursor, errors: &mut ParserErrors) -> NodeParseResult<T> {
|
|
||||||
let start = cursor.next_pos();
|
|
||||||
let (inner, recover) = match T::parse(cursor, errors) {
|
|
||||||
ParseResult::Ok(v) => (Ok(v), false),
|
|
||||||
ParseResult::Recover(v) => (Ok(v), true),
|
|
||||||
ParseResult::Err(e) => {
|
|
||||||
errors.add(e);
|
|
||||||
(Err(()), true)
|
|
||||||
}
|
|
||||||
ParseResult::SubErr => (Err(()), true),
|
|
||||||
};
|
|
||||||
let end = cursor.prev_end();
|
|
||||||
NodeParseResult {
|
|
||||||
node: Self {
|
|
||||||
inner,
|
|
||||||
span: start.to(end),
|
|
||||||
},
|
|
||||||
recover,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: MaybeParsable> Node<T, Unresolved> {
|
|
||||||
pub fn maybe_parse(cursor: &mut TokenCursor, errors: &mut ParserErrors) -> Option<Self> {
|
|
||||||
let start = cursor.next_pos();
|
|
||||||
let inner = match T::maybe_parse(cursor, errors) {
|
|
||||||
Ok(v) => Ok(v?),
|
|
||||||
Err(e) => {
|
|
||||||
errors.add(e);
|
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let end = cursor.prev_end();
|
|
||||||
Some(Self {
|
|
||||||
inner,
|
|
||||||
span: start.to(end),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait NodeParsable {
|
|
||||||
fn parse_node(cursor: &mut TokenCursor, errors: &mut ParserErrors) -> NodeParseResult<Self>
|
|
||||||
where
|
|
||||||
Self: Sized;
|
|
||||||
}
|
|
||||||
impl<T: Parsable> NodeParsable for T {
|
|
||||||
fn parse_node(cursor: &mut TokenCursor, errors: &mut ParserErrors) -> NodeParseResult<Self>
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
Node::<Self, Unresolved>::parse(cursor, errors)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
use std::{iter::Peekable, str::Chars};
|
|
||||||
|
|
||||||
use super::super::ParserError;
|
|
||||||
use super::FilePos;
|
|
||||||
|
|
||||||
pub struct CharCursor<'a> {
|
|
||||||
chars: Peekable<Chars<'a>>,
|
|
||||||
next_pos: FilePos,
|
|
||||||
prev_pos: FilePos,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CharCursor<'_> {
|
|
||||||
pub fn next(&mut self) -> Option<char> {
|
|
||||||
let res = self.peek()?;
|
|
||||||
self.advance();
|
|
||||||
Some(res)
|
|
||||||
}
|
|
||||||
pub fn expect(&mut self, c: char) -> Result<(), ParserError> {
|
|
||||||
let next = self.expect_next()?;
|
|
||||||
if next == c {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(ParserError::at(
|
|
||||||
self.prev_pos,
|
|
||||||
format!("unexpected char '{next}'; expected '{c}'"),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn skip_whitespace(&mut self) {
|
|
||||||
while self.peek().is_some_and(|c| c.is_whitespace()) {
|
|
||||||
self.advance();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn peek(&mut self) -> Option<char> {
|
|
||||||
self.chars.peek().copied()
|
|
||||||
}
|
|
||||||
pub fn advance(&mut self) {
|
|
||||||
let Some(next) = self.chars.next() else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
self.prev_pos = self.next_pos;
|
|
||||||
if next == '\n' {
|
|
||||||
self.next_pos.col = 0;
|
|
||||||
self.next_pos.line += 1;
|
|
||||||
} else {
|
|
||||||
self.next_pos.col += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn expect_next(&mut self) -> Result<char, ParserError> {
|
|
||||||
self.next().ok_or(ParserError::unexpected_end())
|
|
||||||
}
|
|
||||||
pub fn next_pos(&self) -> FilePos {
|
|
||||||
self.next_pos
|
|
||||||
}
|
|
||||||
pub fn prev_pos(&self) -> FilePos {
|
|
||||||
self.prev_pos
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<&'a str> for CharCursor<'a> {
|
|
||||||
fn from(value: &'a str) -> Self {
|
|
||||||
Self {
|
|
||||||
chars: value.chars().peekable(),
|
|
||||||
next_pos: FilePos::start(),
|
|
||||||
prev_pos: FilePos::start(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub struct FilePos {
|
|
||||||
pub line: usize,
|
|
||||||
pub col: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct FileSpan {
|
|
||||||
pub start: FilePos,
|
|
||||||
pub end: FilePos,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FilePos {
|
|
||||||
pub fn start() -> Self {
|
|
||||||
Self { line: 0, col: 0 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FilePos {
|
|
||||||
pub fn to(self, end: FilePos) -> FileSpan {
|
|
||||||
FileSpan { start: self, end }
|
|
||||||
}
|
|
||||||
pub fn char_span(self) -> FileSpan {
|
|
||||||
FileSpan::at(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const BEFORE: usize = 1;
|
|
||||||
const AFTER: usize = 0;
|
|
||||||
|
|
||||||
impl FileSpan {
|
|
||||||
pub fn at(pos: FilePos) -> Self {
|
|
||||||
Self {
|
|
||||||
start: pos,
|
|
||||||
end: pos,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn write_for(&self, writer: &mut impl std::io::Write, file: &str) -> std::io::Result<()> {
|
|
||||||
let start = self.start.line.saturating_sub(BEFORE);
|
|
||||||
let num_before = self.start.line - start;
|
|
||||||
let mut lines = file.lines().skip(start);
|
|
||||||
let width = format!("{}", self.end.line + AFTER).len();
|
|
||||||
let same_line = self.start.line == self.end.line;
|
|
||||||
for i in 0..num_before {
|
|
||||||
writeln!(writer, "{:>width$} | {}", start + i, lines.next().unwrap())?;
|
|
||||||
}
|
|
||||||
let line = lines.next().unwrap();
|
|
||||||
writeln!(writer, "{:>width$} | {}", self.start.line, line)?;
|
|
||||||
let len = if same_line {
|
|
||||||
self.end.col - self.start.col + 1
|
|
||||||
} else {
|
|
||||||
line.len() - self.start.col
|
|
||||||
};
|
|
||||||
writeln!(
|
|
||||||
writer,
|
|
||||||
"{} | {}",
|
|
||||||
" ".repeat(width),
|
|
||||||
" ".repeat(self.start.col) + &"^".repeat(len)
|
|
||||||
)?;
|
|
||||||
if !same_line {
|
|
||||||
for _ in 0..self.end.line - self.start.line - 1 {
|
|
||||||
lines.next();
|
|
||||||
}
|
|
||||||
let line = lines.next().unwrap();
|
|
||||||
writeln!(writer, "{:>width$} | {}", self.end.line, line)?;
|
|
||||||
writeln!(
|
|
||||||
writer,
|
|
||||||
"{} | {}",
|
|
||||||
" ".repeat(width),
|
|
||||||
"^".repeat(self.end.col + 1)
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
// for i in 0..AFTER {
|
|
||||||
// if let Some(next) = lines.next() {
|
|
||||||
// writeln!(writer, "{:>width$} | {}", self.end.line + i + 1, next)?;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
|
||||||
pub enum Keyword {
|
|
||||||
Fn,
|
|
||||||
Let,
|
|
||||||
If,
|
|
||||||
Return,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Keyword {
|
|
||||||
pub fn from_string(str: &str) -> Option<Self> {
|
|
||||||
Some(match str {
|
|
||||||
"fn" => Self::Fn,
|
|
||||||
"let" => Self::Let,
|
|
||||||
"if" => Self::If,
|
|
||||||
"return" => Self::Return,
|
|
||||||
_ => return None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
mod cursor;
|
|
||||||
mod file;
|
|
||||||
mod keyword;
|
|
||||||
mod symbol;
|
|
||||||
|
|
||||||
use std::ops::Deref;
|
|
||||||
|
|
||||||
pub use cursor::*;
|
|
||||||
pub use file::*;
|
|
||||||
pub use keyword::*;
|
|
||||||
pub use symbol::*;
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
||||||
pub enum Token {
|
|
||||||
Symbol(Symbol),
|
|
||||||
Ident(String),
|
|
||||||
Keyword(Keyword),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct TokenInstance {
|
|
||||||
pub token: Token,
|
|
||||||
pub span: FileSpan,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TokenInstance {
|
|
||||||
pub fn parse(cursor: &mut CharCursor) -> Option<TokenInstance> {
|
|
||||||
cursor.skip_whitespace();
|
|
||||||
cursor.peek()?;
|
|
||||||
let start = cursor.next_pos();
|
|
||||||
if let Some(s) = Symbol::parse(cursor) {
|
|
||||||
if s == Symbol::DoubleSlash {
|
|
||||||
while cursor.next() != Some('\n') {}
|
|
||||||
return Self::parse(cursor);
|
|
||||||
}
|
|
||||||
let end = cursor.prev_pos();
|
|
||||||
return Some(Self {
|
|
||||||
token: Token::Symbol(s),
|
|
||||||
span: FileSpan { start, end },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
let mut word = String::new();
|
|
||||||
while let Some(c) = cursor.peek() {
|
|
||||||
if c.is_whitespace() || Symbol::from_char(c).is_some() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
word.push(c);
|
|
||||||
cursor.advance();
|
|
||||||
}
|
|
||||||
let end = cursor.prev_pos();
|
|
||||||
let token = if let Some(keyword) = Keyword::from_string(&word) {
|
|
||||||
Token::Keyword(keyword)
|
|
||||||
} else {
|
|
||||||
Token::Ident(word)
|
|
||||||
};
|
|
||||||
Some(Self {
|
|
||||||
token,
|
|
||||||
span: FileSpan { start, end },
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Token {
|
|
||||||
pub fn is_symbol(&self, symbol: Symbol) -> bool {
|
|
||||||
match self {
|
|
||||||
Token::Symbol(s) => *s == symbol,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn is_symbol_and(&self, f: impl Fn(Symbol) -> bool) -> bool {
|
|
||||||
match self {
|
|
||||||
Token::Symbol(s) => f(*s),
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn is_keyword(&self, kw: Keyword) -> bool {
|
|
||||||
match self {
|
|
||||||
Token::Keyword(k) => *k == kw,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for TokenInstance {
|
|
||||||
type Target = Token;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.token
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,146 +0,0 @@
|
|||||||
use std::fmt::Debug;
|
|
||||||
|
|
||||||
use super::CharCursor;
|
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
|
||||||
pub enum Symbol {
|
|
||||||
Semicolon,
|
|
||||||
Colon,
|
|
||||||
DoubleColon,
|
|
||||||
Equals,
|
|
||||||
DoubleEquals,
|
|
||||||
Arrow,
|
|
||||||
DoubleArrow,
|
|
||||||
Plus,
|
|
||||||
Minus,
|
|
||||||
Asterisk,
|
|
||||||
Slash,
|
|
||||||
DoubleSlash,
|
|
||||||
Dot,
|
|
||||||
OpenParen,
|
|
||||||
CloseParen,
|
|
||||||
OpenCurly,
|
|
||||||
CloseCurly,
|
|
||||||
OpenSquare,
|
|
||||||
CloseSquare,
|
|
||||||
OpenAngle,
|
|
||||||
CloseAngle,
|
|
||||||
SingleQuote,
|
|
||||||
DoubleQuote,
|
|
||||||
Bang,
|
|
||||||
Ampersand,
|
|
||||||
DoubleAmpersand,
|
|
||||||
Pipe,
|
|
||||||
DoublePipe,
|
|
||||||
Comma,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Symbol {
|
|
||||||
pub fn parse(cursor: &mut CharCursor) -> Option<Self> {
|
|
||||||
Self::from_char(cursor.peek()?).map(|mut s| {
|
|
||||||
cursor.advance();
|
|
||||||
s.finish(cursor);
|
|
||||||
s
|
|
||||||
})
|
|
||||||
}
|
|
||||||
pub fn from_char(c: char) -> Option<Self> {
|
|
||||||
Some(match c {
|
|
||||||
'(' => Self::OpenParen,
|
|
||||||
')' => Self::CloseParen,
|
|
||||||
'[' => Self::OpenSquare,
|
|
||||||
']' => Self::CloseSquare,
|
|
||||||
'{' => Self::OpenCurly,
|
|
||||||
'}' => Self::CloseCurly,
|
|
||||||
'<' => Self::OpenAngle,
|
|
||||||
'>' => Self::CloseAngle,
|
|
||||||
';' => Self::Semicolon,
|
|
||||||
':' => Self::Colon,
|
|
||||||
'+' => Self::Plus,
|
|
||||||
'-' => Self::Minus,
|
|
||||||
'*' => Self::Asterisk,
|
|
||||||
'/' => Self::Slash,
|
|
||||||
'=' => Self::Equals,
|
|
||||||
'.' => Self::Dot,
|
|
||||||
'\'' => Self::SingleQuote,
|
|
||||||
'"' => Self::DoubleQuote,
|
|
||||||
'!' => Self::Bang,
|
|
||||||
'&' => Self::Ampersand,
|
|
||||||
'|' => Self::Pipe,
|
|
||||||
',' => Self::Comma,
|
|
||||||
_ => return None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
pub fn finish(&mut self, cursor: &mut CharCursor) {
|
|
||||||
let Some(next) = cursor.peek() else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
*self = match self {
|
|
||||||
Self::Colon => match next {
|
|
||||||
':' => Self::DoubleColon,
|
|
||||||
_ => return,
|
|
||||||
},
|
|
||||||
Self::Minus => match next {
|
|
||||||
'>' => Self::Arrow,
|
|
||||||
_ => return,
|
|
||||||
},
|
|
||||||
Self::Equals => match next {
|
|
||||||
'=' => Self::DoubleEquals,
|
|
||||||
'>' => Self::DoubleArrow,
|
|
||||||
_ => return,
|
|
||||||
},
|
|
||||||
Self::Slash => match next {
|
|
||||||
'/' => Self::DoubleSlash,
|
|
||||||
_ => return,
|
|
||||||
},
|
|
||||||
Self::Ampersand => match next {
|
|
||||||
'&' => Self::DoubleAmpersand,
|
|
||||||
_ => return,
|
|
||||||
},
|
|
||||||
Self::Pipe => match next {
|
|
||||||
'&' => Self::DoublePipe,
|
|
||||||
_ => return,
|
|
||||||
},
|
|
||||||
_ => return,
|
|
||||||
};
|
|
||||||
cursor.advance();
|
|
||||||
}
|
|
||||||
pub fn str(&self) -> &str {
|
|
||||||
match self {
|
|
||||||
Self::Semicolon => ";",
|
|
||||||
Self::Colon => ":",
|
|
||||||
Self::DoubleColon => "::",
|
|
||||||
Self::Equals => "=",
|
|
||||||
Self::DoubleEquals => "==",
|
|
||||||
Self::Arrow => "->",
|
|
||||||
Self::DoubleArrow => "=>",
|
|
||||||
Self::Plus => "+",
|
|
||||||
Self::Minus => "-",
|
|
||||||
Self::Asterisk => "*",
|
|
||||||
Self::Slash => "/",
|
|
||||||
Self::DoubleSlash => "//",
|
|
||||||
Self::Dot => ".",
|
|
||||||
Self::OpenParen => "(",
|
|
||||||
Self::CloseParen => ")",
|
|
||||||
Self::OpenCurly => "{",
|
|
||||||
Self::CloseCurly => "}",
|
|
||||||
Self::OpenSquare => "[",
|
|
||||||
Self::CloseSquare => "]",
|
|
||||||
Self::OpenAngle => "<",
|
|
||||||
Self::CloseAngle => ">",
|
|
||||||
Self::SingleQuote => "'",
|
|
||||||
Self::DoubleQuote => "\"",
|
|
||||||
Self::Bang => "!",
|
|
||||||
Self::Comma => ",",
|
|
||||||
Self::Ampersand => "&",
|
|
||||||
Self::DoubleAmpersand => "&&",
|
|
||||||
Self::Pipe => "|",
|
|
||||||
Self::DoublePipe => "||",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for Symbol {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(f, "'{}'", self.str())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,120 +0,0 @@
|
|||||||
use std::collections::HashSet;
|
|
||||||
use std::fmt::{Debug, Write};
|
|
||||||
use std::sync::LazyLock;
|
|
||||||
|
|
||||||
use crate::util::Padder;
|
|
||||||
|
|
||||||
use super::util::WHITESPACE_SET;
|
|
||||||
use super::CharCursor;
|
|
||||||
use super::Expr;
|
|
||||||
use super::ParserError;
|
|
||||||
|
|
||||||
static NAME_END: LazyLock<HashSet<char>> = LazyLock::new(|| {
|
|
||||||
let mut set = WHITESPACE_SET.clone();
|
|
||||||
set.extend(&['(']);
|
|
||||||
set
|
|
||||||
});
|
|
||||||
|
|
||||||
pub struct Body {
|
|
||||||
statements: Vec<Statement>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum Statement {
|
|
||||||
Let(String, Expr),
|
|
||||||
Return(Expr),
|
|
||||||
Expr(Expr),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Body {
|
|
||||||
pub fn parse(cursor: &mut CharCursor) -> Result<Self, ParserError> {
|
|
||||||
cursor.skip_whitespace();
|
|
||||||
let mut statements = Vec::new();
|
|
||||||
cursor.expect_char('{')?;
|
|
||||||
loop {
|
|
||||||
cursor.skip_whitespace();
|
|
||||||
let next = cursor.expect_peek()?;
|
|
||||||
if next == '}' {
|
|
||||||
cursor.next();
|
|
||||||
return Ok(Self { statements });
|
|
||||||
}
|
|
||||||
statements.push(Statement::parse(cursor)?);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Statement {
|
|
||||||
pub fn parse(cursor: &mut CharCursor) -> Result<Self, ParserError> {
|
|
||||||
cursor.skip_whitespace();
|
|
||||||
Ok(if cursor.advance_if_str("let", &WHITESPACE_SET) {
|
|
||||||
cursor.skip_whitespace();
|
|
||||||
let name = cursor.until(&NAME_END);
|
|
||||||
if name.is_empty() {
|
|
||||||
return Err(ParserError::at(
|
|
||||||
cursor.pos(),
|
|
||||||
"Expected variable name".to_string(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
cursor.skip_whitespace();
|
|
||||||
cursor.expect_char('=')?;
|
|
||||||
let expr = Expr::parse(cursor)?;
|
|
||||||
cursor.skip_whitespace();
|
|
||||||
cursor.expect_char(';')?;
|
|
||||||
Self::Let(name, expr)
|
|
||||||
} else if cursor.advance_if_str("return", &WHITESPACE_SET) {
|
|
||||||
let expr = Expr::parse(cursor)?;
|
|
||||||
cursor.skip_whitespace();
|
|
||||||
cursor.expect_char(';')?;
|
|
||||||
Self::Return(expr)
|
|
||||||
} else {
|
|
||||||
let expr = Expr::parse(cursor)?;
|
|
||||||
match cursor.expect_peek()? {
|
|
||||||
';' => {
|
|
||||||
cursor.next();
|
|
||||||
Self::Expr(expr)
|
|
||||||
}
|
|
||||||
'}' => Self::Return(expr),
|
|
||||||
_ => {
|
|
||||||
cursor.next();
|
|
||||||
return Err(ParserError::at(
|
|
||||||
cursor.prev_pos(),
|
|
||||||
"unexpected end of statement; expected a ';' or '}'".to_string(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for Statement {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
Statement::Let(n, e) => {
|
|
||||||
write!(f, "let {n} = {e:?};")?;
|
|
||||||
}
|
|
||||||
Statement::Return(e) => {
|
|
||||||
write!(f, "return {e:?};")?;
|
|
||||||
}
|
|
||||||
Statement::Expr(e) => {
|
|
||||||
write!(f, "{e:?};")?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for Body {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
if self.statements.first().is_some() {
|
|
||||||
write!(f, "{{\n ")?;
|
|
||||||
let mut padder = Padder::new(f);
|
|
||||||
for s in &self.statements {
|
|
||||||
// they don't expose wrap_buf :grief:
|
|
||||||
writeln!(padder, "{s:?}")?;
|
|
||||||
}
|
|
||||||
write!(f, "}}")?;
|
|
||||||
} else {
|
|
||||||
write!(f, "{{}}")?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,135 +0,0 @@
|
|||||||
use std::{collections::HashSet, iter::Peekable, str::Chars};
|
|
||||||
|
|
||||||
use super::{error::ParserError, util::WHITESPACE_SET};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct FilePos {
|
|
||||||
pub line: usize,
|
|
||||||
pub col: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct FileRegion {
|
|
||||||
pub start: FilePos,
|
|
||||||
pub end: FilePos,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct CharCursor<'a> {
|
|
||||||
chars: Peekable<Chars<'a>>,
|
|
||||||
pos: FilePos,
|
|
||||||
prev_pos: FilePos,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CharCursor<'_> {
|
|
||||||
pub fn until(&mut self, set: &HashSet<char>) -> String {
|
|
||||||
let mut str = String::new();
|
|
||||||
loop {
|
|
||||||
let Some(next) = self.peek() else {
|
|
||||||
return str;
|
|
||||||
};
|
|
||||||
if set.contains(&next) {
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
str.push(next);
|
|
||||||
self.advance();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn skip_whitespace(&mut self) {
|
|
||||||
while self.peek().is_some_and(|c| c.is_whitespace()) {
|
|
||||||
self.advance();
|
|
||||||
}
|
|
||||||
let mut copy = self.chars.clone();
|
|
||||||
if let Some('/') = copy.next() {
|
|
||||||
if let Some('/') = copy.next() {
|
|
||||||
self.advance();
|
|
||||||
self.advance();
|
|
||||||
while self.next() != Some('\n') {}
|
|
||||||
self.skip_whitespace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn next(&mut self) -> Option<char> {
|
|
||||||
let res = self.peek()?;
|
|
||||||
self.advance();
|
|
||||||
Some(res)
|
|
||||||
}
|
|
||||||
pub fn peek(&mut self) -> Option<char> {
|
|
||||||
self.chars.peek().copied()
|
|
||||||
}
|
|
||||||
pub fn advance(&mut self) {
|
|
||||||
self.prev_pos = self.pos;
|
|
||||||
if self.peek().is_some_and(|c| c == '\n') {
|
|
||||||
self.pos.col = 0;
|
|
||||||
self.pos.line += 1;
|
|
||||||
} else {
|
|
||||||
self.pos.col += 1;
|
|
||||||
}
|
|
||||||
self.chars.next();
|
|
||||||
}
|
|
||||||
pub fn advance_if(&mut self, c: char) -> bool {
|
|
||||||
if let Some(c2) = self.peek() {
|
|
||||||
if c2 == c {
|
|
||||||
self.advance();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
pub fn advance_if_str(&mut self, exp: &str, end: &HashSet<char>) -> bool {
|
|
||||||
let mut new = self.chars.clone();
|
|
||||||
for e in exp.chars() {
|
|
||||||
let Some(c) = new.next() else {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
if e != c {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if new.peek().is_some_and(|c| !end.contains(c)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for _ in 0..exp.len() {
|
|
||||||
self.advance();
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
pub fn expect_char(&mut self, c: char) -> Result<(), ParserError> {
|
|
||||||
let next = self.expect_next()?;
|
|
||||||
if next == c {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(ParserError::at(
|
|
||||||
self.prev_pos,
|
|
||||||
format!("unexpected char '{next}'; expected '{c}'"),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn expect_next(&mut self) -> Result<char, ParserError> {
|
|
||||||
self.next().ok_or(ParserError::unexpected_end())
|
|
||||||
}
|
|
||||||
pub fn expect_peek(&mut self) -> Result<char, ParserError> {
|
|
||||||
self.peek().ok_or(ParserError::unexpected_end())
|
|
||||||
}
|
|
||||||
pub fn pos(&self) -> FilePos {
|
|
||||||
self.pos
|
|
||||||
}
|
|
||||||
pub fn prev_pos(&self) -> FilePos {
|
|
||||||
self.prev_pos
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<&'a str> for CharCursor<'a> {
|
|
||||||
fn from(value: &'a str) -> Self {
|
|
||||||
Self {
|
|
||||||
chars: value.chars().peekable(),
|
|
||||||
pos: FilePos::start(),
|
|
||||||
prev_pos: FilePos::start(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FilePos {
|
|
||||||
pub fn start() -> Self {
|
|
||||||
Self { line: 0, col: 0 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
use super::{FilePos, FileRegion};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct ParserError {
|
|
||||||
pub msg: String,
|
|
||||||
pub regions: Vec<FileRegion>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ParserError {
|
|
||||||
pub fn from_msg(msg: String) -> Self {
|
|
||||||
Self {
|
|
||||||
msg,
|
|
||||||
regions: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn at(pos: FilePos, msg: String) -> Self {
|
|
||||||
Self {
|
|
||||||
msg,
|
|
||||||
regions: vec![FileRegion {
|
|
||||||
start: pos,
|
|
||||||
end: pos,
|
|
||||||
}],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn unexpected_end() -> Self {
|
|
||||||
Self::from_msg("Unexpected end of input".to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const BEFORE: usize = 1;
|
|
||||||
const AFTER: usize = 1;
|
|
||||||
|
|
||||||
pub fn print_error(err: ParserError, file: &str) {
|
|
||||||
let after = if err.regions.is_empty() {""} else {":"};
|
|
||||||
println!("error: {}{}", err.msg, after);
|
|
||||||
for reg in err.regions {
|
|
||||||
print_region(file, reg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn print_region(file: &str, reg: FileRegion) {
|
|
||||||
let start = reg.start.line.saturating_sub(BEFORE);
|
|
||||||
let num_before = reg.start.line - start;
|
|
||||||
let mut lines = file.lines().skip(start);
|
|
||||||
let len = reg.end.col - reg.start.col + 1;
|
|
||||||
let width = format!("{}", reg.end.line + AFTER).len();
|
|
||||||
for i in 0..num_before + 1 {
|
|
||||||
println!("{:>width$} | {}", start + i, lines.next().unwrap());
|
|
||||||
}
|
|
||||||
println!(
|
|
||||||
"{} | {}",
|
|
||||||
" ".repeat(width),
|
|
||||||
" ".repeat(reg.start.col) + &"^".repeat(len)
|
|
||||||
);
|
|
||||||
for i in 0..AFTER {
|
|
||||||
if let Some(next) = lines.next() {
|
|
||||||
println!("{:>width$} | {}", reg.end.line + i + 1, next);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,247 +0,0 @@
|
|||||||
use super::{util::WHITESPACE_SET, Body, CharCursor, ParserError};
|
|
||||||
use std::{collections::HashSet, fmt::Debug, sync::LazyLock};
|
|
||||||
|
|
||||||
static SYMBOLS: LazyLock<HashSet<char>> = LazyLock::new(|| {
|
|
||||||
let mut set = HashSet::new();
|
|
||||||
for o in Operator::ALL {
|
|
||||||
for c in o.str().chars() {
|
|
||||||
set.insert(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
set
|
|
||||||
});
|
|
||||||
|
|
||||||
static IDENT_END: LazyLock<HashSet<char>> = LazyLock::new(|| {
|
|
||||||
let mut set = WHITESPACE_SET.clone();
|
|
||||||
let symbols = &SYMBOLS;
|
|
||||||
set.extend(symbols.iter().chain(&[';', '(', ')']));
|
|
||||||
set
|
|
||||||
});
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Val {
|
|
||||||
String(String),
|
|
||||||
Number(String),
|
|
||||||
Unit,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum Expr {
|
|
||||||
Block(Body),
|
|
||||||
Val(Val),
|
|
||||||
Ident(String),
|
|
||||||
BinaryOp(Operator, Box<Expr>, Box<Expr>),
|
|
||||||
Call(Box<Expr>, Vec<Expr>),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
pub enum Operator {
|
|
||||||
Add,
|
|
||||||
Sub,
|
|
||||||
Mul,
|
|
||||||
Div,
|
|
||||||
LessThan,
|
|
||||||
GreaterThan,
|
|
||||||
Offset,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Expr {
|
|
||||||
pub fn parse(cursor: &mut CharCursor) -> Result<Self, ParserError> {
|
|
||||||
cursor.skip_whitespace();
|
|
||||||
let Some(next) = cursor.peek() else {
|
|
||||||
return Ok(Self::Val(Val::Unit));
|
|
||||||
};
|
|
||||||
let mut e1 = match next {
|
|
||||||
'(' => {
|
|
||||||
cursor.advance();
|
|
||||||
let expr = Self::parse(cursor)?;
|
|
||||||
cursor.skip_whitespace();
|
|
||||||
cursor.expect_char(')')?;
|
|
||||||
expr
|
|
||||||
}
|
|
||||||
'{' => {
|
|
||||||
Self::Block(Body::parse(cursor)?)
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
if let Some(val) = Val::parse_nonunit(cursor)? {
|
|
||||||
Self::Val(val)
|
|
||||||
} else {
|
|
||||||
let name = cursor.until(&IDENT_END);
|
|
||||||
Self::Ident(name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
cursor.skip_whitespace();
|
|
||||||
let Some(mut next) = cursor.peek() else {
|
|
||||||
return Ok(e1);
|
|
||||||
};
|
|
||||||
while next == '(' {
|
|
||||||
cursor.advance();
|
|
||||||
let inner = Self::parse(cursor)?;
|
|
||||||
cursor.skip_whitespace();
|
|
||||||
cursor.expect_char(')')?;
|
|
||||||
e1 = Self::Call(Box::new(e1), vec![inner]);
|
|
||||||
let Some(next2) = cursor.peek() else {
|
|
||||||
return Ok(e1);
|
|
||||||
};
|
|
||||||
next = next2
|
|
||||||
}
|
|
||||||
if let Some(op) = Operator::parse(cursor) {
|
|
||||||
let e2 = Self::parse(cursor)?;
|
|
||||||
return Ok(if let Self::BinaryOp(op_next, e2, e3) = e2 {
|
|
||||||
if op.presedence() > op_next.presedence() {
|
|
||||||
Self::BinaryOp(op_next, Box::new(Self::BinaryOp(op, Box::new(e1), e2)), e3)
|
|
||||||
} else {
|
|
||||||
Self::BinaryOp(op, Box::new(e1), Box::new(Self::BinaryOp(op_next, e2, e3)))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Self::BinaryOp(op, Box::new(e1), Box::new(e2))
|
|
||||||
});
|
|
||||||
};
|
|
||||||
Ok(e1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Val {
|
|
||||||
pub fn parse_nonunit(cursor: &mut CharCursor) -> Result<Option<Self>, ParserError> {
|
|
||||||
let Some(next) = cursor.peek() else {
|
|
||||||
return Ok(None);
|
|
||||||
};
|
|
||||||
Ok(Some(match next {
|
|
||||||
'"' => {
|
|
||||||
cursor.advance();
|
|
||||||
let mut str = String::new();
|
|
||||||
loop {
|
|
||||||
let mut next = cursor.expect_next()?;
|
|
||||||
if next == '"' {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if next == '\\' {
|
|
||||||
next = match cursor.expect_next()? {
|
|
||||||
'"' => '"',
|
|
||||||
c => {
|
|
||||||
return Err(ParserError::at(
|
|
||||||
cursor.pos(),
|
|
||||||
format!("unexpected escape char '{c}'"),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
str.push(next);
|
|
||||||
}
|
|
||||||
Self::String(str)
|
|
||||||
}
|
|
||||||
'0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => {
|
|
||||||
let mut str = String::new();
|
|
||||||
loop {
|
|
||||||
let Some(next) = cursor.peek() else {
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
match next {
|
|
||||||
'0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => {
|
|
||||||
str.push(next);
|
|
||||||
}
|
|
||||||
_ => break,
|
|
||||||
}
|
|
||||||
cursor.advance();
|
|
||||||
}
|
|
||||||
Self::Number(str)
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Operator {
|
|
||||||
const ALL: [Self; 7] = [
|
|
||||||
Self::Add,
|
|
||||||
Self::Sub,
|
|
||||||
Self::Mul,
|
|
||||||
Self::Div,
|
|
||||||
Self::Offset,
|
|
||||||
Self::GreaterThan,
|
|
||||||
Self::LessThan,
|
|
||||||
];
|
|
||||||
pub fn presedence(&self) -> u32 {
|
|
||||||
match self {
|
|
||||||
Operator::LessThan => 0,
|
|
||||||
Operator::GreaterThan => 0,
|
|
||||||
Operator::Add => 1,
|
|
||||||
Operator::Sub => 2,
|
|
||||||
Operator::Mul => 3,
|
|
||||||
Operator::Div => 4,
|
|
||||||
Operator::Offset => 5,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn str(&self) -> &str {
|
|
||||||
match self {
|
|
||||||
Self::Add => "+",
|
|
||||||
Self::Sub => "-",
|
|
||||||
Self::Mul => "*",
|
|
||||||
Self::Div => "/",
|
|
||||||
Self::LessThan => "<",
|
|
||||||
Self::GreaterThan => ">",
|
|
||||||
Self::Offset => ".",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn parse(cursor: &mut CharCursor) -> Option<Self> {
|
|
||||||
let res = match cursor.peek()? {
|
|
||||||
'+' => Operator::Add,
|
|
||||||
'-' => Operator::Sub,
|
|
||||||
'*' => Operator::Mul,
|
|
||||||
'/' => Operator::Div,
|
|
||||||
'.' => Operator::Offset,
|
|
||||||
_ => return None,
|
|
||||||
};
|
|
||||||
for _ in 0..res.str().len() {
|
|
||||||
cursor.advance();
|
|
||||||
}
|
|
||||||
Some(res)
|
|
||||||
}
|
|
||||||
pub fn pad(&self) -> bool {
|
|
||||||
match self {
|
|
||||||
Operator::Add => true,
|
|
||||||
Operator::Sub => true,
|
|
||||||
Operator::Mul => true,
|
|
||||||
Operator::Div => true,
|
|
||||||
Operator::LessThan => true,
|
|
||||||
Operator::GreaterThan => true,
|
|
||||||
Operator::Offset => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for Expr {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
Expr::Block(b) => write!(f, "{:?}", b)?,
|
|
||||||
Expr::Ident(n) => f.write_str(n)?,
|
|
||||||
Expr::BinaryOp(op, e1, e2) => {
|
|
||||||
write!(f, "({:?}", *e1)?;
|
|
||||||
if op.pad() {
|
|
||||||
write!(f, " {} ", op.str())?;
|
|
||||||
} else {
|
|
||||||
write!(f, "{}", op.str())?;
|
|
||||||
}
|
|
||||||
write!(f, "{:?})", *e2)?;
|
|
||||||
}
|
|
||||||
Expr::Call(n, args) => {
|
|
||||||
n.fmt(f)?;
|
|
||||||
write!(f, "(")?;
|
|
||||||
if let Some(a) = args.first() {
|
|
||||||
a.fmt(f)?;
|
|
||||||
}
|
|
||||||
for arg in args.iter().skip(1) {
|
|
||||||
write!(f, ", ")?;
|
|
||||||
arg.fmt(f)?;
|
|
||||||
}
|
|
||||||
write!(f, ")")?;
|
|
||||||
}
|
|
||||||
Expr::Val(v) => {
|
|
||||||
write!(f, "{:?}", v)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
use std::io::{BufRead, BufReader};
|
|
||||||
|
|
||||||
mod body;
|
|
||||||
mod cursor;
|
|
||||||
mod error;
|
|
||||||
mod expr;
|
|
||||||
mod module;
|
|
||||||
mod util;
|
|
||||||
|
|
||||||
pub use body::*;
|
|
||||||
pub use cursor::*;
|
|
||||||
pub use error::*;
|
|
||||||
pub use expr::*;
|
|
||||||
pub use module::*;
|
|
||||||
|
|
||||||
pub fn parse_file(file: &str) {
|
|
||||||
match Module::parse(&mut CharCursor::from(file)) {
|
|
||||||
Err(err) => print_error(err, file),
|
|
||||||
Ok(module) => println!("{module:#?}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run_stdin() {
|
|
||||||
for line in BufReader::new(std::io::stdin()).lines() {
|
|
||||||
let str = &line.expect("failed to read line");
|
|
||||||
let mut cursor = CharCursor::from(&str[..]);
|
|
||||||
match Statement::parse(&mut cursor) {
|
|
||||||
Ok(expr) => println!("{:?}", expr),
|
|
||||||
Err(err) => print_error(err, str),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
use std::{collections::HashSet, fmt::Debug, sync::LazyLock};
|
|
||||||
use super::{util::WHITESPACE_SET, Body, CharCursor, ParserError};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Module {
|
|
||||||
functions: Vec<Function>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Function {
|
|
||||||
pub name: String,
|
|
||||||
pub body: Body,
|
|
||||||
}
|
|
||||||
|
|
||||||
static NAME_END: LazyLock<HashSet<char>> = LazyLock::new(|| {
|
|
||||||
let mut set = WHITESPACE_SET.clone();
|
|
||||||
set.extend(&['(']);
|
|
||||||
set
|
|
||||||
});
|
|
||||||
|
|
||||||
impl Module {
|
|
||||||
pub fn parse(cursor: &mut CharCursor) -> Result<Self, ParserError> {
|
|
||||||
let mut functions = Vec::new();
|
|
||||||
loop {
|
|
||||||
let next = cursor.until(&WHITESPACE_SET);
|
|
||||||
if next.is_empty() {
|
|
||||||
return Ok(Self { functions });
|
|
||||||
}
|
|
||||||
if next == "fn" {
|
|
||||||
functions.push(Function::parse(cursor)?);
|
|
||||||
} else {
|
|
||||||
return Err(ParserError::at(cursor.pos(), "expected fn".to_string()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Function {
|
|
||||||
pub fn parse(cursor: &mut CharCursor) -> Result<Self, ParserError> {
|
|
||||||
cursor.skip_whitespace();
|
|
||||||
let name = cursor.until(&NAME_END);
|
|
||||||
if name.is_empty() {
|
|
||||||
return Err(ParserError::at(cursor.pos(), "expected function name".to_string()));
|
|
||||||
}
|
|
||||||
cursor.expect_char('(')?;
|
|
||||||
cursor.expect_char(')')?;
|
|
||||||
let body = Body::parse(cursor)?;
|
|
||||||
Ok(Self { name, body })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for Function {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
f.write_str("fn ")?;
|
|
||||||
f.write_str(&self.name)?;
|
|
||||||
f.write_str("() ")?;
|
|
||||||
self.body.fmt(f)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
use std::{collections::HashSet, sync::LazyLock};
|
|
||||||
|
|
||||||
pub const WHITESPACE: [char; 25] = [
|
|
||||||
'\u{0009}', '\u{000A}', '\u{000B}', '\u{000C}', '\u{000D}', '\u{0020}', '\u{0085}', '\u{00A0}',
|
|
||||||
'\u{1680}', '\u{2000}', '\u{2001}', '\u{2002}', '\u{2003}', '\u{2004}', '\u{2005}', '\u{2006}',
|
|
||||||
'\u{2007}', '\u{2008}', '\u{2009}', '\u{200A}', '\u{2028}', '\u{2029}', '\u{202F}', '\u{205F}',
|
|
||||||
'\u{3000}',
|
|
||||||
];
|
|
||||||
|
|
||||||
pub static WHITESPACE_SET: LazyLock<HashSet<char>> = LazyLock::new(|| HashSet::from_iter(WHITESPACE));
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
use std::ops::{Deref, DerefMut};
|
|
||||||
|
|
||||||
use crate::common::FileID;
|
|
||||||
|
|
||||||
use super::{
|
|
||||||
CompilerMsg, CompilerOutput, Node, NodeParseResult, Parsable, ParsableWith, TokenCursor,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct ParserCtx<'a> {
|
|
||||||
pub cursor: TokenCursor<'a>,
|
|
||||||
pub output: &'a mut CompilerOutput,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Deref for ParserCtx<'a> {
|
|
||||||
type Target = TokenCursor<'a>;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.cursor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for ParserCtx<'_> {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
&mut self.cursor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ParserCtx<'a> {
|
|
||||||
pub fn err(&mut self, msg: CompilerMsg) {
|
|
||||||
self.output.err(msg);
|
|
||||||
}
|
|
||||||
pub fn hint(&mut self, msg: CompilerMsg) {
|
|
||||||
self.output.hint(msg);
|
|
||||||
}
|
|
||||||
pub fn parse<T: Parsable>(&mut self) -> NodeParseResult<T> {
|
|
||||||
Node::parse(self)
|
|
||||||
}
|
|
||||||
pub fn maybe_parse<T>(&mut self) -> Option<NodeParseResult<T>>
|
|
||||||
where
|
|
||||||
Option<T>: Parsable,
|
|
||||||
{
|
|
||||||
Node::maybe_parse(self)
|
|
||||||
}
|
|
||||||
pub fn parse_with<T: ParsableWith>(&mut self, data: T::Data) -> NodeParseResult<T> {
|
|
||||||
Node::parse_with(self, data)
|
|
||||||
}
|
|
||||||
pub fn new(file: FileID, string: &'a str, output: &'a mut CompilerOutput) -> Self {
|
|
||||||
Self {
|
|
||||||
cursor: TokenCursor::from_file_str(file, string),
|
|
||||||
output,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
use crate::common::FileID;
|
|
||||||
|
|
||||||
use super::{
|
|
||||||
token::{CharCursor, Keyword, Symbol, Token, TokenInstance},
|
|
||||||
CompilerMsg, FilePos,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct TokenCursor<'a> {
|
|
||||||
cursor: CharCursor<'a>,
|
|
||||||
next: Option<TokenInstance>,
|
|
||||||
next_start: FilePos,
|
|
||||||
prev_end: FilePos,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> TokenCursor<'a> {
|
|
||||||
pub fn next(&mut self) -> Option<TokenInstance> {
|
|
||||||
self.prev_end = self.cursor.prev_pos();
|
|
||||||
let next = TokenInstance::parse(&mut self.cursor);
|
|
||||||
self.next_start = next
|
|
||||||
.as_ref()
|
|
||||||
.map(|i| i.span.end)
|
|
||||||
.unwrap_or(FilePos::start(self.file()));
|
|
||||||
std::mem::replace(&mut self.next, next)
|
|
||||||
}
|
|
||||||
pub fn expect_next(&mut self) -> Result<TokenInstance, CompilerMsg> {
|
|
||||||
self.peek().ok_or(CompilerMsg::unexpected_end())?;
|
|
||||||
Ok(self.next().unwrap())
|
|
||||||
}
|
|
||||||
pub fn expect_token(&mut self, t: Token) -> Result<(), CompilerMsg> {
|
|
||||||
let next = self.expect_next()?;
|
|
||||||
if t == next.token {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(CompilerMsg::unexpected_token(&next, &format!("{t:?}")))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn expect_sym(&mut self, symbol: Symbol) -> Result<(), CompilerMsg> {
|
|
||||||
self.expect_token(Token::Symbol(symbol))
|
|
||||||
}
|
|
||||||
pub fn next_on_new_line(&mut self) -> bool {
|
|
||||||
self.next_start.line != self.prev_end.line
|
|
||||||
}
|
|
||||||
pub fn seek_sym(&mut self, sym: Symbol) {
|
|
||||||
while self.next().is_some_and(|n| !n.is_symbol(sym)) {}
|
|
||||||
}
|
|
||||||
pub fn seek_syms(&mut self, syms: &[Symbol]) {
|
|
||||||
while self
|
|
||||||
.peek()
|
|
||||||
.is_some_and(|n| !syms.iter().any(|s| n.is_symbol(*s)))
|
|
||||||
{
|
|
||||||
self.next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn seek_sym_on_line(&mut self, sym: Symbol) {
|
|
||||||
while !self.next_on_new_line() && self.next().is_some_and(|n| !n.is_symbol(sym)) {}
|
|
||||||
}
|
|
||||||
pub fn seek(&mut self, f: impl Fn(&TokenInstance) -> bool) -> Option<&TokenInstance> {
|
|
||||||
loop {
|
|
||||||
if f(self.peek()?) {
|
|
||||||
return self.peek();
|
|
||||||
}
|
|
||||||
self.next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn expect_kw(&mut self, kw: Keyword) -> Result<(), CompilerMsg> {
|
|
||||||
self.expect_token(Token::Keyword(kw))
|
|
||||||
}
|
|
||||||
pub fn peek(&self) -> Option<&TokenInstance> {
|
|
||||||
self.next.as_ref()
|
|
||||||
}
|
|
||||||
pub fn expect_peek(&mut self) -> Result<&TokenInstance, CompilerMsg> {
|
|
||||||
self.peek().ok_or(CompilerMsg::unexpected_end())
|
|
||||||
}
|
|
||||||
pub fn chars(&mut self) -> &mut CharCursor<'a> {
|
|
||||||
&mut self.cursor
|
|
||||||
}
|
|
||||||
pub fn prev_end(&self) -> FilePos {
|
|
||||||
self.prev_end
|
|
||||||
}
|
|
||||||
pub fn next_start(&self) -> FilePos {
|
|
||||||
self.next_start
|
|
||||||
}
|
|
||||||
pub fn from_file_str(id: FileID, string: &'a str) -> Self {
|
|
||||||
Self::from(CharCursor::from_file_str(id, string))
|
|
||||||
}
|
|
||||||
pub fn file(&self) -> FileID {
|
|
||||||
self.cursor.file()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<CharCursor<'a>> for TokenCursor<'a> {
|
|
||||||
fn from(mut cursor: CharCursor<'a>) -> Self {
|
|
||||||
let cur = TokenInstance::parse(&mut cursor);
|
|
||||||
Self {
|
|
||||||
next_start: FilePos::start(cursor.file()),
|
|
||||||
prev_end: FilePos::start(cursor.file()),
|
|
||||||
cursor,
|
|
||||||
next: cur,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
use super::Node;
|
|
||||||
use super::PIdent;
|
|
||||||
use super::CompilerMsg;
|
|
||||||
use super::TokenInstance;
|
|
||||||
|
|
||||||
impl CompilerMsg {
|
|
||||||
pub fn from_instances(instances: &[&TokenInstance], msg: String) -> Self {
|
|
||||||
CompilerMsg {
|
|
||||||
msg,
|
|
||||||
spans: instances.iter().map(|i| i.span).collect(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn unexpected_end() -> Self {
|
|
||||||
Self::from_msg("unexpected end of input".to_string())
|
|
||||||
}
|
|
||||||
pub fn identifier_not_found(id: &Node<PIdent>) -> Self {
|
|
||||||
Self {
|
|
||||||
msg: format!("Identifier '{}' not found", id.as_ref().unwrap()),
|
|
||||||
spans: vec![id.origin],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn unexpected_token(inst: &TokenInstance, expected: &str) -> Self {
|
|
||||||
let t = &inst.token;
|
|
||||||
CompilerMsg::from_instances(
|
|
||||||
&[inst],
|
|
||||||
format!("unexpected token {t:?}; expected {expected}"),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
use std::collections::HashSet;
|
|
||||||
|
|
||||||
#[derive(Clone, Eq, PartialEq, Hash)]
|
|
||||||
pub struct Import(pub Vec<String>);
|
|
||||||
pub type Imports = HashSet<Import>;
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
pub mod riscv64;
|
|
||||||
pub use super::*;
|
|
||||||
@@ -1,216 +0,0 @@
|
|||||||
use super::{FnLowerCtx, Node, PAsmArg, PIdent, PInstruction};
|
|
||||||
use crate::{
|
|
||||||
compiler::arch::riscv::*,
|
|
||||||
ir::{
|
|
||||||
arch::riscv64::{RV64Instruction, RegRef},
|
|
||||||
UIdent,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
impl RV64Instruction {
|
|
||||||
pub fn parse(inst: &PInstruction, ctx: &mut FnLowerCtx) -> Option<Self> {
|
|
||||||
let args = &inst.args[..];
|
|
||||||
let opstr = &**inst.op.inner.as_ref()?;
|
|
||||||
// TODO: surely this can be abstracted...
|
|
||||||
let opi = |ctx: &mut FnLowerCtx<'_, '_, '_>, op: Funct3| -> Option<Self> {
|
|
||||||
let [dest, src, imm] = args else {
|
|
||||||
ctx.err(format!("{opstr} requires 3 arguments"));
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
let dest = RegRef::from_arg(dest, ctx)?;
|
|
||||||
let src = RegRef::from_arg(src, ctx)?;
|
|
||||||
let imm = i32_from_arg(imm, ctx)?;
|
|
||||||
Some(Self::OpImm { op, dest, src, imm })
|
|
||||||
};
|
|
||||||
let op = |ctx: &mut FnLowerCtx<'_, '_, '_>, op: Funct3, funct: Funct7| -> Option<Self> {
|
|
||||||
let [dest, src1, src2] = args else {
|
|
||||||
ctx.err(format!("{opstr} requires 3 arguments"));
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
let dest = RegRef::from_arg(dest, ctx)?;
|
|
||||||
let src1 = RegRef::from_arg(src1, ctx)?;
|
|
||||||
let src2 = RegRef::from_arg(src2, ctx)?;
|
|
||||||
Some(Self::Op {
|
|
||||||
op,
|
|
||||||
funct,
|
|
||||||
dest,
|
|
||||||
src1,
|
|
||||||
src2,
|
|
||||||
})
|
|
||||||
};
|
|
||||||
let opif7 = |ctx: &mut FnLowerCtx<'_>, op: Funct3, funct: Funct7| -> Option<Self> {
|
|
||||||
let [dest, src, imm] = args else {
|
|
||||||
ctx.err(format!("{opstr} requires 3 arguments"));
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
let dest = RegRef::from_arg(dest, ctx)?;
|
|
||||||
let src = RegRef::from_arg(src, ctx)?;
|
|
||||||
let imm = i32_from_arg(imm, ctx)?;
|
|
||||||
Some(Self::OpImmF7 {
|
|
||||||
op,
|
|
||||||
funct,
|
|
||||||
dest,
|
|
||||||
src,
|
|
||||||
imm,
|
|
||||||
})
|
|
||||||
};
|
|
||||||
let store = |ctx: &mut FnLowerCtx<'_>, width: Funct3| -> Option<Self> {
|
|
||||||
let [src, offset, base] = args else {
|
|
||||||
ctx.err(format!("{opstr} requires 3 arguments"));
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
let src = RegRef::from_arg(src, ctx)?;
|
|
||||||
let offset = i32_from_arg(offset, ctx)?;
|
|
||||||
let base = RegRef::from_arg(base, ctx)?;
|
|
||||||
Some(Self::Store {
|
|
||||||
width,
|
|
||||||
src,
|
|
||||||
offset,
|
|
||||||
base,
|
|
||||||
})
|
|
||||||
};
|
|
||||||
let load = |ctx: &mut FnLowerCtx<'_>, width: Funct3| -> Option<Self> {
|
|
||||||
let [dest, offset, base] = args else {
|
|
||||||
ctx.err(format!("{opstr} requires 3 arguments"));
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
let dest = RegRef::from_arg(dest, ctx)?;
|
|
||||||
let offset = i32_from_arg(offset, ctx)?;
|
|
||||||
let base = RegRef::from_arg(base, ctx)?;
|
|
||||||
Some(Self::Load {
|
|
||||||
width,
|
|
||||||
dest,
|
|
||||||
offset,
|
|
||||||
base,
|
|
||||||
})
|
|
||||||
};
|
|
||||||
Some(match opstr {
|
|
||||||
"ecall" => Self::ECall,
|
|
||||||
"li" => {
|
|
||||||
let [dest, imm] = args else {
|
|
||||||
ctx.err("li requires 2 arguments".to_string());
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
let dest = RegRef::from_arg(dest, ctx)?;
|
|
||||||
let imm = i32_from_arg(imm, ctx)?;
|
|
||||||
Self::Li { dest, imm }
|
|
||||||
}
|
|
||||||
"la" => {
|
|
||||||
let [dest, src] = args else {
|
|
||||||
ctx.err("la requires 2 arguments".to_string());
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
let dest = RegRef::from_arg(dest, ctx)?;
|
|
||||||
let src = arg_to_var(src, ctx)?;
|
|
||||||
Self::La { dest, src }
|
|
||||||
}
|
|
||||||
"mv" => {
|
|
||||||
let [dest, src] = args else {
|
|
||||||
ctx.err("la requires 2 arguments".to_string());
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
let dest = RegRef::from_arg(dest, ctx)?;
|
|
||||||
let src = RegRef::from_arg(src, ctx)?;
|
|
||||||
Self::Mv { dest, src }
|
|
||||||
}
|
|
||||||
|
|
||||||
"lb" => load(ctx, width::B)?,
|
|
||||||
"lh" => load(ctx, width::H)?,
|
|
||||||
"lw" => load(ctx, width::W)?,
|
|
||||||
"ld" => load(ctx, width::D)?,
|
|
||||||
"lbu" => load(ctx, width::BU)?,
|
|
||||||
"lhu" => load(ctx, width::HU)?,
|
|
||||||
"lwu" => load(ctx, width::WU)?,
|
|
||||||
|
|
||||||
"sb" => store(ctx, width::B)?,
|
|
||||||
"sh" => store(ctx, width::H)?,
|
|
||||||
"sw" => store(ctx, width::W)?,
|
|
||||||
"sd" => store(ctx, width::D)?,
|
|
||||||
|
|
||||||
"addi" => opi(ctx, op32i::ADD)?,
|
|
||||||
"slti" => opi(ctx, op32i::SLT)?,
|
|
||||||
"sltiu" => opi(ctx, op32i::SLTU)?,
|
|
||||||
"xori" => opi(ctx, op32i::XOR)?,
|
|
||||||
"ori" => opi(ctx, op32i::OR)?,
|
|
||||||
"andi" => opi(ctx, op32i::AND)?,
|
|
||||||
|
|
||||||
"slli" => opif7(ctx, op32i::SL, op32i::LOGICAL)?,
|
|
||||||
"srli" => opif7(ctx, op32i::SR, op32i::LOGICAL)?,
|
|
||||||
"srla" => opif7(ctx, op32i::SR, op32i::ARITHMETIC)?,
|
|
||||||
|
|
||||||
"add" => op(ctx, op32i::ADD, op32i::F7ADD)?,
|
|
||||||
"sub" => op(ctx, op32i::ADD, op32i::F7SUB)?,
|
|
||||||
"sll" => op(ctx, op32i::SL, op32i::FUNCT7)?,
|
|
||||||
"slt" => op(ctx, op32i::SLT, op32i::FUNCT7)?,
|
|
||||||
"sltu" => op(ctx, op32i::SLTU, op32i::FUNCT7)?,
|
|
||||||
"xor" => op(ctx, op32i::XOR, op32i::FUNCT7)?,
|
|
||||||
"srl" => op(ctx, op32i::SR, op32i::LOGICAL)?,
|
|
||||||
"sra" => op(ctx, op32i::SR, op32i::ARITHMETIC)?,
|
|
||||||
"or" => op(ctx, op32i::OR, op32i::FUNCT7)?,
|
|
||||||
"and" => op(ctx, op32i::AND, op32i::FUNCT7)?,
|
|
||||||
|
|
||||||
"mul" => op(ctx, op32m::MUL, op32m::FUNCT7)?,
|
|
||||||
"mulh" => op(ctx, op32m::MULH, op32m::FUNCT7)?,
|
|
||||||
"mulhsu" => op(ctx, op32m::MULHSU, op32m::FUNCT7)?,
|
|
||||||
"mulhu" => op(ctx, op32m::MULHU, op32m::FUNCT7)?,
|
|
||||||
"div" => op(ctx, op32m::DIV, op32m::FUNCT7)?,
|
|
||||||
"divu" => op(ctx, op32m::DIVU, op32m::FUNCT7)?,
|
|
||||||
"rem" => op(ctx, op32m::REM, op32m::FUNCT7)?,
|
|
||||||
"remu" => op(ctx, op32m::REMU, op32m::FUNCT7)?,
|
|
||||||
|
|
||||||
w => {
|
|
||||||
ctx.err_at(inst.op.origin, format!("Unknown instruction '{}'", w));
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn arg_to_var(node: &Node<PAsmArg>, ctx: &mut FnLowerCtx) -> Option<UIdent> {
|
|
||||||
let PAsmArg::Ref(node) = node.inner.as_ref()? else {
|
|
||||||
ctx.err_at(
|
|
||||||
node.origin,
|
|
||||||
"Expected variable / function reference".to_string(),
|
|
||||||
);
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
ctx.ident(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RegRef {
|
|
||||||
pub fn from_arg(node: &Node<PAsmArg>, ctx: &mut FnLowerCtx) -> Option<Self> {
|
|
||||||
Some(match node.inner.as_ref()? {
|
|
||||||
PAsmArg::Value(node) => {
|
|
||||||
let reg = Reg::from_ident(node, ctx)?;
|
|
||||||
Self::Reg(reg)
|
|
||||||
}
|
|
||||||
PAsmArg::Ref(node) => Self::Var(ctx.ident(node)?),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Reg {
|
|
||||||
pub fn from_ident(node: &Node<PIdent>, ctx: &mut FnLowerCtx) -> Option<Self> {
|
|
||||||
let s = &**node.inner.as_ref()?;
|
|
||||||
let res = Reg::from_str(s);
|
|
||||||
if res.is_none() {
|
|
||||||
ctx.err_at(node.origin, format!("Unknown reg name '{}'", s));
|
|
||||||
}
|
|
||||||
res
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn i32_from_arg(node: &Node<PAsmArg>, ctx: &mut FnLowerCtx) -> Option<i32> {
|
|
||||||
let PAsmArg::Value(node) = node.inner.as_ref()? else {
|
|
||||||
ctx.err_at(node.origin, "Expected an i32, found reference".to_string());
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
let word = node.inner.as_ref()?;
|
|
||||||
match word.parse::<i32>() {
|
|
||||||
Ok(x) => Some(x),
|
|
||||||
Err(_) => {
|
|
||||||
ctx.err_at(node.origin, format!("Expected an i64, found {}", word));
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user