structs r a lot more sane in code, can now actually assign & stuff
This commit is contained in:
19
README.md
Normal file
19
README.md
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# the
|
||||||
|
|
||||||
|
my child (programming language)
|
||||||
|
|
||||||
|
everything is subject to change rn, and this probably isn't up to date
|
||||||
|
|
||||||
|
`cargo run -- data/test.lang`
|
||||||
|
|
||||||
|
currently working!!:
|
||||||
|
- functions (arguments, returning)
|
||||||
|
- assembly blocks (input, output for expression)
|
||||||
|
- kind of structs (construction, field access, but not modifying lmao)
|
||||||
|
|
||||||
|
todo:
|
||||||
|
- actually handle jumps & LIs that are too large
|
||||||
|
- iterators?
|
||||||
|
- borrow checking
|
||||||
|
- multiple var instances for struct fields / fix all that stuff
|
||||||
|
- basic optimization: use registers, remove temp var moves
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
struct Test {
|
struct Test {
|
||||||
a: 64,
|
a: 64,
|
||||||
b: 64,
|
b: 64,
|
||||||
|
c: 64,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start() {
|
fn start() {
|
||||||
@@ -26,11 +27,13 @@ fn start() {
|
|||||||
print(tester());
|
print(tester());
|
||||||
let test = Test {
|
let test = Test {
|
||||||
a: 10,
|
a: 10,
|
||||||
b: 9,
|
b: 4,
|
||||||
|
c: 0,
|
||||||
};
|
};
|
||||||
|
structer(test);
|
||||||
arger("a", "b", "c");
|
arger("a", "b", "c");
|
||||||
let z = sub(test.a, 10);
|
let z = sub(test.a, 10);
|
||||||
// exit(add(mul(sub(add(5, test.b), 1), 3), z));
|
print_hex(add(mul(sub(add(10, test.b), 1), 3), z));
|
||||||
print("test: 0x");
|
print("test: 0x");
|
||||||
print_hex(31);
|
print_hex(31);
|
||||||
println("");
|
println("");
|
||||||
@@ -38,6 +41,17 @@ fn start() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn structer(test: Test) {
|
fn structer(test: Test) {
|
||||||
|
print("test {\n a: ");
|
||||||
|
print_dec(test.a);
|
||||||
|
print("\n b: ");
|
||||||
|
print_dec(test.b);
|
||||||
|
print("\n c: ");
|
||||||
|
print_dec(test.c);
|
||||||
|
println("\n}");
|
||||||
|
print("update c: ");
|
||||||
|
test.c = add(test.a, test.b);
|
||||||
|
print_dec(test.c);
|
||||||
|
println("");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn thinger() {
|
fn thinger() {
|
||||||
@@ -64,7 +78,7 @@ fn print(msg: slice<8>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn print_hex(x: 64) {
|
fn print_hex(x: 64) {
|
||||||
let i = 32;
|
let i = 64;
|
||||||
loop {
|
loop {
|
||||||
i = sub(i, 4);
|
i = sub(i, 4);
|
||||||
let c = and(shr(x, i), 15);
|
let c = and(shr(x, i), 15);
|
||||||
@@ -84,6 +98,45 @@ fn print_hex(x: 64) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn print_dec(x: 64) {
|
||||||
|
let i = 1;
|
||||||
|
loop {
|
||||||
|
if gt(i, x) {
|
||||||
|
if lt(i, 2) {
|
||||||
|
print("0");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
i = mul(i, 10);
|
||||||
|
};
|
||||||
|
let found = 0;
|
||||||
|
loop {
|
||||||
|
i = div(i, 10);
|
||||||
|
let c = rem(div(x, i), 10);
|
||||||
|
if and(lt(c, 1), not(found)) {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
found = 1;
|
||||||
|
if gt(c, 9) {
|
||||||
|
c = add(c, 7);
|
||||||
|
};
|
||||||
|
c = add(c, 48);
|
||||||
|
asm (a1 = &c) {
|
||||||
|
li a2, 1
|
||||||
|
li a0, 1
|
||||||
|
li a7, 64
|
||||||
|
ecall
|
||||||
|
};
|
||||||
|
if lt(i, 2) {
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
if not(found) {
|
||||||
|
print("0");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn add(a: 64, b: 64) -> 64 {
|
fn add(a: 64, b: 64) -> 64 {
|
||||||
asm (t0 = a, t1 = b, out = t0) {
|
asm (t0 = a, t1 = b, out = t0) {
|
||||||
add t0, t0, t1
|
add t0, t0, t1
|
||||||
|
|||||||
@@ -78,6 +78,10 @@ pub fn compile(program: &IRLProgram) -> UnlinkedProgram<LI> {
|
|||||||
stack_len += align(&f.ret_size);
|
stack_len += align(&f.ret_size);
|
||||||
}
|
}
|
||||||
v.push(LI::addi(sp, sp, -stack_len));
|
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;
|
let has_stack = stack_len > 0;
|
||||||
if has_stack {
|
if has_stack {
|
||||||
if let Some(stack_ra) = stack_ra {
|
if let Some(stack_ra) = stack_ra {
|
||||||
@@ -86,6 +90,15 @@ pub fn compile(program: &IRLProgram) -> UnlinkedProgram<LI> {
|
|||||||
}
|
}
|
||||||
let mut locations = HashMap::new();
|
let mut locations = HashMap::new();
|
||||||
let mut irli = Vec::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 {
|
for i in &f.instructions {
|
||||||
irli.push((v.len(), format!("{i:?}")));
|
irli.push((v.len(), format!("{i:?}")));
|
||||||
match i {
|
match i {
|
||||||
@@ -235,12 +248,15 @@ pub fn compile(program: &IRLProgram) -> UnlinkedProgram<LI> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
IRI::Ret { src } => {
|
IRI::Ret { src } => {
|
||||||
|
if let Some(src) = src {
|
||||||
let Some(rva) = stack_rva else {
|
let Some(rva) = stack_rva else {
|
||||||
panic!("no return value address on stack!")
|
panic!("no return value address on stack!")
|
||||||
};
|
};
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
v.extend(&ret);
|
||||||
|
}
|
||||||
IRI::Jump(location) => {
|
IRI::Jump(location) => {
|
||||||
v.push(LI::J(*location));
|
v.push(LI::J(*location));
|
||||||
}
|
}
|
||||||
@@ -259,13 +275,6 @@ pub fn compile(program: &IRLProgram) -> UnlinkedProgram<LI> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
dbg.push_fn(irli);
|
dbg.push_fn(irli);
|
||||||
if has_stack {
|
|
||||||
if let Some(stack_ra) = stack_ra {
|
|
||||||
v.push(LI::ld(ra, stack_ra, sp));
|
|
||||||
}
|
|
||||||
v.push(LI::addi(sp, sp, stack_len));
|
|
||||||
}
|
|
||||||
v.push(LI::Ret);
|
|
||||||
fns.push(UnlinkedFunction {
|
fns.push(UnlinkedFunction {
|
||||||
instrs: v,
|
instrs: v,
|
||||||
sym: *sym,
|
sym: *sym,
|
||||||
|
|||||||
@@ -51,18 +51,19 @@ pub struct SectionHeader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// this is currently specialized for riscv64; obviously add params later
|
// this is currently specialized for riscv64; obviously add params later
|
||||||
pub fn create(program: Vec<u8>, start_offset: Addr) -> Vec<u8> {
|
pub fn create(program: &[u8], start_offset: Addr) -> Vec<u8> {
|
||||||
let addr_start = 0x1000;
|
let addr_start = 0x1000;
|
||||||
let page_size = 0x1000;
|
let page_size = 0x1000;
|
||||||
let progam_size = std::mem::size_of_val(&program[..]) as u64;
|
// 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 {
|
let program_header = ProgramHeader {
|
||||||
ty: 0x1, // LOAD
|
ty: 0x1, // LOAD
|
||||||
flags: 0b101, // executable, readable
|
flags: 0b101, // executable, readable
|
||||||
offset: 0x0,
|
offset: 0x0,
|
||||||
vaddr: addr_start,
|
vaddr: addr_start,
|
||||||
paddr: addr_start,
|
paddr: addr_start,
|
||||||
filesz: progam_size,
|
filesz: program_size,
|
||||||
memsz: progam_size,
|
memsz: program_size,
|
||||||
align: page_size,
|
align: page_size,
|
||||||
};
|
};
|
||||||
let header_len = (size_of::<ELF64Header>() + size_of::<ProgramHeader>()) as u64;
|
let header_len = (size_of::<ELF64Header>() + size_of::<ProgramHeader>()) as u64;
|
||||||
@@ -104,7 +105,7 @@ unsafe fn as_u8_slice<T: Sized>(p: &T) -> &[u8] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl LinkedProgram {
|
impl LinkedProgram {
|
||||||
pub fn to_elf(self) -> Vec<u8> {
|
pub fn to_elf(&self) -> Vec<u8> {
|
||||||
create(self.code, self.start.expect("no start found"))
|
create(&self.code, self.start.expect("no start found"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std::collections::HashMap;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ir::Symbol,
|
ir::Symbol,
|
||||||
util::{Labelable, LabeledFmt, Labeler},
|
util::{Labelable, LabeledFmt},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::debug::DebugInfo;
|
use super::debug::DebugInfo;
|
||||||
@@ -26,7 +26,7 @@ pub struct UnlinkedFunction<I: Instr> {
|
|||||||
pub locations: HashMap<usize, Symbol>,
|
pub locations: HashMap<usize, Symbol>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I: Instr> UnlinkedProgram<I> {
|
impl<I: Instr + std::fmt::Debug> 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.sym_count);
|
let mut sym_table = SymTable::new(self.sym_count);
|
||||||
|
|||||||
13
src/ir/id.rs
13
src/ir/id.rs
@@ -1,14 +1,17 @@
|
|||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
|
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
|
||||||
pub struct TypeID(pub usize);
|
pub struct StructID(pub usize);
|
||||||
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
|
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
|
||||||
pub struct VarID(pub usize);
|
pub struct VarID(pub usize);
|
||||||
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
|
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
|
||||||
pub struct FnID(pub usize);
|
pub struct FnID(pub usize);
|
||||||
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
|
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
|
||||||
pub struct DataID(pub usize);
|
pub struct DataID(pub usize);
|
||||||
|
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
|
||||||
|
pub struct FieldID(pub usize);
|
||||||
|
|
||||||
|
// I had an idea for why these were different... now I don't
|
||||||
pub type Size = u32;
|
pub type Size = u32;
|
||||||
pub type Len = u32;
|
pub type Len = u32;
|
||||||
|
|
||||||
@@ -18,7 +21,7 @@ impl Debug for VarID {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for TypeID {
|
impl Debug for StructID {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "ty{}", self.0)
|
write!(f, "ty{}", self.0)
|
||||||
}
|
}
|
||||||
@@ -35,3 +38,9 @@ impl Debug for DataID {
|
|||||||
write!(f, "data{}", self.0)
|
write!(f, "data{}", self.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Debug for FieldID {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "field{}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use std::collections::HashMap;
|
|||||||
pub struct IRLFunction {
|
pub struct IRLFunction {
|
||||||
pub instructions: Vec<IRLInstruction>,
|
pub instructions: Vec<IRLInstruction>,
|
||||||
pub stack: HashMap<VarID, Size>,
|
pub stack: HashMap<VarID, Size>,
|
||||||
|
pub subvar_map: HashMap<VarID, VarOffset>,
|
||||||
pub args: Vec<(VarID, Size)>,
|
pub args: Vec<(VarID, Size)>,
|
||||||
pub ret_size: Size,
|
pub ret_size: Size,
|
||||||
pub makes_call: bool,
|
pub makes_call: bool,
|
||||||
@@ -46,7 +47,7 @@ pub enum IRLInstruction {
|
|||||||
outputs: Vec<(Reg, VarID)>,
|
outputs: Vec<(Reg, VarID)>,
|
||||||
},
|
},
|
||||||
Ret {
|
Ret {
|
||||||
src: VarID,
|
src: Option<VarID>,
|
||||||
},
|
},
|
||||||
// TODO I feel like this should be turned into control flow instructions, maybe...
|
// 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
|
// not sure but LLVM has them so might be right play; seems optimal for optimization
|
||||||
@@ -58,3 +59,11 @@ pub enum IRLInstruction {
|
|||||||
Mark(Symbol),
|
Mark(Symbol),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl IRLInstruction {
|
||||||
|
pub fn is_ret(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Ret { .. } => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::ir::{AsmBlockArgType, IRUFunction, IRUInstrInst, Size, SymbolSpace};
|
use crate::ir::{AsmBlockArgType, IRUFunction, IRUInstrInst, Size, SymbolSpace, VarOffset};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
IRLFunction, IRLInstruction, IRUInstruction, IRUProgram, Len, Symbol, SymbolSpaceBuilder, Type,
|
IRLFunction, IRLInstruction, IRUInstruction, IRUProgram, Len, Symbol, SymbolSpaceBuilder, Type,
|
||||||
@@ -31,6 +31,9 @@ impl IRLProgram {
|
|||||||
for i in &f.instructions {
|
for i in &f.instructions {
|
||||||
fbuilder.insert_instr(i);
|
fbuilder.insert_instr(i);
|
||||||
}
|
}
|
||||||
|
if fbuilder.instrs.last().is_none_or(|i| !i.is_ret()) {
|
||||||
|
fbuilder.instrs.push(IRLInstruction::Ret { src: None });
|
||||||
|
}
|
||||||
let res = fbuilder.finish(f);
|
let res = fbuilder.finish(f);
|
||||||
ssbuilder.write_fn(sym, res, Some(f.name.clone()));
|
ssbuilder.write_fn(sym, res, Some(f.name.clone()));
|
||||||
}
|
}
|
||||||
@@ -48,8 +51,15 @@ pub struct IRLFunctionBuilder<'a> {
|
|||||||
builder: &'a mut SymbolSpaceBuilder,
|
builder: &'a mut SymbolSpaceBuilder,
|
||||||
instrs: Vec<IRLInstruction>,
|
instrs: Vec<IRLInstruction>,
|
||||||
stack: HashMap<VarID, Size>,
|
stack: HashMap<VarID, Size>,
|
||||||
|
subvar_map: HashMap<VarID, VarOffset>,
|
||||||
makes_call: bool,
|
makes_call: bool,
|
||||||
outer: Option<Symbol>,
|
loopp: Option<LoopCtx>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct LoopCtx {
|
||||||
|
top: Symbol,
|
||||||
|
bot: Symbol,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> IRLFunctionBuilder<'a> {
|
impl<'a> IRLFunctionBuilder<'a> {
|
||||||
@@ -57,27 +67,36 @@ impl<'a> IRLFunctionBuilder<'a> {
|
|||||||
Self {
|
Self {
|
||||||
instrs: Vec::new(),
|
instrs: Vec::new(),
|
||||||
stack: HashMap::new(),
|
stack: HashMap::new(),
|
||||||
|
subvar_map: HashMap::new(),
|
||||||
makes_call: false,
|
makes_call: false,
|
||||||
program,
|
program,
|
||||||
builder,
|
builder,
|
||||||
outer: None,
|
loopp: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn alloc_stack(&mut self, i: VarID) -> Option<()> {
|
pub fn alloc_stack(&mut self, i: VarID) -> Option<()> {
|
||||||
let size = *self
|
if self.program.size_of_var(i).expect("unsized type") == 0 {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
self.map_subvar(i);
|
||||||
|
let var = self.program.var_offset(i).expect("var offset");
|
||||||
|
*self
|
||||||
.stack
|
.stack
|
||||||
.entry(i)
|
.entry(var.id)
|
||||||
.or_insert(self.program.size_of_var(i).expect("unsized type"));
|
.or_insert(self.program.size_of_var(var.id).expect("unsized type"));
|
||||||
if size == 0 {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(())
|
Some(())
|
||||||
}
|
}
|
||||||
|
pub fn map_subvar(&mut self, i: VarID) {
|
||||||
|
let off = self.program.var_offset(i).expect("var offset");
|
||||||
|
if off.id != i {
|
||||||
|
self.subvar_map.insert(i, off);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pub fn insert_instr(&mut self, i: &IRUInstrInst) -> Option<Option<String>> {
|
pub fn insert_instr(&mut self, i: &IRUInstrInst) -> Option<Option<String>> {
|
||||||
match &i.i {
|
match &i.i {
|
||||||
IRUInstruction::Mv { dest, src } => {
|
IRUInstruction::Mv { dest, src } => {
|
||||||
self.alloc_stack(dest.id)?;
|
self.alloc_stack(dest.id)?;
|
||||||
|
self.map_subvar(src.id);
|
||||||
self.instrs.push(IRLInstruction::Mv {
|
self.instrs.push(IRLInstruction::Mv {
|
||||||
dest: dest.id,
|
dest: dest.id,
|
||||||
dest_offset: 0,
|
dest_offset: 0,
|
||||||
@@ -87,6 +106,7 @@ impl<'a> IRLFunctionBuilder<'a> {
|
|||||||
}
|
}
|
||||||
IRUInstruction::Ref { dest, src } => {
|
IRUInstruction::Ref { dest, src } => {
|
||||||
self.alloc_stack(dest.id)?;
|
self.alloc_stack(dest.id)?;
|
||||||
|
self.map_subvar(src.id);
|
||||||
self.instrs.push(IRLInstruction::Ref {
|
self.instrs.push(IRLInstruction::Ref {
|
||||||
dest: dest.id,
|
dest: dest.id,
|
||||||
src: src.id,
|
src: src.id,
|
||||||
@@ -151,21 +171,28 @@ impl<'a> IRLFunctionBuilder<'a> {
|
|||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
self.instrs.push(IRLInstruction::Call {
|
let call = IRLInstruction::Call {
|
||||||
dest,
|
dest,
|
||||||
f: sym,
|
f: sym,
|
||||||
args: args
|
args: args
|
||||||
.iter()
|
.iter()
|
||||||
.map(|a| (a.id, self.program.size_of_var(a.id).expect("unsized type")))
|
.map(|a| {
|
||||||
|
self.map_subvar(a.id);
|
||||||
|
(a.id, self.program.size_of_var(a.id).expect("unsized type"))
|
||||||
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
});
|
};
|
||||||
|
self.instrs.push(call);
|
||||||
}
|
}
|
||||||
IRUInstruction::AsmBlock { instructions, args } => {
|
IRUInstruction::AsmBlock { instructions, args } => {
|
||||||
let mut inputs = Vec::new();
|
let mut inputs = Vec::new();
|
||||||
let mut outputs = Vec::new();
|
let mut outputs = Vec::new();
|
||||||
for a in args {
|
for a in args {
|
||||||
match a.ty {
|
match a.ty {
|
||||||
AsmBlockArgType::In => inputs.push((a.reg, a.var.id)),
|
AsmBlockArgType::In => {
|
||||||
|
self.map_subvar(a.var.id);
|
||||||
|
inputs.push((a.reg, a.var.id))
|
||||||
|
}
|
||||||
AsmBlockArgType::Out => {
|
AsmBlockArgType::Out => {
|
||||||
self.alloc_stack(a.var.id)?;
|
self.alloc_stack(a.var.id)?;
|
||||||
outputs.push((a.reg, a.var.id));
|
outputs.push((a.reg, a.var.id));
|
||||||
@@ -178,50 +205,37 @@ impl<'a> IRLFunctionBuilder<'a> {
|
|||||||
outputs,
|
outputs,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
IRUInstruction::Ret { src } => self.instrs.push(IRLInstruction::Ret { src: src.id }),
|
IRUInstruction::Ret { src } => {
|
||||||
|
self.map_subvar(src.id);
|
||||||
|
self.instrs.push(IRLInstruction::Ret {
|
||||||
|
src: if self.program.size_of_var(src.id).expect("unsized var") == 0 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(src.id)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
IRUInstruction::Construct { dest, fields } => {
|
IRUInstruction::Construct { dest, fields } => {
|
||||||
self.alloc_stack(dest.id)?;
|
self.alloc_stack(dest.id)?;
|
||||||
let ty = &self.program.get_var(dest.id).ty;
|
let ty = &self.program.get_var(dest.id).ty;
|
||||||
let Type::Concrete(id) = ty else {
|
let &Type::Struct { id, ref args } = ty else {
|
||||||
return Some(Some(format!(
|
return Some(Some(format!(
|
||||||
"Failed to contruct type {}",
|
"Failed to contruct type {}",
|
||||||
self.program.type_name(ty)
|
self.program.type_name(ty)
|
||||||
)));
|
)));
|
||||||
};
|
};
|
||||||
let struc = self.program.get_struct(*id);
|
for (&fid, var) in fields {
|
||||||
for (name, var) in fields {
|
self.map_subvar(var.id);
|
||||||
self.instrs.push(IRLInstruction::Mv {
|
self.instrs.push(IRLInstruction::Mv {
|
||||||
dest: dest.id,
|
dest: dest.id,
|
||||||
src: var.id,
|
src: var.id,
|
||||||
dest_offset: struc.fields[name].offset,
|
dest_offset: self.program.field_offset(id, fid).expect("field offset"),
|
||||||
src_offset: 0,
|
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 } => {
|
IRUInstruction::If { cond, body } => {
|
||||||
|
self.map_subvar(cond.id);
|
||||||
let sym = self.builder.reserve();
|
let sym = self.builder.reserve();
|
||||||
self.instrs.push(IRLInstruction::Branch {
|
self.instrs.push(IRLInstruction::Branch {
|
||||||
to: *sym,
|
to: *sym,
|
||||||
@@ -235,19 +249,27 @@ impl<'a> IRLFunctionBuilder<'a> {
|
|||||||
IRUInstruction::Loop { body } => {
|
IRUInstruction::Loop { body } => {
|
||||||
let top = self.builder.reserve();
|
let top = self.builder.reserve();
|
||||||
let bot = self.builder.reserve();
|
let bot = self.builder.reserve();
|
||||||
let old = self.outer;
|
let old = self.loopp;
|
||||||
self.outer = Some(*bot);
|
self.loopp = Some(LoopCtx {
|
||||||
|
bot: *bot,
|
||||||
|
top: *top,
|
||||||
|
});
|
||||||
self.instrs.push(IRLInstruction::Mark(*top));
|
self.instrs.push(IRLInstruction::Mark(*top));
|
||||||
for i in body {
|
for i in body {
|
||||||
self.insert_instr(i);
|
self.insert_instr(i);
|
||||||
}
|
}
|
||||||
self.instrs.push(IRLInstruction::Jump(*top));
|
self.instrs.push(IRLInstruction::Jump(*top));
|
||||||
self.instrs.push(IRLInstruction::Mark(*bot));
|
self.instrs.push(IRLInstruction::Mark(*bot));
|
||||||
self.outer = old;
|
self.loopp = old;
|
||||||
}
|
}
|
||||||
IRUInstruction::Break => {
|
IRUInstruction::Break => {
|
||||||
self.instrs.push(IRLInstruction::Jump(
|
self.instrs.push(IRLInstruction::Jump(
|
||||||
self.outer.expect("Tried to break outside of loop"),
|
self.loopp.expect("Tried to break outside of loop").bot,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
IRUInstruction::Continue => {
|
||||||
|
self.instrs.push(IRLInstruction::Jump(
|
||||||
|
self.loopp.expect("Tried to break outside of loop").top,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -265,6 +287,7 @@ impl<'a> IRLFunctionBuilder<'a> {
|
|||||||
.collect(),
|
.collect(),
|
||||||
ret_size: self.program.size_of_type(&f.ret).expect("unsized type"),
|
ret_size: self.program.size_of_type(&f.ret).expect("unsized type"),
|
||||||
stack: self.stack,
|
stack: self.stack,
|
||||||
|
subvar_map: self.subvar_map,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
use crate::{common::FileSpan, ir::{Len, Size}};
|
use crate::{
|
||||||
|
common::FileSpan,
|
||||||
|
ir::{FieldID, Len, StructID, VarID},
|
||||||
|
};
|
||||||
|
|
||||||
use super::Type;
|
use super::Type;
|
||||||
use std::{collections::HashMap, fmt::Debug};
|
use std::{collections::HashMap, fmt::Debug};
|
||||||
@@ -13,25 +16,41 @@ pub struct FnDef {
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct StructField {
|
pub struct StructField {
|
||||||
|
pub name: String,
|
||||||
pub ty: Type,
|
pub ty: Type,
|
||||||
pub offset: Len,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct StructDef {
|
pub struct StructDef {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub fields: HashMap<String, StructField>,
|
pub fields: Vec<StructField>,
|
||||||
pub size: Size,
|
pub field_map: HashMap<String, FieldID>,
|
||||||
pub origin: Origin,
|
pub origin: Origin,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct VarDef {
|
pub struct VarDef {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
pub parent: Option<FieldRef>,
|
||||||
pub ty: Type,
|
pub ty: Type,
|
||||||
pub origin: Origin,
|
pub origin: Origin,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
|
||||||
|
pub struct VarOffset {
|
||||||
|
pub id: VarID,
|
||||||
|
pub offset: Len,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
|
||||||
|
pub struct FieldRef {
|
||||||
|
pub var: VarID,
|
||||||
|
// this is technically redundant bc you can get it from the var...
|
||||||
|
// but it makes things a lot easier, and you'd have to recheck the fields anyways
|
||||||
|
pub struc: StructID,
|
||||||
|
pub field: FieldID,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct DataDef {
|
pub struct DataDef {
|
||||||
pub ty: Type,
|
pub ty: Type,
|
||||||
@@ -49,3 +68,15 @@ impl FnDef {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl StructDef {
|
||||||
|
pub fn field(&self, id: FieldID) -> &StructField {
|
||||||
|
&self.fields[id.0]
|
||||||
|
}
|
||||||
|
pub fn get_field(&self, name: &str) -> Option<&StructField> {
|
||||||
|
self.field_map.get(name).map(|id| self.field(*id))
|
||||||
|
}
|
||||||
|
pub fn iter_fields(&self) -> impl Iterator<Item = (FieldID, &StructField)> {
|
||||||
|
self.fields.iter().enumerate().map(|(i, f)| (FieldID(i), f))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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::{compiler::arch::riscv::Reg, util::Padder};
|
use crate::{compiler::arch::riscv::Reg, ir::FieldID, util::Padder};
|
||||||
|
|
||||||
pub struct IRUFunction {
|
pub struct IRUFunction {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
@@ -33,11 +33,6 @@ pub enum IRUInstruction {
|
|||||||
dest: VarInst,
|
dest: VarInst,
|
||||||
src: FnID,
|
src: FnID,
|
||||||
},
|
},
|
||||||
Access {
|
|
||||||
dest: VarInst,
|
|
||||||
src: VarInst,
|
|
||||||
field: String,
|
|
||||||
},
|
|
||||||
Call {
|
Call {
|
||||||
dest: VarInst,
|
dest: VarInst,
|
||||||
f: VarInst,
|
f: VarInst,
|
||||||
@@ -52,7 +47,7 @@ pub enum IRUInstruction {
|
|||||||
},
|
},
|
||||||
Construct {
|
Construct {
|
||||||
dest: VarInst,
|
dest: VarInst,
|
||||||
fields: HashMap<String, VarInst>,
|
fields: HashMap<FieldID, VarInst>,
|
||||||
},
|
},
|
||||||
If {
|
If {
|
||||||
cond: VarInst,
|
cond: VarInst,
|
||||||
@@ -62,6 +57,7 @@ pub enum IRUInstruction {
|
|||||||
body: Vec<IRUInstrInst>,
|
body: Vec<IRUInstrInst>,
|
||||||
},
|
},
|
||||||
Break,
|
Break,
|
||||||
|
Continue,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -95,7 +91,6 @@ impl std::fmt::Debug for IRUInstruction {
|
|||||||
}
|
}
|
||||||
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::If { cond, body } => {
|
Self::If { cond, body } => {
|
||||||
write!(f, "if {cond:?}:")?;
|
write!(f, "if {cond:?}:")?;
|
||||||
if !body.is_empty() {
|
if !body.is_empty() {
|
||||||
@@ -125,6 +120,7 @@ impl std::fmt::Debug for IRUInstruction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::Break => write!(f, "break")?,
|
Self::Break => write!(f, "break")?,
|
||||||
|
Self::Continue => write!(f, "continue")?,
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
use std::{collections::HashMap, fmt::Debug};
|
use std::{collections::HashMap, fmt::Debug};
|
||||||
|
|
||||||
use crate::common::FileSpan;
|
|
||||||
|
|
||||||
use super::{inst::VarInst, *};
|
use super::{inst::VarInst, *};
|
||||||
|
|
||||||
pub struct IRUProgram {
|
pub struct IRUProgram {
|
||||||
pub fn_defs: Vec<FnDef>,
|
pub fn_defs: Vec<FnDef>,
|
||||||
pub var_defs: Vec<VarDef>,
|
pub var_defs: Vec<VarDef>,
|
||||||
pub type_defs: Vec<StructDef>,
|
pub struct_defs: Vec<StructDef>,
|
||||||
pub data_defs: Vec<DataDef>,
|
pub data_defs: Vec<DataDef>,
|
||||||
pub fns: Vec<Option<IRUFunction>>,
|
pub fns: Vec<Option<IRUFunction>>,
|
||||||
pub data: Vec<Vec<u8>>,
|
pub data: Vec<Vec<u8>>,
|
||||||
@@ -21,7 +19,7 @@ impl IRUProgram {
|
|||||||
Self {
|
Self {
|
||||||
fn_defs: Vec::new(),
|
fn_defs: Vec::new(),
|
||||||
var_defs: Vec::new(),
|
var_defs: Vec::new(),
|
||||||
type_defs: Vec::new(),
|
struct_defs: Vec::new(),
|
||||||
data_defs: Vec::new(),
|
data_defs: Vec::new(),
|
||||||
data: Vec::new(),
|
data: Vec::new(),
|
||||||
fn_map: HashMap::new(),
|
fn_map: HashMap::new(),
|
||||||
@@ -57,8 +55,8 @@ impl IRUProgram {
|
|||||||
pub fn get_fn_var(&self, id: VarID) -> Option<&FnDef> {
|
pub fn get_fn_var(&self, id: VarID) -> Option<&FnDef> {
|
||||||
Some(&self.fn_defs[self.fn_map.get(&id)?.0])
|
Some(&self.fn_defs[self.fn_map.get(&id)?.0])
|
||||||
}
|
}
|
||||||
pub fn get_struct(&self, id: TypeID) -> &StructDef {
|
pub fn get_struct(&self, id: StructID) -> &StructDef {
|
||||||
&self.type_defs[id.0]
|
&self.struct_defs[id.0]
|
||||||
}
|
}
|
||||||
pub fn alias_fn(&mut self, name: &str, id: FnID) {
|
pub fn alias_fn(&mut self, name: &str, id: FnID) {
|
||||||
self.insert(name, Ident::Fn(id));
|
self.insert(name, Ident::Fn(id));
|
||||||
@@ -80,9 +78,11 @@ impl IRUProgram {
|
|||||||
pub fn size_of_type(&self, ty: &Type) -> Option<Size> {
|
pub fn size_of_type(&self, ty: &Type) -> Option<Size> {
|
||||||
// TODO: target matters
|
// TODO: target matters
|
||||||
Some(match ty {
|
Some(match ty {
|
||||||
Type::Concrete(id) => self.type_defs[id.0].size,
|
|
||||||
Type::Bits(b) => *b,
|
Type::Bits(b) => *b,
|
||||||
Type::Generic { base, args } => todo!(),
|
Type::Struct { id, args } => self.struct_defs[id.0]
|
||||||
|
.fields
|
||||||
|
.iter()
|
||||||
|
.try_fold(0, |sum, f| Some(sum + self.size_of_type(&f.ty)?))?,
|
||||||
Type::Fn { args, ret } => todo!(),
|
Type::Fn { args, ret } => todo!(),
|
||||||
Type::Ref(_) => 64,
|
Type::Ref(_) => 64,
|
||||||
Type::Array(ty, len) => self.size_of_type(ty)? * len,
|
Type::Array(ty, len) => self.size_of_type(ty)? * len,
|
||||||
@@ -92,12 +92,21 @@ impl IRUProgram {
|
|||||||
Type::Unit => 0,
|
Type::Unit => 0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
pub fn struct_layout() {}
|
||||||
pub fn size_of_var(&self, var: VarID) -> Option<Size> {
|
pub fn size_of_var(&self, var: VarID) -> Option<Size> {
|
||||||
self.size_of_type(&self.var_defs[var.0].ty)
|
self.size_of_type(&self.var_defs[var.0].ty)
|
||||||
}
|
}
|
||||||
|
pub fn temp_subvar(&mut self, origin: Origin, ty: Type, parent: FieldRef) -> VarInst {
|
||||||
|
self.temp_var_inner(origin, ty, Some(parent))
|
||||||
|
}
|
||||||
pub fn temp_var(&mut self, origin: Origin, ty: Type) -> VarInst {
|
pub fn temp_var(&mut self, origin: Origin, ty: Type) -> VarInst {
|
||||||
|
self.temp_var_inner(origin, ty, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn temp_var_inner(&mut self, origin: Origin, ty: Type, parent: Option<FieldRef>) -> VarInst {
|
||||||
let v = self.def_var(VarDef {
|
let v = self.def_var(VarDef {
|
||||||
name: format!("temp{}", self.temp),
|
name: format!("temp{}", self.temp),
|
||||||
|
parent,
|
||||||
origin,
|
origin,
|
||||||
ty,
|
ty,
|
||||||
});
|
});
|
||||||
@@ -107,11 +116,13 @@ impl IRUProgram {
|
|||||||
span: origin,
|
span: origin,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn def_fn(&mut self, def: FnDef) -> FnID {
|
pub fn def_fn(&mut self, def: FnDef) -> FnID {
|
||||||
let i = self.fn_defs.len();
|
let i = self.fn_defs.len();
|
||||||
let id = FnID(i);
|
let id = FnID(i);
|
||||||
let var_def = VarDef {
|
let var_def = VarDef {
|
||||||
name: def.name.clone(),
|
name: def.name.clone(),
|
||||||
|
parent: None,
|
||||||
origin: def.origin,
|
origin: def.origin,
|
||||||
ty: def.ty(),
|
ty: def.ty(),
|
||||||
};
|
};
|
||||||
@@ -126,11 +137,11 @@ impl IRUProgram {
|
|||||||
|
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
pub fn def_type(&mut self, def: StructDef) -> TypeID {
|
pub fn def_struct(&mut self, def: StructDef) -> StructID {
|
||||||
let i = self.type_defs.len();
|
let i = self.struct_defs.len();
|
||||||
let id = TypeID(i);
|
let id = StructID(i);
|
||||||
self.insert(&def.name, Ident::Type(id));
|
self.insert(&def.name, Ident::Type(id));
|
||||||
self.type_defs.push(def);
|
self.struct_defs.push(def);
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
pub fn def_data(&mut self, def: DataDef, bytes: Vec<u8>) -> DataID {
|
pub fn def_data(&mut self, def: DataDef, bytes: Vec<u8>) -> DataID {
|
||||||
@@ -142,10 +153,7 @@ impl IRUProgram {
|
|||||||
pub fn type_name(&self, ty: &Type) -> String {
|
pub fn type_name(&self, ty: &Type) -> String {
|
||||||
let mut str = String::new();
|
let mut str = String::new();
|
||||||
match ty {
|
match ty {
|
||||||
Type::Concrete(t) => {
|
Type::Struct { id: base, args } => {
|
||||||
str += &self.get_struct(*t).name;
|
|
||||||
}
|
|
||||||
Type::Generic { base, args } => {
|
|
||||||
str += &self.get_struct(*base).name;
|
str += &self.get_struct(*base).name;
|
||||||
if let Some(arg) = args.first() {
|
if let Some(arg) = args.first() {
|
||||||
str = str + "<" + &self.type_name(arg);
|
str = str + "<" + &self.type_name(arg);
|
||||||
@@ -201,13 +209,29 @@ impl IRUProgram {
|
|||||||
.enumerate()
|
.enumerate()
|
||||||
.flat_map(|(i, f)| Some((FnID(i), f.as_ref()?)))
|
.flat_map(|(i, f)| Some((FnID(i), f.as_ref()?)))
|
||||||
}
|
}
|
||||||
|
pub fn var_offset(&self, var: VarID) -> Option<VarOffset> {
|
||||||
|
let mut current = VarOffset { id: var, offset: 0 };
|
||||||
|
while let Some(parent) = self.var_defs[current.id.0].parent {
|
||||||
|
current.id = parent.var;
|
||||||
|
current.offset += self.field_offset(parent.struc, parent.field)?;
|
||||||
|
}
|
||||||
|
Some(current)
|
||||||
|
}
|
||||||
|
pub fn field_offset(&self, struct_id: StructID, field: FieldID) -> Option<Len> {
|
||||||
|
let struc = self.get_struct(struct_id);
|
||||||
|
let mut offset = 0;
|
||||||
|
for i in 0..field.0 {
|
||||||
|
offset += self.size_of_type(&struc.fields[i].ty)?;
|
||||||
|
}
|
||||||
|
Some(offset)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum Ident {
|
pub enum Ident {
|
||||||
Var(VarID),
|
Var(VarID),
|
||||||
Fn(FnID),
|
Fn(FnID),
|
||||||
Type(TypeID),
|
Type(StructID),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
@@ -215,7 +239,7 @@ pub struct Idents {
|
|||||||
pub latest: Ident,
|
pub latest: Ident,
|
||||||
pub var: Option<VarID>,
|
pub var: Option<VarID>,
|
||||||
pub func: Option<FnID>,
|
pub func: Option<FnID>,
|
||||||
pub ty: Option<TypeID>,
|
pub struc: Option<StructID>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Idents {
|
impl Idents {
|
||||||
@@ -224,7 +248,7 @@ impl Idents {
|
|||||||
latest,
|
latest,
|
||||||
var: None,
|
var: None,
|
||||||
func: None,
|
func: None,
|
||||||
ty: None,
|
struc: None,
|
||||||
};
|
};
|
||||||
s.insert(latest);
|
s.insert(latest);
|
||||||
s
|
s
|
||||||
@@ -238,7 +262,7 @@ impl Idents {
|
|||||||
Ident::Fn(f) => {
|
Ident::Fn(f) => {
|
||||||
self.func = Some(f);
|
self.func = Some(f);
|
||||||
}
|
}
|
||||||
Ident::Type(t) => self.ty = Some(t),
|
Ident::Type(t) => self.struc = Some(t),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
use super::{IRUProgram, Len, TypeID};
|
use super::{IRUProgram, Len, StructID};
|
||||||
|
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub enum Type {
|
pub enum Type {
|
||||||
Concrete(TypeID),
|
|
||||||
Bits(u32),
|
Bits(u32),
|
||||||
Generic { base: TypeID, args: Vec<Type> },
|
Struct { id: StructID, args: Vec<Type> },
|
||||||
Fn { args: Vec<Type>, ret: Box<Type> },
|
Fn { args: Vec<Type>, ret: Box<Type> },
|
||||||
Ref(Box<Type>),
|
Ref(Box<Type>),
|
||||||
Slice(Box<Type>),
|
Slice(Box<Type>),
|
||||||
@@ -26,6 +25,7 @@ impl Type {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// should impl instead
|
||||||
pub fn resolve_types(ns: &IRUProgram) {
|
pub fn resolve_types(ns: &IRUProgram) {
|
||||||
for (i, f) in ns.iter_fns() {
|
for (i, f) in ns.iter_fns() {
|
||||||
for inst in &f.instructions {
|
for inst in &f.instructions {
|
||||||
|
|||||||
@@ -80,8 +80,8 @@ impl IRUProgram {
|
|||||||
}
|
}
|
||||||
IRUInstruction::Construct { dest, fields } => {
|
IRUInstruction::Construct { dest, fields } => {
|
||||||
let dest_def = self.get_var(dest.id);
|
let dest_def = self.get_var(dest.id);
|
||||||
let tyid = match dest_def.ty {
|
let tyid = match &dest_def.ty {
|
||||||
Type::Concrete(id) => id,
|
Type::Struct { id, args } => *id,
|
||||||
_ => {
|
_ => {
|
||||||
output.err(CompilerMsg {
|
output.err(CompilerMsg {
|
||||||
msg: "uhh type is not struct".to_string(),
|
msg: "uhh type is not struct".to_string(),
|
||||||
@@ -91,44 +91,17 @@ impl IRUProgram {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
let def = self.get_struct(tyid);
|
let def = self.get_struct(tyid);
|
||||||
for (name, field) in &def.fields {
|
for (id, field) in def.iter_fields() {
|
||||||
if let Some(var) = fields.get(name) {
|
if let Some(var) = fields.get(&id) {
|
||||||
let ety = &self.get_var(var.id).ty;
|
let ety = &self.get_var(var.id).ty;
|
||||||
output.check_assign(self, &field.ty, ety, var.span);
|
output.check_assign(self, &field.ty, ety, var.span);
|
||||||
} else {
|
} else {
|
||||||
output.err(CompilerMsg {
|
output.err(CompilerMsg {
|
||||||
msg: format!("field '{name}' missing from struct"),
|
msg: format!("field '{}' missing from struct", field.name),
|
||||||
spans: vec![dest.span],
|
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 } => {
|
IRUInstruction::If { cond, body } => {
|
||||||
let cond = self.get_var(cond.id);
|
let cond = self.get_var(cond.id);
|
||||||
@@ -147,6 +120,15 @@ impl IRUProgram {
|
|||||||
}
|
}
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
IRUInstruction::Continue => {
|
||||||
|
if !breakable {
|
||||||
|
output.err(CompilerMsg {
|
||||||
|
msg: "Can't continue here (outside of loop)".to_string(),
|
||||||
|
spans: vec![i.span],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if needs_ret && no_ret && *ret != Type::Unit {
|
if needs_ret && no_ret && *ret != Type::Unit {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::ir::{IRUInstruction, VarInst};
|
use crate::ir::{IRUInstruction, Type, VarInst};
|
||||||
|
|
||||||
use super::{FnLowerCtx, FnLowerable, PBlock, PStatement};
|
use super::{FnLowerCtx, FnLowerable, PBlock, PStatement};
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ impl FnLowerable for PBlock {
|
|||||||
for statement in &self.statements {
|
for statement in &self.statements {
|
||||||
statement.lower(ctx);
|
statement.lower(ctx);
|
||||||
}
|
}
|
||||||
let res = self.result.as_ref().map(|r| r.lower(ctx)).flatten();
|
let res = self.result.as_ref().and_then(|r| r.lower(ctx));
|
||||||
ctx.program.pop();
|
ctx.program.pop();
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
@@ -28,8 +28,13 @@ impl FnLowerable for PStatement {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
super::PStatement::Return(e) => {
|
super::PStatement::Return(e) => {
|
||||||
|
if let Some(e) = e {
|
||||||
let src = e.lower(ctx)?;
|
let src = e.lower(ctx)?;
|
||||||
ctx.push_at(IRUInstruction::Ret { src }, src.span);
|
ctx.push_at(IRUInstruction::Ret { src }, src.span);
|
||||||
|
} else {
|
||||||
|
let src = ctx.temp(Type::Unit);
|
||||||
|
ctx.push_at(IRUInstruction::Ret { src }, src.span);
|
||||||
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
super::PStatement::Expr(e) => e.lower(ctx),
|
super::PStatement::Expr(e) => e.lower(ctx),
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ impl Node<PVarDef> {
|
|||||||
Some(VarDef {
|
Some(VarDef {
|
||||||
name,
|
name,
|
||||||
ty,
|
ty,
|
||||||
|
parent: None,
|
||||||
origin: self.span,
|
origin: self.span,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -36,18 +37,14 @@ impl PType {
|
|||||||
let Some(name) = self.name.as_ref() else {
|
let Some(name) = self.name.as_ref() else {
|
||||||
return Type::Error;
|
return Type::Error;
|
||||||
};
|
};
|
||||||
match namespace.get(&name).and_then(|ids| ids.ty) {
|
match namespace.get(&name).and_then(|ids| ids.struc) {
|
||||||
Some(id) => {
|
Some(id) => {
|
||||||
if self.args.is_empty() {
|
|
||||||
Type::Concrete(id)
|
|
||||||
} else {
|
|
||||||
let args = self
|
let args = self
|
||||||
.args
|
.args
|
||||||
.iter()
|
.iter()
|
||||||
.map(|n| n.lower(namespace, output))
|
.map(|n| n.lower(namespace, output))
|
||||||
.collect();
|
.collect();
|
||||||
Type::Generic { base: id, args }
|
Type::Struct { id, args }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
if let Ok(num) = name.parse::<u32>() {
|
if let Ok(num) = name.parse::<u32>() {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use super::{func::FnLowerCtx, FnLowerable, PExpr, UnaryOp};
|
use super::{func::FnLowerCtx, FnLowerable, PExpr, UnaryOp};
|
||||||
use crate::{
|
use crate::{
|
||||||
ir::{DataDef, IRUInstruction, Type, VarInst},
|
ir::{DataDef, FieldRef, IRUInstruction, Type, VarInst},
|
||||||
parser::PInfixOp,
|
parser::PInfixOp,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -61,30 +61,36 @@ impl FnLowerable for PExpr {
|
|||||||
let res1 = e1.lower(ctx)?;
|
let res1 = e1.lower(ctx)?;
|
||||||
if *op == PInfixOp::Access {
|
if *op == PInfixOp::Access {
|
||||||
let sty = &ctx.program.get_var(res1.id).ty;
|
let sty = &ctx.program.get_var(res1.id).ty;
|
||||||
let Type::Concrete(tid) = sty else {
|
let Type::Struct {
|
||||||
|
id: struct_id,
|
||||||
|
args,
|
||||||
|
} = sty
|
||||||
|
else {
|
||||||
ctx.err(format!(
|
ctx.err(format!(
|
||||||
"Type {:?} has no fields",
|
"Type {:?} has no fields",
|
||||||
ctx.program.type_name(sty)
|
ctx.program.type_name(sty)
|
||||||
));
|
));
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
let struc = ctx.program.get_struct(*tid);
|
let struc = ctx.program.get_struct(*struct_id);
|
||||||
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("Field accesses must be identifiers".to_string());
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
let fname = &ident.as_ref()?.0;
|
let fname = &ident.as_ref()?.0;
|
||||||
let Some(field) = struc.fields.get(fname) else {
|
let Some(&field) = struc.field_map.get(fname) else {
|
||||||
ctx.err(format!("Field '{fname}' not in struct"));
|
ctx.err(format!("Field '{fname}' not in struct"));
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
let temp = ctx.temp(field.ty.clone());
|
let fdef = struc.field(field);
|
||||||
ctx.push(IRUInstruction::Access {
|
ctx.temp_subvar(
|
||||||
dest: temp,
|
fdef.ty.clone(),
|
||||||
src: res1,
|
FieldRef {
|
||||||
field: fname.to_string(),
|
var: res1.id,
|
||||||
});
|
struc: *struct_id,
|
||||||
temp
|
field,
|
||||||
|
},
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
let res2 = e2.lower(ctx)?;
|
let res2 = e2.lower(ctx)?;
|
||||||
match op {
|
match op {
|
||||||
@@ -186,6 +192,10 @@ impl FnLowerable for PExpr {
|
|||||||
ctx.push(IRUInstruction::Break);
|
ctx.push(IRUInstruction::Break);
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
PExpr::Continue => {
|
||||||
|
ctx.push(IRUInstruction::Continue);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use super::{CompilerMsg, CompilerOutput, FileSpan, FnLowerable, Node, PFunction};
|
use super::{CompilerMsg, CompilerOutput, FileSpan, FnLowerable, Node, PFunction};
|
||||||
use crate::{
|
use crate::{
|
||||||
ir::{
|
ir::{
|
||||||
FnDef, FnID, IRUFunction, IRUInstrInst, IRUInstruction, IRUProgram, Idents, Origin, Type,
|
FnDef, FnID, IRUFunction, IRUInstrInst, IRUInstruction, IRUProgram, Idents, Type, VarDef,
|
||||||
VarDef, VarInst,
|
VarInst, FieldRef,
|
||||||
},
|
},
|
||||||
parser,
|
parser,
|
||||||
};
|
};
|
||||||
@@ -32,6 +32,7 @@ impl PFunction {
|
|||||||
a.lower(map, output).unwrap_or(VarDef {
|
a.lower(map, output).unwrap_or(VarDef {
|
||||||
name: "{error}".to_string(),
|
name: "{error}".to_string(),
|
||||||
origin: a.span,
|
origin: a.span,
|
||||||
|
parent: None,
|
||||||
ty: Type::Error,
|
ty: Type::Error,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -117,6 +118,9 @@ impl FnLowerCtx<'_> {
|
|||||||
pub fn temp(&mut self, ty: Type) -> VarInst {
|
pub fn temp(&mut self, ty: Type) -> VarInst {
|
||||||
self.program.temp_var(self.span, ty)
|
self.program.temp_var(self.span, ty)
|
||||||
}
|
}
|
||||||
|
pub fn temp_subvar(&mut self, ty: Type, parent: FieldRef) -> VarInst {
|
||||||
|
self.program.temp_subvar(self.span, ty, parent)
|
||||||
|
}
|
||||||
pub fn push(&mut self, i: IRUInstruction) {
|
pub fn push(&mut self, i: IRUInstruction) {
|
||||||
self.instructions.push(IRUInstrInst { i, span: self.span });
|
self.instructions.push(IRUInstrInst { i, span: self.span });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
common::{CompilerMsg, CompilerOutput, FileSpan},
|
common::{CompilerOutput, FileSpan},
|
||||||
ir::{IRUInstruction, IRUProgram, Origin, StructDef, StructField, VarInst},
|
ir::{FieldID, IRUInstruction, IRUProgram, StructDef, StructField, Type, VarInst},
|
||||||
parser::{Node, PConstruct, PConstructFields, PStruct, PStructFields},
|
parser::{Node, PConstruct, PConstructFields, PStruct, PStructFields},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -12,6 +12,18 @@ 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.program, ctx.output);
|
let ty = self.name.lower(ctx.program, ctx.output);
|
||||||
|
let field_map = match ty {
|
||||||
|
Type::Struct { id, .. } => ctx.program.get_struct(id),
|
||||||
|
_ => {
|
||||||
|
ctx.err(format!(
|
||||||
|
"Type {} cannot be constructed",
|
||||||
|
ctx.program.type_name(&ty)
|
||||||
|
));
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.field_map
|
||||||
|
.clone();
|
||||||
let fields = match &self.fields {
|
let fields = match &self.fields {
|
||||||
PConstructFields::Named(nodes) => nodes
|
PConstructFields::Named(nodes) => nodes
|
||||||
.iter()
|
.iter()
|
||||||
@@ -19,7 +31,15 @@ impl FnLowerable for PConstruct {
|
|||||||
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 expr = def.val.as_ref()?.lower(ctx)?;
|
let expr = def.val.as_ref()?.lower(ctx)?;
|
||||||
Some((name, expr))
|
let Some(&field) = field_map.get(&name) else {
|
||||||
|
ctx.err(format!(
|
||||||
|
"Struct {} has no field {}",
|
||||||
|
ctx.program.type_name(&ty),
|
||||||
|
name
|
||||||
|
));
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
Some((field, expr))
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
PConstructFields::Tuple(nodes) => nodes
|
PConstructFields::Tuple(nodes) => nodes
|
||||||
@@ -27,10 +47,19 @@ impl FnLowerable for PConstruct {
|
|||||||
.enumerate()
|
.enumerate()
|
||||||
.flat_map(|(i, n)| {
|
.flat_map(|(i, n)| {
|
||||||
let expr = n.as_ref()?.lower(ctx)?;
|
let expr = n.as_ref()?.lower(ctx)?;
|
||||||
Some((format!("{i}"), expr))
|
let name = format!("{i}");
|
||||||
|
let Some(&field) = field_map.get(&name) else {
|
||||||
|
ctx.err(format!(
|
||||||
|
"Struct {} has no field {}",
|
||||||
|
ctx.program.type_name(&ty),
|
||||||
|
name
|
||||||
|
));
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
Some((field, expr))
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
PConstructFields::None => HashMap::new(),
|
PConstructFields::None => Default::default(),
|
||||||
};
|
};
|
||||||
let id = ctx.temp(ty);
|
let id = ctx.temp(ty);
|
||||||
ctx.push(IRUInstruction::Construct { dest: id, fields });
|
ctx.push(IRUInstruction::Construct { dest: id, fields });
|
||||||
@@ -45,7 +74,7 @@ impl PStruct {
|
|||||||
output: &mut CompilerOutput,
|
output: &mut CompilerOutput,
|
||||||
span: FileSpan,
|
span: FileSpan,
|
||||||
) -> Option<()> {
|
) -> Option<()> {
|
||||||
let mut offset = 0;
|
let mut field_map = HashMap::new();
|
||||||
let fields = match &self.fields {
|
let fields = match &self.fields {
|
||||||
PStructFields::Named(nodes) => nodes
|
PStructFields::Named(nodes) => nodes
|
||||||
.iter()
|
.iter()
|
||||||
@@ -54,16 +83,7 @@ impl PStruct {
|
|||||||
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(p, output);
|
let ty = tynode.lower(p, output);
|
||||||
let size = p.size_of_type(&ty).unwrap_or_else(|| {
|
Some((name, ty))
|
||||||
output.err(CompilerMsg {
|
|
||||||
msg: format!("Size of type '{}' unknown", p.type_name(&ty)),
|
|
||||||
spans: vec![tynode.span],
|
|
||||||
});
|
|
||||||
0
|
|
||||||
});
|
|
||||||
let res = Some((name, StructField { ty, offset }));
|
|
||||||
offset += size;
|
|
||||||
res
|
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
PStructFields::Tuple(nodes) => nodes
|
PStructFields::Tuple(nodes) => nodes
|
||||||
@@ -71,18 +91,23 @@ impl PStruct {
|
|||||||
.enumerate()
|
.enumerate()
|
||||||
.flat_map(|(i, n)| {
|
.flat_map(|(i, n)| {
|
||||||
let ty = n.as_ref()?.lower(p, output, span);
|
let ty = n.as_ref()?.lower(p, output, span);
|
||||||
let size = p.size_of_type(&ty)?;
|
Some((format!("{i}"), ty))
|
||||||
let res = Some((format!("{i}"), StructField { ty, offset }));
|
|
||||||
offset += size;
|
|
||||||
res
|
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
PStructFields::None => HashMap::new(),
|
PStructFields::None => vec![],
|
||||||
};
|
}
|
||||||
p.def_type(StructDef {
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, (name, ty))| {
|
||||||
|
let id = FieldID(i);
|
||||||
|
field_map.insert(name.clone(), id);
|
||||||
|
StructField { name, ty }
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
p.def_struct(StructDef {
|
||||||
name: self.name.as_ref()?.to_string(),
|
name: self.name.as_ref()?.to_string(),
|
||||||
origin: span,
|
origin: span,
|
||||||
size: offset,
|
field_map,
|
||||||
fields,
|
fields,
|
||||||
});
|
});
|
||||||
Some(())
|
Some(())
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ pub enum PExpr {
|
|||||||
If(BoxNode, BoxNode),
|
If(BoxNode, BoxNode),
|
||||||
Loop(BoxNode),
|
Loop(BoxNode),
|
||||||
Break,
|
Break,
|
||||||
|
Continue,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parsable for PExpr {
|
impl Parsable for PExpr {
|
||||||
@@ -57,6 +58,9 @@ impl Parsable for PExpr {
|
|||||||
} else if next.is_keyword(Keyword::Break) {
|
} else if next.is_keyword(Keyword::Break) {
|
||||||
ctx.next();
|
ctx.next();
|
||||||
Self::Break
|
Self::Break
|
||||||
|
} else if next.is_keyword(Keyword::Continue) {
|
||||||
|
ctx.next();
|
||||||
|
Self::Continue
|
||||||
} 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()?)
|
||||||
@@ -165,6 +169,7 @@ impl Debug for PExpr {
|
|||||||
PExpr::If(cond, res) => write!(f, "if {cond:?} then {res:?}")?,
|
PExpr::If(cond, res) => write!(f, "if {cond:?} then {res:?}")?,
|
||||||
PExpr::Loop(res) => write!(f, "loop -> {res:?}")?,
|
PExpr::Loop(res) => write!(f, "loop -> {res:?}")?,
|
||||||
PExpr::Break => write!(f, "break")?,
|
PExpr::Break => write!(f, "break")?,
|
||||||
|
PExpr::Continue => write!(f, "continue")?,
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use super::{PExpr, Keyword, Node, Parsable, ParseResult, ParserCtx, Symbol, Token, PVarDef};
|
use super::{Keyword, Node, PExpr, PVarDef, Parsable, ParseResult, ParserCtx, Symbol, Token};
|
||||||
|
|
||||||
pub enum PStatement {
|
pub enum PStatement {
|
||||||
Let(Node<PVarDef>, Node<PExpr>),
|
Let(Node<PVarDef>, Node<PExpr>),
|
||||||
Return(Node<PExpr>),
|
Return(Option<Node<PExpr>>),
|
||||||
Expr(Node<PExpr>),
|
Expr(Node<PExpr>),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -18,7 +18,11 @@ impl Parsable for PStatement {
|
|||||||
}
|
}
|
||||||
Token::Keyword(Keyword::Return) => {
|
Token::Keyword(Keyword::Return) => {
|
||||||
ctx.next();
|
ctx.next();
|
||||||
ctx.parse().map(Self::Return)
|
if ctx.peek().is_some_and(|t| t.is_symbol(Symbol::Semicolon)) {
|
||||||
|
ParseResult::Ok(Self::Return(None))
|
||||||
|
} else {
|
||||||
|
ctx.parse().map(|res| Self::Return(Some(res)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => ctx.parse().map(Self::Expr),
|
_ => ctx.parse().map(Self::Expr),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,9 @@ pub enum Keyword {
|
|||||||
Let,
|
Let,
|
||||||
If,
|
If,
|
||||||
Return,
|
Return,
|
||||||
Break,
|
|
||||||
Loop,
|
Loop,
|
||||||
|
Break,
|
||||||
|
Continue,
|
||||||
Struct,
|
Struct,
|
||||||
Trait,
|
Trait,
|
||||||
Impl,
|
Impl,
|
||||||
@@ -24,6 +25,7 @@ impl Keyword {
|
|||||||
"for" => Self::For,
|
"for" => Self::For,
|
||||||
"return" => Self::Return,
|
"return" => Self::Return,
|
||||||
"break" => Self::Break,
|
"break" => Self::Break,
|
||||||
|
"continue" => Self::Continue,
|
||||||
"loop" => Self::Loop,
|
"loop" => Self::Loop,
|
||||||
"trait" => Self::Trait,
|
"trait" => Self::Trait,
|
||||||
"impl" => Self::Impl,
|
"impl" => Self::Impl,
|
||||||
|
|||||||
Reference in New Issue
Block a user