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 hil;
|
||||||
|
pub mod lil;
|
||||||
|
|||||||
@@ -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
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::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()
|
||||||
|
|||||||
@@ -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)),
|
||||||
|
},
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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> {
|
||||||
@@ -17,7 +18,7 @@ pub struct PassResult<T> {
|
|||||||
|
|
||||||
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
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(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![],
|
||||||
@@ -81,6 +86,7 @@ impl hil::Statement<Location> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl hil::Expression<Location> {
|
impl hil::Expression<Location> {
|
||||||
fn convert(
|
fn convert(
|
||||||
@@ -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),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user