Add a conversion from our high-level IR to a lower-level one.

This commit is contained in:
2022-02-24 21:18:30 -08:00
parent 9d82c8ca2d
commit dcc6562050
9 changed files with 332 additions and 37 deletions

View File

@@ -1 +1,2 @@
pub mod hil; pub mod hil;
pub mod lil;

View File

@@ -75,12 +75,14 @@ pub enum Expression<Annotation> {
Primitive(Annotation, Primitive, Vec<Expression<Annotation>>), Primitive(Annotation, Primitive, Vec<Expression<Annotation>>),
} }
#[derive(Clone, Copy, Debug, PartialEq)] impl<Annotation: Clone> Expression<Annotation> {
pub enum Primitive { pub fn annotation(&self) -> Annotation {
Plus, match self {
Minus, Expression::Value(a, _) => a.clone(),
Times, Expression::Reference(a, _) => a.clone(),
Divide, Expression::Primitive(a, _, _) => a.clone(),
}
}
} }
impl<Annotation> Expression<Annotation> { impl<Annotation> Expression<Annotation> {
@@ -116,6 +118,14 @@ impl<Annotation> Expression<Annotation> {
} }
} }
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Primitive {
Plus,
Minus,
Times,
Divide,
}
impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Primitive impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Primitive
where where
A: 'a, A: 'a,

153
src/asts/lil.rs Normal file
View File

@@ -0,0 +1,153 @@
pub use crate::asts::hil::Value;
use crate::variable_map::{Variable, VariableMap};
use pretty::{DocAllocator, DocBuilder, Pretty};
pub struct Program<Annotation> {
pub statements: Vec<Statement<Annotation>>,
}
impl<Annotation> Program<Annotation> {
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<Annotation> {
VariableBinding(Annotation, Variable, SimpleExpression<Annotation>),
ResultBinding(Annotation, Variable, Primitive<Annotation>),
Print(Annotation, Variable),
}
impl<Annotation> Statement<Annotation> {
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("<unknown>");
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("<unknown>");
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("<unknown>");
allocator
.text("print")
.append(allocator.space())
.append(allocator.text(name.to_string()))
}
}
}
}
pub enum Primitive<Annotation> {
Plus(SimpleExpression<Annotation>, SimpleExpression<Annotation>),
Minus(SimpleExpression<Annotation>, SimpleExpression<Annotation>),
Times(SimpleExpression<Annotation>, SimpleExpression<Annotation>),
Divide(SimpleExpression<Annotation>, SimpleExpression<Annotation>),
}
impl<Annotation> Primitive<Annotation> {
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<Annotation> {
Reference(Annotation, Variable),
Constant(Annotation, Value),
}
impl<Annotation> SimpleExpression<Annotation> {
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("<unknown>");
allocator.text(name.to_string())
}
SimpleExpression::Constant(_, x) => x.pretty(allocator),
}
}
}

View File

@@ -3,6 +3,7 @@ use codespan_reporting::diagnostic::Diagnostic;
use codespan_reporting::files::SimpleFiles; use codespan_reporting::files::SimpleFiles;
use codespan_reporting::term; use codespan_reporting::term;
use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
use ngr::asts::lil;
use ngr::passes::run_front_end; use ngr::passes::run_front_end;
use pretty::Arena; use pretty::Arena;
@@ -27,15 +28,29 @@ fn main() {
let config = codespan_reporting::term::Config::default(); let config = codespan_reporting::term::Config::default();
for error in hil_conversion_result.errors.drain(..) { 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(..) { 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(); 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() .into_doc()
.render_colored(72, StandardStream::stdout(ColorChoice::Auto)) .render_colored(72, StandardStream::stdout(ColorChoice::Auto))
.unwrap() .unwrap()

View File

@@ -159,17 +159,15 @@ impl From<Error> for Diagnostic<usize> {
}, },
Error::UnboundVariable(location, name) => match location { Error::UnboundVariable(location, name) => match location {
Location::Manufactured => Diagnostic::error().with_message(format!( Location::Manufactured => {
"Unbound variable '{}'", Diagnostic::error().with_message(format!("Unbound variable '{}'", name))
name }
)),
Location::InFile(file_id, offset) => Diagnostic::error() Location::InFile(file_id, offset) => Diagnostic::error()
.with_labels(vec![ .with_labels(vec![
Label::primary(*file_id, *offset..*offset).with_message("unbound here") Label::primary(*file_id, *offset..*offset).with_message("unbound here")
]) ])
.with_message(format!("Unbound variable '{}'", name)), .with_message(format!("Unbound variable '{}'", name)),
},
}
} }
} }
} }

View File

@@ -1,12 +1,13 @@
use codespan_reporting::files::SimpleFiles;
use crate::asts::hil; use crate::asts::hil;
use crate::errors::Error; use crate::errors::Error;
use crate::syntax; use crate::syntax;
use crate::syntax::Location; use crate::syntax::Location;
use crate::variable_map::VariableMap; use crate::variable_map::VariableMap;
use crate::warnings::Warning; use crate::warnings::Warning;
use codespan_reporting::files::SimpleFiles;
use std::fs; use std::fs;
mod hil_to_lil;
mod syntax_to_hil; mod syntax_to_hil;
pub struct PassResult<T> { pub struct PassResult<T> {
@@ -15,9 +16,9 @@ pub struct PassResult<T> {
pub errors: Vec<Error>, pub errors: Vec<Error>,
} }
impl<T,E> From<E> for PassResult<Option<T>> impl<T, E> From<E> for PassResult<Option<T>>
where where
Error: From<E> Error: From<E>,
{ {
fn from(x: E) -> Self { fn from(x: E) -> Self {
PassResult { PassResult {

100
src/passes/hil_to_lil.rs Normal file
View File

@@ -0,0 +1,100 @@
use crate::asts::hil;
use crate::asts::lil;
use crate::variable_map::VariableMap;
impl<Annotation: Clone> lil::Program<Annotation> {
pub fn convert(hil_program: hil::Program<Annotation>, 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<Annotation: Clone> lil::Statement<Annotation> {
fn convert(
hil_statement: hil::Statement<Annotation>,
variable_map: &mut VariableMap,
) -> Vec<Self> {
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)],
}
}
}

View File

@@ -44,7 +44,11 @@ impl hil::Statement<Location> {
if let Some(var) = var_map.get_variable(&variable_name) { if let Some(var) = var_map.get_variable(&variable_name) {
if let Some(orig_loc) = var_map.get_binding_site(var) { 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 { } else {
errors.push(Error::BindingSiteFailure( errors.push(Error::BindingSiteFailure(
loc.clone(), loc.clone(),
@@ -65,7 +69,8 @@ impl hil::Statement<Location> {
} }
} }
syntax::Statement::Print(variable_loc, variable_name) => match var_map.get_variable(&variable_name) { syntax::Statement::Print(variable_loc, variable_name) => {
match var_map.get_variable(&variable_name) {
None => PassResult { None => PassResult {
result: hil::Statement::Print(Location::Manufactured, 0), result: hil::Statement::Print(Location::Manufactured, 0),
warnings: vec![], warnings: vec![],
@@ -80,6 +85,7 @@ impl hil::Statement<Location> {
} }
} }
} }
}
} }
impl hil::Expression<Location> { impl hil::Expression<Location> {
@@ -106,7 +112,7 @@ impl hil::Expression<Location> {
warnings: vec![], warnings: vec![],
errors: vec![], errors: vec![],
}, },
} },
syntax::Expression::Primitive(location, name, mut exprs) => { syntax::Expression::Primitive(location, name, mut exprs) => {
let mut args = vec![]; let mut args = vec![];
@@ -145,8 +151,7 @@ impl hil::Expression<Location> {
impl From<syntax::Value> for hil::Value { impl From<syntax::Value> for hil::Value {
fn from(x: syntax::Value) -> hil::Value { fn from(x: syntax::Value) -> hil::Value {
match x { match x {
syntax::Value::Number(base, value) => syntax::Value::Number(base, value) => hil::Value::Number(base, value),
hil::Value::Number(base, value),
} }
} }
} }

View File

@@ -54,4 +54,16 @@ impl VariableMap {
} }
None None
} }
pub fn gensym(&mut self) -> Variable {
let result = self.next_index;
self.next_index += 1;
self.map.insert(
result,
VariableInfo::new(format!("<x:{}>", result), Location::Manufactured),
);
result
}
} }