Add a pretty printer.

This commit is contained in:
2022-02-18 10:37:15 -08:00
parent 6c5ca6b782
commit f45488b9af
3 changed files with 132 additions and 5 deletions

View File

@@ -19,6 +19,7 @@ codespan-reporting = "0.11.1"
lalrpop-util = "^0.19.7" lalrpop-util = "^0.19.7"
lazy_static = "^1.4.0" lazy_static = "^1.4.0"
logos = "^0.12.0" logos = "^0.12.0"
pretty = { version = "^0.11.2", features = ["termcolor"] }
thiserror = "^1.0.30" thiserror = "^1.0.30"
[build-dependencies] [build-dependencies]

View File

@@ -5,6 +5,7 @@ use codespan_reporting::term;
use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
use ngr::error::Error; use ngr::error::Error;
use ngr::syntax::Program; use ngr::syntax::Program;
use pretty::{Arena, Pretty};
use std::fs; use std::fs;
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
@@ -34,11 +35,20 @@ fn main() {
let mut file_database = SimpleFiles::new(); let mut file_database = SimpleFiles::new();
let initial_file_name = &args.file; let initial_file_name = &args.file;
if let Err(e) = compile_file(&mut file_database, initial_file_name) { match compile_file(&mut file_database, initial_file_name) {
Ok(p) => {
let arena = Arena::new();
p.pretty(&arena)
.into_doc()
.render_colored(72, StandardStream::stdout(ColorChoice::Auto))
.unwrap()
}
Err(e) => {
let diagnostic = Diagnostic::from(e); let diagnostic = Diagnostic::from(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();
term::emit(&mut writer.lock(), &config, &file_database, &diagnostic).unwrap(); term::emit(&mut writer.lock(), &config, &file_database, &diagnostic).unwrap();
} }
}
} }

View File

@@ -1,10 +1,32 @@
use crate::syntax::token_stream::Location; use crate::syntax::token_stream::Location;
use pretty::{DocAllocator, DocBuilder, Pretty};
static BINARY_OPERATORS: &[&str] = &["+", "-", "*", "/"];
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct Program { pub struct Program {
pub statements: Vec<Statement>, 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) -> 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
}
}
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum Statement { pub enum Statement {
Binding(Location, String, Expression), Binding(Location, String, Expression),
@@ -12,6 +34,28 @@ pub enum Statement {
Expr(Location, Expression), Expr(Location, Expression),
} }
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) -> DocBuilder<'a, D, A> {
match self {
Statement::Binding(_, var, expr) => allocator
.text(var.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.to_string())),
Statement::Expr(_, expr) => expr.pretty(allocator),
}
}
}
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum Expression { pub enum Expression {
Value(Location, Value), Value(Location, Value),
@@ -19,7 +63,79 @@ pub enum Expression {
Primitive(Location, String, Vec<Expression>), Primitive(Location, String, Vec<Expression>),
} }
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) -> DocBuilder<'a, D, A> {
match self {
Expression::Value(_, val) => val.pretty(allocator),
Expression::Reference(_, var) => allocator.text(var.to_string()),
Expression::Primitive(_, op, exprs) if BINARY_OPERATORS.contains(&op.as_ref()) => {
assert_eq!(
exprs.len(),
2,
"Found binary operator with {} components?",
exprs.len()
);
let left = exprs[0].pretty(allocator);
let right = exprs[1].pretty(allocator);
left.append(allocator.space())
.append(allocator.text(op.to_string()))
.append(allocator.space())
.append(right)
.parens()
}
Expression::Primitive(_, op, exprs) => {
let call = allocator.text(op.to_string());
let args = exprs.iter().map(|x| x.pretty(allocator));
let comma_sepped_args = allocator.intersperse(args, CommaSep {});
call.append(comma_sepped_args.parens())
}
}
}
}
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum Value { pub enum Value {
Number(Option<u8>, i128), 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, 'b, 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())
}
}