This commit is contained in:
2026-04-08 17:54:42 -04:00
parent 11ab9285f1
commit edabc22431
127 changed files with 382 additions and 10885 deletions
-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;
}
-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>>,
}
-136
View File
@@ -1,136 +0,0 @@
use std::{
fmt::Debug,
marker::PhantomData,
ops::{Index, IndexMut},
};
// I had an idea for why these were different... now I don't
pub type Size = u32;
pub type Len = u32;
pub struct ID<T>(pub usize, PhantomData<T>);
impl<T> ID<T> {
pub fn new(i: usize) -> Self {
Self(i, PhantomData)
}
}
impl<T> From<usize> for ID<T> {
fn from(value: usize) -> Self {
Self(value, PhantomData)
}
}
impl<T> Debug for ID<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{{{}}}", self.0)
}
}
impl<T> PartialEq for ID<T> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<T> Eq for ID<T> {}
impl<T> std::hash::Hash for ID<T> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
impl<T> Clone for ID<T> {
fn clone(&self) -> Self {
Self(self.0.clone(), PhantomData)
}
}
impl<T> Copy for ID<T> {}
// :fear:
impl<T> Index<ID<T>> for Vec<T> {
type Output = T;
fn index(&self, i: ID<T>) -> &Self::Output {
&self[i.0]
}
}
impl<T> IndexMut<ID<T>> for Vec<T> {
fn index_mut(&mut self, i: ID<T>) -> &mut Self::Output {
&mut self[i.0]
}
}
impl<T> Index<&ID<T>> for Vec<T> {
type Output = T;
fn index(&self, i: &ID<T>) -> &Self::Output {
&self[i.0]
}
}
impl<T> IndexMut<&ID<T>> for Vec<T> {
fn index_mut(&mut self, i: &ID<T>) -> &mut Self::Output {
&mut self[i.0]
}
}
impl<T> Index<&mut ID<T>> for Vec<T> {
type Output = T;
fn index(&self, i: &mut ID<T>) -> &Self::Output {
&self[i.0]
}
}
impl<T> IndexMut<&mut ID<T>> for Vec<T> {
fn index_mut(&mut self, i: &mut ID<T>) -> &mut Self::Output {
&mut self[i.0]
}
}
impl<T> Index<ID<T>> for [T] {
type Output = T;
fn index(&self, i: ID<T>) -> &Self::Output {
&self[i.0]
}
}
impl<T> IndexMut<ID<T>> for [T] {
fn index_mut(&mut self, i: ID<T>) -> &mut Self::Output {
&mut self[i.0]
}
}
impl<T> Index<&ID<T>> for [T] {
type Output = T;
fn index(&self, i: &ID<T>) -> &Self::Output {
&self[i.0]
}
}
impl<T> IndexMut<&ID<T>> for [T] {
fn index_mut(&mut self, i: &ID<T>) -> &mut Self::Output {
&mut self[i.0]
}
}
impl<T> Index<&mut ID<T>> for [T] {
type Output = T;
fn index(&self, i: &mut ID<T>) -> &Self::Output {
&self[i.0]
}
}
impl<T> IndexMut<&mut ID<T>> for [T] {
fn index_mut(&mut self, i: &mut ID<T>) -> &mut Self::Output {
&mut self[i.0]
}
}
-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
}
}
-16
View File
@@ -1,16 +0,0 @@
//! the IR is split into 2 layers: upper and lower
//! upper handles all of the main language features like types,
//! and the lower is a very concrete format that can be easily
//! translated to assembly and will probably also include
//! the majority of optimization, but not sure
mod upper;
mod lower;
mod id;
mod asm;
pub mod arch;
pub use upper::*;
pub use lower::*;
pub use id::*;
-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),
}
}
+10 -171
View File
@@ -1,178 +1,17 @@
#![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,
};
mod common;
mod compiler;
mod ir;
mod parser;
mod util;
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");
}
Err(e) => {
println!("gdb error: {e:?}");
process.kill().expect("uh oh");
}
}
let code = match std::fs::read_to_string(path) {
Ok(code) => code,
Err(err) => {
println!("Failed to read input file: {err}");
return;
}
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);
// }
};
parser::parse(&code);
}
+75
View File
@@ -0,0 +1,75 @@
use crate::parser::error::ParseError;
pub use span::*;
use std::iter::Peekable;
pub use token::*;
mod span;
mod token;
pub struct Cursor<'a> {
pub span: Span,
tokens: Peekable<Tokens<'a>>,
}
impl<'a> Cursor<'a> {
pub fn new(text: &'a str) -> Self {
Self {
span: Span { first: 0, last: 0 },
tokens: Tokens::new(text).peekable(),
}
}
pub fn next(&mut self) -> Option<Token> {
self.tokens.next().map(|inst| {
self.span = inst.span;
inst.inner
})
}
pub fn peek(&mut self) -> Option<&Token> {
self.tokens.peek().map(|inst| &inst.inner)
}
pub fn expect_next(&mut self) -> Result<Token, ParseError> {
self.next().ok_or_else(|| ParseError {
spans: Vec::new(),
msg: "unexpected end of file".to_string(),
})
}
pub fn expect(&mut self, token: Token) -> Result<Token, ParseError> {
let next = self.expect_next()?;
if next == token {
Ok(next)
} else {
self.unexpected(next, &format!("{token:?}"))
}
}
pub fn expect_ident(&mut self) -> Result<String, ParseError> {
let next = self.expect_next()?;
if let Token::Ident(s) = next {
Ok(s)
} else {
self.unexpected(next, "identifier")
}
}
pub fn unexpected<T>(&self, token: Token, expected: &str) -> Result<T, ParseError> {
Err(ParseError::unexpected_token(
Spanned {
inner: token,
span: self.span,
},
expected,
))
}
pub fn peek_first(&mut self) -> usize {
self.tokens.peek().map(|i| i.span.first).unwrap_or(0)
}
pub fn cur_last(&mut self) -> usize {
self.span.last
}
}
+24
View File
@@ -0,0 +1,24 @@
#[derive(Clone, Copy)]
pub struct Span {
pub first: usize,
pub last: 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
}
}
+161
View File
@@ -0,0 +1,161 @@
use super::{Span, Spanned};
use std::{iter::Peekable, str::CharIndices};
#[derive(PartialEq)]
pub enum Token {
// symbols
Equal,
Colon,
Semicolon,
OpenCurly,
CloseCurly,
OpenParen,
CloseParen,
Dash,
Plus,
Asterisk,
Slash,
// keywords
Let,
Fn,
// other
Ident(String),
Lit(Lit),
}
#[derive(PartialEq)]
pub enum Lit {
Number(String),
Bool(bool),
String(String),
}
impl From<Lit> for Token {
fn from(value: Lit) -> Self {
Self::Lit(value)
}
}
pub type TokenInst = Spanned<Token>;
pub struct Tokens<'a> {
text: Peekable<CharIndices<'a>>,
}
impl<'a> Tokens<'a> {
pub fn new(code: &'a str) -> Self {
Self {
text: code.char_indices().peekable(),
}
}
}
impl Iterator for Tokens<'_> {
type Item = Spanned<Token>;
fn next(&mut self) -> Option<Self::Item> {
let (i, c) = self.text.next()?;
let mut span = Span { first: i, last: i };
Some(Spanned {
inner: match c {
'=' => Token::Equal,
'{' => Token::OpenCurly,
'}' => Token::CloseCurly,
'(' => Token::OpenParen,
')' => Token::CloseParen,
':' => Token::Colon,
';' => Token::Semicolon,
'-' => Token::Dash,
'+' => Token::Plus,
'*' => Token::Asterisk,
'/' => Token::Slash,
'\n' | ' ' => {
return self.next();
}
'0'..='9' => {
let mut s = c.to_string();
while let Some((i, c)) = self.text.peek()
&& c.is_alphanumeric()
{
s.push(*c);
span.last = *i;
self.text.next();
}
Lit::Number(s).into()
}
'"' => {
let mut s = c.to_string();
while let Some((i, c)) = self.text.peek()
&& !matches!(c, '"')
{
s.push(*c);
span.last = *i;
self.text.next();
}
self.text.next();
Lit::String(s).into()
}
_ => {
let mut s = c.to_string();
while let Some((i, c)) = self.text.peek()
&& !matches!(
c,
'=' | '{' | '}' | '(' | ')' | ':' | ';' | '\n' | '"' | ' '
)
{
s.push(*c);
span.last = *i;
self.text.next();
}
match s.as_str() {
"let" => Token::Let,
"fn" => Token::Fn,
"true" => Lit::Bool(true).into(),
"false" => Lit::Bool(false).into(),
_ => Token::Ident(s),
}
}
},
span,
})
}
}
impl std::fmt::Display for Token {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Token::Equal => write!(f, "="),
Token::Colon => write!(f, ":"),
Token::Plus => write!(f, "+"),
Token::Dash => write!(f, "-"),
Token::Asterisk => write!(f, "*"),
Token::Slash => write!(f, "/"),
Token::Semicolon => write!(f, ";"),
Token::OpenCurly => write!(f, "{{"),
Token::CloseCurly => write!(f, "}}"),
Token::OpenParen => write!(f, "("),
Token::CloseParen => write!(f, ")"),
Token::Let => write!(f, "let"),
Token::Fn => write!(f, "fn"),
Token::Ident(s) => write!(f, "{s}"),
Token::Lit(lit) => write!(f, "{lit}"),
}?;
Ok(())
}
}
impl std::fmt::Display for Lit {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Lit::Number(n) => write!(f, "{n}"),
Lit::Bool(b) => write!(f, "{b}"),
Lit::String(s) => write!(f, "{s}"),
}
}
}
impl std::fmt::Debug for Token {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "'{self}'")
}
}
+15
View File
@@ -0,0 +1,15 @@
use crate::parser::cursor::{Span, TokenInst};
pub struct ParseError {
pub spans: Vec<Span>,
pub msg: String,
}
impl ParseError {
pub fn unexpected_token(inst: TokenInst, expected: &str) -> Self {
Self {
spans: vec![inst.span],
msg: format!("Unexpected token {}; expected {expected}", inst.inner),
}
}
}
+4 -4
View File
@@ -1,5 +1,5 @@
// mod v1;
// mod v2;
mod v3;
mod cursor;
mod error;
mod node;
pub use v3::*;
pub fn parse(code: &str) {}
+87
View File
@@ -0,0 +1,87 @@
use std::marker::PhantomData;
use crate::parser::{
cursor::{Cursor, Span, Token},
error::ParseError,
};
pub trait Parsable: Sized {
fn parse(ctx: &mut ParseCtx) -> Result<Self, ParseError>;
fn vec(nodes: &mut Nodes) -> &mut NodeVec<Self>;
}
pub struct ParseCtx<'a> {
cursor: Cursor<'a>,
nodes: Nodes,
}
pub struct Nodes {
statements: NodeVec<Statement>,
exprs: NodeVec<Expr>,
idents: NodeVec<Ident>,
}
pub struct NodeVec<N> {
vec: Vec<N>,
spans: Vec<Span>,
}
impl<N> NodeVec<N> {
pub fn add(&mut self, v: N, span: Span) -> Id<N> {
let id = self.vec.len();
self.vec.push(v);
self.spans.push(span);
Id {
id,
_pd: PhantomData,
}
}
}
pub enum Statement {
Let(Id<Ident>, Id<Expr>),
}
pub enum Expr {
Ident(Id<Ident>),
Negate(Id<Expr>),
Assign(Id<Expr>, Id<Expr>),
}
pub struct Id<T> {
id: usize,
_pd: PhantomData<T>,
}
pub struct Ident {
inner: String,
}
impl Parsable for Expr {
fn parse(ctx: &mut ParseCtx) -> Result<Self, ParseError> {
Ok(match ctx.cursor.expect_next()? {
Token::Dash => Self::Negate(ctx.parse()?),
Token::Ident(s) => Self::Ident(ctx.ident(s)),
other => return ctx.cursor.unexpected(other, "an expression"),
})
}
fn vec(nodes: &mut Nodes) -> &mut NodeVec<Self> {
&mut nodes.exprs
}
}
impl ParseCtx<'_> {
pub fn parse<P: Parsable>(&mut self) -> Result<Id<P>, ParseError> {
let first = self.cursor.peek_first();
P::parse(self).map(|r| {
let last = self.cursor.cur_last();
P::vec(&mut self.nodes).add(r, Span { first, last })
})
}
pub fn ident(&mut self, s: String) -> Id<Ident> {
let span = self.cursor.span;
self.nodes.idents.add(Ident { inner: s }, span)
}
}
-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
}
}
}
-82
View File
@@ -1,82 +0,0 @@
use crate::{
compiler::arch::riscv::Reg,
ir::{
arch::riscv64::RV64Instruction, AsmBlockArg, AsmBlockArgType, IdentID, Type, UInstruction,
},
parser::PAsmBlockArg,
};
use super::{FnLowerCtx, FnLowerable, PAsmBlock, PInstruction, PUAsmBlockArg};
type PLAsmBlockArg = PAsmBlockArg<Reg, IdentID>;
impl FnLowerable for PInstruction {
type Output = RV64Instruction;
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<RV64Instruction> {
RV64Instruction::parse(self, ctx)
}
}
impl FnLowerable for PAsmBlock {
type Output = IdentID;
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<Self::Output> {
let mut args = Vec::new();
let mut output = None;
for a in &self.args {
if let Some(a) = a.lower(ctx) {
match a {
PAsmBlockArg::In { reg, var } => args.push(AsmBlockArg {
reg,
var,
ty: AsmBlockArgType::In,
}),
PAsmBlockArg::Out { reg } => {
if output.is_some() {
ctx.err("cannot evaluate to more than one register".to_string());
continue;
}
let var = ctx.temp(Type::Bits(64));
args.push(AsmBlockArg {
var,
reg,
ty: AsmBlockArgType::Out,
});
output = Some(var)
}
}
}
}
let block = UInstruction::AsmBlock {
instructions: {
let mut v = Vec::new();
for i in &self.instructions {
if let Some(i) = i.lower(ctx) {
v.push(i);
}
}
v
},
args,
};
ctx.push(block);
output
}
}
impl FnLowerable for PUAsmBlockArg {
type Output = PLAsmBlockArg;
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<Self::Output> {
Some(match self {
PAsmBlockArg::In { reg, var } => PLAsmBlockArg::In {
reg: Reg::from_ident(reg, ctx)?,
var: var.as_ref()?.lower(ctx)?,
},
PAsmBlockArg::Out { reg } => PLAsmBlockArg::Out {
reg: Reg::from_ident(reg, ctx)?,
},
})
}
}
-99
View File
@@ -1,99 +0,0 @@
use crate::{
ir::{IdentID, Type, UIdent, UInstruction, UVar},
parser::{PConstStatement, PStatementLike},
};
use super::{FnLowerCtx, FnLowerable, Import, PBlock, PStatement};
impl FnLowerable for PBlock {
type Output = IdentID;
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<IdentID> {
ctx.ident_stack.push();
let mut last = None;
let mut statements = Vec::new();
let mut fn_nodes = Vec::new();
let mut struct_nodes = Vec::new();
let mut import_nodes = Vec::new();
// first sort statements
for s in &self.statements {
let Some(s) = s.as_ref() else {
continue;
};
match s {
PStatementLike::Statement(s) => statements.push(s),
PStatementLike::Const(pconst_statement) => match pconst_statement {
PConstStatement::Fn(f) => fn_nodes.push(f),
PConstStatement::Struct(s) => struct_nodes.push(s),
PConstStatement::Import(i) => import_nodes.push(i),
},
}
}
// then lower imports
for i_n in &import_nodes {
if let Some(i) = i_n.as_ref() {
let name = &i.0;
let path = ctx.path_for(name);
let import = Import(path.clone());
if ctx.imports.insert(import) {
ctx.def_var(UVar {
name: name.clone(),
ty: Type::Module(path),
origin: i_n.origin,
});
}
}
}
// then lower const things
let mut structs = Vec::new();
for s in &struct_nodes {
structs.push(s.lower(ctx.ctx));
}
for (s, id) in struct_nodes.iter().zip(structs) {
if let Some(id) = id {
s.lower(ctx.ctx);
}
}
let mut fns = Vec::new();
for f in &fn_nodes {
fns.push(f.lower(ctx.ctx));
}
for (f, id) in fn_nodes.iter().zip(fns) {
if let Some(id) = id {
f.lower(ctx.ctx);
}
}
// then lower statements
for s in statements {
last = s.lower(ctx);
}
ctx.ident_stack.pop();
last
}
}
impl FnLowerable for PStatement {
type Output = IdentID;
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<IdentID> {
match self {
PStatement::Let(def, e) => {
let def = def.lower(ctx.ctx)?;
let res = e.lower(ctx);
if let Some(res) = res {
ctx.push(UInstruction::Mv { dst: def, src: res });
}
None
}
PStatement::Return(e) => {
if let Some(e) = e {
let src = e.lower(ctx)?;
ctx.push_at(UInstruction::Ret { src }, src.origin);
} else {
let src = ctx.temp(Type::Unit);
ctx.push_at(UInstruction::Ret { src }, src.origin);
}
None
}
PStatement::Expr(e) => e.lower(ctx),
}
}
}
-23
View File
@@ -1,23 +0,0 @@
use std::collections::HashMap;
use crate::ir::{UVar, VarID};
use super::{ModuleLowerCtx, Node, PVarDef};
impl Node<PVarDef> {
pub fn lower(&self, ctx: &mut ModuleLowerCtx) -> Option<VarID> {
let s = self.as_ref()?;
let name = s.name.as_ref().map_or("{error}", |v| v).to_string();
let ty = match &s.ty {
Some(ty) => ty.lower(ctx),
None => ctx.infer(),
};
Some(ctx.def_var(UVar {
name,
ty,
origin: self.origin,
parent: None,
children: HashMap::new(),
}))
}
}
-206
View File
@@ -1,206 +0,0 @@
use super::{func::FnLowerCtx, FnLowerable, PExpr, PostfixOp};
use crate::{
ir::{IdentID, IdentStatus, MemRes, Member, MemberID, MemberIdent, Type, UData, UInstruction},
parser::InfixOp,
};
impl FnLowerable for PExpr {
type Output = IdentID;
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<IdentID> {
let mut e = self;
let mut path = Vec::new();
let mut gargs = None;
loop {
match e {
PExpr::Member(node, ty, ident) => {
e = if let Some(t) = node.as_ref() {
ctx.origin = node.origin;
path.push((ty, ident, gargs.unwrap_or_default()));
&**t
} else {
return None;
};
}
PExpr::Generic(node, nodes) => match gargs {
None => gargs = Some(nodes.iter().map(|t| t.lower(ctx)).collect::<Vec<_>>()),
Some(_) => {
// this should cover the more specific area of ::<...>
// but too lazy rn
ctx.err("Cannot specify generics here".to_string());
return None;
}
},
_ => break,
}
}
while let PExpr::Member(node, ty, ident) = e {}
if path.len() > 0 {
// UIdent {
// origin: ctx.origin,
// status: IdentStatus::Unres { base: (), path: () },
// }
}
let origin = ctx.origin;
Some(match e {
PExpr::Lit(l) => match l {
super::PLiteral::String(s) => {
let sty = Type::Bits(8).slice(ctx.p);
let dst = ctx.temp_var(origin, sty);
let data = s.as_bytes().to_vec();
let dty = Type::Bits(8).arr(ctx.ctx.p, data.len() as u32);
let dty = ctx.def_ty(dty);
let src = ctx.def_data(UData {
name: format!("string \"{}\"", s.replace("\n", "\\n")),
ty: dty,
content: data,
});
ctx.push(UInstruction::LoadSlice { dst, src });
dst
}
super::PLiteral::Char(c) => {
let ty = ctx.def_ty(Type::Bits(8));
let dst = ctx.temp_var(origin, ty.clone());
let src = ctx.def_data(UData {
name: format!("char '{c}'"),
ty,
content: c.to_string().as_bytes().to_vec(),
});
ctx.push(UInstruction::LoadData { dst, src });
dst
}
super::PLiteral::Number(n) => {
// TODO: temp
let ty = ctx.def_ty(Type::Bits(64));
let dst = ctx.temp_var(origin, ty.clone());
let src = ctx.def_data(UData {
name: format!("num {n:?}"),
ty,
content: n.whole.parse::<i64>().unwrap().to_le_bytes().to_vec(),
});
ctx.push(UInstruction::LoadData { dst, src });
dst
}
super::PLiteral::Unit => ctx.temp_var(origin, Type::Unit),
},
PExpr::Ident(i) => ctx.ident(i),
PExpr::BinaryOp(op, e1, e2) => match op {
InfixOp::Add => todo!(),
InfixOp::Sub => todo!(),
InfixOp::Mul => todo!(),
InfixOp::Div => todo!(),
InfixOp::LessThan => todo!(),
InfixOp::GreaterThan => todo!(),
InfixOp::Assign => {
let res1 = e1.lower(ctx)?;
let res2 = e2.lower(ctx)?;
ctx.push(UInstruction::Mv {
dst: res1,
src: res2,
});
res1
}
},
PExpr::PostfixOp(e, op) => {
let res = e.lower(ctx)?;
match op {
PostfixOp::Ref => {
let ty = Type::Ref(ctx.ctx.infer());
let dest = ctx.temp(ty);
ctx.push(UInstruction::Ref {
dst: dest,
src: res,
});
dest
}
PostfixOp::Deref => {
let ty = Type::Deref(ctx.ctx.infer());
let dst = ctx.temp(ty);
ctx.push(UInstruction::Deref { dst, src: res });
dst
}
PostfixOp::Not => todo!(),
}
}
PExpr::Block(b) => b.lower(ctx)?,
PExpr::AsmBlock(b) => b.lower(ctx)?,
PExpr::Call(e, args) => {
let fe = e.lower(ctx)?;
let mut nargs = Vec::new();
for arg in args.iter() {
let arg = arg.lower(ctx)?;
nargs.push(arg);
}
let dest = ctx.temp(Type::Infer);
ctx.push(UInstruction::Call {
dst: dest,
f: fe,
args: nargs,
});
dest
}
PExpr::Group(e) => e.lower(ctx)?,
PExpr::Construct(e, map) => {
let dst = ctx.temp(Type::Infer);
let struc = e.lower(ctx)?;
let fields = map.lower(ctx)?;
ctx.push(UInstruction::Construct { dst, struc, fields });
dst
}
PExpr::If(cond, body) => {
let cond = cond.lower(ctx)?;
ctx.ident_stack.push();
let mut body_ctx = ctx.branch();
body.lower(&mut body_ctx);
let body = body_ctx.instructions;
ctx.ident_stack.pop();
ctx.push(UInstruction::If { cond, body });
return None;
}
PExpr::Loop(body) => {
ctx.ident_stack.push();
let mut body_ctx = ctx.branch();
body.lower(&mut body_ctx);
let body = body_ctx.instructions;
ctx.ident_stack.pop();
ctx.push(UInstruction::Loop { body });
return None;
}
PExpr::Break => {
ctx.push(UInstruction::Break);
return None;
}
PExpr::Continue => {
ctx.push(UInstruction::Continue);
return None;
}
PExpr::Member(e, ty, name) => {
let id = e.lower(ctx)?;
let name_str = name.as_ref()?.0;
let cur = &mut ctx.p.idents[id];
match cur.status {
IdentStatus::Res(res) => {
cur.status = IdentStatus::Unres {
base: MemRes {
mem: Member {
id: MemberID
},
origin: (),
gargs: (),
},
path: (),
}
}
IdentStatus::Unres { base, path } => path.push(MemberIdent {
ty: *ty,
name: name_str,
origin: name.origin,
gargs: Vec::new(),
}),
IdentStatus::Failed(res_err) => return None,
IdentStatus::Cooked => return None,
}
return None;
}
})
}
}
-181
View File
@@ -1,181 +0,0 @@
use std::ops::{Deref, DerefMut};
use super::{CompilerMsg, FileSpan, ModuleLowerCtx, Node, PFunction, Typable};
use crate::{
ir::{
FnID, IdentID, IdentStatus, MemRes, Member, MemberID, MemberIdent, MemberPath, MemberTy,
Origin, Res, Type, UFunc, UIdent, UInstrInst, UInstruction,
},
parser,
};
impl Node<PFunction> {
pub fn lower(&self, ctx: &mut ModuleLowerCtx) -> Option<FnID> {
self.as_ref().map(|s| s.lower(ctx, self.origin)).flatten()
}
}
impl PFunction {
pub fn lower(&self, ctx: &mut ModuleLowerCtx, origin: Origin) -> Option<FnID> {
let header = self.header.as_ref()?;
let name = header.name.as_ref()?.0.clone();
let (generics, args, ret) = if let Some(header) = self.header.as_ref() {
(
header
.gargs
.iter()
.flat_map(|a| a.lower(ctx).map(|g| (g.0, g.1, a.origin)))
.collect(),
header
.args
.iter()
.flat_map(|a| Some(a.lower(ctx)?))
.collect(),
match &header.ret {
Some(ty) => ty.lower(ctx),
None => ctx.def_ty(Type::Unit),
},
)
} else {
(Vec::new(), Vec::new(), ctx.tc.error)
};
let gargs = generics.iter().map(|g| g.1).collect();
let generics = generics
.into_iter()
.map(|g| {
(
g.0,
ctx.def_ident(UIdent {
status: IdentStatus::Res(Res::Generic(g.1)),
origin: g.2,
}),
)
})
.collect::<Vec<_>>();
ctx.ident_stack.extend(generics.into_iter());
let instructions = {
let mut fctx = FnLowerCtx {
instructions: Vec::new(),
ctx,
origin: self.body.origin,
};
let res = self.body.lower(&mut fctx);
let mut instructions = fctx.instructions;
if let Some(src) = res {
let origin = ctx.idents[src].origin;
instructions.push(UInstrInst {
origin,
i: UInstruction::Ret { src },
});
}
instructions
};
let f = UFunc {
origin,
gargs,
name,
args,
ret,
instructions,
};
Some(ctx.def_fn(f))
}
}
pub struct FnLowerCtx<'a, 'b> {
pub ctx: &'a mut ModuleLowerCtx<'b>,
pub instructions: Vec<UInstrInst>,
pub origin: FileSpan,
}
impl<'a, 'b> FnLowerCtx<'a, 'b> {
pub fn ident(&mut self, node: &Node<parser::PIdent>) -> IdentID {
let inst = UIdent {
status: if let Some(n) = node.as_ref() {
if let Some(&res) = self.ident_stack.search(&n.0) {
return res;
} else {
IdentStatus::Unres {
path: vec![MemberIdent {
ty: MemberTy::Member,
name: n.0.clone(),
origin: node.origin,
gargs: Vec::new(),
}],
base: MemRes {
mem: Member {
id: MemberID::Module(self.module),
},
origin: self.origin,
gargs: Vec::new(),
},
}
}
} else {
IdentStatus::Cooked
},
origin: node.origin,
};
self.def_ident(inst)
}
pub fn err(&mut self, msg: String) {
let origin = self.origin;
self.output.err(CompilerMsg::new(msg, origin))
}
pub fn err_at(&mut self, span: FileSpan, msg: String) {
self.output.err(CompilerMsg::new(msg, span))
}
pub fn temp<T: Typable>(&mut self, ty: T) -> IdentID {
self.ctx.temp_var(self.origin, ty)
}
pub fn push(&mut self, i: UInstruction) {
self.push_at(i, self.origin);
}
pub fn push_at(&mut self, i: UInstruction, span: FileSpan) {
self.instructions.push(UInstrInst { i, origin: span });
}
pub fn branch<'c>(&'c mut self) -> FnLowerCtx<'c, 'b> {
FnLowerCtx {
ctx: self.ctx,
instructions: Vec::new(),
origin: self.origin,
}
}
}
impl<'b> Deref for FnLowerCtx<'_, 'b> {
type Target = ModuleLowerCtx<'b>;
fn deref(&self) -> &Self::Target {
self.ctx
}
}
impl DerefMut for FnLowerCtx<'_, '_> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.ctx
}
}
pub trait FnLowerable {
type Output;
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<Self::Output>;
}
impl<T: FnLowerable> FnLowerable for Node<T> {
type Output = T::Output;
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<T::Output> {
let old_span = ctx.origin;
ctx.origin = self.origin;
let res = self.as_ref()?.lower(ctx);
ctx.origin = old_span;
res
}
}
impl<T: FnLowerable> FnLowerable for Box<T> {
type Output = T::Output;
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<T::Output> {
self.as_ref().lower(ctx)
}
}
-22
View File
@@ -1,22 +0,0 @@
use std::collections::HashMap;
use crate::{ir::IdentID, parser::PMap};
use super::{FnLowerCtx, FnLowerable};
impl FnLowerable for PMap {
type Output = HashMap<String, IdentID>;
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<Self::Output> {
Some(
self.0
.iter()
.flat_map(|n| {
let def = n.as_ref()?;
let name = def.name.as_ref()?.to_string();
let expr = def.val.as_ref()?.lower(ctx)?;
Some((name, expr))
})
.collect(),
)
}
}
-134
View File
@@ -1,134 +0,0 @@
mod arch;
mod asm;
mod block;
mod def;
mod expr;
mod func;
mod map;
mod struc;
mod ty;
use std::{
collections::HashMap,
ops::{Deref, DerefMut},
};
use super::*;
use crate::{
ir::{
IdentID, IdentStatus, ModID, Origin, Res, Type, TypeID, UFunc, UIdent, UModule, UProgram,
UVar,
},
util::NameStack,
};
pub use func::{FnLowerCtx, FnLowerable};
impl PModule {
pub fn lower(
&self,
path: Vec<String>,
p: &mut UProgram,
imports: &mut Imports,
output: &mut CompilerOutput,
) -> ModID {
let name = path.last().unwrap().clone();
let f = UFunc {
name: name.clone(),
args: Vec::new(),
instructions: Vec::new(),
gargs: Vec::new(),
ret: p.def_ty(Type::Unit),
origin: self.block.origin,
};
let fid = p.def_fn(f);
let mid = p.def_module(UModule {
name,
members: HashMap::new(),
parent: None,
func: fid,
});
let mut ctx = ModuleLowerCtx {
p,
output,
module: mid,
temp: 0,
ident_stack: NameStack::new(),
};
let mut fctx = FnLowerCtx {
ctx: &mut ctx,
instructions: Vec::new(),
origin: self.block.origin,
};
self.block.lower(&mut fctx);
p.fns[fid].instructions = fctx.instructions;
mid
}
}
pub struct ModuleLowerCtx<'a> {
pub p: &'a mut UProgram,
pub output: &'a mut CompilerOutput,
pub module: ModID,
pub temp: usize,
pub ident_stack: NameStack<IdentID>,
}
impl<'a> ModuleLowerCtx<'a> {
pub fn new(program: &'a mut UProgram, output: &'a mut CompilerOutput, id: ModID) -> Self {
Self {
p: program,
output,
module: id,
temp: 0,
ident_stack: NameStack::new(),
}
}
pub fn temp_var(&mut self, origin: Origin, ty: impl Typable) -> IdentID {
self.temp_var_inner(origin, ty)
}
fn temp_var_inner(&mut self, origin: Origin, ty: impl Typable) -> IdentID {
let var = UVar {
name: format!("temp{}", self.temp),
ty: ty.ty(self),
origin,
parent: None,
children: HashMap::new(),
};
let id = self.p.def_var(var);
self.temp += 1;
self.def_ident(UIdent {
status: IdentStatus::Res(Res::Var(id)),
origin,
})
}
}
pub trait Typable {
fn ty(self, p: &mut UProgram) -> TypeID;
}
impl Typable for Type {
fn ty(self, p: &mut UProgram) -> TypeID {
p.def_ty(self)
}
}
impl Typable for TypeID {
fn ty(self, p: &mut UProgram) -> TypeID {
self
}
}
impl Deref for ModuleLowerCtx<'_> {
type Target = UProgram;
fn deref(&self) -> &Self::Target {
self.p
}
}
impl DerefMut for ModuleLowerCtx<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.p
}
}
-47
View File
@@ -1,47 +0,0 @@
use crate::{
common::FileSpan,
ir::{StructField, StructID, UStruct},
parser::{PStruct, PStructFields},
};
use super::ModuleLowerCtx;
impl PStruct {
pub fn lower(&self, ctx: &mut ModuleLowerCtx, span: FileSpan) -> Option<StructID> {
ctx.ident_stack.push();
let gmap: Vec<_> = self.generics.iter().flat_map(|a| a.lower(ctx)).collect();
let gargs = gmap.iter().map(|(_, id)| *id).collect();
let fields = match &self.fields {
PStructFields::Named(nodes) => nodes
.iter()
.flat_map(|n| {
let def = n.as_ref()?;
let name = def.name.as_ref()?.to_string();
let tynode = def.ty.as_ref()?;
let ty = tynode.lower(ctx);
Some((name, ty))
})
.collect(),
PStructFields::Tuple(nodes) => nodes
.iter()
.enumerate()
.flat_map(|(i, n)| {
let ty = n.as_ref()?.lower(ctx, span);
Some((format!("{i}"), ty))
})
.collect(),
PStructFields::None => vec![],
}
.into_iter()
.map(|(name, ty)| (name, StructField { ty }))
.collect();
let name = self.name.as_ref()?.to_string();
ctx.ident_stack.pop();
Some(ctx.def_struct(UStruct {
name,
gargs,
fields,
origin: span,
}))
}
}
-89
View File
@@ -1,89 +0,0 @@
use crate::{
ir::{GenericID, MemberIdent, MemberPath, Type, TypeID, UGeneric, UProgram},
parser::PGenericDef,
};
use super::{FileSpan, ModuleLowerCtx, Node, PType};
impl Node<Box<PType>> {
pub fn lower(&self, ctx: &mut ModuleLowerCtx) -> TypeID {
self.as_ref()
.map(|t| t.lower(ctx, self.origin))
.unwrap_or(ctx.tc.error)
}
}
impl Node<PType> {
pub fn lower(&self, ctx: &mut ModuleLowerCtx) -> TypeID {
self.as_ref()
.map(|t| t.lower(ctx, self.origin))
.unwrap_or(ctx.tc.error)
}
}
impl PType {
pub fn lower(&self, ctx: &mut ModuleLowerCtx, mut origin: FileSpan) -> TypeID {
let mut ty = self;
let mut path = Vec::new();
while let PType::Member(node, ident) = ty {
ty = if let Some(t) = node.as_ref() {
let Some(name) = ident.as_ref() else {
return ctx.tc.error;
};
origin = node.origin;
path.push(MemberIdent {
name: name.0.clone(),
origin: ident.origin,
});
&**t
} else {
return ctx.tc.error;
};
}
if !path.is_empty() {
let PType::Ident(id) = ty else {
return ctx.tc.error;
};
path.push(MemberIdent {
name: id.0.clone(),
origin,
});
path.reverse();
let ty = Type::Unres(MemberPath {
id: ctx.module,
path,
});
return ctx.def_ty(ty);
}
let ty = match ty {
PType::Member(_, _) => unreachable!(),
PType::Ident(node) => {
path.push(MemberIdent {
name: node.0.clone(),
origin,
});
path.reverse();
Type::Unres(MemberPath {
id: ctx.module,
path,
})
}
PType::Ref(node) => node.lower(ctx).rf(),
PType::Generic(node, nodes) => todo!(),
};
ctx.def_ty(ty)
}
}
impl Node<PGenericDef> {
pub fn lower(&self, p: &mut UProgram) -> Option<(String, GenericID)> {
let s = self.as_ref()?;
let name = s.name.as_ref()?.to_string();
Some((
name.clone(),
p.def_generic(UGeneric {
name,
origin: self.origin,
}),
))
}
}
-21
View File
@@ -1,21 +0,0 @@
mod ctx;
mod cursor;
mod error;
mod lower;
mod node;
mod nodes;
mod parse;
mod token;
mod import;
use crate::common::{CompilerMsg, CompilerOutput, FileSpan, FilePos};
pub use ctx::*;
pub use cursor::*;
pub use node::*;
pub use nodes::*;
pub use parse::*;
pub use token::*;
pub use import::*;
// idea: create generic "map" and "tuple" types which are used for function calls, tuples, struct
// creation, etc. instead of specializing at the parsing level
-54
View File
@@ -1,54 +0,0 @@
use std::{
fmt::Debug,
ops::{Deref, DerefMut},
};
use crate::ir::Origin;
pub struct Node<T> {
pub inner: Option<T>,
pub origin: Origin,
}
impl<T> Node<T> {
pub fn new(inner: T, span: Origin) -> Self {
Self {
inner: Some(inner),
origin: span,
}
}
pub fn bx(self) -> Node<Box<T>> {
Node {
inner: self.inner.map(|v| Box::new(v)),
origin: self.origin,
}
}
pub fn map<T2, F: Fn(T) -> T2>(self, f: F) -> Node<T2> {
Node {
inner: self.inner.map(f),
origin: self.origin,
}
}
}
impl<T> Deref for Node<T> {
type Target = Option<T>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<T> DerefMut for Node<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
impl<T: Debug> Debug for Node<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self.inner {
Some(v) => v.fmt(f),
None => f.write_str("{error}"),
}
}
}
-67
View File
@@ -1,67 +0,0 @@
use super::{
util::parse_list, Node, PExpr, PIdent, PInstruction, Parsable, ParseResult, ParserCtx, Symbol,
};
pub struct PAsmBlock {
pub instructions: Vec<Node<PInstruction>>,
pub args: Vec<Node<PUAsmBlockArg>>,
}
pub enum PAsmBlockArg<R, V> {
In { reg: R, var: V },
Out { reg: R },
}
pub type PUAsmBlockArg = PAsmBlockArg<Node<PIdent>, Node<PExpr>>;
impl Parsable for PAsmBlock {
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
let args = if ctx.expect_peek()?.is_symbol(Symbol::OpenParen) {
ctx.next();
parse_list(ctx, Symbol::CloseParen)?
} else {
Vec::new()
};
ctx.expect_sym(Symbol::OpenCurly)?;
let mut instructions = Vec::new();
while !ctx.expect_peek()?.is_symbol(Symbol::CloseCurly) {
let res = ctx.parse();
instructions.push(res.node);
if res.recover {
ctx.seek_sym_on_line(Symbol::CloseCurly);
}
}
ctx.expect_sym(Symbol::CloseCurly)?;
ParseResult::Ok(Self { instructions, args })
}
}
impl Parsable for PUAsmBlockArg {
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
let reg = ctx.parse::<PIdent>()?;
ParseResult::Ok(if reg.inner.as_ref().is_some_and(|s| s.0 == "out") {
ctx.expect_sym(Symbol::Equals)?;
let reg = ctx.parse()?;
Self::Out { reg }
} else {
ctx.expect_sym(Symbol::Equals)?;
let var = ctx.parse()?;
Self::In { reg, var }
})
}
}
impl std::fmt::Debug for PAsmBlock {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("asm {")?;
for i in &self.instructions {
f.write_str("\n ")?;
i.fmt(f)?;
}
if !self.instructions.is_empty() {
f.write_str("\n")?;
}
f.write_str("}")?;
Ok(())
}
}
-63
View File
@@ -1,63 +0,0 @@
use super::{
util::parse_list, PAsmBlock, PIdent, Keyword, Node, Parsable, ParseResult, ParserCtx, Symbol, PType, PVarDef,
};
// #[derive(Debug)]
// pub struct AsmFunctionHeader {
// pub name: Node<Ident>,
// pub args: Vec<Node<AsmVarDef>>,
// }
//
// #[derive(Debug)]
// pub struct AsmFunction {
// pub header: Node<AsmFunctionHeader>,
// pub body: Node<AsmBlock>,
// }
//
// impl Parsable for AsmFunctionHeader {
// fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
// ctx.expect_kw(Keyword::Asm)?;
// ctx.expect_kw(Keyword::Fn)?;
// let name = ctx.parse()?;
// ctx.expect_sym(Symbol::OpenParen)?;
// let args = parse_list(ctx, Symbol::CloseParen)?;
// ParseResult::Ok(Self { name, args })
// }
// }
//
// impl Parsable for AsmFunction {
// fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
// let header = ctx.parse()?;
// let body = ctx.parse()?;
// ParseResult::Ok(Self { header, body })
// }
// }
//
// pub struct AsmVarDef {
// pub reg: Node<Ident>,
// pub name: Node<Ident>,
// pub ty: Option<Node<Type>>,
// }
//
// impl Parsable for AsmVarDef {
// fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
// let reg = ctx.parse()?;
// let name = ctx.parse()?;
// if ctx.peek().is_some_and(|n| n.is_symbol(Symbol::Colon)) {
// ctx.next();
// ctx.parse().map(|ty| Self { reg, name, ty: Some(ty) })
// } else {
// ParseResult::Ok(Self { reg, name, ty: None })
// }
// }
// }
//
// impl std::fmt::Debug for AsmVarDef {
// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// self.name.fmt(f)?;
// if let Some(ty) = &self.ty {
// write!(f, ": {:?}", ty)?;
// }
// Ok(())
// }
// }
-94
View File
@@ -1,94 +0,0 @@
use super::{CompilerMsg, Node, PIdent, Parsable, ParseResult, ParserCtx, Symbol};
pub struct PInstruction {
pub op: Node<PIdent>,
pub args: Vec<Node<PAsmArg>>,
}
pub enum PAsmArg {
Value(PIdent),
Ref(Node<PIdent>),
}
impl Parsable for PInstruction {
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
let op = ctx.parse()?;
let mut args = Vec::new();
if !ctx.next_on_new_line() {
let arg = ctx.parse()?;
args.push(arg);
loop {
if ctx.next_on_new_line() {
break;
}
ctx.expect_sym(Symbol::Comma)?;
let arg = ctx.parse()?;
args.push(arg);
}
}
ParseResult::Ok(Self { op, args })
}
}
impl Parsable for PAsmArg {
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
if let Some(ident) = ctx.maybe_parse() {
return ParseResult::Wrap(ident.map(Self::Value));
}
let mut next = ctx.expect_peek()?;
if next.is_symbol(Symbol::Minus) {
ctx.next();
if let Some(mut ident) = ctx.maybe_parse::<PIdent>() {
// TODO: this is so messed up
if let Some(i) = ident.node.as_mut() {
i.0.insert(0, '-')
}
return ParseResult::Wrap(ident.map(Self::Value));
}
next = ctx.expect_peek()?;
}
if !next.is_symbol(Symbol::OpenCurly) {
return ParseResult::Err(CompilerMsg::unexpected_token(
next,
"An identifier or {identifier}",
));
}
ctx.next();
let res = ctx.parse();
if res.recover {
ctx.seek_sym(Symbol::CloseCurly);
return ParseResult::SubErr;
}
ctx.expect_sym(Symbol::CloseCurly)?;
ParseResult::Ok(Self::Ref(res.node))
}
}
impl std::fmt::Debug for PAsmArg {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Value(v) => v.fmt(f),
Self::Ref(r) => write!(f, "{{{:?}}}", r),
}
}
}
impl std::fmt::Debug for PInstruction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.op.fmt(f)?;
let mut iter = self.args.iter();
if let Some(arg) = iter.next() {
f.write_str(" ")?;
arg.fmt(f)?;
}
for arg in iter {
f.write_str(", ")?;
arg.fmt(f)?;
}
Ok(())
}
}
-101
View File
@@ -1,101 +0,0 @@
use std::fmt::{Debug, Write};
use super::{
token::Symbol, CompilerMsg, Node, NodeParsable, PStatementLike, ParseResult, ParserCtx,
};
use crate::{
parser::{ParsableWith, TokenInstance},
util::Padder,
};
pub struct PBlock {
pub statements: Vec<Node<PStatementLike>>,
pub ret_last: bool,
}
impl ParsableWith for PBlock {
type Data = Option<Symbol>;
fn parse(ctx: &mut ParserCtx, end: Option<Symbol>) -> ParseResult<Self> {
let mut statements = Vec::new();
let is_end = |t: &TokenInstance| -> bool { end.map(|e| t.is_symbol(e)).unwrap_or(false) };
if ctx.peek().is_none_or(is_end) {
ctx.next();
return ParseResult::Ok(Self {
statements,
ret_last: false,
});
}
let mut expect_semi = false;
let mut recover = false;
loop {
let Some(next) = ctx.peek() else {
if end.is_some() {
recover = true;
ctx.err(CompilerMsg::unexpected_end());
}
break;
};
if is_end(next) {
ctx.next();
break;
}
if next.is_symbol(Symbol::Semicolon) {
ctx.next();
expect_semi = false;
continue;
} else if expect_semi {
ctx.err(CompilerMsg {
msg: "expected ';'".to_string(),
spans: vec![ctx.next_start().char_span()],
});
}
let res = PStatementLike::parse_node(ctx);
expect_semi = res
.node
.as_ref()
.is_some_and(|s| matches!(s, PStatementLike::Statement(..)));
statements.push(res.node);
if res.recover {
ctx.seek_syms(&[Symbol::Semicolon, Symbol::CloseCurly]);
if ctx.peek().is_none() {
recover = true;
break;
}
}
}
ParseResult::from_recover(
Self {
statements,
ret_last: expect_semi,
},
recover,
)
}
}
impl Debug for PBlock {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if !self.statements.is_empty() {
f.write_str("{\n ")?;
let mut padder = Padder::new(f);
let mut end = self.statements.len();
if self.ret_last {
end -= 1;
}
for i in 0..end {
let s = &self.statements[i];
// they don't expose wrap_buf :grief:
padder.write_str(&format!("{s:?};\n"))?;
}
if self.ret_last
&& let Some(s) = self.statements.last()
{
padder.write_str(&format!("{s:?}\n"))?;
}
f.write_char('}')?;
} else {
f.write_str("{}")?;
}
Ok(())
}
}
-112
View File
@@ -1,112 +0,0 @@
use std::fmt::Debug;
use super::{
CompilerMsg, Node, PExpr, PIdent, PType, Parsable, ParseResult, ParserCtx,
Symbol, Token,
};
pub struct PVarDef {
pub name: Node<PIdent>,
pub ty: Option<Node<PType>>,
}
pub struct PFieldDef {
pub name: Node<PIdent>,
pub val: Option<Node<PExpr>>,
}
impl Parsable for PVarDef {
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
let name = ctx.parse()?;
ParseResult::Ok(if ctx.peek().is_some_and(|n| n.is_symbol(Symbol::Colon)) {
ctx.next();
Self {
name,
ty: Some(ctx.parse()?),
}
} else {
Self { name, ty: None }
})
}
}
impl Parsable for PFieldDef {
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
let name = ctx.parse()?;
ParseResult::Ok(if ctx.peek().is_some_and(|n| n.is_symbol(Symbol::Colon)) {
ctx.next();
Self {
name,
val: Some(ctx.parse()?),
}
} else {
Self { name, val: None }
})
}
}
pub struct SelfVar {
pub ty: SelfType,
}
#[derive(PartialEq)]
pub enum SelfType {
Ref,
Take,
}
// impl Parsable for Option<SelfVar> {
// fn maybe_parse(ctx: &mut ParserCtx) -> Result<Option<Self>, CompilerMsg> {
// if let Some(mut next) = ctx.peek() {
// let mut ty = SelfType::Take;
// if next.is_symbol(Symbol::Ampersand) {
// ctx.next();
// ty = SelfType::Ref;
// next = ctx.expect_peek()?;
// }
// if let Token::Word(name) = &next.token {
// if name == "self" {
// ctx.next();
// return Ok(Some(Self { ty }));
// }
// }
// if ty != SelfType::Take {
// return Err(CompilerMsg::unexpected_token(next, "self"));
// }
// }
// Ok(None)
// }
// }
impl Debug for PVarDef {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.name.fmt(f)?;
if let Some(ty) = &self.ty {
write!(f, ": {:?}", ty)?;
}
Ok(())
}
}
impl Debug for PFieldDef {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.name.fmt(f)?;
if let Some(val) = &self.val {
write!(f, ": {:?}", val)?;
}
Ok(())
}
}
impl Debug for SelfVar {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self.ty {
SelfType::Ref => "&self",
SelfType::Take => "self",
}
)
}
}
-207
View File
@@ -1,207 +0,0 @@
use std::fmt::{Debug, Write};
use crate::{common::FilePos, ir::MemberTy, parser::NodeParsableWith};
use super::{
op::{InfixOp, PostfixOp},
util::parse_list,
CompilerMsg, Keyword, Node, PAsmBlock, PBlock, PIdent, PLiteral, PMap, PType, Parsable,
ParseResult, ParserCtx, Symbol,
};
type BoxNode = Node<Box<PExpr>>;
pub enum PExpr {
Lit(PLiteral),
Ident(Node<PIdent>),
BinaryOp(InfixOp, BoxNode, BoxNode),
PostfixOp(BoxNode, PostfixOp),
Block(Node<PBlock>),
Call(BoxNode, Vec<Node<PExpr>>),
Group(BoxNode),
Member(BoxNode, MemberTy, Node<PIdent>),
Generic(BoxNode, Vec<Node<PType>>),
AsmBlock(Node<PAsmBlock>),
Construct(BoxNode, Node<PMap>),
If(BoxNode, BoxNode),
Loop(BoxNode),
Break,
Continue,
}
impl Parsable for PExpr {
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
let start = ctx.next_start();
let mut e1 = Self::parse_unit_postfix(ctx)?;
while let Some(op) = ctx
.peek()
.map(|next| InfixOp::from_token(&next.token))
.flatten()
{
let span = start.to(ctx.prev_end());
ctx.next();
let n2 = ctx.parse()?.bx();
let (n1, op, n2) = fix_precedence(Node::new(e1, span).bx(), op, n2, start);
e1 = Self::BinaryOp(op, n1, n2);
}
return ParseResult::Ok(e1);
}
}
impl PExpr {
fn parse_unit_postfix(ctx: &mut ParserCtx) -> ParseResult<Self> {
let start = ctx.next_start();
// first get unit
let mut e1 = Self::parse_unit(ctx)?;
// then apply post ops
loop {
let span = start.to(ctx.prev_end());
let Some(next) = ctx.peek() else {
break;
};
if next.is_symbol(Symbol::OpenParen) {
ctx.next();
let args = parse_list(ctx, Symbol::CloseParen)?;
e1 = Self::Call(Node::new(e1, span).bx(), args);
continue;
} else if next.is_symbol(Symbol::OpenCurly) {
ctx.next();
let map = ctx.parse()?;
e1 = Self::Construct(Node::new(e1, span).bx(), map);
continue;
} else if next.is_symbol(Symbol::Dot) {
ctx.next();
let field = ctx.parse()?;
e1 = Self::Member(Node::new(e1, span).bx(), MemberTy::Field, field);
continue;
} else if next.is_symbol(Symbol::DoubleColon) {
ctx.next();
if ctx.peek().is_some_and(|i| i.is_symbol(Symbol::OpenAngle)) {
ctx.next();
let gargs = parse_list(ctx, Symbol::CloseAngle)?;
e1 = Self::Generic(Node::new(e1, span).bx(), gargs);
} else {
let field = ctx.parse()?;
e1 = Self::Member(Node::new(e1, span).bx(), MemberTy::Member, field);
}
continue;
} else if let Some(op) = PostfixOp::from_token(next) {
ctx.next();
e1 = Self::PostfixOp(Node::new(e1, span).bx(), op);
continue;
}
break;
}
return ParseResult::Ok(e1);
}
fn parse_unit(ctx: &mut ParserCtx) -> ParseResult<Self> {
let next = ctx.expect_peek()?;
return ParseResult::Ok(if next.is_symbol(Symbol::OpenParen) {
ctx.next();
if ctx.expect_peek()?.is_symbol(Symbol::CloseParen) {
ctx.next();
return ParseResult::Ok(PExpr::Lit(PLiteral::Unit));
}
let res = ctx.parse();
if res.recover {
ctx.seek_sym(Symbol::CloseParen);
}
ctx.expect_sym(Symbol::CloseParen)?;
Self::Group(res.node.bx())
} else if next.is_symbol(Symbol::OpenCurly) {
ctx.next();
Self::Block(PBlock::parse_node(ctx, Some(Symbol::CloseCurly))?)
} else if next.is_keyword(Keyword::If) {
ctx.next();
let cond = ctx.parse()?.bx();
let body = ctx.parse()?.bx();
Self::If(cond, body)
} else if next.is_keyword(Keyword::Loop) {
ctx.next();
let body = ctx.parse()?.bx();
Self::Loop(body)
} else if next.is_keyword(Keyword::Break) {
ctx.next();
Self::Break
} else if next.is_keyword(Keyword::Continue) {
ctx.next();
Self::Continue
} else if next.is_keyword(Keyword::Asm) {
ctx.next();
Self::AsmBlock(ctx.parse()?)
} else if let Some(res) = ctx.maybe_parse::<PLiteral>() {
return ParseResult::Wrap(res.map(Self::Lit));
} else {
let res = ctx.parse();
if res.node.is_some() {
Self::Ident(res.node)
} else {
let next = ctx.expect_peek()?;
return ParseResult::Err(CompilerMsg::unexpected_token(next, "an expression"));
}
});
}
}
pub fn fix_precedence(
mut n1: BoxNode,
mut op: InfixOp,
mut n2: BoxNode,
start: FilePos,
) -> (BoxNode, InfixOp, BoxNode) {
if let Some(box PExpr::BinaryOp(op2, _, _)) = n2.as_ref() {
if op.precedence() > op2.precedence() {
let Some(box PExpr::BinaryOp(op2, n21, n22)) = n2.inner else {
unreachable!();
};
let span = start.to(n21.origin.end);
let (n11, op1, n12) = fix_precedence(n1, op, n21, start);
n1 = Node::new(PExpr::BinaryOp(op1, n11, n12), span).bx();
op = op2;
n2 = n22;
}
}
(n1, op, n2)
}
impl Debug for PExpr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
PExpr::Lit(c) => c.fmt(f)?,
PExpr::Ident(n) => n.fmt(f)?,
PExpr::Block(b) => b.fmt(f)?,
PExpr::BinaryOp(op, e1, e2) => {
write!(f, "({:?}", *e1)?;
if op.pad() {
write!(f, " {} ", op.str())?;
} else {
write!(f, "{}", op.str())?;
}
write!(f, "{:?})", *e2)?;
}
PExpr::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(')')?;
}
PExpr::PostfixOp(e, op) => write!(f, "({:?}{})", e, op.str())?,
PExpr::Group(inner) => inner.fmt(f)?,
PExpr::AsmBlock(inner) => inner.fmt(f)?,
PExpr::Construct(node, inner) => write!(f, "{:?}{:?}", node, inner)?,
PExpr::If(cond, res) => write!(f, "if {cond:?} then {res:?}")?,
PExpr::Loop(res) => write!(f, "loop -> {res:?}")?,
PExpr::Break => write!(f, "break")?,
PExpr::Continue => write!(f, "continue")?,
PExpr::Member(e1, ty, name) => write!(f, "{:?}{}{:?}", e1, ty.sep(), name)?,
PExpr::Generic(e1, gargs) => write!(f, "{:?}<{:?}>", e1, gargs)?,
}
Ok(())
}
}
-96
View File
@@ -1,96 +0,0 @@
use super::{
util::parse_list, Node, PBlock, PGenericDef, PIdent, PType, PVarDef, Parsable, ParseResult,
ParserCtx, Symbol,
};
use std::fmt::Debug;
pub struct PFunctionHeader {
pub name: Node<PIdent>,
pub args: Vec<Node<PVarDef>>,
pub gargs: Vec<Node<PGenericDef>>,
pub ret: Option<Node<PType>>,
}
pub struct PFunction {
pub header: Node<PFunctionHeader>,
pub body: Node<PBlock>,
}
impl Parsable for PFunctionHeader {
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
let name = ctx.parse()?;
let generic_args = if ctx.expect_peek()?.is_symbol(Symbol::OpenAngle) {
ctx.next();
parse_list(ctx, Symbol::CloseAngle)?
} else {
Vec::new()
};
ctx.expect_sym(Symbol::OpenParen)?;
// let sel = ctx.maybe_parse();
// if sel.is_some() {
// if let Err(err) = ctx.expect_sym(Symbol::Comma) {
// ctx.err(err);
// ctx.seek_syms(&[Symbol::Comma, Symbol::CloseParen]);
// if ctx.peek().is_some_and(|i| i.is_symbol(Symbol::Comma)) {
// ctx.next();
// }
// }
// }
let args = parse_list(ctx, Symbol::CloseParen)?;
let ret = if ctx.peek().is_some_and(|i| i.is_symbol(Symbol::Arrow)) {
ctx.next();
Some(ctx.parse()?)
} else {
None
};
ParseResult::Ok(Self {
name,
args,
gargs: generic_args,
ret,
})
}
}
impl Parsable for PFunction {
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
let header = ctx.parse()?;
ctx.expect_sym(Symbol::OpenCurly)?;
let body = ctx.parse_with(Some(Symbol::CloseCurly))?;
ParseResult::Ok(Self { header, body })
}
}
impl Debug for PFunctionHeader {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("fn ")?;
self.name.fmt(f)?;
f.write_str("(")?;
// if let Some(s) = &self.sel {
// s.fmt(f)?;
// if self.args.first().is_some() {
// f.write_str(", ")?;
// }
// }
if let Some(a) = self.args.first() {
a.fmt(f)?;
}
for arg in self.args.iter().skip(1) {
f.write_str(", ")?;
arg.fmt(f)?;
}
f.write_str(")")?;
if let Some(ret) = &self.ret {
write!(f, " -> {:?}", ret)?;
}
Ok(())
}
}
impl Debug for PFunction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.header.fmt(f)?;
f.write_str(" ")?;
self.body.fmt(f)?;
Ok(())
}
}
-54
View File
@@ -1,54 +0,0 @@
use super::{CompilerMsg, Parsable, ParseResult, ParserCtx, Token};
use std::{
fmt::{Debug, Display},
ops::Deref,
};
#[derive(Clone)]
pub struct PIdent(pub String);
impl Parsable for PIdent {
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
let next = ctx.expect_peek()?;
let Token::Word(name) = &next.token else {
return ParseResult::Err(CompilerMsg::unexpected_token(next, "an identifier"));
};
let name = name.to_string();
ctx.next();
ParseResult::Ok(Self(name))
}
}
impl Parsable for Option<PIdent> {
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
let Some(next) = ctx.peek() else {
return ParseResult::Ok(None);
};
let Token::Word(name) = &next.token else {
return ParseResult::Ok(None);
};
let name = name.to_string();
ctx.next();
ParseResult::Ok(Some(PIdent(name)))
}
}
impl Debug for PIdent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl Deref for PIdent {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Display for PIdent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
-111
View File
@@ -1,111 +0,0 @@
use crate::parser::{Parsable, ParseResult};
use super::{PString, ParserCtx, Symbol, Token};
use std::fmt::Debug;
#[derive(Clone, PartialEq, Eq)]
pub enum PLiteral {
String(String),
Char(char),
Number(PNumber),
Unit,
}
#[derive(Clone, PartialEq, Eq)]
pub struct PNumber {
pub whole: String,
pub decimal: Option<String>,
pub ty: Option<String>,
}
impl Parsable for Option<PLiteral> {
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
let inst = ctx.expect_peek()?;
ParseResult::Ok(Some(match &inst.token {
Token::Symbol(Symbol::SingleQuote) => {
let chars = ctx.chars();
let c = chars.expect_next()?;
chars.expect('\'')?;
ctx.next();
PLiteral::Char(c)
}
Token::Symbol(Symbol::DoubleQuote) => {
ctx.next();
let s = ctx.parse::<PString>()?;
return match s.inner {
Some(s) => ParseResult::Ok(Some(PLiteral::String(s.0))),
None => ParseResult::SubErr,
};
}
Token::Word(text) => {
let first = text.chars().next().unwrap();
if !first.is_ascii_digit() {
return ParseResult::Ok(None);
}
let (whole, ty) = parse_whole_num(text);
let mut num = PNumber {
whole,
decimal: None,
ty,
};
ctx.next();
if num.ty.is_none() && ctx.peek().is_some_and(|i| i.is_symbol(Symbol::Dot)) {
ctx.next();
if let Some(next) = ctx.peek() {
if let Token::Word(i) = &next.token {
if i.chars().next().unwrap().is_ascii_digit() {
let (decimal, ty) = parse_whole_num(i);
num.decimal = Some(decimal);
num.ty = ty;
ctx.next();
}
}
}
}
PLiteral::Number(num)
}
_ => return ParseResult::Ok(None),
}))
}
}
pub fn parse_whole_num(text: &str) -> (String, Option<String>) {
let mut whole = String::new();
let mut ty = String::new();
for c in text.chars() {
if ty.is_empty() {
if c.is_ascii_digit() {
whole.push(c);
} else if c != '_' {
ty.push(c);
}
} else {
ty.push(c);
}
}
(whole, if ty.is_empty() { None } else { Some(ty) })
}
impl Debug for PLiteral {
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 PNumber {
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, "_{}", ty)?;
}
Ok(())
}
}

Some files were not shown because too many files have changed in this diff Show More