A fairly major refactor / simplification.

This commit is contained in:
2023-01-16 20:11:06 -08:00
parent 2e82fcf343
commit afcf3c65cd
26 changed files with 800 additions and 1132 deletions

View File

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

View File

@@ -1,183 +0,0 @@
use crate::variable_map::{Variable, VariableMap};
use pretty::{DocAllocator, DocBuilder, Pretty};
#[derive(Debug, PartialEq)]
pub struct Program<Annotation> {
pub statements: Vec<Statement<Annotation>>,
}
impl<Annotation> Program<Annotation> {
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<Annotation> {
Binding(Annotation, Variable, Expression<Annotation>),
Print(Annotation, Variable),
}
impl<Annotation> Statement<Annotation> {
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("<unknown>");
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("<unknown>");
allocator
.text("print")
.append(allocator.space())
.append(allocator.text(name.to_string()))
}
}
}
}
#[derive(Debug, PartialEq)]
pub enum Expression<Annotation> {
Value(Annotation, Value),
Reference(Annotation, Variable),
Primitive(Annotation, Primitive, Vec<Expression<Annotation>>),
}
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> {
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("<unknown>");
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<u8>, 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())
}
}

View File

@@ -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<Annotation> {
pub statements: Vec<Statement<Annotation>>,
pub strings: HashSet<ArcIntern<String>>,
pub variable_info: VariableMap,
}
impl<Annotation> Program<Annotation> {
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<Annotation> {
VariableBinding(Annotation, Variable, SimpleExpression<Annotation>),
ResultBinding(Annotation, Variable, Primitive<Annotation>),
Print(Annotation, ArcIntern<String>, 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, _val) => allocator
.text("print")
.append(allocator.space())
.append(allocator.text(var.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

@@ -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<BackendError> for Diagnostic<usize> {
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<Program, BackendError> {
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<Vec<u8>, object::write::Error> {
self.module.finish().emit()
}
}

150
src/backend/into_crane.rs Normal file
View File

@@ -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<ArcIntern<String>, GlobalValue>;
impl Program {
pub fn into_cranelift<M: Module>(
mut self,
module: &mut M,
rtfuns: &RuntimeFunctions,
) -> Result<ModuleCompiledFunction, super::BackendError> {
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<M: Module>(
&self,
module: &mut M,
func: &mut Function,
) -> Result<StringTable, ModuleError> {
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<ArcIntern<String>, 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))
}
}
}
}

View File

@@ -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<MainError> for Diagnostic<usize> {
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<String, String>) -> 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();
}
}
}

View File

@@ -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<Error> for Diagnostic<usize> {
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)),
}
}
}

5
src/ir.rs Normal file
View File

@@ -0,0 +1,5 @@
mod ast;
mod from_syntax;
mod strings;
pub use ast::*;

172
src/ir/ast.rs Normal file
View File

@@ -0,0 +1,172 @@
use internment::ArcIntern;
use pretty::{DocAllocator, Pretty};
use crate::syntax::Location;
type Variable = ArcIntern<String>;
pub struct Program {
pub statements: Vec<Statement>,
}
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<ValueOrRef>),
}
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<Self, Self::Error> {
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<String>),
}
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<u8>, 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)
}
}
}
}

65
src/ir/from_syntax.rs Normal file
View File

@@ -0,0 +1,65 @@
use internment::ArcIntern;
use crate::ir::ast as ir;
use crate::syntax::ast as syntax;
impl From<syntax::Program> for ir::Program {
fn from(mut value: syntax::Program) -> Self {
ir::Program {
statements: value.statements.drain(..).map(Into::into).collect(),
}
}
}
impl From<syntax::Statement> 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<syntax::Expression> 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<syntax::Expression> 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<syntax::Value> for ir::Value {
fn from(x: syntax::Value) -> Self {
match x {
syntax::Value::Number(base, value) => ir::Value::Number(base, value),
}
}
}

36
src/ir/strings.rs Normal file
View File

@@ -0,0 +1,36 @@
use super::ast::{Expression, Program, Statement};
use internment::ArcIntern;
use std::collections::HashSet;
impl Program {
pub fn strings(&self) -> HashSet<ArcIntern<String>> {
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<ArcIntern<String>>) {
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<ArcIntern<String>>) {
// nothing has a string in here, at the moment
}
}

View File

@@ -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;

36
src/pass_result.rs Normal file
View File

@@ -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<T> {
pub result: Option<T>,
pub warnings: Vec<Warning>,
pub errors: Vec<Error>,
}
impl<T> From<ParserError> for PassResult<T> {
fn from(value: ParserError) -> Self {
PassResult {
result: None,
warnings: vec![],
errors: vec![Error::ParserError(value)],
}
}
}
impl<T, E> From<E> for PassResult<T>
where
Error: From<E>,
{
fn from(x: E) -> Self {
PassResult {
result: None,
warnings: vec![],
errors: vec![Error::from(x)],
}
}
}

124
src/pass_result/errors.rs Normal file
View File

@@ -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<usize> {
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<Error> for Diagnostic<usize> {
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)),
}
}
}

View File

@@ -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<T> {
pub result: T,
pub warnings: Vec<Warning>,
pub errors: Vec<Error>,
}
impl<T> From<ParserError> for PassResult<Option<T>> {
fn from(value: ParserError) -> Self {
PassResult {
result: None,
warnings: vec![],
errors: vec![Error::ParserError(value)],
}
}
}
impl<T> From<std::io::Error> for PassResult<Option<T>> {
fn from(value: std::io::Error) -> Self {
PassResult {
result: None,
warnings: vec![],
errors: vec![Error::IOError(value)],
}
}
}
impl<T> From<codespan_reporting::files::Error> for PassResult<Option<T>> {
fn from(value: codespan_reporting::files::Error) -> Self {
PassResult {
result: None,
warnings: vec![],
errors: vec![Error::InternalFileDBError(value)],
}
}
}
impl<T, E> From<E> for PassResult<Option<T>>
where
Error: From<E>,
{
fn from(x: E) -> Self {
PassResult {
result: None,
warnings: vec![],
errors: vec![Error::from(x)],
}
}
}
pub fn run_front_end(
file_database: &mut SimpleFiles<String, String>,
initial_file_name: &str,
) -> PassResult<Option<(hil::Program<Location>, 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,
}
}

View File

@@ -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<Annotation: Clone> lil::Program<Annotation> {
pub fn convert(hil_program: hil::Program<Annotation>, 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<Annotation: Clone> lil::Statement<Annotation> {
fn convert(
hil_statement: hil::Statement<Annotation>,
variable_map: &mut VariableMap,
strings: &mut HashSet<ArcIntern<String>>,
) -> 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,
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)]
}
}
}
}

View File

@@ -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<Annotation> Program<Annotation> {
pub fn into_cranelift<M: Module>(
mut self,
module: &mut M,
rtfuns: &RuntimeFunctions,
) -> Result<ModuleCompiledFunction, BackendError> {
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<Annotation> SimpleExpression<Annotation> {
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)),
}
}
}

View File

@@ -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<Location> {
pub fn convert(
mut input: syntax::Program,
var_map: &mut VariableMap,
) -> PassResult<hil::Program<Location>> {
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<Location> {
fn convert(
input: syntax::Statement,
var_map: &mut VariableMap,
) -> PassResult<hil::Statement<Location>> {
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<Location> {
fn convert(
input: syntax::Expression,
var_map: &mut VariableMap,
) -> PassResult<hil::Expression<Location>> {
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<syntax::Value> for hil::Value {
fn from(x: syntax::Value) -> hil::Value {
match x {
syntax::Value::Number(base, value) => hil::Value::Number(base, value),
}
}
}

View File

@@ -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<String>),
#[error("Unrecognized token")]
UnrecognizedToken(Location, Location, Token, Vec<String>),
#[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<String, String>,
file_name: &str,
) -> Result<Self, ParserError> {
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<Program, ParserError> {
let lexer = Token::lexer(buffer)
.spanned()

View File

@@ -99,7 +99,7 @@ where
#[derive(Debug, PartialEq, Eq)]
pub enum Value {
Number(Option<u8>, i128),
Number(Option<u8>, i64),
}
impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Value

View File

@@ -20,7 +20,7 @@ extern {
"*" => Token::Operator('*'),
"/" => Token::Operator('/'),
"<num>" => Token::Number((<Option<u8>>,<i128>)),
"<num>" => Token::Number((<Option<u8>>,<i64>)),
"<var>" => Token::Variable(<ArcIntern<String>>),
}
}

49
src/syntax/simplify.rs Normal file
View File

@@ -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<Statement>, 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))
}
}
}
}

View File

@@ -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<u8>, i128)),
Number((Option<u8>, i64)),
#[regex(r"[a-z][a-zA-Z0-9_]*", |v| ArcIntern::new(v.slice().to_string()))]
Variable(ArcIntern<String>),
@@ -71,13 +71,13 @@ impl Token {
fn parse_number(
base: Option<u8>,
value: &Lexer<Token>,
) -> Result<(Option<u8>, i128), ParseIntError> {
) -> Result<(Option<u8>, 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))
}

View File

@@ -1,72 +0,0 @@
use crate::syntax::Location;
use std::collections::HashMap;
pub type Variable = usize;
#[derive(Clone)]
pub struct VariableMap {
map: HashMap<Variable, VariableInfo>,
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<Location> {
self.map.get(&var).map(|x| x.binding_location.clone())
}
pub fn get_variable(&self, name: &str) -> Option<usize> {
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!("<x:{}>", result), Location::manufactured()),
);
result
}
}