build_boot for tests
This commit is contained in:
Generated
+1
@@ -1504,6 +1504,7 @@ dependencies = [
|
||||
name = "vtest"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"forge_core",
|
||||
"vasm_core",
|
||||
"vcore",
|
||||
]
|
||||
|
||||
@@ -3,10 +3,17 @@ use crate::ast::*;
|
||||
use std::collections::btree_map::Entry::Vacant;
|
||||
use std::collections::{BTreeMap};
|
||||
use std::fmt::{Display, Formatter};
|
||||
use crate::forge_parser::{ParseError, parse};
|
||||
|
||||
#[derive(Eq, Clone, PartialEq, Debug)]
|
||||
pub struct CompileError(usize, usize, String);
|
||||
|
||||
impl From<ParseError> for CompileError {
|
||||
fn from(value: ParseError) -> Self {
|
||||
Self(value.0, value.1, value.2)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for CompileError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "({}:{}) {}", self.0, self.1, self.2)
|
||||
@@ -775,6 +782,50 @@ pub fn eval_const(expr: Expr, scope: &Scope) -> Result<i32, CompileError> {
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Turn a `Program` into a list of assembly lines that can be assembled to run it.
|
||||
/// There are various ways to build a program, this one is the simplest: it builds it as a
|
||||
/// complete ROM that will place a jmp to main() at 0x400, so a Vulcan can boot from it.
|
||||
pub fn build_boot(src: &str) -> Result<Vec<String>, CompileError> {
|
||||
let mut state = State::default();
|
||||
let ast = parse(src).map_err(CompileError::from)?;
|
||||
ast.process(&mut state, None, (0, 0).into())?;
|
||||
|
||||
if let Some(Variable::DirectLabel(label)) = state.global_scope.get("main") {
|
||||
// Let's make a vec for the final listing
|
||||
let mut asm: Vec<String> = Vec::new();
|
||||
|
||||
// Now we start piling stuff into the vec, starting with an org:
|
||||
asm.push(".org 0x400".into());
|
||||
|
||||
// jmp into main:
|
||||
asm.push(format!("call {}", label));
|
||||
|
||||
// When main returns, just hlt:
|
||||
asm.push("hlt".into());
|
||||
|
||||
// Now start dumping compiled objects into there:
|
||||
for (label, val) in state.strings.iter() {
|
||||
asm.push(format!("{}: .db \"{}\\0\"", label, val))
|
||||
}
|
||||
for (_, val) in state.functions.iter_mut() {
|
||||
asm.push(format!("{}:", val.label));
|
||||
asm.append(val.body.as_mut());
|
||||
}
|
||||
|
||||
// Final thing is to place a label for the stack:
|
||||
// (this is just a cell with the address of the following word)
|
||||
asm.push("frame: .db $+1".into());
|
||||
asm.push(".db 0".into());
|
||||
Ok(asm)
|
||||
} else {
|
||||
Err(CompileError(0, 0, "Function main not defined (or not a function)".into()))
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
@@ -1226,4 +1277,34 @@ mod test {
|
||||
].join("\n")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_build_boot() {
|
||||
// Very basic test of one main()
|
||||
let asm = build_boot("fn main() { return 5; }".into()).unwrap();
|
||||
assert_eq!(asm.join("\n"), vec![
|
||||
".org 0x400",
|
||||
"call _forge_gensym_1",
|
||||
"hlt",
|
||||
"_forge_gensym_1:",
|
||||
"push 5",
|
||||
"ret",
|
||||
"frame: .db $+1",
|
||||
".db 0"
|
||||
].join("\n"));
|
||||
|
||||
// Slightly more complicated, with a global str
|
||||
let asm = build_boot("const str = \"blah\"; fn main() { return str; }".into()).unwrap();
|
||||
assert_eq!(asm.join("\n"), vec![
|
||||
".org 0x400",
|
||||
"call _forge_gensym_2",
|
||||
"hlt",
|
||||
"_forge_gensym_1: .db \"blah\\0\"",
|
||||
"_forge_gensym_2:",
|
||||
"push _forge_gensym_1",
|
||||
"ret",
|
||||
"frame: .db $+1",
|
||||
".db 0"
|
||||
].join("\n"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ impl PairsExt for Peekable<Pairs<'_>> {
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Clone, Debug)]
|
||||
pub struct ParseError(usize, usize, String);
|
||||
pub struct ParseError(pub usize, pub usize, pub String);
|
||||
|
||||
impl From<pest::error::Error<Rule>> for ParseError {
|
||||
fn from(err: Error<Rule>) -> Self {
|
||||
|
||||
@@ -3,5 +3,5 @@ extern crate pest;
|
||||
extern crate pest_derive;
|
||||
|
||||
mod ast;
|
||||
mod compiler;
|
||||
pub mod compiler;
|
||||
mod forge_parser;
|
||||
|
||||
+2
-1
@@ -7,4 +7,5 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
vcore = { path = "../vcore" }
|
||||
vasm_core = { path = "../vasm_core" }
|
||||
vasm_core = { path = "../vasm_core" }
|
||||
forge_core = { path = "../forge_core" }
|
||||
@@ -0,0 +1,45 @@
|
||||
use vasm_core::assemble_snippet;
|
||||
use vcore::memory::{Memory, PeekPokeExt};
|
||||
use vcore::word::Word;
|
||||
use vcore::CPU;
|
||||
|
||||
fn run_forge(src: &str) -> CPU {
|
||||
let asm = forge_core::compiler::build_boot(src).unwrap();
|
||||
let bin = assemble_snippet(asm).unwrap();
|
||||
let mut cpu = CPU::new(Memory::with_program(bin));
|
||||
cpu.run_to_halt();
|
||||
cpu
|
||||
}
|
||||
|
||||
fn main_return(src: &str) -> i32 {
|
||||
let mut cpu = run_forge(src);
|
||||
cpu.pop_data().into()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic_forge_test() {
|
||||
assert_eq!(
|
||||
main_return("fn main() { return 2 + 3; }"),
|
||||
5
|
||||
)
|
||||
}
|
||||
|
||||
// TODO The reason this fails is that functions don't copy their args to the frame when they enter.
|
||||
// The fix is for fns to know their arity and to do that copy before the body, probably as some
|
||||
// subroutine call itself (push frame, push arity, call blah, which is written in asm)
|
||||
// #[test]
|
||||
// fn loop_test() {
|
||||
// assert_eq!(
|
||||
// main_return(
|
||||
// "fn triangle(rows) {
|
||||
// var total = 0;
|
||||
// repeat (rows) n {
|
||||
// total = total + (n + 1);
|
||||
// }
|
||||
// return total;
|
||||
// }
|
||||
//
|
||||
// fn main() { return triangle(5); }"),
|
||||
// 15
|
||||
// )
|
||||
// }
|
||||
@@ -1,2 +1,3 @@
|
||||
#[cfg(test)]
|
||||
mod integration_tests;
|
||||
mod forge_tests;
|
||||
Reference in New Issue
Block a user