From afcf3c65cdfc22241e902ffb4da54129350b738d Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Mon, 16 Jan 2023 20:11:06 -0800 Subject: [PATCH] A fairly major refactor / simplification. --- src/asts.rs | 2 - src/asts/hil.rs | 183 ------------------------------ src/asts/lil.rs | 149 ------------------------ src/backend.rs | 80 +++++++++++++ src/backend/into_crane.rs | 150 ++++++++++++++++++++++++ src/{ => backend}/runtime.rs | 0 src/bin.rs | 140 +++++++++-------------- src/errors.rs | 114 ------------------- src/ir.rs | 5 + src/ir/ast.rs | 172 ++++++++++++++++++++++++++++ src/ir/from_syntax.rs | 65 +++++++++++ src/ir/strings.rs | 36 ++++++ src/lib.rs | 8 +- src/pass_result.rs | 36 ++++++ src/pass_result/errors.rs | 124 ++++++++++++++++++++ src/{ => pass_result}/warnings.rs | 0 src/passes.rs | 98 ---------------- src/passes/hil_to_lil.rs | 119 ------------------- src/passes/into_crane.rs | 137 ---------------------- src/passes/syntax_to_hil.rs | 157 ------------------------- src/syntax.rs | 26 ++++- src/syntax/ast.rs | 2 +- src/syntax/parser.lalrpop | 2 +- src/syntax/simplify.rs | 49 ++++++++ src/syntax/tokens.rs | 6 +- src/variable_map.rs | 72 ------------ 26 files changed, 800 insertions(+), 1132 deletions(-) delete mode 100644 src/asts.rs delete mode 100644 src/asts/hil.rs delete mode 100644 src/asts/lil.rs create mode 100644 src/backend/into_crane.rs rename src/{ => backend}/runtime.rs (100%) delete mode 100644 src/errors.rs create mode 100644 src/ir.rs create mode 100644 src/ir/ast.rs create mode 100644 src/ir/from_syntax.rs create mode 100644 src/ir/strings.rs create mode 100644 src/pass_result.rs create mode 100644 src/pass_result/errors.rs rename src/{ => pass_result}/warnings.rs (100%) delete mode 100644 src/passes.rs delete mode 100644 src/passes/hil_to_lil.rs delete mode 100644 src/passes/into_crane.rs delete mode 100644 src/passes/syntax_to_hil.rs create mode 100644 src/syntax/simplify.rs delete mode 100644 src/variable_map.rs diff --git a/src/asts.rs b/src/asts.rs deleted file mode 100644 index 529c1cc..0000000 --- a/src/asts.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod hil; -pub mod lil; diff --git a/src/asts/hil.rs b/src/asts/hil.rs deleted file mode 100644 index 6c12c87..0000000 --- a/src/asts/hil.rs +++ /dev/null @@ -1,183 +0,0 @@ -use crate::variable_map::{Variable, VariableMap}; -use pretty::{DocAllocator, DocBuilder, Pretty}; - -#[derive(Debug, PartialEq)] -pub struct Program { - pub statements: Vec>, -} - -impl Program { - pub fn pretty<'a, D, A>( - &self, - variable_map: &VariableMap, - allocator: &'a D, - ) -> DocBuilder<'a, D, A> - where - A: 'a, - D: ?Sized + DocAllocator<'a, A>, - { - let mut result = allocator.nil(); - - for stmt in self.statements.iter() { - result = result - .append(stmt.pretty(variable_map, allocator)) - .append(allocator.text(";")) - .append(allocator.hardline()); - } - - result - } -} - -#[derive(Debug, PartialEq)] -pub enum Statement { - Binding(Annotation, Variable, Expression), - Print(Annotation, Variable), -} - -impl Statement { - pub fn pretty<'a, D, A>( - &self, - variable_map: &VariableMap, - allocator: &'a D, - ) -> DocBuilder<'a, D, A> - where - A: 'a, - D: ?Sized + DocAllocator<'a, A>, - { - match self { - Statement::Binding(_, var, expr) => { - let name = variable_map.get_name(*var).unwrap_or(""); - - allocator - .text(name.to_string()) - .append(allocator.space()) - .append(allocator.text("=")) - .append(allocator.space()) - .append(expr.pretty(variable_map, allocator)) - } - Statement::Print(_, var) => { - let name = variable_map.get_name(*var).unwrap_or(""); - - allocator - .text("print") - .append(allocator.space()) - .append(allocator.text(name.to_string())) - } - } - } -} - -#[derive(Debug, PartialEq)] -pub enum Expression { - Value(Annotation, Value), - Reference(Annotation, Variable), - Primitive(Annotation, Primitive, Vec>), -} - -impl Expression { - pub fn annotation(&self) -> Annotation { - match self { - Expression::Value(a, _) => a.clone(), - Expression::Reference(a, _) => a.clone(), - Expression::Primitive(a, _, _) => a.clone(), - } - } -} - -impl Expression { - fn pretty<'a, A, D>(&self, variable_map: &VariableMap, allocator: &'a D) -> DocBuilder<'a, D, A> - where - A: 'a, - D: ?Sized + DocAllocator<'a, A>, - { - match self { - Expression::Value(_, val) => val.pretty(allocator), - Expression::Reference(_, var) => { - let name = variable_map.get_name(*var).unwrap_or(""); - - allocator.text(name.to_string()) - } - Expression::Primitive(_, op, exprs) if exprs.len() == 1 => op - .pretty(allocator) - .append(exprs[0].pretty(variable_map, allocator)), - Expression::Primitive(_, op, exprs) if exprs.len() == 2 => { - let left = exprs[0].pretty(variable_map, allocator); - let right = exprs[1].pretty(variable_map, allocator); - - left.append(allocator.space()) - .append(op.pretty(allocator)) - .append(allocator.space()) - .append(right) - .parens() - } - Expression::Primitive(_, op, exprs) => { - allocator.text(format!("!!{:?} with {} arguments!!", op, exprs.len())) - } - } - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum Primitive { - Plus, - Minus, - Times, - Divide, -} - -impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Primitive -where - A: 'a, - D: ?Sized + DocAllocator<'a, A>, -{ - fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> { - match self { - Primitive::Plus => allocator.text("+"), - Primitive::Minus => allocator.text("-"), - Primitive::Times => allocator.text("*"), - Primitive::Divide => allocator.text("/"), - } - } -} - -#[derive(Debug, PartialEq, Eq)] -pub enum Value { - Number(Option, i128), -} - -impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Value -where - A: 'a, - D: ?Sized + DocAllocator<'a, A>, -{ - fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> { - match self { - Value::Number(opt_base, value) => { - let value_str = match opt_base { - None => format!("{}", value), - Some(2) => format!("0b{:b}", value), - Some(8) => format!("0o{:o}", value), - Some(10) => format!("0d{}", value), - Some(16) => format!("0x{:x}", value), - Some(_) => format!("!!{:x}!!", value), - }; - - allocator.text(value_str) - } - } - } -} - -#[derive(Clone, Copy)] -struct CommaSep {} - -impl<'a, D, A> Pretty<'a, D, A> for CommaSep -where - A: 'a, - D: ?Sized + DocAllocator<'a, A>, -{ - fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> { - allocator.text(",").append(allocator.space()) - } -} diff --git a/src/asts/lil.rs b/src/asts/lil.rs deleted file mode 100644 index 8aab427..0000000 --- a/src/asts/lil.rs +++ /dev/null @@ -1,149 +0,0 @@ -pub use crate::asts::hil::Value; -use crate::variable_map::{Variable, VariableMap}; -use internment::ArcIntern; -use pretty::{DocAllocator, DocBuilder, Pretty}; -use std::collections::HashSet; - -pub struct Program { - pub statements: Vec>, - pub strings: HashSet>, - pub variable_info: VariableMap, -} - -impl Program { - pub fn pretty<'a, A, D>(&self, allocator: &'a D) -> DocBuilder<'a, D, A> - where - A: 'a, - D: ?Sized + DocAllocator<'a, A>, - { - let mut result = allocator.nil(); - - for stmt in self.statements.iter() { - result = result - .append(stmt.pretty(&self.variable_info, allocator)) - .append(allocator.text(";")) - .append(allocator.hardline()); - } - - result - } -} - -pub enum Statement { - VariableBinding(Annotation, Variable, SimpleExpression), - ResultBinding(Annotation, Variable, Primitive), - Print(Annotation, ArcIntern, Variable), -} - -impl Statement { - pub fn pretty<'a, A, D>( - &self, - variable_map: &VariableMap, - allocator: &'a D, - ) -> DocBuilder<'a, D, A> - where - A: 'a, - D: ?Sized + DocAllocator<'a, A>, - { - match self { - Statement::VariableBinding(_, var, expr) => { - let name = variable_map.get_name(*var).unwrap_or(""); - - allocator - .text(name.to_string()) - .append(allocator.space()) - .append(allocator.text("=")) - .append(allocator.space()) - .append(expr.pretty(variable_map, allocator)) - } - - Statement::ResultBinding(_, var, prim) => { - let name = variable_map.get_name(*var).unwrap_or(""); - - allocator - .text(name.to_string()) - .append(allocator.space()) - .append(allocator.text("=")) - .append(allocator.space()) - .append(prim.pretty(variable_map, allocator)) - } - - Statement::Print(_, var, _val) => allocator - .text("print") - .append(allocator.space()) - .append(allocator.text(var.to_string())), - } - } -} - -pub enum Primitive { - Plus(SimpleExpression, SimpleExpression), - Minus(SimpleExpression, SimpleExpression), - Times(SimpleExpression, SimpleExpression), - Divide(SimpleExpression, SimpleExpression), -} - -impl Primitive { - pub fn pretty<'a, A, D>( - &self, - variable_map: &VariableMap, - allocator: &'a D, - ) -> DocBuilder<'a, D, A> - where - A: 'a, - D: ?Sized + DocAllocator<'a, A>, - { - match self { - Primitive::Plus(a, b) => a - .pretty(variable_map, allocator) - .append(allocator.space()) - .append(allocator.text("+")) - .append(allocator.space()) - .append(b.pretty(variable_map, allocator)), - Primitive::Minus(a, b) => a - .pretty(variable_map, allocator) - .append(allocator.space()) - .append(allocator.text("-")) - .append(allocator.space()) - .append(b.pretty(variable_map, allocator)), - Primitive::Times(a, b) => a - .pretty(variable_map, allocator) - .append(allocator.space()) - .append(allocator.text("*")) - .append(allocator.space()) - .append(b.pretty(variable_map, allocator)), - Primitive::Divide(a, b) => a - .pretty(variable_map, allocator) - .append(allocator.space()) - .append(allocator.text("/")) - .append(allocator.space()) - .append(b.pretty(variable_map, allocator)), - } - } -} - -pub enum SimpleExpression { - Reference(Annotation, Variable), - Constant(Annotation, Value), -} - -impl SimpleExpression { - pub fn pretty<'a, A, D>( - &self, - variable_map: &VariableMap, - allocator: &'a D, - ) -> DocBuilder<'a, D, A> - where - A: 'a, - D: ?Sized + DocAllocator<'a, A>, - { - match self { - SimpleExpression::Reference(_, var) => { - let name = variable_map.get_name(*var).unwrap_or(""); - - allocator.text(name.to_string()) - } - SimpleExpression::Constant(_, x) => x.pretty(allocator), - } - } -} diff --git a/src/backend.rs b/src/backend.rs index 8b13789..4127a74 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -1 +1,81 @@ +mod into_crane; +mod runtime; +use self::runtime::{RuntimeFunctionError, RuntimeFunctions}; +use crate::ir; +use codespan_reporting::diagnostic::Diagnostic; +use cranelift_codegen::isa::LookupError; +use cranelift_codegen::settings::{Configurable, SetError}; +use cranelift_codegen::{isa, settings, CodegenError}; +use cranelift_module::{default_libcall_names, ModuleCompiledFunction, ModuleError}; +use cranelift_object::{ObjectBuilder, ObjectModule, object}; +use target_lexicon::Triple; +use thiserror::Error; + +pub struct Program { + _compiled: ModuleCompiledFunction, + module: ObjectModule, +} + +#[derive(Debug, Error)] +pub enum BackendError { + #[error("Cranelift module error: {0}")] + Cranelift(#[from] ModuleError), + #[error("Builtin function error: {0}")] + BuiltinError(#[from] RuntimeFunctionError), + #[error("Internal variable lookup error")] + VariableLookupFailure, + #[error(transparent)] + CodegenError(#[from] CodegenError), + #[error(transparent)] + SetError(#[from] SetError), + #[error(transparent)] + LookupError(#[from] LookupError), +} + +impl From for Diagnostic { + fn from(value: BackendError) -> Self { + match value { + BackendError::Cranelift(me) => + Diagnostic::error() + .with_message(format!("Internal cranelift error: {}", me)), + BackendError::BuiltinError(me) => + Diagnostic::error() + .with_message(format!("Internal runtime function error: {}", me)), + BackendError::VariableLookupFailure => + Diagnostic::error() + .with_message("Internal variable lookup error!"), + BackendError::CodegenError(me) => + Diagnostic::error() + .with_message(format!("Internal codegen error: {}", me)), + BackendError::SetError(me) => + Diagnostic::error() + .with_message(format!("Internal backend setup error: {}", me)), + BackendError::LookupError(me) => + Diagnostic::error() + .with_message(format!("Internal error: {}", me)), + } + } +} + +impl Program { + pub fn new(platform: Triple, ir: ir::Program) -> Result { + let isa_builder = isa::lookup(platform.clone())?; + let mut settings_builder = settings::builder(); + settings_builder.set("is_pic", "true")?; + let isa = isa_builder.finish(settings::Flags::new(settings_builder))?; + + let object_builder = ObjectBuilder::new(isa, "example", default_libcall_names())?; + let mut object_module = ObjectModule::new(object_builder); + let rtfuns = RuntimeFunctions::new(&platform, &mut object_module)?; + + Ok(Program { + _compiled: ir.into_cranelift(&mut object_module, &rtfuns)?, + module: object_module, + }) + } + + pub fn bytes(self) -> Result, object::write::Error> { + self.module.finish().emit() + } +} diff --git a/src/backend/into_crane.rs b/src/backend/into_crane.rs new file mode 100644 index 0000000..698d0ce --- /dev/null +++ b/src/backend/into_crane.rs @@ -0,0 +1,150 @@ +use std::collections::HashMap; + +use crate::backend::runtime::RuntimeFunctions; +use crate::ir::{Expression, Primitive, Program, Statement, Value, ValueOrRef}; +use cranelift_codegen::entity::EntityRef; +use cranelift_codegen::ir::{ + entities, types, Function, GlobalValue, InstBuilder, Signature, UserFuncName, +}; +use cranelift_codegen::isa::CallConv; +use cranelift_codegen::Context; +use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext, Variable}; +use cranelift_module::{DataContext, Linkage, Module, ModuleCompiledFunction, ModuleError}; +use internment::ArcIntern; + +type StringTable = HashMap, GlobalValue>; + +impl Program { + pub fn into_cranelift( + mut self, + module: &mut M, + rtfuns: &RuntimeFunctions, + ) -> Result { + let basic_signature = Signature { + params: vec![], + returns: vec![], + call_conv: CallConv::SystemV, + }; + + let func_id = module.declare_function("gogogo", Linkage::Export, &basic_signature)?; + let mut ctx = Context::new(); + ctx.func = + Function::with_name_signature(UserFuncName::user(0, func_id.as_u32()), basic_signature); + + let string_table = self.build_string_table(module, &mut ctx.func)?; + let mut variable_table = HashMap::new(); + let mut next_var_num = 1; + let print_func_ref = rtfuns.include_runtime_function("print", module, &mut ctx.func)?; + + let mut fctx = FunctionBuilderContext::new(); + let mut builder = FunctionBuilder::new(&mut ctx.func, &mut fctx); + let main_block = builder.create_block(); + builder.switch_to_block(main_block); + + for stmt in self.statements.drain(..) { + match stmt { + Statement::Print(_ann, var) => { + let local_name_ref = string_table.get(&var).unwrap(); + let name_ptr = builder.ins().symbol_value(types::I64, *local_name_ref); + let value_var_num = variable_table.get(&var).unwrap(); + let val = builder.use_var(Variable::new(*value_var_num)); + builder.ins().call(print_func_ref, &[name_ptr, val]); + } + + Statement::Binding(_, var_name, value) => { + let var = Variable::new(next_var_num); + variable_table.insert(var_name, next_var_num); + next_var_num += 1; + builder.declare_var(var, types::I64); + + let val = match value { + Expression::Value(_, Value::Number(_, v)) => { + builder.ins().iconst(types::I64, v) + } + + Expression::Reference(_, name) => { + let value_var_num = variable_table.get(&name).unwrap(); + builder.use_var(Variable::new(*value_var_num)) + } + + Expression::Primitive(_, prim, mut vals) => { + let right = vals + .pop() + .unwrap() + .into_cranelift(&mut builder, &variable_table); + let left = vals + .pop() + .unwrap() + .into_cranelift(&mut builder, &variable_table); + + match prim { + Primitive::Plus => builder.ins().iadd(left, right), + Primitive::Minus => builder.ins().isub(left, right), + Primitive::Times => builder.ins().imul(left, right), + Primitive::Divide => builder.ins().sdiv(left, right), + } + } + }; + + builder.def_var(var, val); + } + } + } + + builder.ins().return_(&[]); + builder.seal_block(main_block); + builder.finalize(); + + Ok(module.define_function(func_id, &mut ctx)?) + } + + fn build_string_table( + &self, + module: &mut M, + func: &mut Function, + ) -> Result { + let mut string_table = HashMap::new(); + + for (idx, interned_value) in self.strings().drain().enumerate() { + let global_id = module.declare_data( + &format!("local-string-{}", idx), + Linkage::Local, + false, + false, + )?; + let mut data_context = DataContext::new(); + data_context.set_align(8); + data_context.define( + interned_value + .as_str() + .to_owned() + .into_boxed_str() + .into_boxed_bytes(), + ); + module.define_data(global_id, &data_context)?; + let local_data = module.declare_data_in_func(global_id, func); + string_table.insert(interned_value, local_data); + } + + Ok(string_table) + } +} + +impl ValueOrRef { + fn into_cranelift( + self, + builder: &mut FunctionBuilder, + varmap: &HashMap, usize>, + ) -> entities::Value { + match self { + ValueOrRef::Value(_, value) => match value { + Value::Number(_base, numval) => builder.ins().iconst(types::I64, numval), + }, + + ValueOrRef::Ref(_, name) => { + let num = varmap.get(&name).unwrap(); + builder.use_var(Variable::new(*num)) + } + } + } +} diff --git a/src/runtime.rs b/src/backend/runtime.rs similarity index 100% rename from src/runtime.rs rename to src/backend/runtime.rs diff --git a/src/bin.rs b/src/bin.rs index 8cc901c..4a72a35 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -3,15 +3,12 @@ use codespan_reporting::diagnostic::Diagnostic; use codespan_reporting::files::SimpleFiles; use codespan_reporting::term; use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; -use cranelift_codegen::settings::{Configurable, SetError}; -use cranelift_codegen::{isa, settings, CodegenError}; -use cranelift_module::{default_libcall_names, ModuleError}; -use cranelift_object::{object, ObjectBuilder, ObjectModule}; -use ngr::asts::lil; -use ngr::passes::{run_front_end, BackendError}; -use ngr::runtime::RuntimeFunctions; -use pretty::Arena; -use std::io; +use cranelift_object::object; + +use ngr::backend::BackendError; +use ngr::backend::Program as Cranelift; +use ngr::ir::Program as IR; +use ngr::syntax::{ParserError, Program as Syntax}; use target_lexicon::Triple; use thiserror::Error; @@ -28,90 +25,57 @@ struct CommandLineArguments { #[derive(Debug, Error)] enum MainError { - #[error("Error parsing triple: {0}")] - Isa(#[from] isa::LookupError), - - #[error("Code generation error: {0}")] - Codegen(#[from] CodegenError), - - #[error("Module error: {0}")] - Module(#[from] ModuleError), - - #[error("IO error: {0}")] - IO(#[from] io::Error), - - #[error("Object write error: {0}")] - Object(#[from] object::write::Error), - #[error(transparent)] Backend(#[from] BackendError), - - #[error(transparent)] - SettingsError(#[from] SetError), + #[error("Parser error")] + ParserError(#[from] ParserError), + #[error("IO error")] + IoError(#[from] std::io::Error), + #[error("write error")] + WriteError(#[from] object::write::Error), } -fn main() -> Result<(), MainError> { +impl From for Diagnostic { + fn from(value: MainError) -> Self { + match value { + MainError::Backend(be) => be.into(), + MainError::ParserError(pe) => (&pe).into(), + MainError::IoError(e) => Diagnostic::error().with_message(format!("IO error: {}", e)), + MainError::WriteError(e) => Diagnostic::error().with_message(format!("Module write error: {}", e)), + } + } +} + +fn compile(file_database: &mut SimpleFiles) -> Result<(), MainError> { let args = CommandLineArguments::parse(); - let mut file_database = SimpleFiles::new(); - let initial_file_name = &args.file; - let mut hil_conversion_result = run_front_end(&mut file_database, initial_file_name); - - let writer = StandardStream::stderr(ColorChoice::Auto); - let config = codespan_reporting::term::Config::default(); - - for error in hil_conversion_result.errors.drain(..) { - term::emit( - &mut writer.lock(), - &config, - &file_database, - &Diagnostic::from(error), - ) - .unwrap(); - } - for warning in hil_conversion_result.warnings.drain(..) { - term::emit( - &mut writer.lock(), - &config, - &file_database, - &Diagnostic::from(warning), - ) - .unwrap(); - } - - if let Some((hil_tree, variable_map)) = hil_conversion_result.result { - let arena = Arena::new(); - println!("HIL Tree:"); - hil_tree - .pretty(&variable_map, &arena) - .into_doc() - .render_colored(72, StandardStream::stdout(ColorChoice::Auto)) - .unwrap(); - - let lil_tree = lil::Program::convert(hil_tree, variable_map); - println!("LIL Tree:"); - lil_tree - .pretty(&arena) - .into_doc() - .render_colored(72, StandardStream::stdout(ColorChoice::Auto)) - .unwrap(); - - let platform = Triple::host(); - let isa_builder = isa::lookup(platform.clone())?; - let mut settings_builder = settings::builder(); - settings_builder.set("is_pic", "true")?; - let isa = isa_builder.finish(settings::Flags::new(settings_builder))?; - let object_builder = ObjectBuilder::new(isa, "example", default_libcall_names())?; - let mut object_module = ObjectModule::new(object_builder); - let rtfuns = RuntimeFunctions::new(&platform, &mut object_module)?; - - let _compiled = lil_tree.into_cranelift(&mut object_module, &rtfuns)?; - - // somethingvar? - - let bytes = object_module.finish().emit()?; - - std::fs::write("output.o", bytes)?; - } + let syntax = Syntax::parse_file(file_database, &args.file)?; + let ir = IR::from(syntax.simplify()); + let compiled = Cranelift::new(Triple::host(), ir)?; + let bytes = compiled.bytes()?; + std::fs::write( + args.output.unwrap_or_else(|| "output.o".to_string()), + bytes, + )?; Ok(()) } + +fn main() { + let mut file_database = SimpleFiles::new(); + + match compile(&mut file_database) { + Ok(()) => {} + Err(e) => { + let writer = StandardStream::stderr(ColorChoice::Auto); + let config = codespan_reporting::term::Config::default(); + + term::emit( + &mut writer.lock(), + &config, + &file_database, + &Diagnostic::from(e), + ) + .unwrap(); + } + } +} diff --git a/src/errors.rs b/src/errors.rs deleted file mode 100644 index 6b57fc9..0000000 --- a/src/errors.rs +++ /dev/null @@ -1,114 +0,0 @@ -use crate::syntax::{Location, ParserError}; -use codespan_reporting::diagnostic::Diagnostic; -use codespan_reporting::files; -use std::io; - -#[derive(Debug)] -pub enum Error { - IOError(io::Error), - InternalFileDBError(files::Error), - ParserError(ParserError), - BindingSiteFailure(Location, String), - UnboundVariable(Location, String), - InternalError(Location, String), -} - -fn display_expected(expected: &[String]) -> String { - match expected.len() { - 0 => "".to_string(), - 1 => format!("; expected {}", expected[0]), - 2 => format!("; expected {} or {}", expected[0], expected[1]), - n => format!( - "; expected {}or {}", - comma_separate(&expected[0..n - 1]), - expected[n - 1] - ), - } -} - -fn comma_separate(strings: &[String]) -> String { - let mut result = String::new(); - - for s in strings.iter() { - result.push_str(s); - result.push_str(", "); - } - - result -} - -impl From for Diagnostic { - fn from(x: Error) -> Self { - match &x { - Error::IOError(e) => Diagnostic::error().with_message(format!("{}", e)), - - Error::InternalFileDBError(e) => Diagnostic::error().with_message(format!("{}", e)), - - Error::ParserError(pe) => match pe { - // this was just a token we didn't understand - ParserError::InvalidToken(location) => location - .labelled_error("extremely odd token") - .with_message("encountered extremely confusing token"), - - // unexpected EOF! - ParserError::UnrecognizedEOF(location, expected) => location.error().with_message( - format!("expected enf of file{}", display_expected(expected)), - ), - - // encountered a token where it shouldn't be - ParserError::UnrecognizedToken(start, end, token, expected) => { - let expected_str = - format!("unexpected token {}{}", token, display_expected(expected)); - let unexpected_str = format!("unexpected token {}", token); - let mut labels = start.range_label(end); - - Diagnostic::error() - .with_labels( - labels - .drain(..) - .map(|l| l.with_message(unexpected_str.clone())) - .collect(), - ) - .with_message(expected_str) - } - - // I think we get this when we get a token, but were expected EOF - ParserError::ExtraToken(start, token, end) => { - let expected_str = - format!("unexpected token {} after the expected end of file", token); - let unexpected_str = format!("unexpected token {}", token); - let mut labels = start.range_label(end); - - Diagnostic::error() - .with_labels( - labels - .drain(..) - .map(|l| l.with_message(unexpected_str.clone())) - .collect(), - ) - .with_message(expected_str) - } - - // simple lexer errors - ParserError::LexFailure(location) => { - location.error().with_message("unexpected character") - } - }, - - Error::BindingSiteFailure(location, name) => location - .labelled_error("discovered here") - .with_message(format!( - "Internal Error: Lost binding site for bound variable {}", - name - )), - - Error::UnboundVariable(location, name) => location - .labelled_error("unbound here") - .with_message(format!("Unbound variable '{}'", name)), - - Error::InternalError(location, string) => location - .labelled_error("this is related") - .with_message(format!("Internal error: {}", string)), - } - } -} diff --git a/src/ir.rs b/src/ir.rs new file mode 100644 index 0000000..9b5157d --- /dev/null +++ b/src/ir.rs @@ -0,0 +1,5 @@ +mod ast; +mod from_syntax; +mod strings; + +pub use ast::*; diff --git a/src/ir/ast.rs b/src/ir/ast.rs new file mode 100644 index 0000000..97cda55 --- /dev/null +++ b/src/ir/ast.rs @@ -0,0 +1,172 @@ +use internment::ArcIntern; +use pretty::{DocAllocator, Pretty}; + +use crate::syntax::Location; + +type Variable = ArcIntern; + +pub struct Program { + pub statements: Vec, +} + +impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Program +where + A: 'a, + D: ?Sized + DocAllocator<'a, A>, +{ + fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { + let mut result = allocator.nil(); + + for stmt in self.statements.iter() { + result = result + .append(stmt.pretty(allocator)) + .append(allocator.text(";")) + .append(allocator.hardline()); + } + + result + } +} + +pub enum Statement { + Binding(Location, Variable, Expression), + Print(Location, Variable), +} + +impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Statement +where + A: 'a, + D: ?Sized + DocAllocator<'a, A>, +{ + fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { + match self { + Statement::Binding(_, var, expr) => allocator + .text(var.as_ref().to_string()) + .append(allocator.space()) + .append(allocator.text("=")) + .append(allocator.space()) + .append(expr.pretty(allocator)), + Statement::Print(_, var) => allocator + .text("print") + .append(allocator.space()) + .append(allocator.text(var.as_ref().to_string())), + } + } +} + +pub enum Expression { + Value(Location, Value), + Reference(Location, Variable), + Primitive(Location, Primitive, Vec), +} + +impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Expression +where + A: 'a, + D: ?Sized + DocAllocator<'a, A>, +{ + fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { + match self { + Expression::Value(_, val) => val.pretty(allocator), + Expression::Reference(_, var) => allocator.text(var.as_ref().to_string()), + Expression::Primitive(_, op, exprs) if exprs.len() == 1 => { + op.pretty(allocator).append(exprs[0].pretty(allocator)) + } + Expression::Primitive(_, op, exprs) if exprs.len() == 2 => { + let left = exprs[0].pretty(allocator); + let right = exprs[1].pretty(allocator); + + left.append(allocator.space()) + .append(op.pretty(allocator)) + .append(allocator.space()) + .append(right) + .parens() + } + Expression::Primitive(_, op, exprs) => { + allocator.text(format!("!!{:?} with {} arguments!!", op, exprs.len())) + } + } + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum Primitive { + Plus, + Minus, + Times, + Divide, +} + +impl<'a> TryFrom<&'a str> for Primitive { + type Error = String; + + fn try_from(value: &str) -> Result { + match value { + "+" => Ok(Primitive::Plus), + "-" => Ok(Primitive::Minus), + "*" => Ok(Primitive::Times), + "/" => Ok(Primitive::Divide), + _ => Err(format!("Illegal primitive {}", value)), + } + } +} + +impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Primitive +where + A: 'a, + D: ?Sized + DocAllocator<'a, A>, +{ + fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { + match self { + Primitive::Plus => allocator.text("+"), + Primitive::Minus => allocator.text("-"), + Primitive::Times => allocator.text("*"), + Primitive::Divide => allocator.text("/"), + } + } +} + +pub enum ValueOrRef { + Value(Location, Value), + Ref(Location, ArcIntern), +} + +impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b ValueOrRef +where + A: 'a, + D: ?Sized + DocAllocator<'a, A>, +{ + fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { + match self { + ValueOrRef::Value(_, v) => v.pretty(allocator), + ValueOrRef::Ref(_, v) => allocator.text(v.as_ref().to_string()), + } + } +} + +pub enum Value { + Number(Option, i64), +} + +impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Value +where + A: 'a, + D: ?Sized + DocAllocator<'a, A>, +{ + fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { + match self { + Value::Number(opt_base, value) => { + let value_str = match opt_base { + None => format!("{}", value), + Some(2) => format!("0b{:b}", value), + Some(8) => format!("0o{:o}", value), + Some(10) => format!("0d{}", value), + Some(16) => format!("0x{:x}", value), + Some(_) => format!("!!{:x}!!", value), + }; + + allocator.text(value_str) + } + } + } +} diff --git a/src/ir/from_syntax.rs b/src/ir/from_syntax.rs new file mode 100644 index 0000000..73de9d0 --- /dev/null +++ b/src/ir/from_syntax.rs @@ -0,0 +1,65 @@ +use internment::ArcIntern; + +use crate::ir::ast as ir; +use crate::syntax::ast as syntax; + +impl From for ir::Program { + fn from(mut value: syntax::Program) -> Self { + ir::Program { + statements: value.statements.drain(..).map(Into::into).collect(), + } + } +} + +impl From for ir::Statement { + fn from(value: syntax::Statement) -> Self { + match value { + syntax::Statement::Binding(loc, name, expr) => { + ir::Statement::Binding(loc, ArcIntern::from(name), ir::Expression::from(expr)) + } + syntax::Statement::Print(loc, name) => ir::Statement::Print(loc, ArcIntern::from(name)), + } + } +} + +impl From for ir::Expression { + fn from(value: syntax::Expression) -> Self { + match value { + syntax::Expression::Primitive(loc, name, mut exprs) => ir::Expression::Primitive( + loc, + ir::Primitive::try_from(name.as_str()).unwrap(), + exprs.drain(..).map(Into::into).collect(), + ), + syntax::Expression::Reference(loc, name) => { + ir::Expression::Reference(loc, ArcIntern::from(name)) + } + syntax::Expression::Value(loc, value) => { + ir::Expression::Value(loc, ir::Value::from(value)) + } + } + } +} + +impl From for ir::ValueOrRef { + fn from(value: syntax::Expression) -> Self { + match value { + syntax::Expression::Primitive(loc, _, _) => { + panic!("{:?}: couldn't convert to valueorref", loc) + } + + syntax::Expression::Reference(loc, var) => { + ir::ValueOrRef::Ref(loc, ArcIntern::new(var)) + } + + syntax::Expression::Value(loc, val) => ir::ValueOrRef::Value(loc, val.into()), + } + } +} + +impl From for ir::Value { + fn from(x: syntax::Value) -> Self { + match x { + syntax::Value::Number(base, value) => ir::Value::Number(base, value), + } + } +} diff --git a/src/ir/strings.rs b/src/ir/strings.rs new file mode 100644 index 0000000..d0e57a2 --- /dev/null +++ b/src/ir/strings.rs @@ -0,0 +1,36 @@ +use super::ast::{Expression, Program, Statement}; +use internment::ArcIntern; +use std::collections::HashSet; + +impl Program { + pub fn strings(&self) -> HashSet> { + let mut result = HashSet::new(); + + for stmt in self.statements.iter() { + stmt.register_strings(&mut result); + } + + result + } +} + +impl Statement { + fn register_strings(&self, string_set: &mut HashSet>) { + match self { + Statement::Binding(_, name, expr) => { + string_set.insert(name.clone()); + expr.register_strings(string_set); + } + + Statement::Print(_, name) => { + string_set.insert(name.clone()); + } + } + } +} + +impl Expression { + fn register_strings(&self, _string_set: &mut HashSet>) { + // nothing has a string in here, at the moment + } +} diff --git a/src/lib.rs b/src/lib.rs index b04da03..04f3f8f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,4 @@ -pub mod asts; pub mod backend; -pub mod errors; -pub mod passes; -pub mod runtime; +pub mod ir; +pub mod pass_result; pub mod syntax; -pub mod variable_map; -pub mod warnings; diff --git a/src/pass_result.rs b/src/pass_result.rs new file mode 100644 index 0000000..ff2c066 --- /dev/null +++ b/src/pass_result.rs @@ -0,0 +1,36 @@ +pub mod errors; +pub mod warnings; + +use crate::syntax::ParserError; + +pub use self::errors::Error; +pub use self::warnings::Warning; + +pub struct PassResult { + pub result: Option, + pub warnings: Vec, + pub errors: Vec, +} + +impl From for PassResult { + fn from(value: ParserError) -> Self { + PassResult { + result: None, + warnings: vec![], + errors: vec![Error::ParserError(value)], + } + } +} + +impl From for PassResult +where + Error: From, +{ + fn from(x: E) -> Self { + PassResult { + result: None, + warnings: vec![], + errors: vec![Error::from(x)], + } + } +} diff --git a/src/pass_result/errors.rs b/src/pass_result/errors.rs new file mode 100644 index 0000000..0c04343 --- /dev/null +++ b/src/pass_result/errors.rs @@ -0,0 +1,124 @@ +use crate::syntax::{Location, ParserError}; +use codespan_reporting::diagnostic::Diagnostic; +use codespan_reporting::files; +use std::io; + +#[derive(Debug)] +pub enum Error { + IOError(io::Error), + InternalFileDBError(files::Error), + ParserError(ParserError), + BindingSiteFailure(Location, String), + UnboundVariable(Location, String), + InternalError(Location, String), +} + +fn display_expected(expected: &[String]) -> String { + match expected.len() { + 0 => "".to_string(), + 1 => format!("; expected {}", expected[0]), + 2 => format!("; expected {} or {}", expected[0], expected[1]), + n => format!( + "; expected {}or {}", + comma_separate(&expected[0..n - 1]), + expected[n - 1] + ), + } +} + +fn comma_separate(strings: &[String]) -> String { + let mut result = String::new(); + + for s in strings.iter() { + result.push_str(s); + result.push_str(", "); + } + + result +} + +impl<'a> From<&'a ParserError> for Diagnostic { + fn from(value: &ParserError) -> Self { + match value { + // this was just a token we didn't understand + ParserError::InvalidToken(location) => location + .labelled_error("extremely odd token") + .with_message("encountered extremely confusing token"), + + // unexpected EOF! + ParserError::UnrecognizedEOF(location, expected) => location.error().with_message( + format!("expected enf of file{}", display_expected(expected)), + ), + + // encountered a token where it shouldn't be + ParserError::UnrecognizedToken(start, end, token, expected) => { + let expected_str = + format!("unexpected token {}{}", token, display_expected(expected)); + let unexpected_str = format!("unexpected token {}", token); + let mut labels = start.range_label(end); + + Diagnostic::error() + .with_labels( + labels + .drain(..) + .map(|l| l.with_message(unexpected_str.clone())) + .collect(), + ) + .with_message(expected_str) + } + + // I think we get this when we get a token, but were expected EOF + ParserError::ExtraToken(start, token, end) => { + let expected_str = + format!("unexpected token {} after the expected end of file", token); + let unexpected_str = format!("unexpected token {}", token); + let mut labels = start.range_label(end); + + Diagnostic::error() + .with_labels( + labels + .drain(..) + .map(|l| l.with_message(unexpected_str.clone())) + .collect(), + ) + .with_message(expected_str) + } + + // simple lexer errors + ParserError::LexFailure(location) => { + location.error().with_message("unexpected character") + } + + ParserError::FileDatabaseError(e) => Diagnostic::error().with_message(e.to_string()), + + ParserError::ReadError(e) => Diagnostic::error().with_message(e.to_string()), + } + } +} + +impl From for Diagnostic { + fn from(x: Error) -> Self { + match &x { + Error::IOError(e) => Diagnostic::error().with_message(format!("{}", e)), + + Error::InternalFileDBError(e) => Diagnostic::error().with_message(format!("{}", e)), + + Error::ParserError(pe) => pe.into(), + + Error::BindingSiteFailure(location, name) => location + .labelled_error("discovered here") + .with_message(format!( + "Internal Error: Lost binding site for bound variable {}", + name + )), + + Error::UnboundVariable(location, name) => location + .labelled_error("unbound here") + .with_message(format!("Unbound variable '{}'", name)), + + Error::InternalError(location, string) => location + .labelled_error("this is related") + .with_message(format!("Internal error: {}", string)), + } + } +} diff --git a/src/warnings.rs b/src/pass_result/warnings.rs similarity index 100% rename from src/warnings.rs rename to src/pass_result/warnings.rs diff --git a/src/passes.rs b/src/passes.rs deleted file mode 100644 index 00b92ef..0000000 --- a/src/passes.rs +++ /dev/null @@ -1,98 +0,0 @@ -use crate::asts::hil; -use crate::errors::Error; -use crate::syntax::Location; -use crate::syntax::{self, ParserError}; -use crate::variable_map::VariableMap; -use crate::warnings::Warning; -use codespan_reporting::files::SimpleFiles; -use std::fs; - -pub use crate::passes::into_crane::BackendError; - -mod hil_to_lil; -mod into_crane; -mod syntax_to_hil; - -pub struct PassResult { - pub result: T, - pub warnings: Vec, - pub errors: Vec, -} - -impl From for PassResult> { - fn from(value: ParserError) -> Self { - PassResult { - result: None, - warnings: vec![], - errors: vec![Error::ParserError(value)], - } - } -} - -impl From for PassResult> { - fn from(value: std::io::Error) -> Self { - PassResult { - result: None, - warnings: vec![], - errors: vec![Error::IOError(value)], - } - } -} - -impl From for PassResult> { - fn from(value: codespan_reporting::files::Error) -> Self { - PassResult { - result: None, - warnings: vec![], - errors: vec![Error::InternalFileDBError(value)], - } - } -} - -impl From for PassResult> -where - Error: From, -{ - fn from(x: E) -> Self { - PassResult { - result: None, - warnings: vec![], - errors: vec![Error::from(x)], - } - } -} - -pub fn run_front_end( - file_database: &mut SimpleFiles, - initial_file_name: &str, -) -> PassResult, VariableMap)>> { - let initial_file_contents = match fs::read_to_string(initial_file_name) { - Ok(x) => x, - Err(e) => return PassResult::from(e), - }; - - let initial_file = file_database.add(initial_file_name.to_string(), initial_file_contents); - let db_version = match file_database.get(initial_file) { - Ok(x) => x, - Err(e) => return PassResult::from(e), - }; - let db_version_source = db_version.source(); - let raw_syntax = match syntax::Program::parse(initial_file, db_version_source) { - Ok(x) => x, - Err(e) => return PassResult::from(e), - }; - - let mut variable_map = VariableMap::empty(); - let conversion_result = hil::Program::convert(raw_syntax, &mut variable_map); - let result = if conversion_result.errors.is_empty() { - Some((conversion_result.result, variable_map)) - } else { - None - }; - - PassResult { - result, - warnings: conversion_result.warnings, - errors: conversion_result.errors, - } -} diff --git a/src/passes/hil_to_lil.rs b/src/passes/hil_to_lil.rs deleted file mode 100644 index df5fe7a..0000000 --- a/src/passes/hil_to_lil.rs +++ /dev/null @@ -1,119 +0,0 @@ -use internment::ArcIntern; - -use crate::asts::hil; -use crate::asts::lil; -use crate::variable_map::VariableMap; -use std::collections::HashSet; - -impl lil::Program { - pub fn convert(hil_program: hil::Program, mut variable_map: VariableMap) -> Self { - let mut statements = Vec::new(); - let mut strings = HashSet::new(); - - for hil_statement in hil_program.statements { - statements.append(&mut lil::Statement::convert( - hil_statement, - &mut variable_map, - &mut strings, - )); - } - - lil::Program { - statements, - strings, - variable_info: variable_map, - } - } -} - -impl lil::Statement { - fn convert( - hil_statement: hil::Statement, - variable_map: &mut VariableMap, - strings: &mut HashSet>, - ) -> Vec { - match hil_statement { - hil::Statement::Binding(annotation, var, expr) => match expr { - hil::Expression::Reference(rhs_annotation, rhs) => { - vec![lil::Statement::VariableBinding( - annotation, - var, - lil::SimpleExpression::Reference(rhs_annotation, rhs), - )] - } - hil::Expression::Value(rhs_annotation, rhs) => { - vec![lil::Statement::VariableBinding( - annotation, - var, - lil::SimpleExpression::Constant(rhs_annotation, rhs), - )] - } - hil::Expression::Primitive(_, prim, exprs) => { - let mut result = Vec::new(); - let mut arguments = Vec::new(); - - for expr in exprs { - let new_binding = variable_map.gensym(); - let expr_ann = expr.annotation(); - let temporary_statement = - hil::Statement::Binding(expr_ann.clone(), new_binding, expr); - result.append(&mut lil::Statement::convert( - temporary_statement, - variable_map, - strings, - )); - arguments.push(lil::SimpleExpression::Reference(expr_ann, new_binding)); - } - - let final_statement = match prim { - hil::Primitive::Plus => { - let arg1 = arguments.pop().unwrap(); - let arg0 = arguments.pop().unwrap(); - lil::Statement::ResultBinding( - annotation, - var, - lil::Primitive::Plus(arg0, arg1), - ) - } - hil::Primitive::Minus => { - let arg1 = arguments.pop().unwrap(); - let arg0 = arguments.pop().unwrap(); - lil::Statement::ResultBinding( - annotation, - var, - lil::Primitive::Minus(arg0, arg1), - ) - } - hil::Primitive::Times => { - let arg1 = arguments.pop().unwrap(); - let arg0 = arguments.pop().unwrap(); - lil::Statement::ResultBinding( - annotation, - var, - lil::Primitive::Times(arg0, arg1), - ) - } - hil::Primitive::Divide => { - let arg1 = arguments.pop().unwrap(); - let arg0 = arguments.pop().unwrap(); - lil::Statement::ResultBinding( - annotation, - var, - lil::Primitive::Divide(arg0, arg1), - ) - } - }; - - result.push(final_statement); - result - } - }, - hil::Statement::Print(annotation, var) => { - let zero_termed = format!("{}\0", variable_map.get_name(var).unwrap()); - let interned = ArcIntern::new(zero_termed); - strings.insert(interned.clone()); - vec![lil::Statement::Print(annotation, interned, var)] - } - } - } -} diff --git a/src/passes/into_crane.rs b/src/passes/into_crane.rs deleted file mode 100644 index e9a5949..0000000 --- a/src/passes/into_crane.rs +++ /dev/null @@ -1,137 +0,0 @@ -use std::collections::HashMap; - -use crate::asts::lil::{Primitive, Program, SimpleExpression, Statement, Value}; -use crate::runtime::{RuntimeFunctionError, RuntimeFunctions}; -use cranelift_codegen::entity::EntityRef; -use cranelift_codegen::ir::{entities, types, Function, InstBuilder, Signature, UserFuncName}; -use cranelift_codegen::isa::CallConv; -use cranelift_codegen::Context; -use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext, Variable}; -use cranelift_module::{DataContext, Linkage, Module, ModuleCompiledFunction, ModuleError}; -use thiserror::Error; - -#[derive(Debug, Error)] -pub enum BackendError { - #[error("Cranelift module error: {0}")] - Cranelift(#[from] ModuleError), - #[error("Builtin function error: {0}")] - BuiltinError(#[from] RuntimeFunctionError), - #[error("Internal variable lookup error")] - VariableLookupFailure, -} - -impl Program { - pub fn into_cranelift( - mut self, - module: &mut M, - rtfuns: &RuntimeFunctions, - ) -> Result { - let basic_signature = Signature { - params: vec![], - returns: vec![], - call_conv: CallConv::SystemV, - }; - - let func_id = module.declare_function("gogogo", Linkage::Export, &basic_signature)?; - let mut ctx = Context::new(); - ctx.func = - Function::with_name_signature(UserFuncName::user(0, func_id.as_u32()), basic_signature); - let mut variable_name_global_values = HashMap::new(); - - for (idx, interned_value) in self.strings.drain().enumerate() { - let global_id = module.declare_data( - &format!("local-string-{}", idx), - Linkage::Local, - false, - false, - )?; - let mut data_context = DataContext::new(); - data_context.set_align(8); - data_context.define( - interned_value - .as_str() - .to_owned() - .into_boxed_str() - .into_boxed_bytes(), - ); - module.define_data(global_id, &data_context)?; - let local_data = module.declare_data_in_func(global_id, &mut ctx.func); - variable_name_global_values.insert(interned_value, local_data); - } - - let print_func_ref = rtfuns.include_runtime_function("print", module, &mut ctx.func)?; - - let mut fctx = FunctionBuilderContext::new(); - let mut builder = FunctionBuilder::new(&mut ctx.func, &mut fctx); - let main_block = builder.create_block(); - builder.switch_to_block(main_block); - - for stmt in self.statements.drain(..) { - match stmt { - Statement::Print(_ann, name_idx, var) => { - let local_data = *variable_name_global_values.get(&name_idx).unwrap(); - let var_name = builder.ins().symbol_value(types::I64, local_data); - let val = builder.use_var(Variable::new(var)); - builder.ins().call(print_func_ref, &[var_name, val]); - } - - Statement::ResultBinding(_, varnum, value) => { - let var = Variable::new(varnum); - builder.declare_var(var, types::I64); - - let val = match value { - Primitive::Plus(left, right) => { - let left = left.into_cranelift(&mut builder); - let right = right.into_cranelift(&mut builder); - builder.ins().iadd(left, right) - } - - Primitive::Minus(left, right) => { - let left = left.into_cranelift(&mut builder); - let right = right.into_cranelift(&mut builder); - builder.ins().isub(left, right) - } - - Primitive::Times(left, right) => { - let left = left.into_cranelift(&mut builder); - let right = right.into_cranelift(&mut builder); - builder.ins().imul(left, right) - } - - Primitive::Divide(left, right) => { - let left = left.into_cranelift(&mut builder); - let right = right.into_cranelift(&mut builder); - builder.ins().sdiv(left, right) - } - }; - builder.def_var(var, val); - } - - Statement::VariableBinding(_, varnum, exp) => { - let var = Variable::new(varnum); - builder.declare_var(var, types::I64); - let val = exp.into_cranelift(&mut builder); - builder.def_var(var, val); - } - } - } - - builder.ins().return_(&[]); - builder.seal_block(main_block); - builder.finalize(); - - Ok(module.define_function(func_id, &mut ctx)?) - } -} - -impl SimpleExpression { - fn into_cranelift(self, builder: &mut FunctionBuilder) -> entities::Value { - match self { - SimpleExpression::Constant(_, value) => match value { - Value::Number(_base, numval) => builder.ins().iconst(types::I64, numval as i64), - }, - - SimpleExpression::Reference(_, num) => builder.use_var(Variable::new(num)), - } - } -} diff --git a/src/passes/syntax_to_hil.rs b/src/passes/syntax_to_hil.rs deleted file mode 100644 index 45e005a..0000000 --- a/src/passes/syntax_to_hil.rs +++ /dev/null @@ -1,157 +0,0 @@ -use crate::asts::hil::{self, Primitive}; -use crate::errors::Error; -use crate::passes::PassResult; -use crate::syntax; -use crate::syntax::Location; -use crate::variable_map::VariableMap; -use crate::warnings::Warning; - -impl hil::Program { - pub fn convert( - mut input: syntax::Program, - var_map: &mut VariableMap, - ) -> PassResult> { - let mut statements = Vec::new(); - let mut warnings = Vec::new(); - let mut errors = Vec::new(); - - for syntax_stmt in input.statements.drain(..) { - let mut result = hil::Statement::convert(syntax_stmt, var_map); - - statements.push(result.result); - warnings.append(&mut result.warnings); - errors.append(&mut result.errors); - } - - PassResult { - result: hil::Program { statements }, - warnings, - errors, - } - } -} - -impl hil::Statement { - fn convert( - input: syntax::Statement, - var_map: &mut VariableMap, - ) -> PassResult> { - match input { - syntax::Statement::Binding(loc, variable_name, expr) => { - let mut expr_result = hil::Expression::convert(expr, var_map); - let mut errors = Vec::new(); - let mut warnings = Vec::new(); - - if let Some(var) = var_map.get_variable(&variable_name) { - if let Some(orig_loc) = var_map.get_binding_site(var) { - warnings.push(Warning::ShadowedVariable( - orig_loc, - loc.clone(), - variable_name.clone(), - )); - } else { - errors.push(Error::BindingSiteFailure( - loc.clone(), - variable_name.clone(), - )); - } - } - - let variable = var_map.add(variable_name, loc.clone()); - let result = hil::Statement::Binding(loc, variable, expr_result.result); - warnings.append(&mut expr_result.warnings); - errors.append(&mut expr_result.errors); - - PassResult { - result, - warnings, - errors, - } - } - - syntax::Statement::Print(variable_loc, variable_name) => { - match var_map.get_variable(&variable_name) { - None => PassResult { - result: hil::Statement::Print(Location::manufactured(), 0), - warnings: vec![], - errors: vec![Error::UnboundVariable(variable_loc, variable_name)], - }, - - Some(variable) => PassResult { - result: hil::Statement::Print(variable_loc, variable), - warnings: vec![], - errors: vec![], - }, - } - } - } - } -} - -impl hil::Expression { - fn convert( - input: syntax::Expression, - var_map: &mut VariableMap, - ) -> PassResult> { - match input { - syntax::Expression::Value(location, value) => PassResult { - result: hil::Expression::Value(location, hil::Value::from(value)), - warnings: vec![], - errors: vec![], - }, - - syntax::Expression::Reference(location, name) => match var_map.get_variable(&name) { - None => PassResult { - result: hil::Expression::Reference(Location::manufactured(), 0), - warnings: vec![], - errors: vec![Error::UnboundVariable(location, name)], - }, - - Some(variable) => PassResult { - result: hil::Expression::Reference(location, variable), - warnings: vec![], - errors: vec![], - }, - }, - - syntax::Expression::Primitive(location, name, mut exprs) => { - let mut args = vec![]; - let mut warnings = vec![]; - let mut errors = vec![]; - - let op = match name.as_ref() { - "+" => Primitive::Plus, - "-" => Primitive::Minus, - "*" => Primitive::Times, - "/" => Primitive::Divide, - other => { - errors.push(Error::UnboundVariable(location.clone(), other.to_string())); - Primitive::Plus - } - }; - - for orig_expr in exprs.drain(..) { - let mut sub_result = hil::Expression::convert(orig_expr, var_map); - - args.push(sub_result.result); - warnings.append(&mut sub_result.warnings); - errors.append(&mut sub_result.errors); - } - - PassResult { - result: hil::Expression::Primitive(location, op, args), - warnings, - errors, - } - } - } - } -} - -impl From for hil::Value { - fn from(x: syntax::Value) -> hil::Value { - match x { - syntax::Value::Number(base, value) => hil::Value::Number(base, value), - } - } -} diff --git a/src/syntax.rs b/src/syntax.rs index 9498118..d491aa0 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -1,14 +1,16 @@ +use codespan_reporting::files::SimpleFiles; use lalrpop_util::lalrpop_mod; use logos::Logos; mod location; +mod simplify; mod tokens; lalrpop_mod!( #[allow(clippy::just_underscores_and_digits)] parser, "/syntax/parser.rs" ); -mod ast; +pub mod ast; pub use crate::syntax::ast::*; pub use crate::syntax::location::Location; @@ -17,14 +19,24 @@ pub use crate::syntax::tokens::{LexerError, Token}; use lalrpop_util::ParseError; #[cfg(test)] use std::str::FromStr; +use thiserror::Error; -#[derive(Debug)] +#[derive(Debug, Error)] pub enum ParserError { + #[error("Invalid token")] InvalidToken(Location), + #[error("Unrecognized EOF")] UnrecognizedEOF(Location, Vec), + #[error("Unrecognized token")] UnrecognizedToken(Location, Location, Token, Vec), + #[error("Extra token")] ExtraToken(Location, Token, Location), + #[error("Lexing failure")] LexFailure(Location), + #[error("File database error")] + FileDatabaseError(#[from] codespan_reporting::files::Error), + #[error("Read error")] + ReadError(#[from] std::io::Error), } impl ParserError { @@ -62,6 +74,16 @@ impl ParserError { } impl Program { + pub fn parse_file( + file_database: &mut SimpleFiles, + file_name: &str, + ) -> Result { + let file_contents = std::fs::read_to_string(file_name)?; + let file_handle = file_database.add(file_name.to_string(), file_contents); + let file_db_info = file_database.get(file_handle)?; + Program::parse(file_handle, file_db_info.source()) + } + pub fn parse(file_idx: usize, buffer: &str) -> Result { let lexer = Token::lexer(buffer) .spanned() diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index 76bb011..7bc45d6 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -99,7 +99,7 @@ where #[derive(Debug, PartialEq, Eq)] pub enum Value { - Number(Option, i128), + Number(Option, i64), } impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Value diff --git a/src/syntax/parser.lalrpop b/src/syntax/parser.lalrpop index feefaad..cfa2553 100644 --- a/src/syntax/parser.lalrpop +++ b/src/syntax/parser.lalrpop @@ -20,7 +20,7 @@ extern { "*" => Token::Operator('*'), "/" => Token::Operator('/'), - "" => Token::Number((>,)), + "" => Token::Number((>,)), "" => Token::Variable(>), } } diff --git a/src/syntax/simplify.rs b/src/syntax/simplify.rs new file mode 100644 index 0000000..aaa4cc7 --- /dev/null +++ b/src/syntax/simplify.rs @@ -0,0 +1,49 @@ +use crate::syntax::ast::{Program, Statement, Expression}; + +impl Program { + pub fn simplify(mut self) -> Self { + let mut new_statements = Vec::new(); + let mut gensym_index = 1; + + for stmt in self.statements.drain(..) { + match stmt { + Statement::Print(_, _) => new_statements.push(stmt), + Statement::Binding(_, _, Expression::Reference(_, _)) => new_statements.push(stmt), + Statement::Binding(_, _, Expression::Value(_, _)) => new_statements.push(stmt), + Statement::Binding(loc, name, value) => { + let (mut prereqs, new_value) = value.rebind(&name, &mut gensym_index); + new_statements.append(&mut prereqs); + new_statements.push(Statement::Binding(loc, name, new_value)) + } + } + } + + self.statements = new_statements; + self + } +} + +impl Expression { + fn rebind(self, base_name: &str, gensym_index: &mut usize) -> (Vec, Expression) { + match self { + Expression::Value(_, _) => (vec![], self), + Expression::Reference(_, _) => (vec![], self), + Expression::Primitive(loc, prim, mut expressions) => { + let mut prereqs = Vec::new(); + let mut new_exprs = Vec::new(); + + for expr in expressions.drain(..) { + let (mut cur_prereqs, arg) = expr.rebind(base_name, gensym_index); + prereqs.append(&mut cur_prereqs); + new_exprs.push(arg); + } + + let new_name = format!("<{}:{}>", base_name, *gensym_index); + *gensym_index += 1; + prereqs.push(Statement::Binding(loc.clone(), new_name.clone(), Expression::Primitive(loc.clone(), prim, new_exprs))); + + (prereqs, Expression::Reference(loc, new_name)) + } + } + } +} \ No newline at end of file diff --git a/src/syntax/tokens.rs b/src/syntax/tokens.rs index 5332ce9..10818ca 100644 --- a/src/syntax/tokens.rs +++ b/src/syntax/tokens.rs @@ -23,7 +23,7 @@ pub enum Token { #[regex(r"0d[0-9]+", |v| parse_number(Some(10), v))] #[regex(r"0x[0-9a-fA-F]+", |v| parse_number(Some(16), v))] #[regex(r"[0-9]+", |v| parse_number(None, v))] - Number((Option, i128)), + Number((Option, i64)), #[regex(r"[a-z][a-zA-Z0-9_]*", |v| ArcIntern::new(v.slice().to_string()))] Variable(ArcIntern), @@ -71,13 +71,13 @@ impl Token { fn parse_number( base: Option, value: &Lexer, -) -> Result<(Option, i128), ParseIntError> { +) -> Result<(Option, i64), ParseIntError> { let (radix, strval) = match base { None => (10, value.slice()), Some(radix) => (radix, &value.slice()[2..]), }; - let intval = i128::from_str_radix(strval, radix as u32)?; + let intval = i64::from_str_radix(strval, radix as u32)?; Ok((base, intval)) } diff --git a/src/variable_map.rs b/src/variable_map.rs deleted file mode 100644 index 5331c73..0000000 --- a/src/variable_map.rs +++ /dev/null @@ -1,72 +0,0 @@ -use crate::syntax::Location; -use std::collections::HashMap; - -pub type Variable = usize; - -#[derive(Clone)] -pub struct VariableMap { - map: HashMap, - next_index: Variable, -} - -#[derive(Clone)] -struct VariableInfo { - name: String, - binding_location: Location, -} - -impl VariableInfo { - fn new(name: String, binding_location: Location) -> Self { - VariableInfo { - name, - binding_location, - } - } -} - -impl VariableMap { - pub fn empty() -> Self { - VariableMap { - map: HashMap::new(), - next_index: 0, - } - } - - pub fn add(&mut self, name: String, loc: Location) -> Variable { - let result = self.next_index; - - self.next_index += 1; - self.map.insert(result, VariableInfo::new(name, loc)); - - result - } - - pub fn get_name(&self, var: Variable) -> Option<&str> { - self.map.get(&var).map(|x| x.name.as_ref()) - } - - pub fn get_binding_site(&self, var: Variable) -> Option { - self.map.get(&var).map(|x| x.binding_location.clone()) - } - - pub fn get_variable(&self, name: &str) -> Option { - for (num, info) in self.map.iter() { - if info.name == name { - return Some(*num); - } - } - None - } - - pub fn gensym(&mut self) -> Variable { - let result = self.next_index; - - self.next_index += 1; - self.map.insert( - result, - VariableInfo::new(format!("", result), Location::manufactured()), - ); - - result - } -}