BRANCHING (TURING COMPLETE????)

This commit is contained in:
2025-03-29 15:08:15 -04:00
parent 021434d2f1
commit f57af3b2b5
25 changed files with 780 additions and 486 deletions

View File

@@ -7,7 +7,20 @@ fn start() {
println("Helld!"); println("Helld!");
println("Hello World!!!!!"); println("Hello World!!!!!");
thinger(); thinger();
println("what"); let x = 3;
if not(not(lt(x, 5))) {
println("tada!");
};
println("before:");
x = 0;
loop {
if not(lt(x, 5)) {
break;
};
println("RAAAAA");
x = add(x, 1);
};
println("after");
print(tester()); print(tester());
let test = Test { let test = Test {
a: 10, a: 10,
@@ -77,6 +90,27 @@ fn sub(a: 64, b: 64) -> 64 {
c c
} }
fn lt(a: 64, b: 64) -> 64 {
let c: 64 = 0;
asm (t0 = a, t1 = b, a0 = c) {
ld t0, 0, t0
ld t1, 0, t1
slt t0, t0, t1
sd t0, 0, a0
};
c
}
fn not(a: 64) -> 64 {
let c: 64 = 0;
asm (t0 = a, a0 = c) {
ld t0, 0, t0
xori t0, t0, 1
sd t0, 0, a0
};
c
}
fn arger(a: slice<8>, b: slice<8>, c: slice<8>) { fn arger(a: slice<8>, b: slice<8>, c: slice<8>) {
print(a); print(a);
print(b); print(b);

View File

@@ -1,5 +1,7 @@
use crate::{ use crate::{
compiler::program::{Addr, Instr, SymTable}, ir::Symbol, util::LabeledFmt compiler::program::{Addr, Instr, SymTable},
ir::Symbol,
util::LabeledFmt,
}; };
use super::*; use super::*;
@@ -52,6 +54,12 @@ pub enum LinkerInstruction<R = Reg, S = Symbol> {
}, },
Call(S), Call(S),
J(S), J(S),
Branch {
to: S,
typ: Funct3,
left: R,
right: R,
},
Ret, Ret,
ECall, ECall,
EBreak, EBreak,
@@ -66,10 +74,10 @@ pub fn addi(dest: Reg, src: Reg, imm: BitsI32<11, 0>) -> RawInstruction {
} }
impl Instr for LinkerInstruction { impl Instr for LinkerInstruction {
fn push( fn push_to(
&self, &self,
data: &mut Vec<u8>, data: &mut Vec<u8>,
sym_map: &SymTable, sym_map: &mut SymTable,
pos: Addr, pos: Addr,
missing: bool, missing: bool,
) -> Option<Symbol> { ) -> Option<Symbol> {
@@ -135,6 +143,20 @@ impl Instr for LinkerInstruction {
Self::ECall => ecall(), Self::ECall => ecall(),
Self::EBreak => ebreak(), Self::EBreak => ebreak(),
Self::Li { dest, imm } => addi(*dest, zero, BitsI32::new(*imm)), 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()); data.extend(last.to_le_bytes());
None None
@@ -181,7 +203,11 @@ pub struct DebugInstr<'a, R, S, L: Fn(&mut std::fmt::Formatter<'_>, &S) -> std::
} }
impl<R: std::fmt::Debug, S: std::fmt::Debug> LabeledFmt<S> for LinkerInstruction<R, S> { 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 { fn fmt_label(
&self,
f: &mut std::fmt::Formatter<'_>,
label: &dyn crate::util::Labeler<S>,
) -> std::fmt::Result {
match self { match self {
Self::ECall => write!(f, "ecall"), Self::ECall => write!(f, "ecall"),
Self::EBreak => write!(f, "ebreak"), Self::EBreak => write!(f, "ebreak"),
@@ -190,7 +216,7 @@ impl<R: std::fmt::Debug, S: std::fmt::Debug> LabeledFmt<S> for LinkerInstruction
Self::La { dest, src } => { Self::La { dest, src } => {
write!(f, "la {dest:?}, @")?; write!(f, "la {dest:?}, @")?;
label(f, src) label(f, src)
}, }
Self::Load { Self::Load {
width, width,
dest, dest,
@@ -229,6 +255,12 @@ impl<R: std::fmt::Debug, S: std::fmt::Debug> LabeledFmt<S> for LinkerInstruction
write!(f, "j ")?; write!(f, "j ")?;
label(f, s) label(f, s)
} }
Self::Branch {
to,
typ,
left,
right,
} => write!(f, "b{} {left:?} {right:?} {to:?}", branch::str(*typ)),
Self::Ret => write!(f, "ret"), Self::Ret => write!(f, "ret"),
} }
} }

View File

@@ -1,7 +1,7 @@
use std::collections::HashMap; use std::collections::HashMap;
use crate::{ use crate::{
compiler::{arch::riscv::Reg, debug::DebugInfo, UnlinkedProgram}, compiler::{arch::riscv::Reg, debug::DebugInfo, UnlinkedFunction, UnlinkedProgram},
ir::{ ir::{
arch::riscv64::{RV64Instruction as AI, RegRef}, arch::riscv64::{RV64Instruction as AI, RegRef},
IRLInstruction as IRI, IRLProgram, Len, Size, IRLInstruction as IRI, IRLProgram, Len, Size,
@@ -84,6 +84,7 @@ pub fn compile(program: &IRLProgram) -> UnlinkedProgram<LI> {
v.push(LI::sd(ra, stack_ra, sp)); v.push(LI::sd(ra, stack_ra, sp));
} }
} }
let mut locations = HashMap::new();
let mut irli = Vec::new(); let mut irli = Vec::new();
for i in &f.instructions { for i in &f.instructions {
irli.push((v.len(), format!("{i:?}"))); irli.push((v.len(), format!("{i:?}")));
@@ -147,7 +148,7 @@ pub fn compile(program: &IRLProgram) -> UnlinkedProgram<LI> {
} }
fn r(rr: RegRef) -> Reg { fn r(rr: RegRef) -> Reg {
match rr { match rr {
RegRef::Var(var_ident) => todo!(), RegRef::Var(..) => todo!(),
RegRef::Reg(reg) => reg, RegRef::Reg(reg) => reg,
} }
} }
@@ -160,7 +161,7 @@ pub fn compile(program: &IRLProgram) -> UnlinkedProgram<LI> {
dest: r(dest), dest: r(dest),
src: r(src), src: r(src),
}), }),
AI::La { dest, src } => todo!(), AI::La { .. } => todo!(),
AI::Load { AI::Load {
width, width,
dest, dest,
@@ -216,9 +217,10 @@ pub fn compile(program: &IRLProgram) -> UnlinkedProgram<LI> {
imm, imm,
}), }),
AI::Ret => v.push(LI::Ret), AI::Ret => v.push(LI::Ret),
AI::Call(s) => todo!(), AI::Call(..) => todo!(),
AI::Jal { dest, offset } => todo!(), AI::Jal { .. } => todo!(),
AI::J(s) => todo!(), AI::J(..) => todo!(),
AI::Branch { .. } => todo!(),
} }
} }
} }
@@ -229,6 +231,21 @@ pub fn compile(program: &IRLProgram) -> UnlinkedProgram<LI> {
v.push(LI::ld(t0, rva, sp)); v.push(LI::ld(t0, rva, sp));
mov_mem(&mut v, sp, stack[src], t0, 0, t1, align(&f.ret_size) as u32); mov_mem(&mut v, sp, stack[src], t0, 0, t1, align(&f.ret_size) as u32);
} }
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); dbg.push_fn(irli);
@@ -239,12 +256,17 @@ pub fn compile(program: &IRLProgram) -> UnlinkedProgram<LI> {
v.push(LI::addi(sp, sp, stack_len)); v.push(LI::addi(sp, sp, stack_len));
} }
v.push(LI::Ret); v.push(LI::Ret);
fns.push((v, *sym)); fns.push(UnlinkedFunction {
instrs: v,
sym: *sym,
locations,
});
} }
UnlinkedProgram { UnlinkedProgram {
fns: fns.into_iter().map(|(v, s, ..)| (v, s)).collect(), fns,
ro_data: data, ro_data: data,
start: Some(program.entry()), start: Some(program.entry()),
dbg, dbg,
sym_count: program.len(),
} }
} }

View File

@@ -22,6 +22,7 @@ pub const IMM_OP: u32 = 0b0010011;
pub const OP: u32 = 0b0110011; pub const OP: u32 = 0b0110011;
pub const JAL: u32 = 0b1101111; pub const JAL: u32 = 0b1101111;
pub const JALR: u32 = 0b1100111; pub const JALR: u32 = 0b1100111;
pub const BRANCH: u32 = 0b1100011;
pub type Funct3 = Bits32<2, 0>; pub type Funct3 = Bits32<2, 0>;
pub type Funct7 = Bits32<6, 0>; pub type Funct7 = Bits32<6, 0>;
@@ -59,7 +60,7 @@ pub const fn b_type(rs2: Reg, rs1: Reg, funct3: Funct3, imm: Bits32<12, 1>, opco
+ (imm.bits(10, 5) << 25) + (imm.bits(10, 5) << 25)
+ (rs2.val() << 20) + (rs2.val() << 20)
+ (rs1.val() << 15) + (rs1.val() << 15)
+ (funct3.val() << 8) + (funct3.val() << 12)
+ (imm.bits(4, 1) << 8) + (imm.bits(4, 1) << 8)
+ (imm.bit(11) << 7) + (imm.bit(11) << 7)
+ opcode) + opcode)

View File

@@ -63,6 +63,28 @@ pub mod width {
} }
} }
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 { pub const fn ecall() -> RawInstruction {
i_type(Bits32::new(0), zero, Bits32::new(0), zero, SYSTEM) i_type(Bits32::new(0), zero, Bits32::new(0), zero, SYSTEM)
} }
@@ -94,3 +116,7 @@ pub const fn j(offset: BitsI32<20, 1>) -> RawInstruction {
pub const fn ret() -> RawInstruction { pub const fn ret() -> RawInstruction {
jalr(zero, BitsI32::new(0), ra) 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)
}

View File

@@ -13,40 +13,54 @@ pub struct LinkedProgram {
} }
pub struct UnlinkedProgram<I: Instr> { pub struct UnlinkedProgram<I: Instr> {
pub fns: Vec<(Vec<I>, Symbol)>, pub fns: Vec<UnlinkedFunction<I>>,
pub ro_data: Vec<(Vec<u8>, Symbol)>, pub ro_data: Vec<(Vec<u8>, Symbol)>,
pub sym_count: usize,
pub start: Option<Symbol>, pub start: Option<Symbol>,
pub dbg: DebugInfo, pub dbg: DebugInfo,
} }
pub struct UnlinkedFunction<I: Instr> {
pub instrs: Vec<I>,
pub sym: Symbol,
pub locations: HashMap<usize, Symbol>,
}
impl<I: Instr> UnlinkedProgram<I> { impl<I: Instr> UnlinkedProgram<I> {
pub fn link(self) -> LinkedProgram { pub fn link(self) -> LinkedProgram {
let mut data = Vec::new(); let mut data = Vec::new();
let mut sym_table = SymTable::new(self.fns.len() + self.ro_data.len()); let mut sym_table = SymTable::new(self.sym_count);
let mut missing = HashMap::<Symbol, Vec<(Addr, I)>>::new(); let mut missing = HashMap::<Symbol, Vec<(Addr, I)>>::new();
for (val, id) in self.ro_data { for (val, id) in self.ro_data {
sym_table.insert(id, Addr(data.len() as u64)); sym_table.insert(id, Addr(data.len() as u64));
data.extend(val); data.extend(val);
} }
data.resize(data.len() + (4 - data.len() % 4), 0); data.resize(data.len() + (4 - data.len() % 4), 0);
for (fun, id) in self.fns { for f in self.fns {
sym_table.insert(id, Addr(data.len() as u64)); let mut added = vec![f.sym];
for i in fun { 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); let i_pos = Addr(data.len() as u64);
if let Some(sym) = i.push(&mut data, &sym_table, i_pos, false) { 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) { if let Some(vec) = missing.get_mut(&sym) {
vec.push((i_pos, i)); vec.push((i_pos, instr));
} else { } else {
missing.insert(sym, vec![(i_pos, i)]); missing.insert(sym, vec![(i_pos, instr)]);
} }
} }
} }
if let Some(vec) = missing.remove(&id) { for add in added {
for (addr, i) in vec { if let Some(vec) = missing.remove(&add) {
let mut replace = Vec::new(); for (addr, i) in vec {
i.push(&mut replace, &sym_table, addr, true); let mut replace = Vec::new();
let pos = addr.val() as usize; i.push_to(&mut replace, &mut sym_table, addr, true);
data[pos..pos + replace.len()].copy_from_slice(&replace); let pos = addr.val() as usize;
data[pos..pos + replace.len()].copy_from_slice(&replace);
}
} }
} }
} }
@@ -61,8 +75,13 @@ impl<I: Instr> UnlinkedProgram<I> {
} }
pub trait Instr { pub trait Instr {
fn push(&self, data: &mut Vec<u8>, syms: &SymTable, pos: Addr, missing: bool) fn push_to(
-> Option<Symbol>; &self,
data: &mut Vec<u8>,
syms: &mut SymTable,
pos: Addr,
missing: bool,
) -> Option<Symbol>;
} }
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
@@ -92,16 +111,16 @@ impl SymTable {
impl<I: Instr + Labelable<Symbol> + LabeledFmt<Symbol>> std::fmt::Debug for UnlinkedProgram<I> { impl<I: Instr + Labelable<Symbol> + LabeledFmt<Symbol>> std::fmt::Debug for UnlinkedProgram<I> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for ((v, s), irli) in self.fns.iter().zip(&self.dbg.ir_lower) { for (fun, irli) in self.fns.iter().zip(&self.dbg.ir_lower) {
writeln!(f, "{}:", self.dbg.sym_label(*s).unwrap())?; writeln!(f, "{}:", self.dbg.sym_label(fun.sym).unwrap())?;
let mut liter = irli.iter(); let mut liter = irli.iter();
let mut cur = liter.next(); let mut cur = liter.next();
for (i, instr) in v.iter().enumerate() { for (i, instr) in fun.instrs.iter().enumerate() {
if let Some(c) = cur { while let Some(c) = cur
if i == c.0 { && i == c.0
writeln!(f, " {}:", c.1)?; {
cur = liter.next(); writeln!(f, " {}:", c.1)?;
} cur = liter.next();
} }
writeln!( writeln!(
f, f,

View File

@@ -47,4 +47,12 @@ pub enum IRLInstruction {
Ret { Ret {
src: VarID, src: 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),
} }

View File

@@ -1,8 +1,11 @@
use std::collections::HashMap; use std::collections::HashMap;
use crate::ir::SymbolSpace; use crate::ir::{IRUFunction, IRUInstrInst, Size, SymbolSpace};
use super::{IRLFunction, IRLInstruction, IRUInstruction, IRUProgram, Len, Symbol, Type, VarID}; use super::{
IRLFunction, IRLInstruction, IRUInstruction, IRUProgram, Len, Symbol, SymbolSpaceBuilder, Type,
VarID,
};
pub struct IRLProgram { pub struct IRLProgram {
sym_space: SymbolSpace, sym_space: SymbolSpace,
@@ -20,179 +23,18 @@ impl IRLProgram {
} }
} }
let start = start.ok_or("no start method found")?; let start = start.ok_or("no start method found")?;
let mut builder = SymbolSpace::with_entries(&[start]); let mut ssbuilder = SymbolSpaceBuilder::with_entries(&[start]);
let entry = builder.func(&start); let entry = ssbuilder.func(&start);
while let Some((sym, i)) = builder.pop_fn() { while let Some((sym, i)) = ssbuilder.pop_fn() {
let f = p.fns[i.0].as_ref().unwrap(); let f = p.fns[i.0].as_ref().unwrap();
let mut instrs = Vec::new(); let mut fbuilder = IRLFunctionBuilder::new(p, &mut ssbuilder);
let mut stack = HashMap::new();
let mut makes_call = false;
let mut alloc_stack = |i: VarID| -> bool {
let size = *stack
.entry(i)
.or_insert(p.size_of_var(i).expect("unsized type"));
size == 0
};
for i in &f.instructions { for i in &f.instructions {
match &i.i { fbuilder.insert_instr(i);
IRUInstruction::Mv { dest, src } => {
if alloc_stack(dest.id) {
continue;
}
instrs.push(IRLInstruction::Mv {
dest: dest.id,
dest_offset: 0,
src: src.id,
src_offset: 0,
});
}
IRUInstruction::Ref { dest, src } => {
if alloc_stack(dest.id) {
continue;
}
instrs.push(IRLInstruction::Ref {
dest: dest.id,
src: src.id,
});
}
IRUInstruction::LoadData { dest, src } => {
if alloc_stack(dest.id) {
continue;
}
let data = &p.data[src.0];
let ddef = p.get_data(*src);
let sym = builder.ro_data(src, data, Some(ddef.label.clone()));
instrs.push(IRLInstruction::LoadData {
dest: dest.id,
offset: 0,
len: data.len() as Len,
src: sym,
});
}
IRUInstruction::LoadSlice { dest, src } => {
if alloc_stack(dest.id) {
continue;
}
let data = &p.data[src.0];
let def = p.get_data(*src);
let Type::Array(ty, len) = &def.ty else {
return Err(format!("tried to load {} as slice", p.type_name(&def.ty)));
};
let sym = builder.ro_data(src, data, Some(def.label.clone()));
instrs.push(IRLInstruction::LoadAddr {
dest: dest.id,
offset: 0,
src: sym,
});
let sym = builder.anon_ro_data(
&(*len as u64).to_le_bytes(),
Some(format!("len: {}", len)),
);
instrs.push(IRLInstruction::LoadData {
dest: dest.id,
offset: 8,
len: 8,
src: sym,
});
}
IRUInstruction::LoadFn { dest, src } => {
if alloc_stack(dest.id) {
continue;
}
let sym = builder.func(src);
instrs.push(IRLInstruction::LoadAddr {
dest: dest.id,
offset: 0,
src: sym,
});
}
IRUInstruction::Call { dest, f, args } => {
alloc_stack(dest.id);
makes_call = true;
let fid = &p.fn_map[&f.id];
let sym = builder.func(fid);
let ret_size = p.size_of_var(dest.id).expect("unsized type");
let dest = if ret_size > 0 {
Some((dest.id, ret_size))
} else {
None
};
instrs.push(IRLInstruction::Call {
dest,
f: sym,
args: args
.iter()
.map(|a| (a.id, p.size_of_var(a.id).expect("unsized type")))
.collect(),
});
}
IRUInstruction::AsmBlock { instructions, args } => {
instrs.push(IRLInstruction::AsmBlock {
instructions: instructions.clone(),
args: args.iter().cloned().map(|(r, v)| (r, v.id)).collect(),
})
}
IRUInstruction::Ret { src } => instrs.push(IRLInstruction::Ret { src: src.id }),
IRUInstruction::Construct { dest, fields } => {
if alloc_stack(dest.id) {
continue;
}
let ty = &p.get_var(dest.id).ty;
let Type::Concrete(id) = ty else {
return Err(format!("Failed to contruct type {}", p.type_name(ty)));
};
let struc = p.get_struct(*id);
for (name, var) in fields {
instrs.push(IRLInstruction::Mv {
dest: dest.id,
src: var.id,
dest_offset: struc.fields[name].offset,
src_offset: 0,
})
}
}
IRUInstruction::Access { dest, src, field } => {
if alloc_stack(dest.id) {
continue;
}
let ty = &p.get_var(src.id).ty;
let Type::Concrete(id) = ty else {
return Err(format!(
"Failed to access field of struct {}",
p.type_name(ty)
));
};
let struc = p.get_struct(*id);
let Some(field) = struc.fields.get(field) else {
return Err(format!("No field {field} in struct {}", p.type_name(ty)));
};
instrs.push(IRLInstruction::Mv {
dest: dest.id,
src: src.id,
src_offset: field.offset,
dest_offset: 0,
})
}
};
} }
builder.write_fn( let res = fbuilder.finish(f);
sym, ssbuilder.write_fn(sym, res, Some(f.name.clone()));
IRLFunction {
instructions: instrs,
makes_call,
args: f
.args
.iter()
.map(|a| (*a, p.size_of_var(*a).expect("unsized type")))
.collect(),
ret_size: p.size_of_type(&f.ret).expect("unsized type"),
stack,
},
Some(f.name.clone()),
);
} }
let sym_space = builder.finish().expect("we failed the mission"); let sym_space = ssbuilder.finish().expect("we failed the mission");
Ok(Self { sym_space, entry }) Ok(Self { sym_space, entry })
} }
@@ -201,6 +43,220 @@ impl IRLProgram {
} }
} }
pub struct IRLFunctionBuilder<'a> {
program: &'a IRUProgram,
builder: &'a mut SymbolSpaceBuilder,
instrs: Vec<IRLInstruction>,
stack: HashMap<VarID, Size>,
makes_call: bool,
outer: Option<Symbol>,
}
impl<'a> IRLFunctionBuilder<'a> {
pub fn new(program: &'a IRUProgram, builder: &'a mut SymbolSpaceBuilder) -> Self {
Self {
instrs: Vec::new(),
stack: HashMap::new(),
makes_call: false,
program,
builder,
outer: None,
}
}
pub fn alloc_stack(&mut self, i: VarID) -> Option<()> {
let size = *self
.stack
.entry(i)
.or_insert(self.program.size_of_var(i).expect("unsized type"));
if size == 0 {
None
} else {
Some(())
}
}
pub fn insert_instr(&mut self, i: &IRUInstrInst) -> Option<Option<String>> {
match &i.i {
IRUInstruction::Mv { dest, src } => {
self.alloc_stack(dest.id)?;
self.instrs.push(IRLInstruction::Mv {
dest: dest.id,
dest_offset: 0,
src: src.id,
src_offset: 0,
});
}
IRUInstruction::Ref { dest, src } => {
self.alloc_stack(dest.id)?;
self.instrs.push(IRLInstruction::Ref {
dest: dest.id,
src: src.id,
});
}
IRUInstruction::LoadData { dest, src } => {
self.alloc_stack(dest.id)?;
let data = &self.program.data[src.0];
let ddef = self.program.get_data(*src);
let sym = self.builder.ro_data(src, data, Some(ddef.label.clone()));
self.instrs.push(IRLInstruction::LoadData {
dest: dest.id,
offset: 0,
len: data.len() as Len,
src: sym,
});
}
IRUInstruction::LoadSlice { dest, src } => {
self.alloc_stack(dest.id)?;
let data = &self.program.data[src.0];
let def = self.program.get_data(*src);
let Type::Array(_, len) = &def.ty else {
return Some(Some(format!(
"tried to load {} as slice",
self.program.type_name(&def.ty)
)));
};
let sym = self.builder.ro_data(src, data, Some(def.label.clone()));
self.instrs.push(IRLInstruction::LoadAddr {
dest: dest.id,
offset: 0,
src: sym,
});
let sym = self
.builder
.anon_ro_data(&(*len as u64).to_le_bytes(), Some(format!("len: {}", len)));
self.instrs.push(IRLInstruction::LoadData {
dest: dest.id,
offset: 8,
len: 8,
src: sym,
});
}
IRUInstruction::LoadFn { dest, src } => {
self.alloc_stack(dest.id)?;
let sym = self.builder.func(src);
self.instrs.push(IRLInstruction::LoadAddr {
dest: dest.id,
offset: 0,
src: sym,
});
}
IRUInstruction::Call { dest, f, args } => {
self.alloc_stack(dest.id);
self.makes_call = true;
let fid = &self.program.fn_map[&f.id];
let sym = self.builder.func(fid);
let ret_size = self.program.size_of_var(dest.id).expect("unsized type");
let dest = if ret_size > 0 {
Some((dest.id, ret_size))
} else {
None
};
self.instrs.push(IRLInstruction::Call {
dest,
f: sym,
args: args
.iter()
.map(|a| (a.id, self.program.size_of_var(a.id).expect("unsized type")))
.collect(),
});
}
IRUInstruction::AsmBlock { instructions, args } => {
self.instrs.push(IRLInstruction::AsmBlock {
instructions: instructions.clone(),
args: args.iter().cloned().map(|(r, v)| (r, v.id)).collect(),
})
}
IRUInstruction::Ret { src } => self.instrs.push(IRLInstruction::Ret { src: src.id }),
IRUInstruction::Construct { dest, fields } => {
self.alloc_stack(dest.id)?;
let ty = &self.program.get_var(dest.id).ty;
let Type::Concrete(id) = ty else {
return Some(Some(format!(
"Failed to contruct type {}",
self.program.type_name(ty)
)));
};
let struc = self.program.get_struct(*id);
for (name, var) in fields {
self.instrs.push(IRLInstruction::Mv {
dest: dest.id,
src: var.id,
dest_offset: struc.fields[name].offset,
src_offset: 0,
})
}
}
IRUInstruction::Access { dest, src, field } => {
self.alloc_stack(dest.id)?;
let ty = &self.program.get_var(src.id).ty;
let Type::Concrete(id) = ty else {
return Some(Some(format!(
"Failed to access field of struct {}",
self.program.type_name(ty)
)));
};
let struc = self.program.get_struct(*id);
let Some(field) = struc.fields.get(field) else {
return Some(Some(format!(
"No field {field} in struct {}",
self.program.type_name(ty)
)));
};
self.instrs.push(IRLInstruction::Mv {
dest: dest.id,
src: src.id,
src_offset: field.offset,
dest_offset: 0,
})
}
IRUInstruction::If { cond, body } => {
let sym = self.builder.reserve();
self.instrs.push(IRLInstruction::Branch {
to: *sym,
cond: cond.id,
});
for i in body {
self.insert_instr(i);
}
self.instrs.push(IRLInstruction::Mark(*sym));
}
IRUInstruction::Loop { body } => {
let top = self.builder.reserve();
let bot = self.builder.reserve();
let old = self.outer;
self.outer = Some(*bot);
self.instrs.push(IRLInstruction::Mark(*top));
for i in body {
self.insert_instr(i);
}
self.instrs.push(IRLInstruction::Jump(*top));
self.instrs.push(IRLInstruction::Mark(*bot));
self.outer = old;
}
IRUInstruction::Break => {
self.instrs.push(IRLInstruction::Jump(
self.outer.expect("Tried to break outside of loop"),
));
}
};
Some(None)
}
pub fn finish(self, f: &IRUFunction) -> IRLFunction {
IRLFunction {
instructions: self.instrs,
makes_call: self.makes_call,
args: f
.args
.iter()
.map(|a| (*a, self.program.size_of_var(*a).expect("unsized type")))
.collect(),
ret_size: self.program.size_of_type(&f.ret).expect("unsized type"),
stack: self.stack,
}
}
}
impl std::ops::Deref for IRLProgram { impl std::ops::Deref for IRLProgram {
type Target = SymbolSpace; type Target = SymbolSpace;

View File

@@ -18,6 +18,7 @@ impl std::ops::Deref for WritableSymbol {
pub struct SymbolSpace { pub struct SymbolSpace {
ro_data: Vec<(Symbol, Vec<u8>)>, ro_data: Vec<(Symbol, Vec<u8>)>,
fns: Vec<(Symbol, IRLFunction)>, fns: Vec<(Symbol, IRLFunction)>,
len: usize,
labels: Vec<Option<String>>, labels: Vec<Option<String>>,
} }
@@ -32,21 +33,6 @@ pub struct SymbolSpaceBuilder {
} }
impl SymbolSpace { impl SymbolSpace {
pub fn with_entries(entries: &[FnID]) -> SymbolSpaceBuilder {
let mut s = SymbolSpaceBuilder {
symbols: 0,
unwritten_fns: Vec::new(),
fn_map: HashMap::new(),
data_map: HashMap::new(),
ro_data: Vec::new(),
fns: Vec::new(),
labels: Vec::new(),
};
for e in entries {
s.func(e);
}
s
}
pub fn ro_data(&self) -> &[(Symbol, Vec<u8>)] { pub fn ro_data(&self) -> &[(Symbol, Vec<u8>)] {
&self.ro_data &self.ro_data
} }
@@ -56,9 +42,30 @@ impl SymbolSpace {
pub fn labels(&self) -> &[Option<String>] { pub fn labels(&self) -> &[Option<String>] {
&self.labels &self.labels
} }
pub fn len(&self) -> usize {
self.len
}
} }
impl SymbolSpaceBuilder { 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)> { pub fn pop_fn(&mut self) -> Option<(WritableSymbol, FnID)> {
self.unwritten_fns.pop() self.unwritten_fns.pop()
} }
@@ -94,7 +101,6 @@ impl SymbolSpaceBuilder {
data: Vec<u8>, data: Vec<u8>,
name: Option<String>, name: Option<String>,
) -> Symbol { ) -> Symbol {
let data = data.into();
self.ro_data.push((*sym, data)); self.ro_data.push((*sym, data));
self.labels[sym.0 .0] = name; self.labels[sym.0 .0] = name;
*sym *sym
@@ -116,11 +122,12 @@ impl SymbolSpaceBuilder {
WritableSymbol(Symbol(val)) WritableSymbol(Symbol(val))
} }
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.fns.len() + self.ro_data.len() self.symbols
} }
pub fn finish(self) -> Option<SymbolSpace> { pub fn finish(self) -> Option<SymbolSpace> {
if self.unwritten_fns.is_empty() { if self.unwritten_fns.is_empty() {
Some(SymbolSpace { Some(SymbolSpace {
len: self.symbols,
fns: self.fns, fns: self.fns,
ro_data: self.ro_data, ro_data: self.ro_data,
labels: self.labels, labels: self.labels,

View File

@@ -3,7 +3,7 @@ use std::{collections::HashMap, fmt::Write};
use super::{ use super::{
arch::riscv64::RV64Instruction, inst::VarInst, DataID, FnID, IRUInstrInst, Type, VarID, arch::riscv64::RV64Instruction, inst::VarInst, DataID, FnID, IRUInstrInst, Type, VarID,
}; };
use crate::{common::FileSpan, compiler::arch::riscv::Reg, util::Padder}; use crate::{compiler::arch::riscv::Reg, util::Padder};
pub struct IRUFunction { pub struct IRUFunction {
pub name: String, pub name: String,
@@ -54,50 +54,64 @@ pub enum IRUInstruction {
dest: VarInst, dest: VarInst,
fields: HashMap<String, VarInst>, fields: HashMap<String, VarInst>,
}, },
} If {
cond: VarInst,
pub struct IRInstructions { body: Vec<IRUInstrInst>,
vec: Vec<IRUInstrInst>, },
} Loop {
body: Vec<IRUInstrInst>,
impl IRUFunction { },
pub fn new(name: String, args: Vec<VarID>, ret: Type, instructions: IRInstructions) -> Self { Break,
Self {
name,
ret,
args,
instructions: instructions.vec,
}
}
}
impl IRInstructions {
pub fn new() -> Self {
Self { vec: Vec::new() }
}
pub fn push(&mut self, i: IRUInstruction, span: FileSpan) {
self.vec.push(IRUInstrInst { i, span });
}
} }
impl std::fmt::Debug for IRUInstruction { impl std::fmt::Debug for IRUInstruction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
Self::Mv { dest, src } => write!(f, "{dest:?} <- {src:?}"), Self::Mv { dest, src } => write!(f, "{dest:?} <- {src:?}")?,
Self::Ref { dest, src } => write!(f, "{dest:?} <- &{src:?}"), Self::Ref { dest, src } => write!(f, "{dest:?} <- &{src:?}")?,
Self::LoadData { dest, src } => write!(f, "{dest:?} <- {src:?}"), Self::LoadData { dest, src } => write!(f, "{dest:?} <- {src:?}")?,
Self::LoadFn { dest, src } => write!(f, "{dest:?} <- {src:?}"), Self::LoadFn { dest, src } => write!(f, "{dest:?} <- {src:?}")?,
Self::LoadSlice { dest, src } => write!(f, "{dest:?} <- &[{src:?}]"), Self::LoadSlice { dest, src } => write!(f, "{dest:?} <- &[{src:?}]")?,
Self::Call { Self::Call {
dest, dest,
f: func, f: func,
args, args,
} => write!(f, "{dest:?} <- {func:?}({args:?})"), } => write!(f, "{dest:?} <- {func:?}({args:?})")?,
Self::AsmBlock { args, instructions } => write!(f, "asm {args:?} {instructions:#?}"), Self::AsmBlock { args, instructions } => write!(f, "asm {args:?} {instructions:#?}")?,
Self::Ret { src } => f.debug_struct("Ret").field("src", src).finish(), Self::Ret { src } => f.debug_struct("Ret").field("src", src).finish()?,
Self::Construct { dest, fields } => write!(f, "{dest:?} <- {fields:?}"), Self::Construct { dest, fields } => write!(f, "{dest:?} <- {fields:?}")?,
Self::Access { dest, src, field } => write!(f, "{dest:?} <- {src:?}.{field}"), Self::Access { dest, src, field } => write!(f, "{dest:?} <- {src:?}.{field}")?,
Self::If { cond, body } => {
write!(f, "if {cond:?}:")?;
if !body.is_empty() {
f.write_str("{\n ")?;
let mut padder = Padder::new(f);
for i in body {
// they don't expose wrap_buf :grief:
padder.write_str(&format!("{i:?};\n"))?;
}
f.write_char('}')?;
} else {
f.write_str("{}")?;
}
}
Self::Loop { body } => {
write!(f, "loop:")?;
if !body.is_empty() {
f.write_str("{\n ")?;
let mut padder = Padder::new(f);
for i in body {
// they don't expose wrap_buf :grief:
padder.write_str(&format!("{i:?};\n"))?;
}
f.write_char('}')?;
} else {
f.write_str("{}")?;
}
}
Self::Break => write!(f, "break")?,
} }
Ok(())
} }
} }

View File

@@ -1,7 +1,6 @@
use std::{ use std::{
collections::HashMap, collections::HashMap,
fmt::Debug, fmt::Debug,
ops::{Deref, DerefMut},
}; };
use crate::common::FileSpan; use crate::common::FileSpan;
@@ -34,9 +33,11 @@ impl IRUProgram {
stack: vec![HashMap::new()], stack: vec![HashMap::new()],
} }
} }
pub fn push(&mut self) -> NamespaceGuard { pub fn push(&mut self) {
self.stack.push(HashMap::new()); self.stack.push(HashMap::new());
NamespaceGuard(self) }
pub fn pop(&mut self) {
self.stack.pop();
} }
pub fn get(&self, name: &str) -> Option<Idents> { pub fn get(&self, name: &str) -> Option<Idents> {
for map in self.stack.iter().rev() { for map in self.stack.iter().rev() {
@@ -205,27 +206,6 @@ impl IRUProgram {
} }
} }
pub struct NamespaceGuard<'a>(&'a mut IRUProgram);
impl Drop for NamespaceGuard<'_> {
fn drop(&mut self) {
self.0.stack.pop();
}
}
impl Deref for NamespaceGuard<'_> {
type Target = IRUProgram;
fn deref(&self) -> &Self::Target {
self.0
}
}
impl DerefMut for NamespaceGuard<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.0
}
}
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub enum Ident { pub enum Ident {
Var(VarID), Var(VarID),

View File

@@ -1,114 +1,140 @@
// TODO: move this into ir, not parser // TODO: move this into ir, not parser
use super::{IRUInstruction, IRUProgram, Type}; use super::{IRUInstrInst, IRUInstruction, IRUProgram, Type};
use crate::common::{CompilerMsg, CompilerOutput}; use crate::common::{CompilerMsg, CompilerOutput};
impl IRUProgram { impl IRUProgram {
pub fn validate(&self) -> CompilerOutput { pub fn validate(&self) -> CompilerOutput {
let mut output = CompilerOutput::new(); let mut output = CompilerOutput::new();
for (f, fd) in self.fns.iter().flatten().zip(&self.fn_defs) { for (f, fd) in self.fns.iter().flatten().zip(&self.fn_defs) {
for i in &f.instructions { self.validate_fn(&f.instructions, &fd.ret, &mut output, false);
match &i.i {
IRUInstruction::Mv { dest, src } => {
let dest = self.get_var(dest.id);
let src = self.get_var(src.id);
output.check_assign(self, &src.ty, &dest.ty, i.span);
}
IRUInstruction::Ref { dest, src } => todo!(),
IRUInstruction::LoadData { dest, src } => {
let dest = self.get_var(dest.id);
let src = self.get_data(*src);
output.check_assign(self, &src.ty, &dest.ty, i.span);
}
IRUInstruction::LoadSlice { dest, src } => {
let dest = self.get_var(dest.id);
let src = self.get_data(*src);
let Type::Array(srcty, ..) = &src.ty else {
todo!()
};
output.check_assign(self, &Type::Slice(srcty.clone()), &dest.ty, i.span);
}
IRUInstruction::LoadFn { dest, src } => todo!(),
IRUInstruction::Call { dest, f, args } => {
let destty = &self.get_var(dest.id).ty;
let f = self.get_var(f.id);
let Type::Fn { args: argtys, ret } = &f.ty else {
todo!()
};
output.check_assign(self, ret, destty, dest.span);
if args.len() != argtys.len() {
output.err(CompilerMsg {
msg: "Wrong number of arguments to function".to_string(),
spans: vec![dest.span],
});
}
for (argv, argt) in args.iter().zip(argtys) {
let dest = self.get_var(argv.id);
output.check_assign(self, argt, &dest.ty, argv.span);
}
}
IRUInstruction::AsmBlock { instructions, args } => {
// TODO
}
IRUInstruction::Ret { src } => {
let srcty = &self.get_var(src.id).ty;
output.check_assign(self, srcty, &fd.ret, src.span);
}
IRUInstruction::Construct { dest, fields } => {
let dest_def = self.get_var(dest.id);
let tyid = match dest_def.ty {
Type::Concrete(id) => id,
_ => {
output.err(CompilerMsg {
msg: "uhh type is not struct".to_string(),
spans: vec![dest.span],
});
continue;
}
};
let def = self.get_struct(tyid);
for (name, field) in &def.fields {
if let Some(var) = fields.get(name) {
let ety = &self.get_var(var.id).ty;
output.check_assign(self, &field.ty, ety, var.span);
} else {
output.err(CompilerMsg {
msg: format!("field '{name}' missing from struct"),
spans: vec![dest.span],
});
}
}
for name in fields.keys() {
if !def.fields.contains_key(name) {
output.err(CompilerMsg {
msg: format!("field '{name}' not in struct"),
spans: vec![dest.span],
});
}
}
}
IRUInstruction::Access { dest, src, field } => {
let dest_def = self.get_var(dest.id);
let src_def = self.get_var(src.id);
let tyid = match src_def.ty {
Type::Concrete(id) => id,
_ => {
output.err(CompilerMsg {
msg: "uhh type is not struct".to_string(),
spans: vec![dest.span],
});
continue;
}
};
let def = self.get_struct(tyid);
let field = def.fields.get(field).expect(
"already validated during parse lowering... probably shouldn't be?",
);
output.check_assign(self, &field.ty, &dest_def.ty, i.span);
// TODO
}
}
}
} }
output output
} }
pub fn validate_fn(
&self,
instructions: &[IRUInstrInst],
ret: &Type,
output: &mut CompilerOutput,
breakable: bool,
) {
for i in instructions {
match &i.i {
IRUInstruction::Mv { dest, src } => {
let dest = self.get_var(dest.id);
let src = self.get_var(src.id);
output.check_assign(self, &src.ty, &dest.ty, i.span);
}
IRUInstruction::Ref { dest, src } => todo!(),
IRUInstruction::LoadData { dest, src } => {
let dest = self.get_var(dest.id);
let src = self.get_data(*src);
output.check_assign(self, &src.ty, &dest.ty, i.span);
}
IRUInstruction::LoadSlice { dest, src } => {
let dest = self.get_var(dest.id);
let src = self.get_data(*src);
let Type::Array(srcty, ..) = &src.ty else {
todo!()
};
output.check_assign(self, &Type::Slice(srcty.clone()), &dest.ty, i.span);
}
IRUInstruction::LoadFn { dest, src } => todo!(),
IRUInstruction::Call { dest, f, args } => {
let destty = &self.get_var(dest.id).ty;
let f = self.get_var(f.id);
let Type::Fn { args: argtys, ret } = &f.ty else {
todo!()
};
output.check_assign(self, ret, destty, dest.span);
if args.len() != argtys.len() {
output.err(CompilerMsg {
msg: "Wrong number of arguments to function".to_string(),
spans: vec![dest.span],
});
}
for (argv, argt) in args.iter().zip(argtys) {
let dest = self.get_var(argv.id);
output.check_assign(self, argt, &dest.ty, argv.span);
}
}
IRUInstruction::AsmBlock { instructions, args } => {
// TODO
}
IRUInstruction::Ret { src } => {
let srcty = &self.get_var(src.id).ty;
output.check_assign(self, srcty, ret, src.span);
}
IRUInstruction::Construct { dest, fields } => {
let dest_def = self.get_var(dest.id);
let tyid = match dest_def.ty {
Type::Concrete(id) => id,
_ => {
output.err(CompilerMsg {
msg: "uhh type is not struct".to_string(),
spans: vec![dest.span],
});
continue;
}
};
let def = self.get_struct(tyid);
for (name, field) in &def.fields {
if let Some(var) = fields.get(name) {
let ety = &self.get_var(var.id).ty;
output.check_assign(self, &field.ty, ety, var.span);
} else {
output.err(CompilerMsg {
msg: format!("field '{name}' missing from struct"),
spans: vec![dest.span],
});
}
}
for name in fields.keys() {
if !def.fields.contains_key(name) {
output.err(CompilerMsg {
msg: format!("field '{name}' not in struct"),
spans: vec![dest.span],
});
}
}
}
IRUInstruction::Access { dest, src, field } => {
let dest_def = self.get_var(dest.id);
let src_def = self.get_var(src.id);
let tyid = match src_def.ty {
Type::Concrete(id) => id,
_ => {
output.err(CompilerMsg {
msg: "uhh type is not struct".to_string(),
spans: vec![dest.span],
});
continue;
}
};
let def = self.get_struct(tyid);
let field = def.fields.get(field).expect(
"already validated during parse lowering... probably shouldn't be?",
);
output.check_assign(self, &field.ty, &dest_def.ty, i.span);
}
IRUInstruction::If { cond, body } => {
let cond = self.get_var(cond.id);
output.check_assign(self, &cond.ty, &Type::Bits(64), i.span);
self.validate_fn(body, ret, output, breakable);
}
IRUInstruction::Loop { body } => {
self.validate_fn(body, ret, output, true);
}
IRUInstruction::Break => {
if !breakable {
output.err(CompilerMsg {
msg: "Can't break here (outside of loop)".to_string(),
spans: vec![i.span],
});
}
// TODO
}
}
}
}
} }

View File

@@ -41,8 +41,8 @@ fn run_file(file: &str, gdb: bool, asm: bool) {
// println!("Parsed:"); // println!("Parsed:");
// println!("{:#?}", res.node); // println!("{:#?}", res.node);
if let Some(module) = res.node.as_ref() { if let Some(module) = res.node.as_ref() {
let mut namespace = IRUProgram::new(); let mut program = IRUProgram::new();
module.lower(&mut namespace.push(), &mut ctx.output); module.lower(&mut program, &mut ctx.output);
if ctx.output.errs.is_empty() { if ctx.output.errs.is_empty() {
// println!("vars:"); // println!("vars:");
// for (id, def) in namespace.iter_vars() { // for (id, def) in namespace.iter_vars() {
@@ -51,10 +51,10 @@ fn run_file(file: &str, gdb: bool, asm: bool) {
// for (id, f) in namespace.iter_fns() { // for (id, f) in namespace.iter_fns() {
// println!("{id:?} = {:#?}", f.unwrap()); // println!("{id:?} = {:#?}", f.unwrap());
// } // }
let output = namespace.validate(); let output = program.validate();
output.write_for(&mut stdout(), file); output.write_for(&mut stdout(), file);
if output.errs.is_empty() { if output.errs.is_empty() {
let program = IRLProgram::create(&namespace).expect("morir"); let program = IRLProgram::create(&program).expect("morir");
let unlinked = compiler::compile(&program); let unlinked = compiler::compile(&program);
if asm { if asm {
println!("{:?}", unlinked); println!("{:?}", unlinked);

View File

@@ -11,7 +11,8 @@ impl RV64Instruction {
pub fn parse(inst: &PInstruction, ctx: &mut FnLowerCtx) -> Option<Self> { pub fn parse(inst: &PInstruction, ctx: &mut FnLowerCtx) -> Option<Self> {
let args = &inst.args[..]; let args = &inst.args[..];
let opstr = &**inst.op.inner.as_ref()?; let opstr = &**inst.op.inner.as_ref()?;
let opi = |ctx: &mut FnLowerCtx<'_, '_>, op: Funct3| -> Option<Self> { // TODO: surely this can be abstracted...
let opi = |ctx: &mut FnLowerCtx<'_>, op: Funct3| -> Option<Self> {
let [dest, src, imm] = args else { let [dest, src, imm] = args else {
ctx.err(format!("{opstr} requires 3 arguments")); ctx.err(format!("{opstr} requires 3 arguments"));
return None; return None;
@@ -21,7 +22,7 @@ impl RV64Instruction {
let imm = i32_from_arg(imm, ctx)?; let imm = i32_from_arg(imm, ctx)?;
Some(Self::OpImm { op, dest, src, imm }) Some(Self::OpImm { op, dest, src, imm })
}; };
let op = |ctx: &mut FnLowerCtx<'_, '_>, op: Funct3, funct: Funct7| -> Option<Self> { let op = |ctx: &mut FnLowerCtx<'_>, op: Funct3, funct: Funct7| -> Option<Self> {
let [dest, src1, src2] = args else { let [dest, src1, src2] = args else {
ctx.err(format!("{opstr} requires 3 arguments")); ctx.err(format!("{opstr} requires 3 arguments"));
return None; return None;
@@ -37,7 +38,7 @@ impl RV64Instruction {
src2, src2,
}) })
}; };
let opif7 = |ctx: &mut FnLowerCtx<'_, '_>, op: Funct3, funct: Funct7| -> Option<Self> { let opif7 = |ctx: &mut FnLowerCtx<'_>, op: Funct3, funct: Funct7| -> Option<Self> {
let [dest, src, imm] = args else { let [dest, src, imm] = args else {
ctx.err(format!("{opstr} requires 3 arguments")); ctx.err(format!("{opstr} requires 3 arguments"));
return None; return None;
@@ -53,7 +54,7 @@ impl RV64Instruction {
imm, imm,
}) })
}; };
let store = |ctx: &mut FnLowerCtx<'_, '_>, width: Funct3| -> Option<Self> { let store = |ctx: &mut FnLowerCtx<'_>, width: Funct3| -> Option<Self> {
let [src, offset, base] = args else { let [src, offset, base] = args else {
ctx.err(format!("{opstr} requires 3 arguments")); ctx.err(format!("{opstr} requires 3 arguments"));
return None; return None;
@@ -68,7 +69,7 @@ impl RV64Instruction {
base, base,
}) })
}; };
let load = |ctx: &mut FnLowerCtx<'_, '_>, width: Funct3| -> Option<Self> { let load = |ctx: &mut FnLowerCtx<'_>, width: Funct3| -> Option<Self> {
let [dest, offset, base] = args else { let [dest, offset, base] = args else {
ctx.err(format!("{opstr} requires 3 arguments")); ctx.err(format!("{opstr} requires 3 arguments"));
return None; return None;

View File

@@ -5,11 +5,13 @@ use super::{FnLowerCtx, FnLowerable, PBlock, PStatement};
impl FnLowerable for PBlock { impl FnLowerable for PBlock {
type Output = VarInst; type Output = VarInst;
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<VarInst> { fn lower(&self, ctx: &mut FnLowerCtx) -> Option<VarInst> {
let ctx = &mut ctx.sub(); ctx.program.push();
for statement in &self.statements { for statement in &self.statements {
statement.lower(ctx); statement.lower(ctx);
} }
self.result.as_ref()?.lower(ctx) let res = self.result.as_ref().map(|r| r.lower(ctx)).flatten();
ctx.program.pop();
res
} }
} }
@@ -18,10 +20,10 @@ impl FnLowerable for PStatement {
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<VarInst> { fn lower(&self, ctx: &mut FnLowerCtx) -> Option<VarInst> {
match self { match self {
super::PStatement::Let(def, e) => { super::PStatement::Let(def, e) => {
let def = def.lower(ctx.map, ctx.output)?; let def = def.lower(ctx.program, ctx.output)?;
let res = e.lower(ctx); let res = e.lower(ctx);
if let Some(res) = res { if let Some(res) = res {
ctx.map.name_var(&def, res.id); ctx.program.name_var(&def, res.id);
} }
None None
} }

View File

@@ -1,17 +1,17 @@
use crate::ir::{NamespaceGuard, Origin, Type, VarDef}; use crate::ir::{IRUProgram, Origin, Type, VarDef};
use super::{CompilerMsg, CompilerOutput, FileSpan, Node, PType, PVarDef}; use super::{CompilerMsg, CompilerOutput, FileSpan, Node, PType, PVarDef};
impl Node<PVarDef> { impl Node<PVarDef> {
pub fn lower( pub fn lower(
&self, &self,
namespace: &mut NamespaceGuard, program: &mut IRUProgram,
output: &mut CompilerOutput, output: &mut CompilerOutput,
) -> Option<VarDef> { ) -> Option<VarDef> {
let s = self.as_ref()?; let s = self.as_ref()?;
let name = s.name.as_ref()?.to_string(); let name = s.name.as_ref()?.to_string();
let ty = match &s.ty { let ty = match &s.ty {
Some(ty) => ty.lower(namespace, output), Some(ty) => ty.lower(program, output),
None => Type::Infer, None => Type::Infer,
}; };
Some(VarDef { Some(VarDef {
@@ -23,7 +23,7 @@ impl Node<PVarDef> {
} }
impl Node<PType> { impl Node<PType> {
pub fn lower(&self, namespace: &mut NamespaceGuard, output: &mut CompilerOutput) -> Type { pub fn lower(&self, namespace: &mut IRUProgram, output: &mut CompilerOutput) -> Type {
self.as_ref() self.as_ref()
.map(|t| t.lower(namespace, output, self.span)) .map(|t| t.lower(namespace, output, self.span))
.unwrap_or(Type::Error) .unwrap_or(Type::Error)
@@ -33,7 +33,7 @@ impl Node<PType> {
impl PType { impl PType {
pub fn lower( pub fn lower(
&self, &self,
namespace: &mut NamespaceGuard, namespace: &mut IRUProgram,
output: &mut CompilerOutput, output: &mut CompilerOutput,
span: FileSpan, span: FileSpan,
) -> Type { ) -> Type {

View File

@@ -10,9 +10,9 @@ impl FnLowerable for PExpr {
Some(match self { Some(match self {
PExpr::Lit(l) => match l.as_ref()? { PExpr::Lit(l) => match l.as_ref()? {
super::PLiteral::String(s) => { super::PLiteral::String(s) => {
let dest = ctx.map.temp_var(l.span, Type::Bits(8).slice()); let dest = ctx.program.temp_var(l.span, Type::Bits(8).slice());
let data = s.as_bytes().to_vec(); let data = s.as_bytes().to_vec();
let src = ctx.map.def_data( let src = ctx.program.def_data(
DataDef { DataDef {
ty: Type::Bits(8).arr(data.len() as u32), ty: Type::Bits(8).arr(data.len() as u32),
origin: Origin::File(l.span), origin: Origin::File(l.span),
@@ -25,8 +25,8 @@ impl FnLowerable for PExpr {
} }
super::PLiteral::Char(c) => { super::PLiteral::Char(c) => {
let ty = Type::Bits(8); let ty = Type::Bits(8);
let dest = ctx.map.temp_var(l.span, ty.clone()); let dest = ctx.program.temp_var(l.span, ty.clone());
let src = ctx.map.def_data( let src = ctx.program.def_data(
DataDef { DataDef {
ty, ty,
origin: Origin::File(l.span), origin: Origin::File(l.span),
@@ -40,8 +40,8 @@ impl FnLowerable for PExpr {
super::PLiteral::Number(n) => { super::PLiteral::Number(n) => {
// TODO: temp // TODO: temp
let ty = Type::Bits(64); let ty = Type::Bits(64);
let dest = ctx.map.temp_var(l.span, Type::Bits(64)); let dest = ctx.program.temp_var(l.span, Type::Bits(64));
let src = ctx.map.def_data( let src = ctx.program.def_data(
DataDef { DataDef {
ty, ty,
origin: Origin::File(l.span), origin: Origin::File(l.span),
@@ -60,12 +60,15 @@ impl FnLowerable for PExpr {
PExpr::BinaryOp(op, e1, e2) => { PExpr::BinaryOp(op, e1, e2) => {
let res1 = e1.lower(ctx)?; let res1 = e1.lower(ctx)?;
if *op == PInfixOp::Access { if *op == PInfixOp::Access {
let sty = &ctx.map.get_var(res1.id).ty; let sty = &ctx.program.get_var(res1.id).ty;
let Type::Concrete(tid) = sty else { let Type::Concrete(tid) = sty else {
ctx.err(format!("Type {:?} has no fields", ctx.map.type_name(sty))); ctx.err(format!(
"Type {:?} has no fields",
ctx.program.type_name(sty)
));
return None; return None;
}; };
let struc = ctx.map.get_struct(*tid); let struc = ctx.program.get_struct(*tid);
let Some(box PExpr::Ident(ident)) = &e2.inner else { let Some(box PExpr::Ident(ident)) = &e2.inner else {
ctx.err(format!("Field accesses must be identifiers",)); ctx.err(format!("Field accesses must be identifiers",));
return None; return None;
@@ -84,14 +87,29 @@ impl FnLowerable for PExpr {
temp temp
} else { } else {
let res2 = e2.lower(ctx)?; let res2 = e2.lower(ctx)?;
todo!() match op {
PInfixOp::Add => todo!(),
PInfixOp::Sub => todo!(),
PInfixOp::Mul => todo!(),
PInfixOp::Div => todo!(),
PInfixOp::LessThan => todo!(),
PInfixOp::GreaterThan => todo!(),
PInfixOp::Access => todo!(),
PInfixOp::Assign => {
ctx.push(IRUInstruction::Mv {
dest: res1,
src: res2,
});
res1
}
}
} }
} }
PExpr::UnaryOp(op, e) => { PExpr::UnaryOp(op, e) => {
let res = e.lower(ctx)?; let res = e.lower(ctx)?;
match op { match op {
UnaryOp::Ref => { UnaryOp::Ref => {
let temp = ctx.temp(ctx.map.get_var(res.id).ty.clone().rf()); let temp = ctx.temp(ctx.program.get_var(res.id).ty.clone().rf());
ctx.push(IRUInstruction::Ref { ctx.push(IRUInstruction::Ref {
dest: temp, dest: temp,
src: res, src: res,
@@ -99,11 +117,11 @@ impl FnLowerable for PExpr {
temp temp
} }
UnaryOp::Deref => { UnaryOp::Deref => {
let t = &ctx.map.get_var(res.id).ty; let t = &ctx.program.get_var(res.id).ty;
let Type::Ref(inner) = t else { let Type::Ref(inner) = t else {
ctx.err(format!( ctx.err(format!(
"Cannot dereference type {:?}", "Cannot dereference type {:?}",
ctx.map.type_name(t) ctx.program.type_name(t)
)); ));
return None; return None;
}; };
@@ -124,7 +142,7 @@ impl FnLowerable for PExpr {
let arg = arg.lower(ctx)?; let arg = arg.lower(ctx)?;
nargs.push(arg); nargs.push(arg);
} }
let def = ctx.map.get_fn_var(fe.id); let def = ctx.program.get_fn_var(fe.id);
let ty = match def { let ty = match def {
Some(def) => def.ret.clone(), Some(def) => def.ret.clone(),
None => { None => {
@@ -132,7 +150,7 @@ impl FnLowerable for PExpr {
e.span, e.span,
format!( format!(
"Expected function, found {}", "Expected function, found {}",
ctx.map.type_name(&ctx.map.get_var(fe.id).ty) ctx.program.type_name(&ctx.program.get_var(fe.id).ty)
), ),
); );
Type::Error Type::Error
@@ -148,6 +166,29 @@ impl FnLowerable for PExpr {
} }
PExpr::Group(e) => e.lower(ctx)?, PExpr::Group(e) => e.lower(ctx)?,
PExpr::Construct(c) => c.lower(ctx)?, PExpr::Construct(c) => c.lower(ctx)?,
PExpr::If(cond, body) => {
let cond = cond.lower(ctx)?;
ctx.program.push();
let mut body_ctx = ctx.branch();
body.lower(&mut body_ctx);
let body = body_ctx.instructions;
ctx.program.pop();
ctx.push(IRUInstruction::If { cond, body });
return None;
}
PExpr::Loop(body) => {
ctx.program.push();
let mut body_ctx = ctx.branch();
body.lower(&mut body_ctx);
let body = body_ctx.instructions;
ctx.program.pop();
ctx.push(IRUInstruction::Loop { body });
return None;
}
PExpr::Break => {
ctx.push(IRUInstruction::Break);
return None;
}
}) })
} }
} }

View File

@@ -1,24 +1,20 @@
use super::{CompilerMsg, CompilerOutput, FileSpan, FnLowerable, Node, PFunction}; use super::{CompilerMsg, CompilerOutput, FileSpan, FnLowerable, Node, PFunction};
use crate::{ use crate::{
ir::{ ir::{
FnDef, FnID, IRInstructions, IRUFunction, IRUInstruction, Idents, NamespaceGuard, Origin, FnDef, FnID, IRUFunction, IRUInstrInst, IRUInstruction, IRUProgram, Idents, Origin, Type,
Type, VarDef, VarInst, VarDef, VarInst,
}, },
parser, parser,
}; };
impl Node<PFunction> { impl Node<PFunction> {
pub fn lower_header( pub fn lower_header(&self, map: &mut IRUProgram, output: &mut CompilerOutput) -> Option<FnID> {
&self,
map: &mut NamespaceGuard,
output: &mut CompilerOutput,
) -> Option<FnID> {
self.as_ref()?.lower_header(map, output) self.as_ref()?.lower_header(map, output)
} }
pub fn lower_body( pub fn lower_body(
&self, &self,
id: FnID, id: FnID,
map: &mut NamespaceGuard, map: &mut IRUProgram,
output: &mut CompilerOutput, output: &mut CompilerOutput,
) -> Option<IRUFunction> { ) -> Option<IRUFunction> {
Some(self.as_ref()?.lower_body(id, map, output)) Some(self.as_ref()?.lower_body(id, map, output))
@@ -26,11 +22,7 @@ impl Node<PFunction> {
} }
impl PFunction { impl PFunction {
pub fn lower_header( pub fn lower_header(&self, map: &mut IRUProgram, output: &mut CompilerOutput) -> Option<FnID> {
&self,
map: &mut NamespaceGuard,
output: &mut CompilerOutput,
) -> Option<FnID> {
let header = self.header.as_ref()?; let header = self.header.as_ref()?;
let name = header.name.as_ref()?; let name = header.name.as_ref()?;
let args = header let args = header
@@ -58,44 +50,43 @@ impl PFunction {
pub fn lower_body( pub fn lower_body(
&self, &self,
id: FnID, id: FnID,
map: &mut NamespaceGuard, map: &mut IRUProgram,
output: &mut CompilerOutput, output: &mut CompilerOutput,
) -> IRUFunction { ) -> IRUFunction {
let mut instructions = IRInstructions::new();
let def = map.get_fn(id).clone(); let def = map.get_fn(id).clone();
let args = def.args.iter().map(|a| map.named_var(a.clone())).collect(); let args = def.args.iter().map(|a| map.named_var(a.clone())).collect();
let mut ctx = FnLowerCtx { let mut ctx = FnLowerCtx {
instructions: &mut instructions, instructions: Vec::new(),
map, program: map,
output, output,
span: self.body.span, span: self.body.span,
}; };
if let Some(src) = self.body.lower(&mut ctx) { if let Some(src) = self.body.lower(&mut ctx) {
instructions.push(IRUInstruction::Ret { src }, src.span); ctx.instructions.push(IRUInstrInst {
i: IRUInstruction::Ret { src },
span: src.span,
});
}
IRUFunction {
name: def.name.clone(),
args,
ret: def.ret,
instructions: ctx.instructions,
} }
IRUFunction::new(def.name.clone(), args, def.ret, instructions)
} }
} }
pub struct FnLowerCtx<'a, 'n> { pub struct FnLowerCtx<'a> {
pub map: &'a mut NamespaceGuard<'n>, pub program: &'a mut IRUProgram,
pub instructions: &'a mut IRInstructions, pub instructions: Vec<IRUInstrInst>,
pub output: &'a mut CompilerOutput, pub output: &'a mut CompilerOutput,
pub span: FileSpan, pub span: FileSpan,
} }
impl<'n> FnLowerCtx<'_, 'n> { impl FnLowerCtx<'_> {
pub fn span<'b>(&'b mut self, span: FileSpan) -> FnLowerCtx<'b, 'n> {
FnLowerCtx {
map: self.map,
instructions: self.instructions,
output: self.output,
span,
}
}
pub fn get(&mut self, node: &Node<parser::PIdent>) -> Option<Idents> { pub fn get(&mut self, node: &Node<parser::PIdent>) -> Option<Idents> {
let name = node.inner.as_ref()?; let name = node.inner.as_ref()?;
let res = self.map.get(name); let res = self.program.get(name);
if res.is_none() { if res.is_none() {
self.err_at(node.span, format!("Identifier '{}' not found", name)); self.err_at(node.span, format!("Identifier '{}' not found", name));
} }
@@ -124,18 +115,18 @@ impl<'n> FnLowerCtx<'_, 'n> {
self.output.err(CompilerMsg::from_span(span, msg)) self.output.err(CompilerMsg::from_span(span, msg))
} }
pub fn temp(&mut self, ty: Type) -> VarInst { pub fn temp(&mut self, ty: Type) -> VarInst {
self.map.temp_var(self.span, ty) self.program.temp_var(self.span, ty)
} }
pub fn push(&mut self, i: IRUInstruction) { pub fn push(&mut self, i: IRUInstruction) {
self.instructions.push(i, self.span); self.instructions.push(IRUInstrInst { i, span: self.span });
} }
pub fn push_at(&mut self, i: IRUInstruction, span: FileSpan) { pub fn push_at(&mut self, i: IRUInstruction, span: FileSpan) {
self.instructions.push(i, span); self.instructions.push(IRUInstrInst { i, span });
} }
pub fn sub<'b>(&'b mut self) -> FnLowerCtx<'b, 'n> { pub fn branch<'a>(&'a mut self) -> FnLowerCtx<'a> {
FnLowerCtx { FnLowerCtx {
map: self.map, program: self.program,
instructions: self.instructions, instructions: Vec::new(),
output: self.output, output: self.output,
span: self.span, span: self.span,
} }

View File

@@ -19,7 +19,11 @@ pub trait FnLowerable {
impl<T: FnLowerable> FnLowerable for Node<T> { impl<T: FnLowerable> FnLowerable for Node<T> {
type Output = T::Output; type Output = T::Output;
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<T::Output> { fn lower(&self, ctx: &mut FnLowerCtx) -> Option<T::Output> {
self.as_ref()?.lower(&mut ctx.span(self.span)) let old_span = ctx.span;
ctx.span = self.span;
let res = self.as_ref()?.lower(ctx);
ctx.span = old_span;
res
} }
} }

View File

@@ -1,15 +1,15 @@
use crate::ir::NamespaceGuard; use crate::ir::IRUProgram;
use super::{PModule, CompilerOutput}; use super::{PModule, CompilerOutput};
impl PModule { impl PModule {
pub fn lower(&self, map: &mut NamespaceGuard, output: &mut CompilerOutput) { pub fn lower(&self, p: &mut IRUProgram, output: &mut CompilerOutput) {
for s in &self.structs { for s in &self.structs {
s.lower(map, output); s.lower(p, output);
} }
let mut fns = Vec::new(); let mut fns = Vec::new();
for f in &self.functions { for f in &self.functions {
if let Some(id) = f.lower_header(map, output) { if let Some(id) = f.lower_header(p, output) {
fns.push(Some(id)); fns.push(Some(id));
} else { } else {
fns.push(None) fns.push(None)
@@ -17,8 +17,8 @@ impl PModule {
} }
for (f, id) in self.functions.iter().zip(fns) { for (f, id) in self.functions.iter().zip(fns) {
if let Some(id) = id { if let Some(id) = id {
if let Some(res) = f.lower_body(id, map, output) { if let Some(res) = f.lower_body(id, p, output) {
map.write_fn(id, res); p.write_fn(id, res);
} }
} }
} }

View File

@@ -2,7 +2,7 @@ use std::collections::HashMap;
use crate::{ use crate::{
common::{CompilerMsg, CompilerOutput, FileSpan}, common::{CompilerMsg, CompilerOutput, FileSpan},
ir::{IRUInstruction, NamespaceGuard, Origin, StructDef, StructField, VarInst}, ir::{IRUInstruction, IRUProgram, Origin, StructDef, StructField, VarInst},
parser::{Node, PConstruct, PConstructFields, PStruct, PStructFields}, parser::{Node, PConstruct, PConstructFields, PStruct, PStructFields},
}; };
@@ -11,7 +11,7 @@ use super::{FnLowerCtx, FnLowerable};
impl FnLowerable for PConstruct { impl FnLowerable for PConstruct {
type Output = VarInst; type Output = VarInst;
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<VarInst> { fn lower(&self, ctx: &mut FnLowerCtx) -> Option<VarInst> {
let ty = self.name.lower(ctx.map, ctx.output); let ty = self.name.lower(ctx.program, ctx.output);
let fields = match &self.fields { let fields = match &self.fields {
PConstructFields::Named(nodes) => nodes PConstructFields::Named(nodes) => nodes
.iter() .iter()
@@ -41,7 +41,7 @@ impl FnLowerable for PConstruct {
impl PStruct { impl PStruct {
pub fn lower( pub fn lower(
&self, &self,
map: &mut NamespaceGuard, p: &mut IRUProgram,
output: &mut CompilerOutput, output: &mut CompilerOutput,
span: FileSpan, span: FileSpan,
) -> Option<()> { ) -> Option<()> {
@@ -53,10 +53,10 @@ impl PStruct {
let def = n.as_ref()?; let def = n.as_ref()?;
let name = def.name.as_ref()?.to_string(); let name = def.name.as_ref()?.to_string();
let tynode = def.ty.as_ref()?; let tynode = def.ty.as_ref()?;
let ty = tynode.lower(map, output); let ty = tynode.lower(p, output);
let size = map.size_of_type(&ty).unwrap_or_else(|| { let size = p.size_of_type(&ty).unwrap_or_else(|| {
output.err(CompilerMsg { output.err(CompilerMsg {
msg: format!("Size of type '{}' unknown", map.type_name(&ty)), msg: format!("Size of type '{}' unknown", p.type_name(&ty)),
spans: vec![tynode.span], spans: vec![tynode.span],
}); });
0 0
@@ -70,8 +70,8 @@ impl PStruct {
.iter() .iter()
.enumerate() .enumerate()
.flat_map(|(i, n)| { .flat_map(|(i, n)| {
let ty = n.as_ref()?.lower(map, output, span); let ty = n.as_ref()?.lower(p, output, span);
let size = map.size_of_type(&ty)?; let size = p.size_of_type(&ty)?;
let res = Some((format!("{i}"), StructField { ty, offset })); let res = Some((format!("{i}"), StructField { ty, offset }));
offset += size; offset += size;
res res
@@ -79,7 +79,7 @@ impl PStruct {
.collect(), .collect(),
PStructFields::None => HashMap::new(), PStructFields::None => HashMap::new(),
}; };
map.def_type(StructDef { p.def_type(StructDef {
name: self.name.as_ref()?.to_string(), name: self.name.as_ref()?.to_string(),
origin: Origin::File(span), origin: Origin::File(span),
size: offset, size: offset,
@@ -90,7 +90,7 @@ impl PStruct {
} }
impl Node<PStruct> { impl Node<PStruct> {
pub fn lower(&self, map: &mut NamespaceGuard, output: &mut CompilerOutput) { pub fn lower(&self, p: &mut IRUProgram, output: &mut CompilerOutput) {
self.as_ref().map(|i| i.lower(map, output, self.span)); self.as_ref().map(|i| i.lower(p, output, self.span));
} }
} }

View File

@@ -1,4 +1,4 @@
use super::{Node, PIdent, Parsable, ParseResult, ParserCtx, Symbol, CompilerMsg}; use super::{CompilerMsg, Node, PIdent, Parsable, ParseResult, ParserCtx, Symbol};
pub struct PInstruction { pub struct PInstruction {
pub op: Node<PIdent>, pub op: Node<PIdent>,
@@ -36,7 +36,19 @@ impl Parsable for PAsmArg {
return ParseResult::Ok(Self::Value(ident)); return ParseResult::Ok(Self::Value(ident));
} }
let next = ctx.expect_peek()?; 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.as_mut() {
i.0.insert(0, '-')
}
return ParseResult::Ok(Self::Value(ident));
}
next = ctx.expect_peek()?;
}
if !next.is_symbol(Symbol::OpenCurly) { if !next.is_symbol(Symbol::OpenCurly) {
return ParseResult::Err(CompilerMsg::unexpected_token( return ParseResult::Err(CompilerMsg::unexpected_token(
next, next,

View File

@@ -19,6 +19,9 @@ pub enum PExpr {
Group(BoxNode), Group(BoxNode),
AsmBlock(Node<PAsmBlock>), AsmBlock(Node<PAsmBlock>),
Construct(Node<PConstruct>), Construct(Node<PConstruct>),
If(BoxNode, BoxNode),
Loop(BoxNode),
Break,
} }
impl Parsable for PExpr { impl Parsable for PExpr {
@@ -42,6 +45,18 @@ impl Parsable for PExpr {
Self::Group(res.node.bx()) Self::Group(res.node.bx())
} else if next.is_symbol(Symbol::OpenCurly) { } else if next.is_symbol(Symbol::OpenCurly) {
Self::Block(PBlock::parse_node(ctx)?) Self::Block(PBlock::parse_node(ctx)?)
} 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::Asm) { } else if next.is_keyword(Keyword::Asm) {
ctx.next(); ctx.next();
Self::AsmBlock(ctx.parse()?) Self::AsmBlock(ctx.parse()?)
@@ -143,14 +158,13 @@ impl Debug for PExpr {
} }
f.write_char(')')?; f.write_char(')')?;
} }
PExpr::UnaryOp(op, e) => { PExpr::UnaryOp(op, e) => write!(f, "({}{:?})", op.str(), e)?,
write!(f, "(")?;
write!(f, "{}", op.str())?;
write!(f, "{:?})", *e)?;
}
PExpr::Group(inner) => inner.fmt(f)?, PExpr::Group(inner) => inner.fmt(f)?,
PExpr::AsmBlock(inner) => inner.fmt(f)?, PExpr::AsmBlock(inner) => inner.fmt(f)?,
PExpr::Construct(inner) => inner.fmt(f)?, PExpr::Construct(inner) => inner.fmt(f)?,
PExpr::If(cond, res) => write!(f, "if {cond:?} then {res:?}")?,
PExpr::Loop(res) => write!(f, "loop -> {res:?}")?,
PExpr::Break => write!(f, "break")?,
} }
Ok(()) Ok(())
} }

View File

@@ -37,7 +37,7 @@ impl MaybeParsable for PLiteral {
if !first.is_ascii_digit() { if !first.is_ascii_digit() {
return Ok(None); return Ok(None);
} }
let (whole, ty) = parse_whole_num(&text); let (whole, ty) = parse_whole_num(text);
let mut num = PNumber { let mut num = PNumber {
whole, whole,
decimal: None, decimal: None,

View File

@@ -4,6 +4,8 @@ pub enum Keyword {
Let, Let,
If, If,
Return, Return,
Break,
Loop,
Struct, Struct,
Trait, Trait,
Impl, Impl,
@@ -21,6 +23,8 @@ impl Keyword {
"if" => Self::If, "if" => Self::If,
"for" => Self::For, "for" => Self::For,
"return" => Self::Return, "return" => Self::Return,
"break" => Self::Break,
"loop" => Self::Loop,
"trait" => Self::Trait, "trait" => Self::Trait,
"impl" => Self::Impl, "impl" => Self::Impl,
"asm" => Self::Asm, "asm" => Self::Asm,