Add a conversion from our high-level IR to a lower-level one.
This commit is contained in:
@@ -1 +1,2 @@
|
||||
pub mod hil;
|
||||
pub mod lil;
|
||||
|
||||
@@ -75,12 +75,14 @@ pub enum Expression<Annotation> {
|
||||
Primitive(Annotation, Primitive, Vec<Expression<Annotation>>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum Primitive {
|
||||
Plus,
|
||||
Minus,
|
||||
Times,
|
||||
Divide,
|
||||
impl<Annotation: Clone> Expression<Annotation> {
|
||||
pub fn annotation(&self) -> Annotation {
|
||||
match self {
|
||||
Expression::Value(a, _) => a.clone(),
|
||||
Expression::Reference(a, _) => a.clone(),
|
||||
Expression::Primitive(a, _, _) => a.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
where
|
||||
A: 'a,
|
||||
|
||||
153
src/asts/lil.rs
Normal file
153
src/asts/lil.rs
Normal 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),
|
||||
}
|
||||
}
|
||||
}
|
||||
23
src/bin.rs
23
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()
|
||||
|
||||
@@ -159,17 +159,15 @@ impl From<Error> for Diagnostic<usize> {
|
||||
},
|
||||
|
||||
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)),
|
||||
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<T> {
|
||||
@@ -17,7 +18,7 @@ pub struct PassResult<T> {
|
||||
|
||||
impl<T, E> From<E> for PassResult<Option<T>>
|
||||
where
|
||||
Error: From<E>
|
||||
Error: From<E>,
|
||||
{
|
||||
fn from(x: E) -> Self {
|
||||
PassResult {
|
||||
|
||||
100
src/passes/hil_to_lil.rs
Normal file
100
src/passes/hil_to_lil.rs
Normal 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)],
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,11 @@ impl hil::Statement<Location> {
|
||||
|
||||
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,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 {
|
||||
result: hil::Statement::Print(Location::Manufactured, 0),
|
||||
warnings: vec![],
|
||||
@@ -81,6 +86,7 @@ impl hil::Statement<Location> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl hil::Expression<Location> {
|
||||
fn convert(
|
||||
@@ -106,7 +112,7 @@ impl hil::Expression<Location> {
|
||||
warnings: vec![],
|
||||
errors: vec![],
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
syntax::Expression::Primitive(location, name, mut exprs) => {
|
||||
let mut args = vec![];
|
||||
@@ -145,8 +151,7 @@ impl hil::Expression<Location> {
|
||||
impl From<syntax::Value> 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),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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!("<x:{}>", result), Location::Manufactured),
|
||||
);
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user