diff --git a/lustc/src/compiler.rs b/lustc/src/compiler.rs index d448dfd..4f5e416 100644 --- a/lustc/src/compiler.rs +++ b/lustc/src/compiler.rs @@ -12,6 +12,7 @@ use std::collections::HashMap; use crate::conditional; +use crate::conversions::print_lustc_word; use crate::data; use crate::escape; use crate::fatal; @@ -65,7 +66,12 @@ pub(crate) struct Context<'a> { impl Default for JIT { fn default() -> Self { - let builder = JITBuilder::new(cranelift_module::default_libcall_names()); + let mut builder = JITBuilder::new(cranelift_module::default_libcall_names()); + + // Register the print function. + let print_addr = print_lustc_word as *const u8; + builder.symbol("print_lustc_word", print_addr); + let module = JITModule::new(builder); let mut jit = Self { builder_context: FunctionBuilderContext::new(), diff --git a/lustc/src/conversions.rs b/lustc/src/conversions.rs index d0f2f92..2c70966 100644 --- a/lustc/src/conversions.rs +++ b/lustc/src/conversions.rs @@ -1,3 +1,4 @@ +use std::fmt; use std::os::raw::c_char; use crate::{Expr, UWord, Word}; @@ -144,6 +145,34 @@ impl Expr { } } +pub fn print_lustc_word(word: Word) -> Word { + let expr = Expr::from_immediate(word); + println!("{}", expr); + Expr::Nil.immediate_rep() +} + +impl fmt::Display for Expr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Expr::Integer(i) => write!(f, "{}", i), + Expr::Char(c) => write!(f, "{}", c), + Expr::Bool(b) => write!(f, "{}", b), + Expr::Nil => write!(f, "nil"), + Expr::List(l) => write!(f, "({}, {})", l[0], l[1]), + // sbcl capitalizes symbols when writing them out to stdout. + Expr::Symbol(s) => write!(f, "{}", s.to_uppercase()), + Expr::String(s) => write!( + f, + "{}", + match s.to_str() { + Ok(s) => s, + Err(_) => return Err(fmt::Error), + } + ), + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/lustc/src/main.rs b/lustc/src/main.rs index f9c465c..2f756b7 100644 --- a/lustc/src/main.rs +++ b/lustc/src/main.rs @@ -30,8 +30,7 @@ fn main() { timer::init(cli_opts.is_present("timeit")); - match lustc::roundtrip_file(file) { - Ok(e) => println!("=> {:?}", e), - Err(s) => eprintln!("error: {}", s), + if let Err(s) = lustc::roundtrip_file(file) { + eprintln!("error: {}", s) } } diff --git a/lustc/src/primitives.rs b/lustc/src/primitives.rs index 086df99..d1255a1 100644 --- a/lustc/src/primitives.rs +++ b/lustc/src/primitives.rs @@ -1,4 +1,9 @@ -//! Handles primitive functions in Lust. +//! Handles primitive functions in Lust. For primitives that are only +//! used in the head position of a list we don't need to create a +//! higher order version of them and can just emit them inline. For +//! that we use the emit_primcall function. For primitives that are +//! used in a higher order context we don't have such a luxury and +//! need to actually make a function. use std::collections::HashMap; use std::collections::HashSet; @@ -176,6 +181,36 @@ pub(crate) fn emit_primitives( })?); } + if higher_order_primitives.contains("print") { + res.push(emit_primitive("print", 1, jit, |ctx| { + let block = ctx.builder.current_block().unwrap(); + let args = ctx.builder.block_params(block); + emit_check_arg_count(1, args[1], ctx, false)?; + let args = get_primitive_args(ctx, block, 1); + let accum = args[0]; + + let mut sig = ctx.module.make_signature(); + sig.params.push(AbiParam::new(ctx.word)); + sig.returns.push(AbiParam::new(ctx.word)); + + let callee = ctx + .module + .declare_function("print_lustc_word", cranelift_module::Linkage::Import, &sig) + .map_err(|e| e.to_string())?; + + let local_callee = ctx + .module + .declare_func_in_func(callee, &mut ctx.builder.func); + + let args = vec![accum]; + + let call = ctx.builder.ins().call(local_callee, &args); + let res = ctx.builder.inst_results(call)[0]; + + Ok(res) + })?); + } + if higher_order_primitives.contains("integer->char") { res.push(emit_primitive("integer->char", 1, jit, |ctx| { let block = ctx.builder.current_block().unwrap(); @@ -789,6 +824,28 @@ pub(crate) fn emit_primcall(name: &str, args: &[Expr], ctx: &mut Context) -> Res .ins() .load(ctx.word, MemFlags::new(), address, ctx.word.bytes() as i32) } + + "print" => { + check_arg_len("print", args, 1)?; + let arg = emit_expr(&args[0], ctx)?; + let args = vec![arg]; + + let mut sig = ctx.module.make_signature(); + sig.params.push(AbiParam::new(ctx.word)); + sig.returns.push(AbiParam::new(ctx.word)); + + let callee = ctx + .module + .declare_function("print_lustc_word", cranelift_module::Linkage::Import, &sig) + .map_err(|e| e.to_string())?; + + let local_callee = ctx + .module + .declare_func_in_func(callee, &mut ctx.builder.func); + + let call = ctx.builder.ins().call(local_callee, &args); + ctx.builder.inst_results(call)[0] + } _ => panic!("non primitive in emit_primcall: {}", name), }) } @@ -812,6 +869,7 @@ pub(crate) fn string_is_builtin(s: &str) -> bool { pub(crate) fn string_is_primitive(s: &str) -> bool { s == "add1" + || s == "print" || s == "integer->char" || s == "char->integer" || s == "null?"