Some todos and splitting apart direct and indirect labels
This commit is contained in:
+70
-11
@@ -15,12 +15,14 @@ impl Display for CompileError {
|
|||||||
|
|
||||||
/// A variable, of various different types:
|
/// A variable, of various different types:
|
||||||
/// - Literals have static values and can just be pushed to the stack
|
/// - Literals have static values and can just be pushed to the stack
|
||||||
/// - Labels contain the label pointing to the value
|
/// - IndirectLabels contain the label pointing to the value (think `foo: .db 0`)
|
||||||
|
/// - Direct labels are the value themselves (think `call foo`)
|
||||||
/// - Locals contain an index into the local frame
|
/// - Locals contain an index into the local frame
|
||||||
#[derive(Clone, PartialEq, Debug)]
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
pub enum Variable {
|
pub enum Variable {
|
||||||
Literal(i32),
|
Literal(i32),
|
||||||
Label(String),
|
IndirectLabel(String),
|
||||||
|
DirectLabel(String),
|
||||||
Local(usize),
|
Local(usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,6 +43,28 @@ pub enum Variable {
|
|||||||
/// you make a call to another function, you need to increment frame by the current frame size,
|
/// you make a call to another function, you need to increment frame by the current frame size,
|
||||||
/// and then after the other function has returned, decrement it back so that frame again points
|
/// and then after the other function has returned, decrement it back so that frame again points
|
||||||
/// at your stack frame. Locals can be found by adding some offset from the frame pointer.
|
/// at your stack frame. Locals can be found by adding some offset from the frame pointer.
|
||||||
|
///
|
||||||
|
/// todo: the allocator problem has (probably) been solved! Make an alloca() that increases the
|
||||||
|
/// current frame pointer by some size. To dynamically allocate memory, just put it in the stack
|
||||||
|
/// frame of the current fn. All fns return one word and all params are one word long (structs
|
||||||
|
/// get passed around by reference)
|
||||||
|
///
|
||||||
|
/// todo: more of a global todo. Add a register that stores an offset that's added implicitly to
|
||||||
|
/// all absolute addresses. This makes it a lot simpler to make relocatable code
|
||||||
|
///
|
||||||
|
/// alternate todo: add a callr instruction. This moves the basic unit of linking up from "fn" to
|
||||||
|
/// "library". Near calls are callr, far calls are call. Globals, reserve a word in the zero page
|
||||||
|
/// for a global frame pointer, save / restore that around a far call (using the rstack).
|
||||||
|
///
|
||||||
|
/// Weird todo: add an abs (and maybe rel) instruction that converts a relative address on the
|
||||||
|
/// stack to an absolute one by adding the instruction pointer (of the abs / rel). Make room for it
|
||||||
|
/// by replacing sdp / setsdp / setint with just reg / setreg, which will push / pop register
|
||||||
|
/// values to the stack, given a register index (0 for data ptr, 1 for rstack, 2 for int enabled,
|
||||||
|
/// whatever). For a second removable opcode, how often is peekr actually used?
|
||||||
|
///
|
||||||
|
/// Another instruction todo: remove the copy instruction (currently a dumb copy-region command)
|
||||||
|
/// and replace it with the design from last year with the mode argument... but as a DMA "device"
|
||||||
|
/// with an interface in the zero page.
|
||||||
#[derive(Clone, PartialEq, Debug, Default)]
|
#[derive(Clone, PartialEq, Debug, Default)]
|
||||||
pub struct Signature {
|
pub struct Signature {
|
||||||
pub label: Label,
|
pub label: Label,
|
||||||
@@ -190,12 +214,26 @@ impl Compilable for Function {
|
|||||||
sig.add_local(&arg.name)?
|
sig.add_local(&arg.name)?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// todo we need to store arity somehow in the state, so we can check arglists even
|
||||||
|
// with recursive calls. This probably becomes splitting "signature" from "function context"
|
||||||
|
// and setting signature immutably right now, passing context down ("block?") and setting it
|
||||||
|
// at the end
|
||||||
|
|
||||||
|
// Add it to the global namespace so we can make recursive calls
|
||||||
|
state.add_global(&self.name, |_| Variable::DirectLabel(sig.label.clone()))?;
|
||||||
|
|
||||||
// Compile each statement:
|
// Compile each statement:
|
||||||
for stmt in self.body.0 {
|
for stmt in self.body.0 {
|
||||||
match stmt {
|
match stmt {
|
||||||
Statement::Return(_) => {}
|
Statement::Return(_) => {}
|
||||||
Statement::Assignment(assign) => assign.process(state, Some(&mut sig))?,
|
Statement::Assignment(assign) => assign.process(state, Some(&mut sig))?,
|
||||||
Statement::Call(_) => todo!(),
|
Statement::Call(call) => {
|
||||||
|
call.process(state, Some(&mut sig))?;
|
||||||
|
// Every call leaves a single-word return value on the stack. In an rvalue this
|
||||||
|
// is useful but in a statement it's garbage (because nothing else is about to
|
||||||
|
// pick it up) so, drop it:
|
||||||
|
sig.emit("pop")
|
||||||
|
}
|
||||||
Statement::VarDecl(vardecl) => vardecl.process(state, Some(&mut sig))?,
|
Statement::VarDecl(vardecl) => vardecl.process(state, Some(&mut sig))?,
|
||||||
Statement::Conditional(_) | Statement::WhileLoop(_) | Statement::RepeatLoop(_) => {
|
Statement::Conditional(_) | Statement::WhileLoop(_) | Statement::RepeatLoop(_) => {
|
||||||
todo!()
|
todo!()
|
||||||
@@ -203,9 +241,9 @@ impl Compilable for Function {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
state.add_global(&self.name, |_| Variable::Label(sig.label.clone()))?;
|
// This can't fail because if it were a dupe name, adding the global would have failed
|
||||||
// This can't fail because if it were a dupe name, the one before it would fail
|
|
||||||
state.functions.insert(self.name.clone(), sig);
|
state.functions.insert(self.name.clone(), sig);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -247,6 +285,16 @@ impl Compilable for Assignment {
|
|||||||
|
|
||||||
///////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
impl Compilable for Call {
|
||||||
|
fn process(self, state: &mut State, sig: Option<&mut Signature>) -> Result<(), CompileError> {
|
||||||
|
// todo arity check
|
||||||
|
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////
|
||||||
|
|
||||||
impl Compilable for VarDecl {
|
impl Compilable for VarDecl {
|
||||||
fn process(self, state: &mut State, sig: Option<&mut Signature>) -> Result<(), CompileError> {
|
fn process(self, state: &mut State, sig: Option<&mut Signature>) -> Result<(), CompileError> {
|
||||||
let mut sig = sig.expect("Var declaration outside function");
|
let mut sig = sig.expect("Var declaration outside function");
|
||||||
@@ -282,10 +330,14 @@ impl Compilable for Lvalue {
|
|||||||
Lvalue::Name(name) => {
|
Lvalue::Name(name) => {
|
||||||
if let Some(var) = lookup(&name, global_scope, &sig.local_scope) {
|
if let Some(var) = lookup(&name, global_scope, &sig.local_scope) {
|
||||||
match var {
|
match var {
|
||||||
Variable::Literal(_) => {
|
Variable::Literal(_) | Variable::DirectLabel(_) => {
|
||||||
|
// Direct labels are (probably) functions, the important part is the
|
||||||
|
// label itself, which we can't alter, so, error:
|
||||||
Err(CompileError(0, 0, format!("Invalid lvalue {}", name)))
|
Err(CompileError(0, 0, format!("Invalid lvalue {}", name)))
|
||||||
}
|
}
|
||||||
Variable::Label(label) => {
|
Variable::IndirectLabel(label) => {
|
||||||
|
// Indirect labels are variables, the label is where the data is stored,
|
||||||
|
// so we push that label so we can store stuff there
|
||||||
let label = label.clone();
|
let label = label.clone();
|
||||||
sig.emit_arg("push", label);
|
sig.emit_arg("push", label);
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -338,11 +390,18 @@ impl Compilable for Node {
|
|||||||
sig.emit_arg("push", *val);
|
sig.emit_arg("push", *val);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Some(Variable::Label(label)) => {
|
Some(Variable::IndirectLabel(label)) => {
|
||||||
// Names pointing at labels are loaded (rvalue; for lvalues they aren't)
|
// Names pointing at labels are loaded (rvalue; for lvalues they aren't)
|
||||||
|
// Indirect labels are the address of where the value is stored (a var, .db)
|
||||||
sig.emit_arg("loadw", label.clone());
|
sig.emit_arg("loadw", label.clone());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Some(Variable::DirectLabel(label)) => {
|
||||||
|
// Direct labels are like functions, the label itself is the value, so just
|
||||||
|
// push it:
|
||||||
|
sig.emit_arg("push", label.clone());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
Some(Variable::Local(offset)) => {
|
Some(Variable::Local(offset)) => {
|
||||||
// Names of locals are added from the frame pointer
|
// Names of locals are added from the frame pointer
|
||||||
let offset = *offset;
|
let offset = *offset;
|
||||||
@@ -427,7 +486,7 @@ impl Compilable for Global {
|
|||||||
if self.typename.is_some() || self.size.is_some() {
|
if self.typename.is_some() || self.size.is_some() {
|
||||||
todo!("Structs and arrays are not yet supported")
|
todo!("Structs and arrays are not yet supported")
|
||||||
}
|
}
|
||||||
state.add_global(&self.name, |s| Variable::Label(s.gensym()))
|
state.add_global(&self.name, |s| Variable::IndirectLabel(s.gensym()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -437,7 +496,7 @@ impl Compilable for Const {
|
|||||||
fn process(self, state: &mut State, _: Option<&mut Signature>) -> Result<(), CompileError> {
|
fn process(self, state: &mut State, _: Option<&mut Signature>) -> Result<(), CompileError> {
|
||||||
let var = if self.string.is_some() {
|
let var = if self.string.is_some() {
|
||||||
// If it's a string, add it to the string table
|
// If it's a string, add it to the string table
|
||||||
Variable::Label(state.add_string(&self.string.unwrap()))
|
Variable::DirectLabel(state.add_string(&self.string.unwrap()))
|
||||||
} else if let Some(expr) = self.value {
|
} else if let Some(expr) = self.value {
|
||||||
// Otherwise eval_const it
|
// Otherwise eval_const it
|
||||||
Variable::Literal(eval_const(expr, &state.global_scope)?)
|
Variable::Literal(eval_const(expr, &state.global_scope)?)
|
||||||
@@ -570,7 +629,7 @@ mod test {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
state.global_scope,
|
state.global_scope,
|
||||||
[("a".into(), Variable::Label("_gensym_1".into()))].into()
|
[("a".into(), Variable::IndirectLabel("_gensym_1".into()))].into()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user