λ Support functions! #5

Open
acw wants to merge 59 commits from awick/functions into develop
13 changed files with 295 additions and 460 deletions
Showing only changes of commit e9fbd275a2 - Show all commits

View File

@@ -30,6 +30,8 @@ target-lexicon = "0.12.14"
tempfile = "3.10.0" tempfile = "3.10.0"
thiserror = "1.0.57" thiserror = "1.0.57"
anyhow = "1.0.80" anyhow = "1.0.80"
tracing = "0.1.40"
tracing-subscriber = { version = "0.3.18", features = ["time", "json", "env-filter"] }
[build-dependencies] [build-dependencies]
lalrpop = "0.20.0" lalrpop = "0.20.0"

View File

@@ -83,7 +83,7 @@ impl<M: Module> Backend<M> {
true, true,
false, false,
)?; )?;
println!("defining {} with primitive type {}", top_level_name, pt); tracing::info!(name = %top_level_name, data_type = %pt, "defining top-level data");
self.module.define_data(data_id, &pt.blank_data())?; self.module.define_data(data_id, &pt.blank_data())?;
self.defined_symbols self.defined_symbols
.insert(top_level_name, (data_id, pt.into())); .insert(top_level_name, (data_id, pt.into()));
@@ -125,7 +125,7 @@ impl<M: Module> Backend<M> {
argument_types: Vec<Type>, argument_types: Vec<Type>,
return_type: Type, return_type: Type,
) -> Result<FuncId, cranelift_module::ModuleError> { ) -> Result<FuncId, cranelift_module::ModuleError> {
println!("Declaring {:?} function {}", linkage, name); tracing::info!(linkage = ?linkage, name, "Declaring function");
let basic_signature = Signature { let basic_signature = Signature {
params: argument_types params: argument_types
.iter() .iter()
@@ -148,6 +148,7 @@ impl<M: Module> Backend<M> {
} }
/// Compile the given function. /// Compile the given function.
#[tracing::instrument(level = "debug", skip(self, variables, body))]
pub fn compile_function( pub fn compile_function(
&mut self, &mut self,
variables: &mut HashMap<Variable, ReferenceBuilder>, variables: &mut HashMap<Variable, ReferenceBuilder>,
@@ -156,22 +157,6 @@ impl<M: Module> Backend<M> {
return_type: Type, return_type: Type,
body: Expression<Type>, body: Expression<Type>,
) -> Result<FuncId, BackendError> { ) -> Result<FuncId, BackendError> {
println!("Compiling function {}", function_name);
{
use pretty::{DocAllocator, Pretty};
let allocator = pretty::BoxAllocator;
allocator
.text("Function body:")
.append(allocator.hardline())
.append(body.pretty(&allocator))
.append(allocator.hardline())
.1
.render_colored(
70,
pretty::termcolor::StandardStream::stdout(pretty::termcolor::ColorChoice::Auto),
)
.expect("rendering works");
}
// reset the next variable counter. this value shouldn't matter; hopefully // reset the next variable counter. this value shouldn't matter; hopefully
// we won't be using close to 2^32 variables! // we won't be using close to 2^32 variables!
self.reset_local_variable_tracker(); self.reset_local_variable_tracker();

View File

@@ -14,6 +14,7 @@ struct CommandLineArguments {
} }
fn main() { fn main() {
tracing_subscriber::fmt::init();
let args = CommandLineArguments::parse(); let args = CommandLineArguments::parse();
let mut compiler = ngr::Compiler::default(); let mut compiler = ngr::Compiler::default();

View File

@@ -3,6 +3,7 @@ use rustyline::error::ReadlineError;
use rustyline::DefaultEditor; use rustyline::DefaultEditor;
fn main() -> Result<(), BackendError> { fn main() -> Result<(), BackendError> {
tracing_subscriber::fmt::init();
let mut editor = DefaultEditor::new().expect("rustyline works"); let mut editor = DefaultEditor::new().expect("rustyline works");
let mut line_no = 0; let mut line_no = 0;
let mut state = ngr::REPL::default(); let mut state = ngr::REPL::default();
@@ -19,7 +20,7 @@ fn main() -> Result<(), BackendError> {
// it's not clear to me what this could be, but OK // it's not clear to me what this could be, but OK
Err(ReadlineError::Io(e)) => { Err(ReadlineError::Io(e)) => {
eprintln!("IO error: {}", e); tracing::error!(error = %e, "IO error");
break; break;
} }
@@ -31,7 +32,7 @@ fn main() -> Result<(), BackendError> {
// what would cause this, but ... // what would cause this, but ...
#[cfg(not(windows))] #[cfg(not(windows))]
Err(ReadlineError::Errno(e)) => { Err(ReadlineError::Errno(e)) => {
eprintln!("Unknown syscall error: {}", e); tracing::error!(error = %e, "Unknown syscall.");
break; break;
} }
@@ -41,7 +42,7 @@ fn main() -> Result<(), BackendError> {
// Why on earth are there so many error types? // Why on earth are there so many error types?
Err(e) => { Err(e) => {
eprintln!("Unknown internal error: {}", e); tracing::error!(error = %e, "Unknown internal error");
break; break;
} }
} }

View File

@@ -42,6 +42,7 @@ fn jit(ir: ngr::ir::Program<ngr::ir::Type>) -> Result<fn(), ngr::backend::Backen
} }
fn main() { fn main() {
tracing_subscriber::fmt::init();
let cli = CommandLineArguments::parse(); let cli = CommandLineArguments::parse();
let mut file_database = SimpleFiles::new(); let mut file_database = SimpleFiles::new();
let mut console = StandardStream::stdout(pretty::termcolor::ColorChoice::Auto); let mut console = StandardStream::stdout(pretty::termcolor::ColorChoice::Auto);
@@ -72,7 +73,7 @@ fn main() {
if cli.interpreter == Interpreter::Syntax { if cli.interpreter == Interpreter::Syntax {
match syntax.eval() { match syntax.eval() {
Err(e) => println!("Evaluation error: {}", e), Err(e) => tracing::error!(error = %e, "Evaluation error"),
Ok(v) => print_result(v), Ok(v) => print_result(v),
} }
return; return;
@@ -98,7 +99,7 @@ fn main() {
if cli.interpreter == Interpreter::IR { if cli.interpreter == Interpreter::IR {
match ir.eval() { match ir.eval() {
Err(e) => println!("Evaluation error: {}", e), Err(e) => tracing::error!(error = %e, "Evaluation error"),
Ok(v) => print_result(v), Ok(v) => print_result(v),
} }
return; return;

View File

@@ -2,6 +2,7 @@ use crate::{
eval::{PrimOpError, Value}, eval::{PrimOpError, Value},
syntax::ConstantType, syntax::ConstantType,
}; };
use pretty::{Arena, DocAllocator, DocBuilder};
use std::{fmt::Display, str::FromStr}; use std::{fmt::Display, str::FromStr};
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
@@ -17,19 +18,27 @@ pub enum PrimitiveType {
I64, I64,
} }
impl PrimitiveType {
pub fn pretty<'a>(&self, allocator: &'a Arena<'a, ()>) -> DocBuilder<'a, Arena<'a, ()>> {
match self {
PrimitiveType::Void => allocator.text("void"),
PrimitiveType::I8 => allocator.text("i8"),
PrimitiveType::I16 => allocator.text("i16"),
PrimitiveType::I32 => allocator.text("i32"),
PrimitiveType::I64 => allocator.text("i64"),
PrimitiveType::U8 => allocator.text("u8"),
PrimitiveType::U16 => allocator.text("u16"),
PrimitiveType::U32 => allocator.text("u32"),
PrimitiveType::U64 => allocator.text("u64"),
}
}
}
impl Display for PrimitiveType { impl Display for PrimitiveType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { let arena = Arena::new();
PrimitiveType::Void => write!(f, "void"), let doc = self.pretty(&arena);
PrimitiveType::I8 => write!(f, "i8"), doc.render_fmt(72, f)
PrimitiveType::I16 => write!(f, "i16"),
PrimitiveType::I32 => write!(f, "i32"),
PrimitiveType::I64 => write!(f, "i64"),
PrimitiveType::U8 => write!(f, "u8"),
PrimitiveType::U16 => write!(f, "u16"),
PrimitiveType::U32 => write!(f, "u32"),
PrimitiveType::U64 => write!(f, "u64"),
}
} }
} }

View File

@@ -15,6 +15,7 @@
mod arbitrary; mod arbitrary;
pub mod ast; pub mod ast;
mod eval; mod eval;
mod pretty;
mod strings; mod strings;
mod top_level; mod top_level;

View File

@@ -1,13 +1,8 @@
use crate::{ use crate::eval::PrimitiveType;
eval::PrimitiveType, use crate::syntax::{ConstantType, Location};
syntax::{self, ConstantType, Location},
util::pretty::{pretty_comma_separated, PrettySymbol},
};
use internment::ArcIntern; use internment::ArcIntern;
use pretty::{BoxAllocator, DocAllocator, Pretty};
use proptest::arbitrary::Arbitrary; use proptest::arbitrary::Arbitrary;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::fmt;
use std::str::FromStr; use std::str::FromStr;
use std::sync::atomic::AtomicUsize; use std::sync::atomic::AtomicUsize;
@@ -58,29 +53,6 @@ pub struct Program<Type> {
pub(crate) items: Vec<TopLevel<Type>>, pub(crate) items: Vec<TopLevel<Type>>,
} }
impl<'a, 'b, D, A, Type> Pretty<'a, D, A> for &'b Program<Type>
where
A: 'a,
D: ?Sized + DocAllocator<'a, A> + 'a,
&'b Type: Pretty<'a, D, A>,
pretty::DocBuilder<'a, D, A>: Clone,
{
fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> {
let mut result = allocator.nil();
for stmt in self.items.iter() {
// there's probably a better way to do this, rather than constantly
// adding to the end, but this works.
result = result
.append(stmt.pretty(allocator))
.append(allocator.text(";"))
.append(allocator.hardline());
}
result
}
}
impl Arbitrary for Program<Type> { impl Arbitrary for Program<Type> {
type Parameters = (); type Parameters = ();
type Strategy = ProgramGenerator; type Strategy = ProgramGenerator;
@@ -114,35 +86,6 @@ impl<T: Clone + TypeWithVoid + TypeWithFunction> TopLevel<T> {
} }
} }
impl<'a, 'b, D, A, Type> Pretty<'a, D, A> for &'b TopLevel<Type>
where
A: 'a,
D: ?Sized + DocAllocator<'a, A> + 'a,
&'b Type: Pretty<'a, D, A>,
pretty::DocBuilder<'a, D, A>: Clone,
{
fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> {
match self {
TopLevel::Function(name, args, _, expr) => allocator
.text("function")
.append(allocator.space())
.append(allocator.text(name.as_ref().to_string()))
.append(allocator.space())
.append(
pretty_comma_separated(
allocator,
&args.iter().map(|(x, _)| PrettySymbol::from(x)).collect(),
)
.parens(),
)
.append(allocator.space())
.append(expr.pretty(allocator)),
TopLevel::Statement(stmt) => stmt.pretty(allocator),
}
}
}
/// The representation of an expression. /// The representation of an expression.
/// ///
/// Note that expressions, like everything else in this syntax tree, /// Note that expressions, like everything else in this syntax tree,
@@ -196,94 +139,6 @@ impl<Type: Clone + TypeWithVoid> Expression<Type> {
} }
} }
impl<'a, 'b, D, A, Type> Pretty<'a, D, A> for &'b Expression<Type>
where
A: 'a,
D: ?Sized + DocAllocator<'a, A> + 'a,
&'b Type: Pretty<'a, D, A>,
pretty::DocBuilder<'a, D, A>: Clone,
{
fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> {
match self {
Expression::Atomic(x) => x.pretty(allocator),
Expression::Cast(_, t, e) => allocator
.text("<")
.append(t.pretty(allocator))
.append(allocator.text(">"))
.append(e.pretty(allocator)),
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()))
}
Expression::Call(_, _, fun, args) => {
let args = args.iter().map(|x| x.pretty(allocator));
let comma_sepped_args =
allocator.intersperse(args, crate::syntax::pretty::CommaSep {});
fun.pretty(allocator).append(comma_sepped_args.parens())
}
Expression::Block(_, _, exprs) => match exprs.split_last() {
None => allocator.text("()"),
Some((last, &[])) => last.pretty(allocator),
Some((last, start)) => {
let mut result = allocator.text("{").append(allocator.hardline());
let starts = start.iter().map(|x| {
x.pretty(allocator)
.append(allocator.text(";"))
.append(allocator.hardline())
.indent(4)
});
let last = last
.pretty(allocator)
.append(allocator.hardline())
.indent(4);
for start in starts {
result = result.append(start);
}
result.append(last).append(allocator.text("}"))
}
},
Expression::Print(_, var) => allocator
.text("print")
.append(allocator.space())
.append(var.pretty(allocator)),
Expression::Bind(_, var, ty, expr) => allocator
.text(var.as_ref().to_string())
.append(allocator.space())
.append(allocator.text(":"))
.append(allocator.space())
.append(ty.pretty(allocator))
.append(allocator.space())
.append(allocator.text("="))
.append(allocator.space())
.append(expr.pretty(allocator)),
}
}
}
impl Expression<Type> {
pub fn to_pretty(&self) -> String {
let arena = pretty::Arena::<()>::new();
let doc = self.pretty(&arena);
let mut output_bytes = Vec::new();
doc.render(72, &mut output_bytes).unwrap();
String::from_utf8(output_bytes).expect("pretty generates valid utf-8")
}
}
/// A type representing the primitives allowed in the language. /// A type representing the primitives allowed in the language.
/// ///
/// Having this as an enumeration avoids a lot of "this should not happen" /// Having this as an enumeration avoids a lot of "this should not happen"
@@ -312,27 +167,6 @@ impl FromStr for Primitive {
} }
} }
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("/"),
}
}
}
impl fmt::Display for Primitive {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
<&Primitive as Pretty<'_, BoxAllocator, ()>>::pretty(self, &BoxAllocator).render_fmt(72, f)
}
}
/// An expression that is always either a value or a reference. /// An expression that is always either a value or a reference.
/// ///
/// This is the type used to guarantee that we don't nest expressions /// This is the type used to guarantee that we don't nest expressions
@@ -344,19 +178,6 @@ pub enum ValueOrRef<Type> {
Ref(Location, Type, ArcIntern<String>), Ref(Location, Type, ArcIntern<String>),
} }
impl<'a, 'b, D, A, Type> Pretty<'a, D, A> for &'b ValueOrRef<Type>
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()),
}
}
}
impl<Type: Clone> ValueOrRef<Type> { impl<Type: Clone> ValueOrRef<Type> {
pub fn type_of(&self) -> Type { pub fn type_of(&self) -> Type {
match self { match self {
@@ -408,50 +229,6 @@ impl Value {
} }
} }
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> {
let pretty_internal = |opt_base: &Option<u8>, x, t| {
syntax::Value::Number(*opt_base, Some(t), x).pretty(allocator)
};
let pretty_internal_signed = |opt_base, x: i64, t| {
let base = pretty_internal(opt_base, x.unsigned_abs(), t);
allocator.text("-").append(base)
};
match self {
Value::I8(opt_base, value) => {
pretty_internal_signed(opt_base, *value as i64, ConstantType::I8)
}
Value::I16(opt_base, value) => {
pretty_internal_signed(opt_base, *value as i64, ConstantType::I16)
}
Value::I32(opt_base, value) => {
pretty_internal_signed(opt_base, *value as i64, ConstantType::I32)
}
Value::I64(opt_base, value) => {
pretty_internal_signed(opt_base, *value, ConstantType::I64)
}
Value::U8(opt_base, value) => {
pretty_internal(opt_base, *value as u64, ConstantType::U8)
}
Value::U16(opt_base, value) => {
pretty_internal(opt_base, *value as u64, ConstantType::U16)
}
Value::U32(opt_base, value) => {
pretty_internal(opt_base, *value as u64, ConstantType::U32)
}
Value::U64(opt_base, value) => pretty_internal(opt_base, *value, ConstantType::U64),
Value::Void => allocator.text("<void>"),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
pub enum Type { pub enum Type {
Primitive(PrimitiveType), Primitive(PrimitiveType),
@@ -466,46 +243,6 @@ impl Type {
} }
} }
impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Type
where
A: 'a,
D: ?Sized + DocAllocator<'a, A>,
{
fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> {
match self {
Type::Primitive(pt) => allocator.text(format!("{}", pt)),
Type::Function(args, rettype) => {
pretty_comma_separated(allocator, &args.iter().collect())
.parens()
.append(allocator.space())
.append(allocator.text("->"))
.append(allocator.space())
.append(rettype.pretty(allocator))
}
}
}
}
impl fmt::Display for Type {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Type::Primitive(pt) => pt.fmt(f),
Type::Function(args, ret) => {
write!(f, "(")?;
let mut argiter = args.iter().peekable();
while let Some(arg) = argiter.next() {
arg.fmt(f)?;
if argiter.peek().is_some() {
write!(f, ",")?;
}
}
write!(f, "->")?;
ret.fmt(f)
}
}
}
}
impl From<PrimitiveType> for Type { impl From<PrimitiveType> for Type {
fn from(value: PrimitiveType) -> Self { fn from(value: PrimitiveType) -> Self {
Type::Primitive(value) Type::Primitive(value)
@@ -530,55 +267,6 @@ pub enum TypeOrVar {
Function(Vec<TypeOrVar>, Box<TypeOrVar>), Function(Vec<TypeOrVar>, Box<TypeOrVar>),
} }
impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b TypeOrVar
where
A: 'a,
D: ?Sized + DocAllocator<'a, A>,
{
fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> {
match self {
TypeOrVar::Primitive(x) => allocator.text(format!("{}", x)),
TypeOrVar::Variable(_, x) => allocator.text(x.to_string()),
TypeOrVar::Function(args, rettype) => {
pretty_comma_separated(allocator, &args.iter().collect())
.parens()
.append(allocator.space())
.append(allocator.text("->"))
.append(allocator.space())
.append(rettype.pretty(allocator))
}
}
}
}
impl fmt::Display for TypeOrVar {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TypeOrVar::Primitive(x) => x.fmt(f),
TypeOrVar::Variable(_, v) => write!(f, "{}", v),
TypeOrVar::Function(args, rettype) => {
write!(f, "<function:")?;
match args.split_last() {
None => write!(f, "()")?,
Some((single, &[])) => {
write!(f, "({})", single)?;
}
Some((last_one, rest)) => {
write!(f, "(")?;
for arg in rest.iter() {
write!(f, "{}, ", arg)?;
}
write!(f, "{})", last_one)?;
}
}
write!(f, "->")?;
rettype.fmt(f)?;
write!(f, ">")
}
}
}
}
impl Default for TypeOrVar { impl Default for TypeOrVar {
fn default() -> Self { fn default() -> Self {
TypeOrVar::new() TypeOrVar::new()

205
src/ir/pretty.rs Normal file
View File

@@ -0,0 +1,205 @@
use crate::ir::{Expression, Primitive, Program, TopLevel, Type, TypeOrVar, Value, ValueOrRef};
use crate::syntax::{self, ConstantType};
use crate::util::pretty::{Allocator, pretty_function_type, derived_display};
use pretty::{Arena, DocAllocator, DocBuilder};
impl Program<Type> {
pub fn pretty<'a>(&self, allocator: &'a Allocator<'a>) -> DocBuilder<'a, Allocator<'a>> {
allocator
.intersperse(
self.items.iter().map(|x| x.pretty(allocator)),
allocator.line(),
)
.align()
}
}
impl TopLevel<Type> {
pub fn pretty<'a>(
&self,
allocator: &'a Arena<'a, ()>,
) -> pretty::DocBuilder<'a, Arena<'a, ()>, ()> {
match self {
TopLevel::Function(name, args, _, expr) => allocator
.text("function")
.append(allocator.space())
.append(allocator.text(name.as_ref().to_string()))
.append(allocator.space())
.append(
allocator
.intersperse(
args.iter().map(|(x, _)| allocator.text(x.to_string())),
allocator.text(","),
)
.parens(),
)
.append(allocator.space())
.append(expr.pretty(allocator)),
TopLevel::Statement(stmt) => stmt.pretty(allocator),
}
}
}
impl Expression<Type> {
pub fn pretty<'a>(
&self,
allocator: &'a Arena<'a, ()>,
) -> pretty::DocBuilder<'a, Arena<'a, ()>, ()> {
match self {
Expression::Atomic(x) => x.pretty(allocator),
Expression::Cast(_, t, e) => allocator
.text("<")
.append(t.pretty(allocator))
.append(allocator.text(">"))
.append(e.pretty(allocator)),
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()))
}
Expression::Call(_, _, fun, args) => {
let args = args.iter().map(|x| x.pretty(allocator));
let comma_sepped_args =
allocator.intersperse(args, allocator.text(","));
fun.pretty(allocator).append(comma_sepped_args.parens())
}
Expression::Block(_, _, exprs) => match exprs.split_last() {
None => allocator.text("()"),
Some((last, &[])) => last.pretty(allocator),
Some((last, start)) => {
let mut result = allocator.text("{").append(allocator.hardline());
let starts = start.iter().map(|x| {
x.pretty(allocator)
.append(allocator.text(";"))
.append(allocator.hardline())
.indent(4)
});
let last = last
.pretty(allocator)
.append(allocator.hardline())
.indent(4);
for start in starts {
result = result.append(start);
}
result.append(last).append(allocator.text("}"))
}
},
Expression::Print(_, var) => allocator
.text("print")
.append(allocator.space())
.append(var.pretty(allocator)),
Expression::Bind(_, var, ty, expr) => allocator
.text(var.as_ref().to_string())
.append(allocator.space())
.append(allocator.text(":"))
.append(allocator.space())
.append(ty.pretty(allocator))
.append(allocator.space())
.append(allocator.text("="))
.append(allocator.space())
.append(expr.pretty(allocator)),
}
}
}
impl Primitive {
pub fn pretty<'a>(&self, allocator: &'a Allocator<'a>) -> DocBuilder<'a, Allocator<'a>> {
match self {
Primitive::Plus => allocator.text("+"),
Primitive::Minus => allocator.text("-"),
Primitive::Times => allocator.text("*"),
Primitive::Divide => allocator.text("/"),
}
}
}
impl ValueOrRef<Type> {
pub fn pretty<'a>(&self, allocator: &'a Allocator<'a>) -> DocBuilder<'a, Allocator<'a>> {
match self {
ValueOrRef::Value(_, _, v) => v.pretty(allocator),
ValueOrRef::Ref(_, _, v) => allocator.text(v.as_ref().to_string()),
}
}
}
impl Value {
pub fn pretty<'a>(&self, allocator: &'a Allocator<'a>) -> DocBuilder<'a, Allocator<'a>> {
let pretty_internal = |opt_base: &Option<u8>, x, t| {
syntax::Value::Number(*opt_base, Some(t), x).pretty(allocator)
};
let pretty_internal_signed = |opt_base, x: i64, t| {
let base = pretty_internal(opt_base, x.unsigned_abs(), t);
allocator.text("-").append(base)
};
match self {
Value::I8(opt_base, value) => {
pretty_internal_signed(opt_base, *value as i64, ConstantType::I8)
}
Value::I16(opt_base, value) => {
pretty_internal_signed(opt_base, *value as i64, ConstantType::I16)
}
Value::I32(opt_base, value) => {
pretty_internal_signed(opt_base, *value as i64, ConstantType::I32)
}
Value::I64(opt_base, value) => {
pretty_internal_signed(opt_base, *value, ConstantType::I64)
}
Value::U8(opt_base, value) => {
pretty_internal(opt_base, *value as u64, ConstantType::U8)
}
Value::U16(opt_base, value) => {
pretty_internal(opt_base, *value as u64, ConstantType::U16)
}
Value::U32(opt_base, value) => {
pretty_internal(opt_base, *value as u64, ConstantType::U32)
}
Value::U64(opt_base, value) => pretty_internal(opt_base, *value, ConstantType::U64),
Value::Void => allocator.text("<void>"),
}
}
}
impl Type {
pub fn pretty<'a>(&self, allocator: &'a Allocator<'a>) -> DocBuilder<'a, Allocator<'a>> {
match self {
Type::Function(args, rettype) => pretty_function_type!(allocator, args, rettype),
Type::Primitive(prim) => prim.pretty(allocator),
}
}
}
impl TypeOrVar {
pub fn pretty<'a>(&self, allocator: &'a Allocator<'a>) -> DocBuilder<'a, Allocator<'a>> {
match self {
TypeOrVar::Function(args, rettype) => pretty_function_type!(allocator, args, rettype),
TypeOrVar::Primitive(prim) => prim.pretty(allocator),
TypeOrVar::Variable(_, name) => allocator.text(name.to_string()),
}
}
}
derived_display!(Program<Type>);
derived_display!(TopLevel<Type>);
derived_display!(Expression<Type>);
derived_display!(Primitive);
derived_display!(Type);
derived_display!(TypeOrVar);
derived_display!(ValueOrRef<Type>);
derived_display!(Value);

View File

@@ -106,9 +106,9 @@ impl REPL {
pub fn process_input(&mut self, line_no: usize, command: String) { pub fn process_input(&mut self, line_no: usize, command: String) {
if let Err(err) = self.process(line_no, command) { if let Err(err) = self.process(line_no, command) {
if let Err(e) = self.emit_diagnostic(Diagnostic::from(err)) { if let Err(e) = self.emit_diagnostic(Diagnostic::from(err)) {
eprintln!( tracing::error!(
"WOAH! System having trouble printing error messages. This is very bad. ({})", error = %e,
e "WOAH! System having trouble printing error messages. This is very bad.",
); );
} }
} }

View File

@@ -1,14 +1,9 @@
use crate::syntax::ast::{Expression, Program, Statement, Value}; use crate::syntax::ast::{ConstantType, Expression, Program, Statement, TopLevel, Value};
use pretty::{DocAllocator, DocBuilder, Pretty}; use crate::util::pretty::{Allocator, derived_display};
use pretty::{DocAllocator, DocBuilder};
use super::{ConstantType, TopLevel}; impl Program {
pub fn pretty<'a>(&self, allocator: &'a Allocator<'a>) -> DocBuilder<'a, Allocator<'a>> {
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) -> DocBuilder<'a, D, A> {
let mut result = allocator.nil(); let mut result = allocator.nil();
for tl in self.items.iter() { for tl in self.items.iter() {
@@ -22,12 +17,8 @@ where
} }
} }
impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b TopLevel impl TopLevel {
where pub fn pretty<'a>(&self, allocator: &'a Allocator<'a>) -> DocBuilder<'a, Allocator<'a>> {
A: 'a,
D: ?Sized + DocAllocator<'a, A>,
{
fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> {
match self { match self {
TopLevel::Statement(stmt) => stmt.pretty(allocator), TopLevel::Statement(stmt) => stmt.pretty(allocator),
TopLevel::Function(name, arg_names, body) => allocator TopLevel::Function(name, arg_names, body) => allocator
@@ -42,7 +33,7 @@ where
allocator allocator
.intersperse( .intersperse(
arg_names.iter().map(|x| allocator.text(x.to_string())), arg_names.iter().map(|x| allocator.text(x.to_string())),
CommaSep {}, allocator.text(","),
) )
.parens(), .parens(),
) )
@@ -52,12 +43,8 @@ where
} }
} }
impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Statement impl Statement {
where pub fn pretty<'a>(&self, allocator: &'a Allocator<'a>) -> DocBuilder<'a, Allocator<'a>> {
A: 'a,
D: ?Sized + DocAllocator<'a, A>,
{
fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> {
match self { match self {
Statement::Binding(_, var, expr) => allocator Statement::Binding(_, var, expr) => allocator
.text(var.to_string()) .text(var.to_string())
@@ -73,12 +60,8 @@ where
} }
} }
impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Expression impl Expression {
where pub fn pretty<'a>(&self, allocator: &'a Allocator<'a>) -> DocBuilder<'a, Allocator<'a>> {
A: 'a,
D: ?Sized + DocAllocator<'a, A>,
{
fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> {
match self { match self {
Expression::Value(_, val) => val.pretty(allocator), Expression::Value(_, val) => val.pretty(allocator),
Expression::Reference(_, var) => allocator.text(var.to_string()), Expression::Reference(_, var) => allocator.text(var.to_string()),
@@ -102,12 +85,12 @@ where
Expression::Primitive(_, op, exprs) => { Expression::Primitive(_, op, exprs) => {
let call = allocator.text(op.to_string()); let call = allocator.text(op.to_string());
let args = exprs.iter().map(|x| x.pretty(allocator)); let args = exprs.iter().map(|x| x.pretty(allocator));
let comma_sepped_args = allocator.intersperse(args, CommaSep {}); let comma_sepped_args = allocator.intersperse(args, allocator.text(","));
call.append(comma_sepped_args.parens()) call.append(comma_sepped_args.parens())
} }
Expression::Call(_, fun, args) => { Expression::Call(_, fun, args) => {
let args = args.iter().map(|x| x.pretty(allocator)); let args = args.iter().map(|x| x.pretty(allocator));
let comma_sepped_args = allocator.intersperse(args, CommaSep {}); let comma_sepped_args = allocator.intersperse(args, allocator.text(","));
fun.pretty(allocator).append(comma_sepped_args.parens()) fun.pretty(allocator).append(comma_sepped_args.parens())
} }
Expression::Block(_, stmts) => match stmts.split_last() { Expression::Block(_, stmts) => match stmts.split_last() {
@@ -133,12 +116,8 @@ where
} }
} }
impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Value impl Value {
where pub fn pretty<'a>(&self, allocator: &'a Allocator<'a>) -> DocBuilder<'a, Allocator<'a>> {
A: 'a,
D: ?Sized + DocAllocator<'a, A>,
{
fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> {
match self { match self {
Value::Number(opt_base, ty, value) => { Value::Number(opt_base, ty, value) => {
let value_str = match opt_base { let value_str = match opt_base {
@@ -171,15 +150,8 @@ fn type_suffix(x: &Option<ConstantType>) -> &'static str {
} }
} }
#[derive(Clone, Copy)] derived_display!(Program);
pub struct CommaSep {} derived_display!(TopLevel);
derived_display!(Statement);
impl<'a, D, A> Pretty<'a, D, A> for CommaSep derived_display!(Expression);
where derived_display!(Value);
A: 'a,
D: ?Sized + DocAllocator<'a, A>,
{
fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> {
allocator.text(",").append(allocator.space())
}
}

View File

@@ -6,24 +6,8 @@ pub fn finalize_program(
mut program: Program<TypeOrVar>, mut program: Program<TypeOrVar>,
resolutions: &TypeResolutions, resolutions: &TypeResolutions,
) -> Program<Type> { ) -> Program<Type> {
println!("RESOLUTIONS:");
for (name, ty) in resolutions.iter() { for (name, ty) in resolutions.iter() {
println!("{} => {}", name, ty); tracing::debug!(name = %name, resolved_type = %ty, "resolved type variable");
}
println!("PROGRAM:");
{
use pretty::{DocAllocator, Pretty};
let allocator = pretty::BoxAllocator;
allocator
.text("---------------")
.append(allocator.hardline())
.append(program.pretty(&allocator))
.1
.render_colored(
70,
pretty::termcolor::StandardStream::stdout(pretty::termcolor::ColorChoice::Auto),
)
.expect("rendering works");
} }
Program { Program {

View File

@@ -1,49 +1,35 @@
use internment::ArcIntern; use pretty::Arena;
use pretty::{DocAllocator, Pretty};
#[derive(Clone)] pub type Allocator<'a> = Arena<'a, ()>;
pub struct PrettySymbol {
name: ArcIntern<String>, macro_rules! pretty_function_type {
($allocator: ident, $args: ident, $rettype: ident) => {
$allocator
.intersperse(
$args.iter().map(|x| x.pretty($allocator)),
$allocator.text(","),
)
.parens()
.append($allocator.space())
.append($allocator.text("->"))
.append($allocator.space())
.append($rettype.pretty($allocator))
};
} }
impl<'a> From<&'a ArcIntern<String>> for PrettySymbol { macro_rules! derived_display {
fn from(value: &'a ArcIntern<String>) -> Self { ($type: ty) => {
PrettySymbol { impl std::fmt::Display for $type {
name: value.clone(), fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let arena = pretty::Arena::new();
let doc = self.pretty(&arena);
doc.render_fmt(732, f)
} }
} }
};
} }
impl<'a, D, A> Pretty<'a, D, A> for PrettySymbol // this is a dumb Rust trick to export the functions to the rest
where // of the crate, but not globally.
A: 'a, pub(crate) use pretty_function_type;
D: ?Sized + DocAllocator<'a, A>, pub(crate) use derived_display;
{
fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> {
allocator.text(self.name.as_ref().to_string())
}
}
impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b PrettySymbol
where
A: 'a,
D: ?Sized + DocAllocator<'a, A>,
{
fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> {
allocator.text(self.name.as_ref().to_string())
}
}
#[allow(clippy::ptr_arg)]
pub fn pretty_comma_separated<'a, D, A, P>(
allocator: &'a D,
args: &Vec<P>,
) -> pretty::DocBuilder<'a, D, A>
where
A: 'a,
D: ?Sized + DocAllocator<'a, A>,
P: Pretty<'a, D, A> + Clone,
{
let individuals = args.iter().map(|x| x.clone().pretty(allocator));
allocator.intersperse(individuals, ", ")
}