A bunch of prerequisites before we can do arrays, part 2
This commit is contained in:
@@ -68,7 +68,6 @@ mod test {
|
|||||||
"peekr", // Push the addr of x
|
"peekr", // Push the addr of x
|
||||||
"swap 12", // The asm body, which swaps 12 behind it and stores it there
|
"swap 12", // The asm body, which swaps 12 behind it and stores it there
|
||||||
"storew",
|
"storew",
|
||||||
"popr", "pop", "ret 0" // Implicit void return
|
|
||||||
]
|
]
|
||||||
.join("\n")
|
.join("\n")
|
||||||
)
|
)
|
||||||
@@ -105,10 +104,7 @@ mod test {
|
|||||||
"storew", // taking the same (now freed) frame slot that c took, because c is
|
"storew", // taking the same (now freed) frame slot that c took, because c is
|
||||||
"peekr", // now out of scope
|
"peekr", // now out of scope
|
||||||
"loadw",
|
"loadw",
|
||||||
"popr", // Blow away old frame ptr
|
"jmpr @_forge_gensym_2",
|
||||||
"pop",
|
|
||||||
"ret",
|
|
||||||
"popr", "pop", "ret 0" // Implicit void return
|
|
||||||
]
|
]
|
||||||
.join("\n")
|
.join("\n")
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -56,7 +56,6 @@ mod test {
|
|||||||
"push _forge_gensym_1", // evaluating target (this fn)
|
"push _forge_gensym_1", // evaluating target (this fn)
|
||||||
"call", // Actually make the call
|
"call", // Actually make the call
|
||||||
"pop", // expr-as-statement drops the evaluated value
|
"pop", // expr-as-statement drops the evaluated value
|
||||||
"popr", "pop", "ret 0" // Implicit void return
|
|
||||||
]
|
]
|
||||||
.join("\n")
|
.join("\n")
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ mod test {
|
|||||||
"peekr",
|
"peekr",
|
||||||
"storew", // The branch, x = 1
|
"storew", // The branch, x = 1
|
||||||
"#end",
|
"#end",
|
||||||
"popr", "pop", "ret 0" // Implicit void return
|
|
||||||
]
|
]
|
||||||
.join("\n")
|
.join("\n")
|
||||||
);
|
);
|
||||||
@@ -70,7 +69,6 @@ mod test {
|
|||||||
"peekr",
|
"peekr",
|
||||||
"storew", // x = 7
|
"storew", // x = 7
|
||||||
"#end",
|
"#end",
|
||||||
"popr", "pop", "ret 0" // Implicit void return
|
|
||||||
]
|
]
|
||||||
.join("\n")
|
.join("\n")
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ impl Compilable for Expr {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Expr::Call(call) => call.process(state, Some(sig), loc),
|
Expr::Call(call) => call.process(state, Some(sig), loc),
|
||||||
Expr::New(size) => todo!("new not yet supported"),
|
Expr::New(_size) => todo!("new not yet supported"),
|
||||||
Expr::Subscript(_, _) => todo!("Structs and arrays are not yet supported"),
|
Expr::Subscript(_, _) => todo!("Structs and arrays are not yet supported"),
|
||||||
Expr::Infix(lhs, op, rhs) => {
|
Expr::Infix(lhs, op, rhs) => {
|
||||||
// Recurse on expressions, handling operators
|
// Recurse on expressions, handling operators
|
||||||
@@ -152,21 +152,20 @@ mod test {
|
|||||||
state.strings,
|
state.strings,
|
||||||
vec![
|
vec![
|
||||||
("_forge_gensym_1".into(), "foo".into()),
|
("_forge_gensym_1".into(), "foo".into()),
|
||||||
("_forge_gensym_3".into(), "bar".into()), // gensym 2 is the entrypoint of blah()
|
("_forge_gensym_4".into(), "bar".into()), // gensym 2 and 3 are the entry and outro of blah()
|
||||||
("_forge_gensym_4".into(), "norp".into()),
|
("_forge_gensym_5".into(), "norp".into()),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
test_body(state),
|
test_body(state),
|
||||||
vec![
|
vec![
|
||||||
"push _forge_gensym_3",
|
"push _forge_gensym_4",
|
||||||
"peekr",
|
"peekr",
|
||||||
"storew", // the assignment for x
|
"storew", // the assignment for x
|
||||||
"push _forge_gensym_4",
|
"push _forge_gensym_5",
|
||||||
"peekr",
|
"peekr",
|
||||||
"add 3", // the address of y (frame + 3) and put gensym_4 in it
|
"add 3", // the address of y (frame + 3) and put gensym_4 in it
|
||||||
"storew",
|
"storew",
|
||||||
"popr", "pop", "ret 0" // Implicit void return
|
|
||||||
]
|
]
|
||||||
.join("\n")
|
.join("\n")
|
||||||
)
|
)
|
||||||
@@ -184,7 +183,6 @@ mod test {
|
|||||||
"peekr",
|
"peekr",
|
||||||
"add 3", // the address of y (frame + 3) and put the addr of x (frame) in it
|
"add 3", // the address of y (frame + 3) and put the addr of x (frame) in it
|
||||||
"storew",
|
"storew",
|
||||||
"popr", "pop", "ret 0" // Implicit void return
|
|
||||||
]
|
]
|
||||||
.join("\n")
|
.join("\n")
|
||||||
)
|
)
|
||||||
@@ -203,7 +201,6 @@ mod test {
|
|||||||
"loadw", // Then load the value at 3
|
"loadw", // Then load the value at 3
|
||||||
"push 1000", // Push the addr 1000, for the lvalue
|
"push 1000", // Push the addr 1000, for the lvalue
|
||||||
"storew", // Store whatever's at 3 to 1000
|
"storew", // Store whatever's at 3 to 1000
|
||||||
"popr", "pop", "ret 0" // Implicit void return
|
|
||||||
]
|
]
|
||||||
.join("\n")
|
.join("\n")
|
||||||
)
|
)
|
||||||
@@ -214,12 +211,11 @@ mod test {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
test_body(state_for("const foo = \"foo\"; fn test() { var x = \"bar\" + 3; }")),
|
test_body(state_for("const foo = \"foo\"; fn test() { var x = \"bar\" + 3; }")),
|
||||||
vec![
|
vec![
|
||||||
"push _forge_gensym_3", // 1 is the label in the string table for "foo", 2 for "blah,"
|
"push _forge_gensym_4", // 1 is the label in the string table for "foo", 2 for "blah," 3 for blah's outro
|
||||||
"push 3", // so 3 is the string "bar"
|
"push 3", // so 3 is the string "bar"
|
||||||
"add", // Add 3 to that address
|
"add", // Add 3 to that address
|
||||||
"peekr", // Store it in the first var
|
"peekr", // Store it in the first var
|
||||||
"storew",
|
"storew",
|
||||||
"popr", "pop", "ret 0" // Implicit void return
|
|
||||||
]
|
]
|
||||||
.join("\n")
|
.join("\n")
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::ast::{Function, Location, Return};
|
use crate::ast::{Function, Location};
|
||||||
use crate::compiler::compilable::Compilable;
|
use crate::compiler::compilable::Compilable;
|
||||||
use crate::compiler::compiled_fn::CompiledFn;
|
use crate::compiler::compiled_fn::CompiledFn;
|
||||||
use crate::compiler::CompileError;
|
use crate::compiler::CompileError;
|
||||||
@@ -7,10 +7,12 @@ use crate::compiler::state::State;
|
|||||||
impl Compilable for Function {
|
impl Compilable for Function {
|
||||||
fn process(self, state: &mut State, _: Option<&mut CompiledFn>, loc: Location) -> Result<(), CompileError> {
|
fn process(self, state: &mut State, _: Option<&mut CompiledFn>, loc: Location) -> Result<(), CompileError> {
|
||||||
let label = state.find_or_declare_function(self.name.as_str(), loc)?;
|
let label = state.find_or_declare_function(self.name.as_str(), loc)?;
|
||||||
|
let end_label = state.gensym(); // The label of the outro of the fn
|
||||||
|
|
||||||
// The CompiledFn for this function, which will eventually get stuff populated into it:
|
// The CompiledFn for this function, which will eventually get stuff populated into it:
|
||||||
let mut sig = CompiledFn {
|
let mut sig = CompiledFn {
|
||||||
label,
|
label,
|
||||||
|
end_label,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -32,11 +34,6 @@ impl Compilable for Function {
|
|||||||
// created here will be emitted before (preamble) and after (outro) the body
|
// created here will be emitted before (preamble) and after (outro) the body
|
||||||
sig.generate_preamble_outro(&self.args)?;
|
sig.generate_preamble_outro(&self.args)?;
|
||||||
|
|
||||||
// This fn probably has a return statement... but it's not required. As a final catch just
|
|
||||||
// in case we fall through to this point, we'll generate a void return and compile it:
|
|
||||||
// TODO this can die soon because the outro is implicitly a return so we'd just fall into that
|
|
||||||
Return(None).process(state, Some(&mut sig), loc)?;
|
|
||||||
|
|
||||||
// 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, adding the global would have failed
|
||||||
state.functions.insert(self.name.clone(), sig);
|
state.functions.insert(self.name.clone(), sig);
|
||||||
|
|
||||||
@@ -63,7 +60,6 @@ mod test {
|
|||||||
"peekr", // Calculate the lvalue
|
"peekr", // Calculate the lvalue
|
||||||
"add 3", // "b" arg is frame + 3
|
"add 3", // "b" arg is frame + 3
|
||||||
"storew", // Finally store
|
"storew", // Finally store
|
||||||
"popr", "pop", "ret 0" // Implicit void return
|
|
||||||
]
|
]
|
||||||
.join("\n")
|
.join("\n")
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -126,10 +126,7 @@ mod test {
|
|||||||
"peekr", // Load x so we can return it
|
"peekr", // Load x so we can return it
|
||||||
"add 3",
|
"add 3",
|
||||||
"loadw",
|
"loadw",
|
||||||
"popr", // toss old frame ptr
|
"jmpr @_forge_gensym_2",
|
||||||
"pop",
|
|
||||||
"ret",
|
|
||||||
"popr", "pop", "ret 0" // Implicit void return
|
|
||||||
]
|
]
|
||||||
.join("\n")
|
.join("\n")
|
||||||
);
|
);
|
||||||
@@ -165,10 +162,7 @@ mod test {
|
|||||||
"peekr", // Load x so we can return it
|
"peekr", // Load x so we can return it
|
||||||
"add 3",
|
"add 3",
|
||||||
"loadw",
|
"loadw",
|
||||||
"popr", // Toss old frame ptr
|
"jmpr @_forge_gensym_2",
|
||||||
"pop",
|
|
||||||
"ret",
|
|
||||||
"popr", "pop", "ret 0" // Implicit void return
|
|
||||||
].join("\n")
|
].join("\n")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,24 +11,17 @@ impl Compilable for Return {
|
|||||||
match self {
|
match self {
|
||||||
Return(None) => {
|
Return(None) => {
|
||||||
// Returning nothing, so just default to returning a 0:
|
// Returning nothing, so just default to returning a 0:
|
||||||
// (but pop our frame pointer first)
|
sig.emit_arg("push", 0);
|
||||||
sig.emit("popr");
|
|
||||||
sig.emit("pop");
|
|
||||||
sig.emit_arg("ret", 0)
|
|
||||||
}
|
}
|
||||||
Return(Some(expr)) => {
|
Return(Some(expr)) => {
|
||||||
// Eval the expr and emit a ret for it
|
// Eval the expr and emit a ret for it
|
||||||
expr.process(state, Some(sig), loc)?;
|
expr.process(state, Some(sig), loc)?;
|
||||||
|
|
||||||
// Now that we're done with it, blow away the frame ptr:
|
|
||||||
sig.emit("popr");
|
|
||||||
sig.emit("pop");
|
|
||||||
|
|
||||||
// Finally ret
|
|
||||||
sig.emit("ret")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The return value is on the stack so jmpr to the outro to do the actual return
|
||||||
|
sig.emit_arg("jmpr", format!("@{}",sig.end_label));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -46,10 +39,7 @@ mod test {
|
|||||||
"loadw",
|
"loadw",
|
||||||
"push 3", // Add 3
|
"push 3", // Add 3
|
||||||
"add",
|
"add",
|
||||||
"popr", // Toss frame ptr
|
"jmpr @_forge_gensym_2", // Return that
|
||||||
"pop",
|
|
||||||
"ret", // Return that
|
|
||||||
"popr", "pop", "ret 0" // Implicit void return
|
|
||||||
]
|
]
|
||||||
.join("\n")
|
.join("\n")
|
||||||
);
|
);
|
||||||
@@ -65,11 +55,9 @@ mod test {
|
|||||||
"push 0", // Compare to 0
|
"push 0", // Compare to 0
|
||||||
"agt",
|
"agt",
|
||||||
"#if", // If statement
|
"#if", // If statement
|
||||||
"popr", // Toss frame ptr
|
"push 0", // Default return value, for an expr-less return
|
||||||
"pop",
|
"jmpr @_forge_gensym_2",
|
||||||
"ret 0", // Default return value, for an expr-less return
|
|
||||||
"#end",
|
"#end",
|
||||||
"popr", "pop", "ret 0" // Implicit void return
|
|
||||||
]
|
]
|
||||||
.join("\n")
|
.join("\n")
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -47,7 +47,6 @@ mod test {
|
|||||||
"mul", // b * 2 evaluated
|
"mul", // b * 2 evaluated
|
||||||
"peekr", // Loading "a" as an lvalue
|
"peekr", // Loading "a" as an lvalue
|
||||||
"storew", // doing the assignment
|
"storew", // doing the assignment
|
||||||
"popr", "pop", "ret 0" // Implicit void return
|
|
||||||
]
|
]
|
||||||
.join("\n")
|
.join("\n")
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -58,7 +58,6 @@ mod test {
|
|||||||
"add 3",
|
"add 3",
|
||||||
"storew", // c = c + 1
|
"storew", // c = c + 1
|
||||||
"#end", // End the loop body
|
"#end", // End the loop body
|
||||||
"popr", "pop", "ret 0" // Implicit void return
|
|
||||||
]
|
]
|
||||||
.join("\n")
|
.join("\n")
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use std::collections::btree_map::Entry::Vacant;
|
use std::collections::btree_map::Entry::Vacant;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use crate::ast::Function;
|
|
||||||
use crate::compiler::compile_error::CompileError;
|
use crate::compiler::compile_error::CompileError;
|
||||||
use crate::compiler::utils::{Label, Scope, Variable};
|
use crate::compiler::utils::{Label, Scope, Variable};
|
||||||
|
|
||||||
@@ -12,14 +11,17 @@ use crate::compiler::utils::{Label, Scope, Variable};
|
|||||||
///
|
///
|
||||||
/// A complete function implementation consists of:
|
/// A complete function implementation consists of:
|
||||||
/// - A label for the entrypoint
|
/// - A label for the entrypoint
|
||||||
/// - The function body (including preamble code to set up the stack frame)
|
/// - A label for the outro (returns jmpr here)
|
||||||
|
/// - The function body
|
||||||
|
/// - The function preamble (code to set up the stack frame, capture args)
|
||||||
|
/// - The function outro (tear down the stack frame)
|
||||||
///
|
///
|
||||||
/// The stack frame is managed by the function through a global pointer called "frame". When
|
/// The stack frame is managed by the function through a pointer passed to it. When
|
||||||
/// the function is called, it can assume that all memory after "frame" is free for use (this
|
/// the function is called, it can assume that all memory after that pointer is free for use (this
|
||||||
/// isn't actually true because you can blow out the stack, but within reason it is). So when
|
/// isn't actually true because you can blow out the stack, but within reason it is). So when
|
||||||
/// 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 put that on the top of the data stack. Functions store their pointer in the top of the
|
||||||
/// at your stack frame. Locals can be found by adding some offset from the frame pointer.
|
/// rstack, and 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
|
/// 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
|
/// current frame pointer by some size. To dynamically allocate memory, just put it in the stack
|
||||||
@@ -45,6 +47,7 @@ use crate::compiler::utils::{Label, Scope, Variable};
|
|||||||
#[derive(Clone, PartialEq, Debug, Default)]
|
#[derive(Clone, PartialEq, Debug, Default)]
|
||||||
pub struct CompiledFn {
|
pub struct CompiledFn {
|
||||||
pub label: Label,
|
pub label: Label,
|
||||||
|
pub end_label: Label,
|
||||||
pub frame_size: usize,
|
pub frame_size: usize,
|
||||||
pub local_scope: Scope,
|
pub local_scope: Scope,
|
||||||
pub arity: usize,
|
pub arity: usize,
|
||||||
@@ -124,13 +127,11 @@ impl CompiledFn {
|
|||||||
/// depends on knowledge of the body of the fn, but what this produces will be emitted to
|
/// depends on knowledge of the body of the fn, but what this produces will be emitted to
|
||||||
/// the final listing before the fn body
|
/// the final listing before the fn body
|
||||||
pub(crate) fn generate_preamble_outro(&mut self, args: &Vec<String>) -> Result<(), CompileError> {
|
pub(crate) fn generate_preamble_outro(&mut self, args: &Vec<String>) -> Result<(), CompileError> {
|
||||||
let mut arg_names: Vec<&str> = Vec::new();
|
|
||||||
|
|
||||||
// Does our fn make any allocations? If so we need to save the old alloc pool pointer:
|
// Does our fn make any allocations? If so we need to save the old alloc pool pointer:
|
||||||
if self.alloc {
|
if self.alloc {
|
||||||
todo!("alloc pool not actually implemented");
|
todo!("alloc pool not actually implemented");
|
||||||
self.preamble_emit("loadw pool");
|
//self.preamble_emit("loadw pool");
|
||||||
self.preamble_emit("pushr");
|
//self.preamble_emit("pushr");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Top argument is the frame ptr; copy it to the rstack:
|
// Top argument is the frame ptr; copy it to the rstack:
|
||||||
@@ -153,6 +154,28 @@ impl CompiledFn {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create the outro:
|
||||||
|
// First, we might fall through to here, so, leave a push 0 on the stack. Normally we roll
|
||||||
|
// with an empty stack, or have some data and jmpr here, but if we fall through this will
|
||||||
|
// ensure that the following return returns something:
|
||||||
|
self.outro_emit("push 0");
|
||||||
|
|
||||||
|
// Now, the outro label:
|
||||||
|
self.outro_emit(format!("{}:", self.end_label).as_str());
|
||||||
|
|
||||||
|
// The top of the rstack is, of course, the frame ptr. So we need to get rid of that:
|
||||||
|
self.outro_emit("popr");
|
||||||
|
self.outro_emit("pop");
|
||||||
|
|
||||||
|
// If we alloced anything, we need to drain that pool:
|
||||||
|
if self.alloc {
|
||||||
|
todo!("alloc pool not implemented yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
// We're in the same condition we entered in except that our return value is on the stack
|
||||||
|
// (or a default 0 is) so time to actually return:
|
||||||
|
self.outro_emit("ret");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -78,10 +78,12 @@ mod test {
|
|||||||
"_forge_gensym_1:",
|
"_forge_gensym_1:",
|
||||||
"pushr",
|
"pushr",
|
||||||
"push 5",
|
"push 5",
|
||||||
|
"jmpr @_forge_gensym_2",
|
||||||
|
"push 0",
|
||||||
|
"_forge_gensym_2:",
|
||||||
"popr",
|
"popr",
|
||||||
"pop",
|
"pop",
|
||||||
"ret",
|
"ret",
|
||||||
"popr", "pop", "ret 0", // Implicit void return
|
|
||||||
"stack: .db 0",
|
"stack: .db 0",
|
||||||
].join("\n"));
|
].join("\n"));
|
||||||
|
|
||||||
@@ -95,11 +97,13 @@ mod test {
|
|||||||
"_forge_gensym_1: .db \"blah\\0\"",
|
"_forge_gensym_1: .db \"blah\\0\"",
|
||||||
"_forge_gensym_2:",
|
"_forge_gensym_2:",
|
||||||
"pushr",
|
"pushr",
|
||||||
"push _forge_gensym_1",
|
"push _forge_gensym_1", // Push the str
|
||||||
|
"jmpr @_forge_gensym_3", // Return
|
||||||
|
"push 0", // Implicit return val
|
||||||
|
"_forge_gensym_3:", // outro
|
||||||
"popr",
|
"popr",
|
||||||
"pop",
|
"pop",
|
||||||
"ret",
|
"ret",
|
||||||
"popr", "pop", "ret 0", // Implicit void return
|
|
||||||
"stack: .db 0",
|
"stack: .db 0",
|
||||||
].join("\n"))
|
].join("\n"))
|
||||||
}
|
}
|
||||||
@@ -115,22 +119,26 @@ mod test {
|
|||||||
assert_eq!(asm.join("\n"), vec![
|
assert_eq!(asm.join("\n"), vec![
|
||||||
".org 0x400",
|
".org 0x400",
|
||||||
"push stack",
|
"push stack",
|
||||||
"call _forge_gensym_2",
|
"call _forge_gensym_3",
|
||||||
"hlt",
|
"hlt",
|
||||||
"_forge_gensym_1:", // fn foo()
|
"_forge_gensym_1:", // fn foo()
|
||||||
"pushr", // capture frame ptr
|
"pushr", // capture frame ptr
|
||||||
"popr", // drop it and ret
|
"push 0", // implicit return
|
||||||
|
"_forge_gensym_2:",
|
||||||
|
"popr",
|
||||||
"pop",
|
"pop",
|
||||||
"ret 0",
|
"ret",
|
||||||
"_forge_gensym_2:", // fn main()
|
"_forge_gensym_3:", // fn main()
|
||||||
"pushr", // capture frame ptr
|
"pushr", // capture frame ptr
|
||||||
"peekr", // prep frame ptr to send to foo
|
"peekr", // prep frame ptr to send to foo
|
||||||
"push _forge_gensym_1", // load foo
|
"push _forge_gensym_1", // load foo
|
||||||
"call", // call it
|
"call", // call it
|
||||||
"pop", // Throw away its return value
|
"pop", // Throw away its return value
|
||||||
"popr", // drop frame ptr and ret
|
"push 0", // Implicit return value
|
||||||
|
"_forge_gensym_4:", // Outro start
|
||||||
|
"popr", // Restore frame ptr
|
||||||
"pop",
|
"pop",
|
||||||
"ret 0",
|
"ret",
|
||||||
"stack: .db 0",
|
"stack: .db 0",
|
||||||
].join("\n"))
|
].join("\n"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,11 +105,11 @@ fn cursed_call_test() {
|
|||||||
assert_eq!(compiler_output(src).join("\n"), vec![
|
assert_eq!(compiler_output(src).join("\n"), vec![
|
||||||
".org 0x400",
|
".org 0x400",
|
||||||
"push stack",
|
"push stack",
|
||||||
"call _forge_gensym_2",
|
"call _forge_gensym_3",
|
||||||
"hlt",
|
"hlt",
|
||||||
"_forge_gensym_1:", // foo()
|
"_forge_gensym_1:", // foo()
|
||||||
"pushr","push 2","popr","pop","ret","popr","pop","ret 0",
|
"pushr","push 2","jmpr @_forge_gensym_2","push 0","_forge_gensym_2:","popr","pop","ret",
|
||||||
"_forge_gensym_2:", // main()
|
"_forge_gensym_3:", // main()
|
||||||
"pushr", // preamble (no args)
|
"pushr", // preamble (no args)
|
||||||
"push 0", "peekr", "storew", // repeat counter var (n)
|
"push 0", "peekr", "storew", // repeat counter var (n)
|
||||||
"push 2", // repeat limit
|
"push 2", // repeat limit
|
||||||
@@ -134,7 +134,9 @@ fn cursed_call_test() {
|
|||||||
"#end",
|
"#end",
|
||||||
"peekr","dup","loadw","add 1","swap","storew","#end", // inc n, end the loop
|
"peekr","dup","loadw","add 1","swap","storew","#end", // inc n, end the loop
|
||||||
"pop", // Drop the loop limit off
|
"pop", // Drop the loop limit off
|
||||||
"popr","pop","ret 0", // Implicit return 0 to end main
|
"push 0", // Implicit return value
|
||||||
|
"_forge_gensym_4:",
|
||||||
|
"popr","pop","ret", // Standard outro
|
||||||
"stack: .db 0", // The stack
|
"stack: .db 0", // The stack
|
||||||
].join("\n"))
|
].join("\n"))
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user