Merge remote-tracking branch 'origin/main'
This commit is contained in:
@@ -19,18 +19,19 @@ dsa_tasks = { path = "../../libraries/dsa_tasks" }
|
||||
|
||||
zxq5_egui = { path = "../../libraries/zxq5_egui" }
|
||||
assembler = { path = "../../tooling/assembler" }
|
||||
dscs_compiler = { path = "../../tooling/dscs_compiler"}
|
||||
dsfl_compiler = { path = "../../tooling/dsfl_compiler" }
|
||||
dsa_emulator = { version = "0.3.0", path = "../dsa_emulator" }
|
||||
disassembler = { version = "0.3.0", path = "../../tooling/disassembler" }
|
||||
|
||||
clap = { version = "4.5.60", features = ["derive"] }
|
||||
ron = "0.12.0"
|
||||
egui = "0.35.0"
|
||||
eframe = "0.35.0"
|
||||
egui_tiles = "0.16.0"
|
||||
egui_code_editor = "0.3.7"
|
||||
egui_file = "0.27.0"
|
||||
egui_extras = "0.35.0"
|
||||
egui = "0.36.1"
|
||||
eframe = "0.36.1"
|
||||
egui_tiles = "0.17.1"
|
||||
egui_code_editor = "0.4.1"
|
||||
egui_file = "0.28.0"
|
||||
egui_extras = "0.36.1"
|
||||
|
||||
egui-file-dialog = "0.14.1"
|
||||
egui_commonmark = { version = "0.24.0", features = ["better_syntax_highlighting", "macros"] }
|
||||
egui-file-dialog = "0.15.0"
|
||||
egui_commonmark = { version = "0.25.0", features = ["better_syntax_highlighting", "macros"] }
|
||||
@@ -29,6 +29,36 @@ impl ProcessSpawner {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ProcessJob<Proc, OnComplete>
|
||||
where
|
||||
Proc: TraceableProcess,
|
||||
OnComplete: FnOnce(Result<Proc::Output, Proc::Error>) -> Action + Send + 'static,
|
||||
{
|
||||
input: Proc::Input,
|
||||
proc: Proc,
|
||||
on_complete: OnComplete,
|
||||
}
|
||||
|
||||
impl<Proc, OnComplete> ProcessJob<Proc, OnComplete>
|
||||
where
|
||||
Proc: TraceableProcess,
|
||||
OnComplete: FnOnce(Result<Proc::Output, Proc::Error>) -> Action + Send + 'static,
|
||||
{
|
||||
pub fn new(input: Proc::Input, proc: Proc, on_complete: OnComplete) -> Self {
|
||||
Self {
|
||||
input,
|
||||
proc,
|
||||
on_complete,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_action(self) -> Action {
|
||||
Action::SpawnProcess(ProcessSpawner::new(move |pm| {
|
||||
pm.spawn(self.proc.label(), self.proc, self.input, self.on_complete)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ProcessHandle {
|
||||
id: usize,
|
||||
name: String,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::state::Action;
|
||||
use crate::state::DataSource::Data;
|
||||
use crate::ui::process_manager::ProcessSpawner;
|
||||
use crate::ui::process_manager::{ProcessJob, ProcessSpawner};
|
||||
use crate::ui::tiles::TilePane;
|
||||
use crate::ui::tiles::editor::syntax::SyntaxFrom;
|
||||
use assembler::prelude::Assembler;
|
||||
@@ -12,6 +12,7 @@ use egui_tiles::TileId;
|
||||
use std::any::Any;
|
||||
use std::fs::File;
|
||||
use std::path::{Path, PathBuf};
|
||||
use dscs_compiler::DSCSCompiler;
|
||||
use theme::THEME;
|
||||
use zxq5_egui::buttons::PaddedWidget;
|
||||
|
||||
@@ -67,32 +68,48 @@ impl Editor {
|
||||
&mut self.buffer
|
||||
}
|
||||
|
||||
// pub fn get_build_command(file_path: PathBuf) -> impl TraceableProcess {
|
||||
// let filename = file_path.file_name().unwrap().to_str().unwrap();
|
||||
// match file_path.extension().unwrap().to_str().unwrap() {
|
||||
// "dsa" => Assembler::new(filename),
|
||||
// "dsfl" => DsflCompiler::new(filename),
|
||||
// }
|
||||
// }
|
||||
|
||||
pub fn create_build_action(
|
||||
&self,
|
||||
filename: PathBuf,
|
||||
on_complete: impl FnOnce(DseExecutable) -> Action + Send + 'static,
|
||||
) -> Action {
|
||||
ProcessSpawner::new(move |pm| {
|
||||
let builder = Assembler::new(filename.file_name().unwrap().to_str().unwrap());
|
||||
pm.spawn(
|
||||
builder.label(),
|
||||
builder,
|
||||
filename,
|
||||
move |result| match result {
|
||||
Ok(bin) => on_complete(bin),
|
||||
Err(e) => Action::ShowError(e.to_string()),
|
||||
},
|
||||
);
|
||||
})
|
||||
.into_action()
|
||||
) -> Option<Action> {
|
||||
let Some(ext) = filename.extension() else {
|
||||
return None;
|
||||
};
|
||||
|
||||
match ext.to_str().unwrap() {
|
||||
"dsa" => {
|
||||
let builder = Assembler::new(filename.file_name().unwrap().to_str().unwrap());
|
||||
Some(ProcessJob::new(
|
||||
filename, builder,
|
||||
move |result| match result {
|
||||
Ok(bin) => on_complete(bin),
|
||||
Err(e) => Action::ShowError(e.to_string()),
|
||||
}
|
||||
).into_action())
|
||||
},
|
||||
"dsc" => {
|
||||
let builder = DSCSCompiler::new(filename.display().to_string(), false);
|
||||
Some(ProcessJob::new(
|
||||
filename.clone(), builder,
|
||||
move |result| match result {
|
||||
Ok(bin) => {
|
||||
let output = filename.with_extension("dsa");
|
||||
std::fs::write(&output, &bin).expect("Failed to write output");
|
||||
Action::OpenFile(output)
|
||||
},
|
||||
Err(e) => Action::ShowError(e.to_string()),
|
||||
}
|
||||
).into_action())
|
||||
|
||||
}
|
||||
_ => {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,9 +132,9 @@ impl TilePane for Editor {
|
||||
return;
|
||||
};
|
||||
|
||||
action = Some(self.create_build_action(filename, |res| {
|
||||
action = self.create_build_action(filename, |res| {
|
||||
Action::LoadBinary(Data(res.to_memory_image()))
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
if PaddedWidget::button("Build").ui(ui).clicked() {
|
||||
@@ -126,12 +143,12 @@ impl TilePane for Editor {
|
||||
return;
|
||||
};
|
||||
|
||||
action = Some(self.create_build_action(filename.clone(), move |res| {
|
||||
action = self.create_build_action(filename.clone(), move |res| {
|
||||
let filename = filename.with_extension("dsb");
|
||||
let mut writer = File::create(&filename).unwrap();
|
||||
res.write(&mut writer).unwrap();
|
||||
Action::OpenFile(filename)
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
PaddedWidget::label(
|
||||
|
||||
@@ -5,4 +5,4 @@ version.workspace = true
|
||||
authors.workspace = true
|
||||
|
||||
[dependencies]
|
||||
egui = "0.35.0"
|
||||
egui = "0.36.1"
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
// GENERATED BY DSC COMPILER
|
||||
// Generated at 2026-08-20 11:13:23
|
||||
|
||||
// Imports
|
||||
include alloc: "./lib/memory/block_alloc.dsa"
|
||||
include print: "./lib/io/print.dsa"
|
||||
|
||||
// Globals & Reserved Memory
|
||||
|
||||
// Entry Point
|
||||
dw stack: 0x010000
|
||||
db message: "Process Exited with code:"
|
||||
_init:
|
||||
ldw stack, bpr, 0
|
||||
mov bpr, spr
|
||||
push zero
|
||||
call main
|
||||
call print::print_newline
|
||||
lwi message, rg0
|
||||
push rg0
|
||||
call print::print
|
||||
pop zero
|
||||
call print::print_hex_word
|
||||
pop zero
|
||||
hlt
|
||||
|
||||
// Return
|
||||
_ret:
|
||||
mov bpr, spr
|
||||
pop bpr
|
||||
ret
|
||||
|
||||
db str_4: "successful free of ptr"
|
||||
// fn main() -> u32
|
||||
main:
|
||||
push bpr
|
||||
mov spr, bpr
|
||||
lli 32, rg0
|
||||
lli 64, rg1
|
||||
// push arg 1
|
||||
push rg0
|
||||
// push arg 0
|
||||
push rg1
|
||||
call alloc::init
|
||||
pop rg2
|
||||
pop zero
|
||||
push rg2
|
||||
// push arg 0
|
||||
push rg2
|
||||
call print::print_hex_word
|
||||
pop zero
|
||||
call print::print_newline
|
||||
ldw spr, rg0, 0
|
||||
stw rg0, spr, 0
|
||||
// push arg 0
|
||||
push rg0
|
||||
call alloc::alloc
|
||||
pop rg1
|
||||
push rg1
|
||||
// push arg 0
|
||||
push rg1
|
||||
call print::print_hex_word
|
||||
pop zero
|
||||
lli 200, rg0
|
||||
ldw spr, rg1, 0
|
||||
stw rg0, rg1, 0
|
||||
stw rg1, spr, 0
|
||||
call print::print_newline
|
||||
ldw spr, rg0, 4
|
||||
stw rg0, spr, 4
|
||||
// push arg 0
|
||||
push rg0
|
||||
call alloc::alloc
|
||||
pop rg2
|
||||
push rg2
|
||||
// push arg 0
|
||||
push rg2
|
||||
call print::print_hex_word
|
||||
pop zero
|
||||
call print::print_newline
|
||||
ldw spr, rg0, 4
|
||||
ldw rg0, rg2, 0
|
||||
stw rg0, spr, 4
|
||||
// push arg 0
|
||||
push rg2
|
||||
call print::print_num
|
||||
pop zero
|
||||
ldw spr, rg2, 4
|
||||
stw rg2, spr, 4
|
||||
addi spr, 4, rg3
|
||||
ldw spr, rg2, 8
|
||||
stw rg2, spr, 8
|
||||
// push arg 1
|
||||
push rg3
|
||||
// push arg 0
|
||||
push rg2
|
||||
call alloc::free
|
||||
pop zero
|
||||
pop zero
|
||||
ldw spr, rg2, 8
|
||||
stw rg2, spr, 8
|
||||
// push arg 0
|
||||
push rg2
|
||||
call alloc::alloc
|
||||
pop rg3
|
||||
push rg3
|
||||
call print::print_newline
|
||||
ldw spr, rg2, 0
|
||||
stw rg2, spr, 0
|
||||
// push arg 0
|
||||
push rg2
|
||||
call print::print_hex_word
|
||||
pop zero
|
||||
call print::print_newline
|
||||
ldw spr, rg2, 8
|
||||
stw rg2, spr, 8
|
||||
// push arg 0
|
||||
push rg2
|
||||
call print::print_hex_word
|
||||
pop zero
|
||||
ldw spr, rg2, 8
|
||||
lli 0, rg4
|
||||
ieq rg2, rg4, rg5
|
||||
jez rg5, _else_2
|
||||
_then_1:
|
||||
lwi str_4, rg4
|
||||
stw rg2, spr, 8
|
||||
// push arg 0
|
||||
push rg4
|
||||
call print::print
|
||||
pop zero
|
||||
jmp _end_3
|
||||
_else_2:
|
||||
nop
|
||||
_end_3:
|
||||
lli 0, rg4
|
||||
stw rg4, bpr, 8
|
||||
jmp _ret
|
||||
@@ -0,0 +1,102 @@
|
||||
use std::fmt;
|
||||
use std::fmt::Formatter;
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
pub enum Statement {
|
||||
Const(VirtualReg, Literal),
|
||||
BinaryOp { left: VirtualReg, op: PrimitiveOperation,
|
||||
right: VirtualReg, result: VirtualReg },
|
||||
AddressOf {
|
||||
value: VirtualReg, result: VirtualReg },
|
||||
Dereference {
|
||||
address: VirtualReg, result: VirtualReg },
|
||||
GetStructField {
|
||||
struct_addr: VirtualReg, result: VirtualReg, offset: usize },
|
||||
SetStructField {
|
||||
struct_addr: VirtualReg, value: VirtualReg, offset: usize },
|
||||
Call {
|
||||
return_value: VirtualReg, func: FunctionName, args: Vec<VirtualReg> },
|
||||
}
|
||||
|
||||
pub struct IRProgram {
|
||||
pub functions: Vec<Function>,
|
||||
}
|
||||
|
||||
pub struct Function {
|
||||
pub name: FunctionName,
|
||||
pub params: Vec<VirtualReg>,
|
||||
pub blocks: Vec<BasicBlock>,
|
||||
}
|
||||
|
||||
pub struct BasicBlock {
|
||||
pub label: Label,
|
||||
pub statements: Vec<Statement>,
|
||||
pub terminator: Terminator,
|
||||
}
|
||||
|
||||
pub enum Terminator {
|
||||
Jump(Label),
|
||||
ConditionalJump { if_flag: VirtualReg, then_goto: Label, else_goto: Label },
|
||||
Return(VirtualReg),
|
||||
ReturnVoid,
|
||||
}
|
||||
|
||||
pub type FunctionName = String;
|
||||
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Label {
|
||||
id: usize,
|
||||
name: Rc<String>
|
||||
}
|
||||
|
||||
impl Label {
|
||||
pub fn new_with_id(name: impl Into<String>, id: usize) -> Self {
|
||||
Self { id, name: Rc::new(name.into()) }
|
||||
}
|
||||
|
||||
pub fn new(name: impl Into<String>) -> Self {
|
||||
Self { id: Self::get_new_id(), name: Rc::new(name.into()) }
|
||||
}
|
||||
|
||||
pub fn get_new_id() -> usize {
|
||||
LABEL_ID.fetch_add(1, Ordering::Relaxed)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Label {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "__{}_{}", self.name, self.id)
|
||||
}
|
||||
}
|
||||
|
||||
static LABEL_ID: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct VirtualReg(pub isize);
|
||||
|
||||
impl VirtualReg {
|
||||
pub fn void() -> Self {
|
||||
Self(-1)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub enum Literal {
|
||||
UInt(usize),
|
||||
Int(isize),
|
||||
Char(char),
|
||||
}
|
||||
|
||||
pub enum PrimitiveOperation {
|
||||
Add,
|
||||
Sub,
|
||||
And,
|
||||
Or,
|
||||
Xor,
|
||||
Nor,
|
||||
Nand
|
||||
}
|
||||
|
||||
@@ -0,0 +1,445 @@
|
||||
use std::collections::HashMap;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use clap::builder::Resettable::Value;
|
||||
use dsa_tasks::ProcessAgent;
|
||||
use crate::backend::scope::Allocator;
|
||||
use crate::model::{Block, Call, CompilerError, Declaration, Expression, Program, Statement, TypeId, Variable};
|
||||
|
||||
use crate::backend::intermediate_rep as IR;
|
||||
use crate::backend::intermediate_rep::{BasicBlock, Label, Terminator, VirtualReg};
|
||||
|
||||
pub struct IrLoweringCodeGenerator {
|
||||
ast: Program,
|
||||
|
||||
functions: Vec<IR::Function>
|
||||
}
|
||||
|
||||
impl IrLoweringCodeGenerator {
|
||||
pub fn new(ast: Program) -> Self {
|
||||
Self { ast, functions: vec![] }
|
||||
}
|
||||
|
||||
pub fn run(&mut self, ctx: &ProcessAgent) -> Result<IR::IRProgram, CompilerError> {
|
||||
for block in self.ast.clone().declarations {
|
||||
match block {
|
||||
Declaration::Variable { ..} => todo!(),
|
||||
Declaration::Dependency(_) => todo!(),
|
||||
Declaration::Function { name, ..} => todo!(),
|
||||
Declaration::Struct { .. } => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
for block in self.ast.clone().declarations {
|
||||
self.generate_block(&block)?;
|
||||
}
|
||||
|
||||
self.output(ctx)
|
||||
}
|
||||
|
||||
pub fn output(&mut self, ctx: &ProcessAgent) -> Result<IR::IRProgram, CompilerError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn generate_block(&mut self, decl: &Declaration) -> Result<(), CompilerError> {
|
||||
match decl {
|
||||
Declaration::Function { name, params, body, return_type } => {
|
||||
let function = self.generate_function(name, params, body, return_type)?;
|
||||
self.functions.push(function);
|
||||
},
|
||||
Declaration::Struct { .. } => todo!("no codegen required for structs yet"),
|
||||
Declaration::Dependency(_) => todo!("no codegen required for dependencies yet"),
|
||||
Declaration::Variable { .. } => todo!("codegen for statics and constants"),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn generate_function(
|
||||
&mut self, function_name: &str,
|
||||
params: &[Variable],
|
||||
body: &Block,
|
||||
return_type: &TypeId
|
||||
) -> Result<IR::Function, CompilerError> {
|
||||
let mut context = FunctionContext::new(Label::new(function_name.to_string()));
|
||||
|
||||
let params = (0..params.len())
|
||||
.map(|_| context.new_vreg()).collect();
|
||||
|
||||
self.collapse_block(body, &mut context)?;
|
||||
|
||||
let blocks = context.terminate(IR::Terminator::ReturnVoid);
|
||||
|
||||
Ok(IR::Function {
|
||||
name: function_name.to_string(), params, blocks,
|
||||
})
|
||||
}
|
||||
|
||||
fn collapse_block(
|
||||
&self,
|
||||
block: &Block,
|
||||
ctx: &mut FunctionContext,
|
||||
) -> Result<(), CompilerError> {
|
||||
for statement in block {
|
||||
self.collapse_statement(statement, ctx)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn collapse_statement(
|
||||
&self,
|
||||
stmt: &Statement,
|
||||
ctx: &mut FunctionContext,
|
||||
) -> Result<(), CompilerError> {
|
||||
match stmt {
|
||||
Statement::Block(block) => {
|
||||
self.collapse_block(block, ctx)
|
||||
}
|
||||
|
||||
Statement::If { condition, then_stmt, else_stmt } => {
|
||||
/* if statement expansion. produces the following layout
|
||||
reg_cond = ... expansion of {condition} ...
|
||||
if reg_cond goto then_{id} else goto else_{id}
|
||||
then_{id}:
|
||||
... expansion of {then_stmt} ...
|
||||
goto end_{id}
|
||||
else_{id}:
|
||||
... expansion of {else_stmt} ...
|
||||
goto end_{id}
|
||||
end_{id}:
|
||||
... not our responsibility ...
|
||||
*/
|
||||
let id = Label::get_new_id();
|
||||
let then_label = Label::new_with_id("then", id);
|
||||
let else_label = Label::new_with_id("else", id);
|
||||
let end_label = Label::new_with_id("endif", id);
|
||||
|
||||
let eval_result = self.collapse_expression(condition, ctx)?;
|
||||
|
||||
ctx.next_block(
|
||||
Terminator::ConditionalJump {
|
||||
if_flag: eval_result,
|
||||
then_goto: then_label.clone(),
|
||||
else_goto: else_label.clone(),
|
||||
},
|
||||
then_label.clone()
|
||||
);
|
||||
|
||||
self.collapse_block(then_stmt, ctx)?;
|
||||
|
||||
ctx.next_block(
|
||||
Terminator::Jump(end_label.clone()),
|
||||
else_label.clone()
|
||||
);
|
||||
|
||||
self.collapse_block(else_stmt, ctx)?;
|
||||
|
||||
ctx.next_block(
|
||||
Terminator::Jump(end_label.clone()),
|
||||
end_label.clone()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Statement::Defer(call) => {
|
||||
// defers stmt. all currently in scope defers are called in FILO order on return
|
||||
ctx.defer(call.clone());
|
||||
Ok(())
|
||||
},
|
||||
|
||||
Statement::Return(expr) => {
|
||||
// return stmt. calls relevant defer methods and expands the body of the return statement.
|
||||
|
||||
let result = if let Some(value) = expr {
|
||||
self.collapse_expression(value, ctx)?
|
||||
} else { VirtualReg::void() };
|
||||
|
||||
// run cleanup logic (defers)
|
||||
for call in ctx.get_deferred_calls() {
|
||||
self.collapse_call(&call, ctx)?;
|
||||
}
|
||||
|
||||
ctx.next_block(Terminator::Return(result), Label::new("unreachable"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Statement::Expression { expr } => {
|
||||
// generate the code for the expression but discard the result
|
||||
// let the optimiser remove it if it provably has no side effects.
|
||||
|
||||
let _ = self.collapse_expression(expr, ctx)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Statement::Continue => {
|
||||
// continue stmt. goes to the 'start' block of the loop
|
||||
|
||||
let Some(scope) = ctx.current_scope() else {
|
||||
return Err(CompilerError::generic("Invalid 'Continue' statement (not inside any loop)"));
|
||||
};
|
||||
|
||||
ctx.next_block(Terminator::Jump(scope.start), Label::new("unreachable"));
|
||||
Ok(())
|
||||
},
|
||||
|
||||
Statement::Break => {
|
||||
// break stmt. jumps to the 'end' block of the loop
|
||||
|
||||
let Some(scope) = ctx.current_scope() else {
|
||||
return Err(CompilerError::generic("Invalid 'Continue' statement (not inside any loop)"));
|
||||
};
|
||||
|
||||
ctx.next_block(Terminator::Jump(scope.end), Label::new("unreachable"));
|
||||
Ok(())
|
||||
},
|
||||
|
||||
Statement::While { condition, body } => {
|
||||
/* while loop. produces the following layout
|
||||
goto while_check_{id}
|
||||
while_check_{id}:
|
||||
if {condition} goto while_body_{id} else goto while_end_{id}
|
||||
while_body_{id}:
|
||||
... expansion of {body} ...
|
||||
goto while_check_{id}
|
||||
while_end_{id}:
|
||||
... not our responsibility ...
|
||||
*/
|
||||
|
||||
let id = Label::get_new_id();
|
||||
let check_label = Label::new_with_id("while_check", id);
|
||||
let body_label = Label::new_with_id("while_body", id);
|
||||
let end_label = Label::new_with_id("while_end", id);
|
||||
|
||||
ctx.next_block(Terminator::Jump(check_label.clone()), check_label.clone());
|
||||
|
||||
let result = self.collapse_expression(condition, ctx)?;
|
||||
|
||||
ctx.next_block(
|
||||
Terminator::ConditionalJump {
|
||||
if_flag: result,
|
||||
then_goto: body_label.clone(),
|
||||
else_goto: end_label.clone()
|
||||
},
|
||||
body_label.clone(),
|
||||
);
|
||||
|
||||
self.collapse_block(body, ctx)?;
|
||||
|
||||
ctx.next_block(
|
||||
Terminator::Jump(check_label.clone()),
|
||||
end_label.clone()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Statement::Loop(block) => {
|
||||
/* indefinite loop. produces the following layout
|
||||
goto start_loop_{id}
|
||||
start_loop_{id}:
|
||||
goto start_loop_{id}
|
||||
while_end_{id}:
|
||||
... not our responsibility ...
|
||||
*/
|
||||
|
||||
let id = Label::get_new_id();
|
||||
let start_label = Label::new_with_id("start_loop", id);
|
||||
let end_label = Label::new_with_id("end_loop", id);
|
||||
|
||||
ctx.next_block(Terminator::Jump(start_label.clone()), start_label.clone());
|
||||
|
||||
ctx.enter_scope(Scope::new(start_label.clone(), end_label.clone()));
|
||||
|
||||
self.collapse_block(block, ctx)?;
|
||||
|
||||
ctx.exit_scope();
|
||||
|
||||
ctx.next_block(Terminator::Jump(start_label.clone()), end_label);
|
||||
|
||||
Ok(())
|
||||
},
|
||||
_ => todo!()
|
||||
}
|
||||
}
|
||||
|
||||
fn collapse_call(
|
||||
&self,
|
||||
call: &Call,
|
||||
ctx: &mut FunctionContext
|
||||
) -> Result<VirtualReg, CompilerError> {
|
||||
let reg = ctx.new_vreg();
|
||||
|
||||
let args = call.args.iter()
|
||||
.map(|arg| self.collapse_expression(&arg, ctx))
|
||||
.collect::<Result<Vec<VirtualReg>, _>>()?;
|
||||
|
||||
ctx.push_stmt(IR::Statement::Call {
|
||||
return_value: reg,
|
||||
func: call.name.to_string(),
|
||||
args
|
||||
});
|
||||
|
||||
Ok(reg)
|
||||
}
|
||||
|
||||
fn collapse_expression(
|
||||
&self,
|
||||
expr: &Expression,
|
||||
ctx: &mut FunctionContext,
|
||||
) -> Result<VirtualReg, CompilerError> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
struct BasicBlockBuilder {
|
||||
label: Label,
|
||||
statements: Vec<IR::Statement>
|
||||
}
|
||||
|
||||
impl BasicBlockBuilder {
|
||||
fn new(label: Label) -> Self {
|
||||
Self {
|
||||
label,
|
||||
statements: vec![]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push(&mut self, statement: IR::Statement) {
|
||||
self.statements.push(statement);
|
||||
}
|
||||
|
||||
pub fn next_block(&mut self, terminator: Terminator, label: Label) -> BasicBlock {
|
||||
BasicBlock {
|
||||
label: std::mem::replace(&mut self.label, label),
|
||||
statements: std::mem::take(&mut self.statements),
|
||||
terminator,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn terminate_block(self, terminator: Terminator) -> BasicBlock {
|
||||
BasicBlock {
|
||||
label: self.label,
|
||||
statements: self.statements,
|
||||
terminator,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Scope {
|
||||
start: Label,
|
||||
end: Label,
|
||||
|
||||
defers: Vec<Call>,
|
||||
locals: Vec<(VirtualReg, Variable)>,
|
||||
}
|
||||
|
||||
impl Scope {
|
||||
pub fn new(start: Label, end: Label) -> Self {
|
||||
Self { start, end, defers: vec![], locals: vec![] }
|
||||
}
|
||||
}
|
||||
|
||||
struct FunctionContext {
|
||||
entrypoint: Label,
|
||||
scopes: Vec<Scope>,
|
||||
blocks: Vec<BasicBlock>,
|
||||
|
||||
builder: BasicBlockBuilder,
|
||||
|
||||
v_register_idx: usize,
|
||||
}
|
||||
|
||||
impl FunctionContext {
|
||||
pub fn new(entrypoint: Label) -> Self {
|
||||
Self {
|
||||
entrypoint: entrypoint.clone(),
|
||||
blocks: Vec::new(),
|
||||
scopes: vec![Scope::new(entrypoint.clone(), Label::new("unreachable"))],
|
||||
builder: BasicBlockBuilder::new(entrypoint),
|
||||
v_register_idx: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_vreg(&mut self) -> VirtualReg {
|
||||
self.v_register_idx += 1;
|
||||
VirtualReg(self.v_register_idx as isize - 1)
|
||||
}
|
||||
|
||||
pub fn defer(&mut self, call: Call) {
|
||||
self.scopes.last_mut().unwrap().defers.push(call);
|
||||
}
|
||||
|
||||
pub fn new_local(&mut self, variable: Variable) -> VirtualReg {
|
||||
let vreg = self.new_vreg();
|
||||
self.scopes.last_mut().unwrap().locals.push((vreg, variable));
|
||||
vreg
|
||||
}
|
||||
|
||||
pub fn enter_scope(&mut self, scope: Scope) {
|
||||
self.scopes.push(scope);
|
||||
}
|
||||
|
||||
pub fn current_scope(&mut self) -> Option<Scope> {
|
||||
self.scopes.pop()
|
||||
}
|
||||
|
||||
pub fn get_deferred_calls(&self) -> Vec<Call> {
|
||||
self.scopes
|
||||
.iter()
|
||||
.rev()
|
||||
.flat_map(|s|
|
||||
s.defers
|
||||
.iter()
|
||||
.cloned()
|
||||
.rev()
|
||||
).collect()
|
||||
}
|
||||
|
||||
pub fn find_local(&self, var: &Variable) -> Option<VirtualReg> {
|
||||
for scope in self.scopes.iter().rev() {
|
||||
for local in &scope.locals {
|
||||
if local.1 == *var {
|
||||
return Some(local.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn exit_scope(&mut self) {
|
||||
self.scopes.pop().expect("(BUG) Cannot exit outermost scope");
|
||||
}
|
||||
|
||||
pub fn next_block(&mut self, terminator: Terminator, label: Label) {
|
||||
self.blocks.push(self.builder.next_block(terminator, label));
|
||||
}
|
||||
|
||||
pub fn push_stmt(&mut self, stmt: IR::Statement) {
|
||||
self.builder.push(stmt);
|
||||
}
|
||||
|
||||
pub fn terminate(mut self, terminator: Terminator) -> Vec<IR::BasicBlock> {
|
||||
self.blocks.push(self.builder.terminate_block(terminator));
|
||||
self.blocks
|
||||
}
|
||||
|
||||
pub fn take(&mut self) -> Vec<IR::BasicBlock> {
|
||||
std::mem::take(&mut self.blocks)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@ mod codegen;
|
||||
mod instruction;
|
||||
mod registers;
|
||||
mod scope;
|
||||
pub mod intermediate_rep;
|
||||
pub mod ir_lowering_codegen;
|
||||
|
||||
pub fn generate_code(ast: &Program, ctx: &ProcessAgent) -> Result<String, CompilerError> {
|
||||
let mut codegen = codegen::CodeGenerator::new(ast.clone());
|
||||
|
||||
@@ -22,6 +22,12 @@ impl fmt::Display for CompilerError {
|
||||
}
|
||||
}
|
||||
|
||||
impl CompilerError {
|
||||
pub fn generic(msg: impl Into<String>) -> Self {
|
||||
Self::Generic(msg.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub struct Name {
|
||||
pub name: String,
|
||||
|
||||
Reference in New Issue
Block a user