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::files::SimpleFiles;
|
||||||
use codespan_reporting::term;
|
use codespan_reporting::term;
|
||||||
use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
|
use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
|
||||||
use cranelift_codegen::settings::{Configurable, SetError};
|
use cranelift_object::object;
|
||||||
use cranelift_codegen::{isa, settings, CodegenError};
|
|
||||||
use cranelift_module::{default_libcall_names, ModuleError};
|
use ngr::backend::BackendError;
|
||||||
use cranelift_object::{object, ObjectBuilder, ObjectModule};
|
use ngr::backend::Program as Cranelift;
|
||||||
use ngr::asts::lil;
|
use ngr::ir::Program as IR;
|
||||||
use ngr::passes::{run_front_end, BackendError};
|
use ngr::syntax::{ParserError, Program as Syntax};
|
||||||
use ngr::runtime::RuntimeFunctions;
|
|
||||||
use pretty::Arena;
|
|
||||||
use std::io;
|
|
||||||
use target_lexicon::Triple;
|
use target_lexicon::Triple;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
@@ -28,90 +25,57 @@ struct CommandLineArguments {
|
|||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
enum MainError {
|
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)]
|
#[error(transparent)]
|
||||||
Backend(#[from] BackendError),
|
Backend(#[from] BackendError),
|
||||||
|
#[error("Parser error")]
|
||||||
#[error(transparent)]
|
ParserError(#[from] ParserError),
|
||||||
SettingsError(#[from] SetError),
|
#[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> {
|
||||||
let args = CommandLineArguments::parse();
|
fn from(value: MainError) -> Self {
|
||||||
let mut file_database = SimpleFiles::new();
|
match value {
|
||||||
let initial_file_name = &args.file;
|
MainError::Backend(be) => be.into(),
|
||||||
let mut hil_conversion_result = run_front_end(&mut file_database, initial_file_name);
|
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 writer = StandardStream::stderr(ColorChoice::Auto);
|
||||||
let config = codespan_reporting::term::Config::default();
|
let config = codespan_reporting::term::Config::default();
|
||||||
|
|
||||||
for error in hil_conversion_result.errors.drain(..) {
|
|
||||||
term::emit(
|
term::emit(
|
||||||
&mut writer.lock(),
|
&mut writer.lock(),
|
||||||
&config,
|
&config,
|
||||||
&file_database,
|
&file_database,
|
||||||
&Diagnostic::from(error),
|
&Diagnostic::from(e),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.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 backend;
|
||||||
pub mod errors;
|
pub mod ir;
|
||||||
pub mod passes;
|
pub mod pass_result;
|
||||||
pub mod runtime;
|
|
||||||
pub mod syntax;
|
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 lalrpop_util::lalrpop_mod;
|
||||||
use logos::Logos;
|
use logos::Logos;
|
||||||
|
|
||||||
mod location;
|
mod location;
|
||||||
|
mod simplify;
|
||||||
mod tokens;
|
mod tokens;
|
||||||
lalrpop_mod!(
|
lalrpop_mod!(
|
||||||
#[allow(clippy::just_underscores_and_digits)]
|
#[allow(clippy::just_underscores_and_digits)]
|
||||||
parser,
|
parser,
|
||||||
"/syntax/parser.rs"
|
"/syntax/parser.rs"
|
||||||
);
|
);
|
||||||
mod ast;
|
pub mod ast;
|
||||||
|
|
||||||
pub use crate::syntax::ast::*;
|
pub use crate::syntax::ast::*;
|
||||||
pub use crate::syntax::location::Location;
|
pub use crate::syntax::location::Location;
|
||||||
@@ -17,14 +19,24 @@ pub use crate::syntax::tokens::{LexerError, Token};
|
|||||||
use lalrpop_util::ParseError;
|
use lalrpop_util::ParseError;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Error)]
|
||||||
pub enum ParserError {
|
pub enum ParserError {
|
||||||
|
#[error("Invalid token")]
|
||||||
InvalidToken(Location),
|
InvalidToken(Location),
|
||||||
|
#[error("Unrecognized EOF")]
|
||||||
UnrecognizedEOF(Location, Vec<String>),
|
UnrecognizedEOF(Location, Vec<String>),
|
||||||
|
#[error("Unrecognized token")]
|
||||||
UnrecognizedToken(Location, Location, Token, Vec<String>),
|
UnrecognizedToken(Location, Location, Token, Vec<String>),
|
||||||
|
#[error("Extra token")]
|
||||||
ExtraToken(Location, Token, Location),
|
ExtraToken(Location, Token, Location),
|
||||||
|
#[error("Lexing failure")]
|
||||||
LexFailure(Location),
|
LexFailure(Location),
|
||||||
|
#[error("File database error")]
|
||||||
|
FileDatabaseError(#[from] codespan_reporting::files::Error),
|
||||||
|
#[error("Read error")]
|
||||||
|
ReadError(#[from] std::io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ParserError {
|
impl ParserError {
|
||||||
@@ -62,6 +74,16 @@ impl ParserError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Program {
|
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> {
|
pub fn parse(file_idx: usize, buffer: &str) -> Result<Program, ParserError> {
|
||||||
let lexer = Token::lexer(buffer)
|
let lexer = Token::lexer(buffer)
|
||||||
.spanned()
|
.spanned()
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ where
|
|||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
Number(Option<u8>, i128),
|
Number(Option<u8>, i64),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Value
|
impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Value
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ extern {
|
|||||||
"*" => Token::Operator('*'),
|
"*" => Token::Operator('*'),
|
||||||
"/" => Token::Operator('/'),
|
"/" => Token::Operator('/'),
|
||||||
|
|
||||||
"<num>" => Token::Number((<Option<u8>>,<i128>)),
|
"<num>" => Token::Number((<Option<u8>>,<i64>)),
|
||||||
"<var>" => Token::Variable(<ArcIntern<String>>),
|
"<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"0d[0-9]+", |v| parse_number(Some(10), v))]
|
||||||
#[regex(r"0x[0-9a-fA-F]+", |v| parse_number(Some(16), v))]
|
#[regex(r"0x[0-9a-fA-F]+", |v| parse_number(Some(16), v))]
|
||||||
#[regex(r"[0-9]+", |v| parse_number(None, 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()))]
|
#[regex(r"[a-z][a-zA-Z0-9_]*", |v| ArcIntern::new(v.slice().to_string()))]
|
||||||
Variable(ArcIntern<String>),
|
Variable(ArcIntern<String>),
|
||||||
@@ -71,13 +71,13 @@ impl Token {
|
|||||||
fn parse_number(
|
fn parse_number(
|
||||||
base: Option<u8>,
|
base: Option<u8>,
|
||||||
value: &Lexer<Token>,
|
value: &Lexer<Token>,
|
||||||
) -> Result<(Option<u8>, i128), ParseIntError> {
|
) -> Result<(Option<u8>, i64), ParseIntError> {
|
||||||
let (radix, strval) = match base {
|
let (radix, strval) = match base {
|
||||||
None => (10, value.slice()),
|
None => (10, value.slice()),
|
||||||
Some(radix) => (radix, &value.slice()[2..]),
|
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))
|
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