diff --git a/src/asts.rs b/src/asts.rs index b743795..529c1cc 100644 --- a/src/asts.rs +++ b/src/asts.rs @@ -1 +1,2 @@ pub mod hil; +pub mod lil; diff --git a/src/asts/hil.rs b/src/asts/hil.rs index f25246e..069b6dd 100644 --- a/src/asts/hil.rs +++ b/src/asts/hil.rs @@ -75,12 +75,14 @@ pub enum Expression { Primitive(Annotation, Primitive, Vec>), } -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum Primitive { - Plus, - Minus, - Times, - Divide, +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 { @@ -116,6 +118,14 @@ impl Expression { } } +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum Primitive { + Plus, + Minus, + Times, + Divide, +} + impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Primitive where A: 'a, diff --git a/src/asts/lil.rs b/src/asts/lil.rs new file mode 100644 index 0000000..c2bf641 --- /dev/null +++ b/src/asts/lil.rs @@ -0,0 +1,153 @@ +pub use crate::asts::hil::Value; +use crate::variable_map::{Variable, VariableMap}; +use pretty::{DocAllocator, DocBuilder, Pretty}; + +pub struct Program { + pub statements: Vec>, +} + +impl Program { + 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>, + { + 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 + } +} + +pub enum Statement { + VariableBinding(Annotation, Variable, SimpleExpression), + ResultBinding(Annotation, Variable, Primitive), + Print(Annotation, 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) => { + let name = variable_map.get_name(*var).unwrap_or(""); + + allocator + .text("print") + .append(allocator.space()) + .append(allocator.text(name.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/bin.rs b/src/bin.rs index 99d6d6f..2abe403 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -3,6 +3,7 @@ use codespan_reporting::diagnostic::Diagnostic; use codespan_reporting::files::SimpleFiles; use codespan_reporting::term; use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; +use ngr::asts::lil; use ngr::passes::run_front_end; use pretty::Arena; @@ -27,15 +28,29 @@ fn main() { 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(); + 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(); + term::emit( + &mut writer.lock(), + &config, + &file_database, + &Diagnostic::from(warning), + ) + .unwrap(); } - if let Some((tree, variable_map)) = hil_conversion_result.result { + if let Some((hil_tree, mut variable_map)) = hil_conversion_result.result { let arena = Arena::new(); - tree.pretty(&variable_map, &arena) + let lil_tree = lil::Program::convert(hil_tree, &mut variable_map); + lil_tree + .pretty(&variable_map, &arena) .into_doc() .render_colored(72, StandardStream::stdout(ColorChoice::Auto)) .unwrap() diff --git a/src/errors.rs b/src/errors.rs index 09b2d75..c80a1f0 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -159,17 +159,15 @@ impl From for Diagnostic { }, Error::UnboundVariable(location, name) => match location { - Location::Manufactured => Diagnostic::error().with_message(format!( - "Unbound variable '{}'", - name - )), + Location::Manufactured => { + Diagnostic::error().with_message(format!("Unbound variable '{}'", name)) + } Location::InFile(file_id, offset) => Diagnostic::error() .with_labels(vec![ Label::primary(*file_id, *offset..*offset).with_message("unbound here") ]) .with_message(format!("Unbound variable '{}'", name)), - - } + }, } } } diff --git a/src/passes.rs b/src/passes.rs index fad4677..2846d5d 100644 --- a/src/passes.rs +++ b/src/passes.rs @@ -1,12 +1,13 @@ -use codespan_reporting::files::SimpleFiles; use crate::asts::hil; use crate::errors::Error; use crate::syntax; use crate::syntax::Location; use crate::variable_map::VariableMap; use crate::warnings::Warning; +use codespan_reporting::files::SimpleFiles; use std::fs; +mod hil_to_lil; mod syntax_to_hil; pub struct PassResult { @@ -15,9 +16,9 @@ pub struct PassResult { pub errors: Vec, } -impl From for PassResult> - where - Error: From +impl From for PassResult> +where + Error: From, { fn from(x: E) -> Self { PassResult { @@ -61,4 +62,4 @@ pub fn run_front_end( warnings: conversion_result.warnings, errors: conversion_result.errors, } -} \ No newline at end of file +} diff --git a/src/passes/hil_to_lil.rs b/src/passes/hil_to_lil.rs new file mode 100644 index 0000000..310b7dc --- /dev/null +++ b/src/passes/hil_to_lil.rs @@ -0,0 +1,100 @@ +use crate::asts::hil; +use crate::asts::lil; +use crate::variable_map::VariableMap; + +impl lil::Program { + pub fn convert(hil_program: hil::Program, variable_map: &mut VariableMap) -> Self { + let mut statements = Vec::new(); + + for hil_statement in hil_program.statements { + statements.append(&mut lil::Statement::convert(hil_statement, variable_map)); + } + + lil::Program { statements } + } +} + +impl lil::Statement { + fn convert( + hil_statement: hil::Statement, + variable_map: &mut VariableMap, + ) -> 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, + )); + 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::Plus(arg0, arg1), + ) + } + hil::Primitive::Times => { + let arg1 = arguments.pop().unwrap(); + let arg0 = arguments.pop().unwrap(); + lil::Statement::ResultBinding( + annotation, + var, + lil::Primitive::Plus(arg0, arg1), + ) + } + hil::Primitive::Divide => { + let arg1 = arguments.pop().unwrap(); + let arg0 = arguments.pop().unwrap(); + lil::Statement::ResultBinding( + annotation, + var, + lil::Primitive::Plus(arg0, arg1), + ) + } + }; + + result.push(final_statement); + result + } + }, + hil::Statement::Print(annotation, var) => vec![lil::Statement::Print(annotation, var)], + } + } +} diff --git a/src/passes/syntax_to_hil.rs b/src/passes/syntax_to_hil.rs index 21b6b55..6266423 100644 --- a/src/passes/syntax_to_hil.rs +++ b/src/passes/syntax_to_hil.rs @@ -44,7 +44,11 @@ impl hil::Statement { 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())); + warnings.push(Warning::ShadowedVariable( + orig_loc, + loc.clone(), + variable_name.clone(), + )); } else { errors.push(Error::BindingSiteFailure( loc.clone(), @@ -65,18 +69,20 @@ impl hil::Statement { } } - 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)], - }, + 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![], - }, + Some(variable) => PassResult { + result: hil::Statement::Print(variable_loc, variable), + warnings: vec![], + errors: vec![], + }, + } } } } @@ -106,7 +112,7 @@ impl hil::Expression { warnings: vec![], errors: vec![], }, - } + }, syntax::Expression::Primitive(location, name, mut exprs) => { let mut args = vec![]; @@ -145,8 +151,7 @@ impl hil::Expression { impl From for hil::Value { fn from(x: syntax::Value) -> hil::Value { match x { - syntax::Value::Number(base, value) => - hil::Value::Number(base, value), + syntax::Value::Number(base, value) => hil::Value::Number(base, value), } } -} \ No newline at end of file +} diff --git a/src/variable_map.rs b/src/variable_map.rs index 882bdd6..f3177f2 100644 --- a/src/variable_map.rs +++ b/src/variable_map.rs @@ -54,4 +54,16 @@ impl VariableMap { } 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 + } }