A fairly major refactor / simplification.
This commit is contained in:
@@ -1,2 +0,0 @@
|
||||
pub mod hil;
|
||||
pub mod lil;
|
||||
183
src/asts/hil.rs
183
src/asts/hil.rs
@@ -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())
|
||||
}
|
||||
}
|
||||
149
src/asts/lil.rs
149
src/asts/lil.rs
@@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
150
src/backend/into_crane.rs
Normal 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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
122
src/bin.rs
122
src/bin.rs
@@ -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> {
|
||||
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);
|
||||
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 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();
|
||||
|
||||
for error in hil_conversion_result.errors.drain(..) {
|
||||
term::emit(
|
||||
&mut writer.lock(),
|
||||
&config,
|
||||
&file_database,
|
||||
&Diagnostic::from(error),
|
||||
&Diagnostic::from(e),
|
||||
)
|
||||
.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)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
114
src/errors.rs
114
src/errors.rs
@@ -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
5
src/ir.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
mod ast;
|
||||
mod from_syntax;
|
||||
mod strings;
|
||||
|
||||
pub use ast::*;
|
||||
172
src/ir/ast.rs
Normal file
172
src/ir/ast.rs
Normal 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
65
src/ir/from_syntax.rs
Normal 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
36
src/ir/strings.rs
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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
36
src/pass_result.rs
Normal 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
124
src/pass_result/errors.rs
Normal 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)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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)]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
49
src/syntax/simplify.rs
Normal 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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user