🤷 The initial version of the compiler, both static and JIT.

This implements a full compiler, with both static compilation and JIT
support, for the world's simplest and silliest programming language. You
can do math, and print variables. That's it. On the bright side, it
implements every part of the compiler, from the lexer and parser;
through analysis and simplification; and into a reasonable code
generator. This should be a good jumping off point for adding more
advanced features.

Tests, including proptests, are included to help avoid regressions.
This commit is contained in:
2020-08-01 20:45:33 -07:00
commit b2f6b12ced
30 changed files with 2178 additions and 0 deletions

115
src/syntax/pretty.rs Normal file
View File

@@ -0,0 +1,115 @@
use crate::syntax::ast::{Expression, Program, Statement, Value, BINARY_OPERATORS};
use pretty::{DocAllocator, DocBuilder, Pretty};
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
}
}
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())),
}
}
}
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())
}
}
}
}
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 sign = if *value < 0 { "-" } else { "" };
let value_str = match opt_base {
None => format!("{}", value),
Some(2) => format!("{}0b{:b}", sign, value.abs()),
Some(8) => format!("{}0o{:o}", sign, value.abs()),
Some(10) => format!("{}0d{}", sign, value.abs()),
Some(16) => format!("{}0x{:x}", sign, value.abs()),
Some(_) => format!("!!{}{:x}!!", sign, value.abs()),
};
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())
}
}