Add pretty-printing of the parsed expression
Adds `--unpretty` unstable flag, which currently accepts two arguments, `dot` and `graphdotviz`, which are synonymous, and which prints the expression we parsed in a graphdotviz-compatible format, i.e. in a form of a directed graph.
This commit is contained in:
parent
ba409b9be0
commit
3d26398ac7
93
src/main.rs
93
src/main.rs
|
@ -1,9 +1,16 @@
|
||||||
|
#![feature(box_syntax)]
|
||||||
|
|
||||||
// Try to keep this string updated with the argument parsing, otherwise it will
|
// Try to keep this string updated with the argument parsing, otherwise it will
|
||||||
// get confusing for users.
|
// get confusing for users.
|
||||||
static USAGE: &'static str = "usage: pine [options] input
|
static USAGE: &'static str = "usage: pine [options] input
|
||||||
|
|
||||||
options:
|
options:
|
||||||
--help print all options";
|
--help print all options
|
||||||
|
|
||||||
|
unstable options:
|
||||||
|
--unpretty val print un-prettified representation of the source code
|
||||||
|
valid options for `val` are:
|
||||||
|
dot, graphdotviz (dot-compatible graph)";
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// Throw away the first argument, which usually is the executable name.
|
// Throw away the first argument, which usually is the executable name.
|
||||||
|
@ -20,6 +27,8 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut path: Option<&str> = None;
|
let mut path: Option<&str> = None;
|
||||||
|
let mut output_pretty: Option<&str> = None;
|
||||||
|
|
||||||
// Handle command-line arguments.
|
// Handle command-line arguments.
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
loop {
|
loop {
|
||||||
|
@ -35,6 +44,22 @@ fn main() {
|
||||||
println!("{}\n", USAGE);
|
println!("{}\n", USAGE);
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
|
"unpretty" => {
|
||||||
|
if i + 1 == args.len() {
|
||||||
|
eprintln!("pine: \x1b[1;31merror\x1b[0m: expected option to '{}'", arg);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
output_pretty = match args[i + 1].as_str() {
|
||||||
|
opt @ ("dot" | "graphdotviz") => Some(opt),
|
||||||
|
opt => {
|
||||||
|
eprintln!("pine: \x1b[1;31merror\x1b[0m: invalid option '{}' to '{}'", opt, arg);
|
||||||
|
std::process::exit(1);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
i += 1;
|
||||||
|
},
|
||||||
_ => {
|
_ => {
|
||||||
eprintln!("pine: \x1b[1;31merror\x1b[0m: unknown argument '{}'", arg);
|
eprintln!("pine: \x1b[1;31merror\x1b[0m: unknown argument '{}'", arg);
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
|
@ -70,6 +95,32 @@ fn main() {
|
||||||
let mut tokens = TokenStream::from(source);
|
let mut tokens = TokenStream::from(source);
|
||||||
let expr = parse_expression(&mut tokens);
|
let expr = parse_expression(&mut tokens);
|
||||||
eprintln!("{:?}", expr);
|
eprintln!("{:?}", expr);
|
||||||
|
|
||||||
|
match output_pretty {
|
||||||
|
Some("dot" | "graphdotviz") => expr.then(|e| {
|
||||||
|
let graph = e.create_graphviz_graph(unsafe { GLOBAL_COUNTER.next() });
|
||||||
|
let graphviz_format = "node [shape = box, style = filled, color = \"#bfd1e5\", fontname = monospace, fontsize = 12]";
|
||||||
|
eprintln!("digraph {{\n{}\n{}\n}}", graphviz_format, graph);
|
||||||
|
}),
|
||||||
|
// This case is validated at the command-line parsing time, and we reject everything
|
||||||
|
// not specified there. This is why this can never happen, unless a solar flare changes
|
||||||
|
// a single bit.
|
||||||
|
Some(_) => unreachable!(),
|
||||||
|
None => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait WithContinuation<T> {
|
||||||
|
fn then<F>(&self, f: F) where F: FnOnce(&T);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> WithContinuation<T> for std::option::Option<T> {
|
||||||
|
fn then<F>(&self, f: F) where F: FnOnce(&T) {
|
||||||
|
match self {
|
||||||
|
None => {},
|
||||||
|
Some(v) => f(v),
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -78,6 +129,46 @@ enum Expression {
|
||||||
Binary(Token, Box<Expression>, Box<Expression>),
|
Binary(Token, Box<Expression>, Box<Expression>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Counter {
|
||||||
|
state: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Counter {
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
return Self {
|
||||||
|
state: 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next(&mut self) -> usize {
|
||||||
|
let last_state = self.state;
|
||||||
|
self.state += 1;
|
||||||
|
return last_state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static mut GLOBAL_COUNTER: Counter = Counter::new();
|
||||||
|
|
||||||
|
impl Expression {
|
||||||
|
pub fn create_graphviz_graph(&self, id: usize) -> String {
|
||||||
|
return match self {
|
||||||
|
Expression::Literal(Token::IntegerLiteral(i)) => {
|
||||||
|
format!("Node{} [label = \"{}\"]", id, i)
|
||||||
|
},
|
||||||
|
Expression::Literal(_) => unreachable!(),
|
||||||
|
Expression::Binary(op, left, right) => {
|
||||||
|
let left_id = unsafe { GLOBAL_COUNTER.next() };
|
||||||
|
let right_id = unsafe { GLOBAL_COUNTER.next() };
|
||||||
|
|
||||||
|
format!("Node{} -> {{ Node{} Node{} }}\nNode{} [label = \"{}\"]\n{}\n{}",
|
||||||
|
id, left_id, right_id,
|
||||||
|
id, op,
|
||||||
|
left.create_graphviz_graph(left_id), right.create_graphviz_graph(right_id))
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_expression<'a>(tokens: &'a mut TokenStream<'a>) -> Option<Expression> {
|
fn parse_expression<'a>(tokens: &'a mut TokenStream<'a>) -> Option<Expression> {
|
||||||
let lhs = match tokens.next()? {
|
let lhs = match tokens.next()? {
|
||||||
token @ Token::IntegerLiteral(_) => Expression::Literal(token),
|
token @ Token::IntegerLiteral(_) => Expression::Literal(token),
|
||||||
|
|
Loading…
Reference in a new issue