Add a pretty printer.
This commit is contained in:
@@ -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]
|
||||||
|
|||||||
20
src/bin.rs
20
src/bin.rs
@@ -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) {
|
||||||
let diagnostic = Diagnostic::from(e);
|
Ok(p) => {
|
||||||
let writer = StandardStream::stderr(ColorChoice::Auto);
|
let arena = Arena::new();
|
||||||
let config = codespan_reporting::term::Config::default();
|
p.pretty(&arena)
|
||||||
|
.into_doc()
|
||||||
|
.render_colored(72, StandardStream::stdout(ColorChoice::Auto))
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
let diagnostic = Diagnostic::from(e);
|
||||||
|
let writer = StandardStream::stderr(ColorChoice::Auto);
|
||||||
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user