11 Commits

Author SHA1 Message Date
iris d864adfd05 work 2026-04-18 00:16:03 -04:00
iris b3f77076d4 work 2026-04-17 18:51:12 -04:00
iris 2f91e454dd stuff 2026-04-17 01:49:43 -04:00
iris e5ae506a84 work 2026-04-17 00:09:00 -04:00
iris 83edad0cd8 lol 2026-04-12 17:38:35 -04:00
iris f702f47714 gaming 2026-04-12 17:26:39 -04:00
iris 2582e8c87e work 2026-04-11 15:21:03 -04:00
iris 229b026573 work 2026-04-11 03:50:43 -04:00
iris 29316e6353 work 2026-04-10 16:13:45 -04:00
iris bdf08ce52c stuff 2026-04-08 23:28:50 -04:00
iris edabc22431 parser3 2026-04-08 17:54:42 -04:00
141 changed files with 1252 additions and 10858 deletions
+3 -1
View File
@@ -1,4 +1,6 @@
[package]
name = "lang"
version = "0.1.0"
edition = "2021"
edition = "2024"
[dependencies]
-22
View File
@@ -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
-35
View File
@@ -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
View File
@@ -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"
}
-6
View File
@@ -1,6 +0,0 @@
import util;
util.println("hello!");
let x = 39;
util.exit(x);
-146
View File
@@ -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
}
}
-51
View File
@@ -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)
-117
View File
@@ -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(())
}
}
-5
View File
@@ -1,5 +0,0 @@
mod output;
mod file;
pub use output::*;
pub use file::*;
-73
View 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
View File
@@ -1 +0,0 @@
pub mod riscv;
-357
View File
@@ -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"),
}
}
}
-224
View File
@@ -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(),
}
}
-94
View File
@@ -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,
)
}
-10
View File
@@ -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::*;
-122
View File
@@ -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)
}
-16
View File
@@ -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);
}
-27
View File
@@ -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",
}
}
-11
View File
@@ -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::*;
-180
View File
@@ -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);
-23
View File
@@ -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()
}
}
-111
View File
@@ -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"))
}
}
-3
View File
@@ -1,3 +0,0 @@
pub enum Instruction {
}
-14
View File
@@ -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)
}
-138
View File
@@ -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(())
}
}
-7
View File
@@ -1,7 +0,0 @@
pub trait Target {
type Reg;
}
pub trait RegType {
type Size;
}
+121
View File
@@ -0,0 +1,121 @@
#[derive(Debug, Clone, Copy)]
pub struct Span {
pub file: usize,
pub start: usize,
pub end: usize,
}
pub struct Spanned<T> {
pub inner: T,
pub span: Span,
}
impl<T> std::ops::Deref for Spanned<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<T> std::ops::DerefMut for Spanned<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
pub struct CompilerMsg {
pub spans: Vec<Span>,
pub msg: String,
}
#[derive(Default)]
pub struct CompilerOutput {
pub errors: Vec<CompilerMsg>,
pub files: Vec<String>,
}
impl CompilerOutput {
pub fn new() -> Self {
Self::default()
}
pub fn error(&mut self, msg: impl Into<CompilerMsg>) {
self.errors.push(msg.into());
}
pub fn write(&self, w: &mut impl std::io::Write) {
let files: Vec<_> = self
.files
.iter()
.map(|path| std::fs::read_to_string(path).unwrap())
.collect();
for error in &self.errors {
writeln!(w, "Error: {}", error.msg).unwrap();
for span in &error.spans {
span.write(w, &files[span.file]).unwrap();
}
}
}
}
impl Span {
pub fn write(&self, w: &mut impl std::io::Write, text: &str) -> std::io::Result<()> {
let mut line_start = 0;
let mut found = false;
let mut line = 1;
let mut spans = Vec::new();
for (i, c) in text.char_indices() {
if i == self.start {
found = true;
}
if i == self.end {
found = true;
}
if c == '\n' {
if found {
spans.push((line, line_start..i));
}
line_start = i + 1;
line += 1;
found = false;
}
}
let underline = "\x1b[4:3m";
let underline_color = "\x1b[58;5;1m";
let end = "\x1b[0m";
if let [(line, range)] = &spans[..] {
writeln!(
w,
" {line:3} | {}{underline}{underline_color}{}{end}{}",
&text[range.start..self.start],
&text[self.start..=self.end],
&text[(self.end + 1)..range.end]
)?;
} else if let [(sline, srange), (eline, erange)] = &spans[..] {
writeln!(
w,
" {sline:3} | {}{underline}{underline_color}{}{end}",
&text[srange.start..self.start],
&text[self.start..=srange.end - 1],
)?;
if *eline != *sline + 1 {
writeln!(w, " ...")?;
}
writeln!(
w,
" {eline:3} | {underline}{underline_color}{}{end}{}",
&text[erange.start..=self.end],
&text[(self.end + 1)..=erange.end - 1],
)?;
}
Ok(())
}
}
impl From<String> for CompilerMsg {
fn from(value: String) -> Self {
Self {
spans: Vec::new(),
msg: value.to_string(),
}
}
}
-1
View File
@@ -1 +0,0 @@
pub mod riscv64;
-20
View File
@@ -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),
}
}
}
-7
View File
@@ -1,7 +0,0 @@
use super::{arch::riscv64::RegRef, IdentID};
#[derive(Clone)]
pub struct IRAsmInstruction {
op: String,
args: Vec<RegRef<IdentID, String>>,
}
+37 -119
View File
@@ -1,136 +1,54 @@
use std::{
fmt::Debug,
marker::PhantomData,
ops::{Index, IndexMut},
use std::ops::{Index, IndexMut};
pub struct Id<T> {
idx: usize,
_pd: std::marker::PhantomData<T>,
}
pub struct IdVec<T> {
vec: Vec<T>,
}
impl<T> IdVec<T> {
pub fn add(&mut self, val: T) -> Id<T> {
let id = Id {
idx: self.vec.len(),
_pd: Default::default(),
};
// 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)
self.vec.push(val);
id
}
}
impl<T> From<usize> for ID<T> {
fn from(value: usize) -> Self {
Self(value, PhantomData)
impl<T> Index<Id<T>> for IdVec<T> {
type Output = T;
fn index(&self, index: Id<T>) -> &Self::Output {
&self.vec[index.idx]
}
}
impl<T> Debug for ID<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{{{}}}", self.0)
impl<T> IndexMut<Id<T>> for IdVec<T> {
fn index_mut(&mut self, index: Id<T>) -> &mut Self::Output {
&mut self.vec[index.idx]
}
}
impl<T> PartialEq for ID<T> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
impl<T> Default for IdVec<T> {
fn default() -> Self {
Self {
vec: Default::default(),
}
}
}
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> {
impl<T> Clone for Id<T> {
fn clone(&self) -> Self {
Self(self.0.clone(), PhantomData)
Self {
idx: self.idx.clone(),
_pd: self._pd.clone(),
}
}
}
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]
}
}
impl<T> Copy for Id<T> {}
-66
View File
@@ -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 { .. })
}
}
-10
View File
@@ -1,10 +0,0 @@
mod func;
mod program;
mod symbol;
mod res;
pub use func::*;
pub use program::*;
pub use symbol::*;
use super::*;
-465
View File
@@ -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
}
}
-92
View File
@@ -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),
})
})
}
}
-153
View File
@@ -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
}
}
+20 -13
View File
@@ -1,16 +1,23 @@
//! 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::*;
mod structs;
pub use id::*;
pub use structs::*;
pub struct Ir {
pub root: Id<Namespace>,
pub namespaces: IdVec<Namespace>,
}
impl Ir {
pub fn root(&mut self) -> &mut Namespace {
&mut self.namespaces[self.root]
}
}
impl Default for Ir {
fn default() -> Self {
let mut namespaces = IdVec::default();
let root = namespaces.add(Namespace::default());
Self { root, namespaces }
}
}
+4
View File
@@ -0,0 +1,4 @@
mod namespace;
pub use namespace::*;
use super::Id;
+17
View File
@@ -0,0 +1,17 @@
use super::*;
use crate::parser::Ident;
use std::collections::HashMap;
#[derive(Default)]
pub struct Namespace {
pub items: HashMap<Ident, Item>,
}
pub enum Item {
Import(Id<Namespace>),
}
// issue: if I try to parse a function body, I'll want to have clear statements such as
// "call trait fn func on x" or "call field func of x", but you (often) can't tell until typed
// x.func
// x'func
-140
View File
@@ -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,
}
}
}
-94
View File
@@ -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,
}
-161
View File
@@ -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",
})
}
}
-17
View File
@@ -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::*;
-178
View File
@@ -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
}
}
-213
View File
@@ -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)
)
}
-195
View File
@@ -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(())
}
-115
View File
@@ -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
}
-182
View File
@@ -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,
});
}
}
}
}
-146
View File
@@ -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),
}
}
}
-293
View File
@@ -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
}
}
-101
View File
@@ -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),
}
}
+14 -168
View File
@@ -1,178 +1,24 @@
#![feature(box_patterns)]
#![feature(try_trait_v2)]
#![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,
use crate::{
io::CompilerOutput,
parser::{Node, parse_file},
parser_ir::parse_program,
};
mod common;
mod compiler;
mod io;
mod ir;
mod parser;
mod util;
mod parser_ir;
fn main() {
let file = std::env::args_os().nth(1);
// 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());
let mut args = std::env::args();
let Some(path) = args.nth(1) else {
println!("file expected");
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");
let mut output = CompilerOutput::new();
let root = parse_file(&path, &mut output);
if let Some(root) = root {
print!("{}", root.new_dsp());
}
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);
// }
output.write(&mut std::io::stdout());
}
+38
View File
@@ -0,0 +1,38 @@
use super::Token;
use crate::io::Span;
pub struct Lit {
pub ty: LitTy,
pub span: Span,
}
#[derive(PartialEq)]
pub enum LitTy {
Number(String),
Bool(bool),
String(String),
Unit,
}
impl From<LitTy> for Token {
fn from(value: LitTy) -> Self {
Self::Lit(value)
}
}
impl std::fmt::Display for LitTy {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Number(n) => write!(f, "{n}"),
Self::Bool(b) => write!(f, "{b}"),
Self::String(s) => write!(f, "\"{s}\""),
Self::Unit => write!(f, "()"),
}
}
}
impl std::fmt::Display for Lit {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.ty.fmt(f)
}
}
+102
View File
@@ -0,0 +1,102 @@
use std::borrow::Borrow;
use crate::io::{CompilerMsg, Span, Spanned};
mod lit;
mod token;
pub use lit::*;
pub use token::*;
pub struct Cursor<'a> {
pub span: Span,
next: Option<TokenInst>,
tokens: Tokens<'a>,
}
impl<'a> Cursor<'a> {
pub fn new(text: &'a str, file: usize) -> Self {
let mut s = Self {
span: Span {
start: 0,
end: 0,
file,
},
next: None,
tokens: Tokens::new(text, file),
};
s.next();
s
}
pub fn next(&mut self) -> Option<Token> {
let mut next = self.tokens.next();
std::mem::swap(&mut self.next, &mut next);
next.map(|inst| {
self.span = inst.span;
inst.inner
})
}
pub fn next_if(&mut self, token: impl Borrow<Token>) -> bool {
if self.peek().is_some_and(|t| t == token.borrow()) {
self.next();
true
} else {
false
}
}
pub fn peek(&self) -> Option<&Token> {
self.next.as_ref().map(|i| &i.inner)
}
pub fn expect_next(&mut self) -> Result<Token, CompilerMsg> {
self.next().ok_or_else(CompilerMsg::unexpected_eof)
}
pub fn expect_peek(&self) -> Result<&Token, CompilerMsg> {
self.peek().ok_or_else(CompilerMsg::unexpected_eof)
}
pub fn expect(&mut self, token: impl Borrow<Token>) -> Result<Token, CompilerMsg> {
let token = token.borrow();
let next = self.expect_next()?;
if next == *token {
Ok(next)
} else {
self.unexpected(&next, &format!("'{token}'"))
}
}
pub fn unexpected<T>(&self, token: &Token, expected: &str) -> Result<T, CompilerMsg> {
Err(CompilerMsg::unexpected_token(token, self.span, expected))
}
pub fn peek_start(&mut self) -> usize {
self.next.as_ref().map(|i| i.span.start).unwrap_or(0)
}
pub fn cur_end(&mut self) -> usize {
self.span.end
}
pub fn file(&mut self) -> usize {
self.span.file
}
}
impl CompilerMsg {
pub fn unexpected_token(token: &Token, span: Span, expected: &str) -> Self {
Self {
spans: vec![span],
msg: format!("Unexpected token '{}', expected {expected}", token),
}
}
pub fn unexpected_eof() -> Self {
Self {
spans: Vec::new(),
msg: "unexpected end of file".to_string(),
}
}
}
+200
View File
@@ -0,0 +1,200 @@
use crate::parser::cursor::LitTy;
use super::{Span, Spanned};
use std::{iter::Peekable, str::CharIndices};
def_tokens! {
symbol {
Dot: ".",
Comma: ",",
Equal: "=",
Colon: ":",
Semicolon: ";",
Plus: "+",
Dash: "-",
Asterisk: "*",
Slash: "/",
OpenParen: "(",
CloseParen: ")",
OpenSquare: "[",
CloseSquare: "]",
OpenCurly: "{",
CloseCurly: "}",
Arrow: "->",
DoubleArrow: "=>",
PlusEqual: "+=",
DashEqual: "-=",
AsteriskEqual: "*=",
SlashEqual: "/=",
DoubleColon: "::",
Hash: "#",
}
keyword {
Let: "let",
Import: "import",
Fn: "fn",
If: "if",
Loop: "loop",
While: "while",
For: "for",
Match: "match",
}
other {
Ident(String),
Lit(LitTy),
}
}
pub type TokenInst = Spanned<Token>;
pub struct Tokens<'a> {
file: usize,
chars: Peekable<CharIndices<'a>>,
}
impl<'a> Tokens<'a> {
pub fn new(code: &'a str, file: usize) -> Self {
Self {
file,
chars: code.char_indices().peekable(),
}
}
}
impl Iterator for Tokens<'_> {
type Item = Spanned<Token>;
fn next(&mut self) -> Option<Self::Item> {
let (i, c) = self.chars.next()?;
let mut span = Span {
start: i,
end: i,
file: self.file,
};
if c.is_whitespace() {
return self.next();
}
macro_rules! then {
(_ => $def:expr, $($char:expr => $to:expr,)*) => {
match self.chars.peek() {
$(Some((_, $char)) => {
self.chars.next();
$to
},)*
_ => $def,
}
};
}
let inner = match c {
'.' => Token::Dot,
',' => Token::Comma,
'(' => Token::OpenParen,
')' => Token::CloseParen,
'[' => Token::OpenSquare,
']' => Token::CloseSquare,
'{' => Token::OpenCurly,
'}' => Token::CloseCurly,
'#' => Token::Hash,
'+' => then! {
_ => Token::Plus,
'=' => Token::PlusEqual,
},
'-' => then! {
_ => Token::Dash,
'=' => Token::DashEqual,
'>' => Token::Arrow,
},
'*' => then! {
_ => Token::Asterisk,
'=' => Token::AsteriskEqual,
},
'/' => then! {
_ => Token::Slash,
'=' => Token::SlashEqual,
},
':' => then! {
_ => Token::Colon,
':' => Token::DoubleColon,
},
';' => Token::Semicolon,
'=' => then! {
_ => Token::Equal,
'>' => Token::DoubleArrow,
},
'0'..='9' => {
let mut s = c.to_string();
while let Some((i, c)) = self.chars.peek()
&& c.is_alphanumeric()
{
s.push(*c);
span.end = *i;
self.chars.next();
}
LitTy::Number(s).into()
}
'"' => {
let mut s = String::new();
while let Some((i, c)) = self.chars.next()
&& !matches!(c, '"')
{
s.push(c);
span.end = i;
}
LitTy::String(s).into()
}
_ => {
let mut s = c.to_string();
while let Some((i, c)) = self.chars.peek()
&& c.is_alphanumeric()
{
s.push(*c);
span.end = *i;
self.chars.next();
}
match s.as_str() {
"true" => LitTy::Bool(true).into(),
"false" => LitTy::Bool(false).into(),
_ => from_str(s),
}
}
};
Some(Spanned { inner, span })
}
}
macro_rules! def_tokens {
{
symbol {
$($sym_name:ident: $sym_str:expr,)*
}
keyword {
$($kw_name:ident: $kw_str:expr,)*
}
other {
$($other_name:ident($data:ty),)*
}
} => {
#[derive(PartialEq)]
pub enum Token {
$($sym_name,)*
$($kw_name,)*
$($other_name($data),)*
}
fn from_str(s: String) -> Token {
match s.as_str() {
$($kw_str => Token::$kw_name,)*
_ => Token::Ident(s),
}
}
impl std::fmt::Display for Token {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
$(Token::$sym_name => write!(f, "{}", $sym_str),)*
$(Token::$kw_name => write!(f, $kw_str),)*
$(Token::$other_name(v) => write!(f, "{v}"),)*
}
}
}
};
}
use def_tokens;
+28 -4
View File
@@ -1,5 +1,29 @@
// mod v1;
// mod v2;
mod v3;
mod cursor;
mod node;
mod nodes;
pub use v3::*;
use cursor::*;
pub use node::*;
pub use nodes::*;
use crate::io::CompilerOutput;
pub fn parse_file(path: &str, output: &mut CompilerOutput) -> Option<Body> {
let code = match std::fs::read_to_string(path) {
Ok(code) => code,
Err(err) => {
output.error(format!("Failed to read input file: {err}"));
return None;
}
};
output.files.push(path.to_string());
let mut ctx = ParseCtx::new(Cursor::new(&code, 0));
let root = match ctx.parse() {
Ok(v) => v,
Err(msg) => {
output.error(msg);
return None;
}
};
Some(root)
}
+83
View File
@@ -0,0 +1,83 @@
use crate::{
io::{CompilerMsg, Span},
parser::{
Ident, Node,
cursor::{Cursor, Lit, LitTy, Token},
},
};
pub struct ParseCtx<'a> {
start: usize,
cursor: Cursor<'a>,
}
impl<'a> ParseCtx<'a> {
pub fn new(cursor: Cursor<'a>) -> Self {
Self { start: 0, cursor }
}
pub fn parse_box<N: Node>(&mut self) -> Result<Box<N>, CompilerMsg> {
self.parse_with(N::parse).map(Box::new)
}
pub fn parse<N: Node>(&mut self) -> Result<N, CompilerMsg> {
self.parse_with(N::parse)
}
pub fn parse_with<N: Node>(
&mut self,
f: impl FnOnce(&mut Self) -> Result<N, CompilerMsg>,
) -> Result<N, CompilerMsg> {
let old_start = self.start;
self.start = self.cursor.peek_start();
let res = f(self).map(|r| r);
self.start = old_start;
res
}
pub fn ident(&mut self, s: String) -> Ident {
let span = self.cursor.span;
Ident { name: s, span }
}
pub fn lit(&mut self, ty: LitTy) -> Lit {
let span = self.cursor.span;
Lit { ty, span }
}
pub fn span(&mut self) -> Span {
let end = self.cursor.cur_end();
Span {
file: self.cursor.file(),
start: self.start,
end,
}
}
pub fn list<N: Node>(&mut self, sep: Token, end: Token) -> Result<Vec<N>, CompilerMsg> {
let mut list = Vec::new();
if self.next_if(&end) {
return Ok(list);
}
list.push(self.parse()?);
while self.next_if(&sep) {
list.push(self.parse()?);
}
self.expect(end)?;
Ok(list)
}
}
impl<'a> std::ops::Deref for ParseCtx<'a> {
type Target = Cursor<'a>;
fn deref(&self) -> &Self::Target {
&self.cursor
}
}
impl<'a> std::ops::DerefMut for ParseCtx<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.cursor
}
}
+50
View File
@@ -0,0 +1,50 @@
use crate::parser::Node;
#[derive(Clone, Copy)]
pub struct DisplayCtx {
pub indent: usize,
}
pub struct NodeDsp<'a, N: Node> {
pub node: &'a N,
pub ctx: DisplayCtx,
}
impl<N: Node> std::fmt::Display for NodeDsp<'_, N> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.node.fmt(f, self.ctx)
}
}
pub struct VecDsp<'a, N> {
list: &'a Vec<N>,
ctx: DisplayCtx,
}
impl<N: Node> std::fmt::Display for VecDsp<'_, N> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if let Some((last, rest)) = self.list.split_last() {
for arg in rest {
write!(f, "{}, ", arg.dsp(self.ctx))?;
}
write!(f, "{}", last.dsp(self.ctx))?;
}
Ok(())
}
}
pub trait VecDspT<N> {
fn dsp<'a, 'b>(&'a self, ctx: impl Into<DisplayCtx>) -> VecDsp<'b, N>
where
'a: 'b;
}
impl<N> VecDspT<N> for Vec<N> {
fn dsp<'a, 'b>(&'a self, ctx: impl Into<DisplayCtx>) -> VecDsp<'b, N>
where
'a: 'b,
{
let ctx = ctx.into();
VecDsp { list: self, ctx }
}
}
+17
View File
@@ -0,0 +1,17 @@
mod ctx;
mod dsp;
pub use ctx::*;
pub use dsp::*;
use crate::io::CompilerMsg;
pub trait Node: Sized {
fn parse(ctx: &mut ParseCtx) -> Result<Self, CompilerMsg>;
fn fmt(&self, f: &mut std::fmt::Formatter, ctx: DisplayCtx) -> std::fmt::Result;
fn dsp(&self, ctx: DisplayCtx) -> NodeDsp<'_, Self> {
NodeDsp { node: self, ctx }
}
fn new_dsp(&self) -> NodeDsp<'_, Self> {
self.dsp(DisplayCtx { indent: 0 })
}
}
+58
View File
@@ -0,0 +1,58 @@
use super::*;
pub struct Body {
pub items: Vec<Item>,
pub final_semicolon: bool,
pub span: Span,
}
impl Node for Body {
fn parse(ctx: &mut ParseCtx) -> Result<Self, CompilerMsg> {
let mut items = Vec::new();
fn at_end(ctx: &mut ParseCtx) -> bool {
ctx.peek().is_none_or(|t| *t == Token::CloseCurly)
}
let final_semicolon = loop {
if at_end(ctx) {
break true;
}
let item: Item = ctx.parse()?;
let needs_semicolon = item.needs_semicolon();
items.push(item);
if at_end(ctx) {
break false;
}
if needs_semicolon {
ctx.expect(Token::Semicolon)?;
}
while ctx.next_if(Token::Semicolon) {}
};
Ok(Self {
items,
final_semicolon,
span: ctx.span(),
})
}
fn fmt(&self, f: &mut std::fmt::Formatter, ctx: DisplayCtx) -> std::fmt::Result {
if let Some((last, rest)) = self.items.split_last() {
for i in rest {
writeln!(
f,
"{}{}{}",
" ".repeat(ctx.indent),
i.dsp(ctx),
if i.needs_semicolon() { ";" } else { "" }
)?;
}
writeln!(
f,
"{}{}{}",
" ".repeat(ctx.indent),
last.dsp(ctx),
if self.final_semicolon { ";" } else { "" }
)?;
}
Ok(())
}
}
+186
View File
@@ -0,0 +1,186 @@
use crate::parser::VecDspT;
pub use super::*;
pub struct Expr {
span: Span,
ty: ExprTy,
}
pub enum ExprTy {
Block(Body),
Group(Box<Expr>),
Ident(Ident),
Lit(Lit),
Negate(Box<Expr>),
Call { target: Box<Expr>, args: Vec<Expr> },
Assign { target: Box<Expr>, val: Box<Expr> },
If { cond: Box<Expr>, body: Box<Expr> },
Loop { body: Box<Expr> },
While { cond: Box<Expr>, body: Box<Expr> },
Fn(Box<Func>),
}
impl Node for Expr {
fn parse(ctx: &mut ParseCtx) -> Result<Self, CompilerMsg> {
let mut res = Self::unit(ctx)?;
while let Some(next) = ctx.peek() {
let ty = match next {
Token::Equal => {
ctx.next();
let target = Box::new(res);
let val = Box::new(ctx.parse_with(Self::unit)?);
ExprTy::Assign { target, val }
}
Token::OpenParen => {
ctx.next();
let target = Box::new(res);
let args = ctx.list(Token::Comma, Token::CloseParen)?;
ExprTy::Call { target, args }
}
_ => break,
};
res = Self {
ty,
span: ctx.span(),
};
}
Ok(res)
}
fn fmt(&self, f: &mut std::fmt::Formatter, ctx: DisplayCtx) -> std::fmt::Result {
self.ty.fmt(f, ctx)
}
}
impl ExprTy {
fn fmt(&self, f: &mut std::fmt::Formatter, mut ctx: DisplayCtx) -> std::fmt::Result {
match self {
Self::Ident(ident) => ident.fmt(f, ctx),
Self::Group(expr) => write!(f, "({})", expr.dsp(ctx)),
Self::Fn(func) => func.fmt(f, ctx),
Self::Lit(lit) => write!(f, "{}", lit),
Self::Negate(expr) => {
write!(f, "-{}", expr.dsp(ctx))
}
Self::Call { target, args } => {
write!(f, "{}({})", target.dsp(ctx), args.dsp(ctx))
}
Self::Assign { target, val } => {
write!(f, "{} = {}", target.dsp(ctx), val.dsp(ctx))
}
Self::If { cond, body } => {
write!(f, "if {} {}", cond.dsp(ctx), body.dsp(ctx))
}
Self::While { cond, body } => {
write!(f, "while {} {}", cond.dsp(ctx), body.dsp(ctx))
}
Self::Loop { body } => {
write!(f, "loop {}", body.dsp(ctx))
}
Self::Block(body) => {
write!(f, "{{")?;
if !body.items.is_empty() {
writeln!(f)?;
ctx.indent += 3;
body.fmt(f, ctx)?;
}
write!(f, "}}")?;
Ok(())
}
}
}
}
impl Expr {
pub fn fmt_body(&self, f: &mut std::fmt::Formatter, ctx: DisplayCtx) -> std::fmt::Result {
match self.ty {
ExprTy::Block(_) => self.fmt(f, ctx),
_ => write!(f, "=> {}", self.dsp(ctx)),
}
}
fn unit(ctx: &mut ParseCtx) -> Result<Self, CompilerMsg> {
let ty = match ctx.expect_next()? {
Token::Dash => ExprTy::Negate(ctx.parse_box()?),
Token::Ident(s) => ExprTy::Ident(ctx.ident(s)),
Token::Lit(l) => ExprTy::Lit(ctx.lit(l)),
Token::Fn => ExprTy::Fn(ctx.parse_box()?),
Token::If => {
let cond = ctx.parse_box()?;
let body = Box::new(Self::body(ctx)?);
ExprTy::If { cond, body }
}
Token::While => {
let cond = ctx.parse_box()?;
let body = Box::new(Self::body(ctx)?);
ExprTy::While { cond, body }
}
Token::Loop => {
let body = ctx.parse_box()?;
ExprTy::Loop { body }
}
Token::OpenParen => {
if ctx.next_if(Token::CloseParen) {
ExprTy::Lit(Lit {
ty: LitTy::Unit,
span: ctx.span(),
})
} else {
let inner = ctx.parse_box()?;
ctx.expect(Token::CloseParen)?;
ExprTy::Group(inner)
}
}
Token::OpenCurly => {
let body = ctx.parse()?;
ctx.expect(Token::CloseCurly)?;
ExprTy::Block(body)
}
other => return ctx.unexpected(&other, "an expression"),
};
Ok(Self {
ty,
span: ctx.span(),
})
}
pub fn is_group(&self) -> bool {
matches!(self.ty, ExprTy::Group(_))
}
pub fn is_block(&self) -> bool {
matches!(self.ty, ExprTy::Block(_))
}
pub fn block(ctx: &mut ParseCtx) -> Result<Expr, CompilerMsg> {
ctx.expect(Token::OpenCurly)?;
let id = ctx.parse()?;
ctx.expect(Token::CloseCurly)?;
Ok(Expr {
ty: ExprTy::Block(id),
span: ctx.span(),
})
}
pub fn body(ctx: &mut ParseCtx) -> Result<Expr, CompilerMsg> {
if ctx.next_if(Token::DoubleArrow) {
ctx.parse()
} else {
ctx.parse_with(Expr::block)
}
}
pub fn ends_with_block(&self) -> bool {
match &self.ty {
ExprTy::Block(..) => true,
ExprTy::Loop { body }
| ExprTy::While { body, .. }
| ExprTy::If { body, .. }
| ExprTy::Negate(body)
| ExprTy::Assign { val: body, .. } => body.ends_with_block(),
ExprTy::Fn(f) => f.ends_with_block(),
_ => false,
}
}
}
+62
View File
@@ -0,0 +1,62 @@
use super::*;
pub struct Func {
args: Vec<Param>,
name: Option<Ident>,
ret: Option<Type>,
body: Expr,
span: Span,
}
impl Node for Func {
fn parse(ctx: &mut ParseCtx) -> Result<Self, CompilerMsg> {
let mut name = None;
if let Token::Ident(ident) = ctx.expect_peek()? {
// yucky
let ident = ident.to_string();
ctx.next();
let ident = ctx.ident(ident);
name = Some(ident);
}
ctx.expect(Token::OpenParen)?;
let args = ctx.list(Token::Comma, Token::CloseParen)?;
let mut ret = None;
if ctx.next_if(Token::Arrow) {
ret = Some(ctx.parse()?);
}
let body = Expr::body(ctx)?;
Ok(Self {
args,
ret,
body,
name,
span: ctx.span(),
})
}
fn fmt(&self, f: &mut std::fmt::Formatter, ctx: DisplayCtx) -> std::fmt::Result {
write!(f, "fn")?;
if let Some(name) = &self.name {
write!(f, " {name}")?;
}
write!(f, "(")?;
if let Some((last, rest)) = self.args.split_last() {
for arg in rest {
write!(f, "{}, ", arg.dsp(ctx))?;
}
write!(f, "{}", last.dsp(ctx))?;
}
write!(f, ") ")?;
if let Some(ret) = &self.ret {
write!(f, "-> {} ", ret.dsp(ctx))?;
}
self.body.fmt_body(f, ctx)?;
Ok(())
}
}
impl Func {
pub fn ends_with_block(&self) -> bool {
self.body.ends_with_block()
}
}
+25
View File
@@ -0,0 +1,25 @@
use super::*;
pub struct Ident {
pub name: String,
pub span: Span,
}
impl Node for Ident {
fn parse(ctx: &mut super::ParseCtx) -> Result<Self, crate::io::CompilerMsg> {
match ctx.expect_next()? {
Token::Ident(ident) => Ok(ctx.ident(ident)),
t => ctx.unexpected(&t, "an identifier"),
}
}
fn fmt(&self, f: &mut std::fmt::Formatter, _: DisplayCtx) -> std::fmt::Result {
write!(f, "{}", self.name)
}
}
impl std::fmt::Display for Ident {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.name.fmt(f)
}
}
+78
View File
@@ -0,0 +1,78 @@
use super::*;
pub struct Item {
pub ty: ItemTy,
pub span: Span,
}
pub enum ItemTy {
Let {
name: Ident,
ty: Option<Type>,
val: Expr,
},
Fn(Func),
Expr(Expr),
Import(Ident),
}
impl Node for Item {
fn parse(ctx: &mut ParseCtx) -> Result<Self, CompilerMsg> {
let ty = match ctx.expect_peek()? {
Token::Fn => {
ctx.next();
ItemTy::Fn(ctx.parse()?)
}
Token::Let => {
ctx.next();
let name = ctx.parse()?;
let mut ty = None;
if ctx.next_if(Token::Colon) {
ty = Some(ctx.parse()?);
}
ctx.expect(Token::Equal)?;
let val = ctx.parse()?;
ItemTy::Let { name, ty, val }
}
Token::Import => {
ctx.next();
ItemTy::Import(ctx.parse()?)
}
_ => ItemTy::Expr(ctx.parse()?),
};
Ok(Self {
ty,
span: ctx.span(),
})
}
fn fmt(&self, f: &mut std::fmt::Formatter, ctx: DisplayCtx) -> std::fmt::Result {
match &self.ty {
ItemTy::Fn(func) => func.fmt(f, ctx)?,
ItemTy::Let { name, ty, val } => {
write!(f, "let {}", name.dsp(ctx))?;
if let Some(ty) = ty {
write!(f, ": {}", ty.dsp(ctx))?;
}
write!(f, " = {}", val.dsp(ctx))?;
}
ItemTy::Expr(expr) => expr.fmt(f, ctx)?,
ItemTy::Import(ident) => write!(f, "import {}", ident.dsp(ctx))?,
}
Ok(())
}
}
impl Item {
pub fn ends_with_block(&self) -> bool {
match &self.ty {
ItemTy::Let { val, .. } => val.ends_with_block(),
ItemTy::Expr(id) => id.ends_with_block(),
ItemTy::Fn(f) => f.ends_with_block(),
ItemTy::Import(ident) => false,
}
}
pub fn needs_semicolon(&self) -> bool {
!self.ends_with_block()
}
}
+18
View File
@@ -0,0 +1,18 @@
mod body;
mod expr;
mod func;
mod ident;
mod item;
mod param;
mod struct_;
mod ty;
pub use body::*;
pub use expr::*;
pub use func::*;
pub use ident::*;
pub use item::*;
pub use param::*;
pub use ty::*;
use super::{DisplayCtx, Lit, LitTy, Node, ParseCtx, Token};
use crate::io::{CompilerMsg, Span};
+25
View File
@@ -0,0 +1,25 @@
use super::*;
pub struct Param {
name: Ident,
ty: Option<Type>,
}
impl Node for Param {
fn parse(ctx: &mut ParseCtx) -> Result<Self, CompilerMsg> {
let name = ctx.parse()?;
let mut ty = None;
if ctx.next_if(Token::Colon) {
ty = Some(ctx.parse()?);
}
Ok(Self { name, ty })
}
fn fmt(&self, f: &mut std::fmt::Formatter, ctx: DisplayCtx) -> std::fmt::Result {
self.name.fmt(f, ctx)?;
if let Some(ty) = &self.ty {
write!(f, ": {}", ty.dsp(ctx))?;
}
Ok(())
}
}
+8
View File
@@ -0,0 +1,8 @@
use super::*;
pub struct Struct {
name: String,
fields: Vec<Field>,
}
pub struct Field {}
+20
View File
@@ -0,0 +1,20 @@
use super::*;
pub enum Type {
Ident(Ident),
}
impl Node for Type {
fn parse(ctx: &mut ParseCtx) -> Result<Self, CompilerMsg> {
Ok(match ctx.expect_next()? {
Token::Ident(s) => Self::Ident(ctx.ident(s)),
t => ctx.unexpected(&t, "a type")?,
})
}
fn fmt(&self, f: &mut std::fmt::Formatter, ctx: DisplayCtx) -> std::fmt::Result {
match self {
Type::Ident(id) => id.fmt(f, ctx),
}
}
}
-91
View File
@@ -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(),
}
}
}
-62
View File
@@ -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);
}
}
-47
View File
@@ -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();
}
}
}
-90
View File
@@ -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()?))
}
}
-85
View File
@@ -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(())
}
}
-176
View File
@@ -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(())
}
}
-39
View File
@@ -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()?,
})
}
}
-34
View File
@@ -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)
}
}
-120
View File
@@ -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(())
}
}
-19
View File
@@ -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::*;
-48
View File
@@ -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<_, _>>()?,
})
}
}
-96
View File
@@ -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;
}
})
}
}
-63
View File
@@ -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(())
}
}
-178
View File
@@ -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)
}
}
-68
View File
@@ -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(),
}
}
}
-80
View File
@@ -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(())
}
}
-19
View File
@@ -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,
})
}
}
-90
View File
@@ -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
}
}
-146
View File
@@ -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())
}
}
-120
View File
@@ -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(())
}
}
-135
View File
@@ -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 }
}
}
-60
View File
@@ -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);
}
}
}
-247
View File
@@ -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(())
}
}
-32
View File
@@ -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),
}
}
}
-59
View File
@@ -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(())
}
}
-10
View File
@@ -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));
-53
View File
@@ -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,
}
}
}
-101
View File
@@ -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,
}
}
}
-29
View File
@@ -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}"),
)
}
}
-5
View File
@@ -1,5 +0,0 @@
use std::collections::HashSet;
#[derive(Clone, Eq, PartialEq, Hash)]
pub struct Import(pub Vec<String>);
pub type Imports = HashSet<Import>;
-2
View File
@@ -1,2 +0,0 @@
pub mod riscv64;
pub use super::*;
-216
View File
@@ -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