🤷 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:
172
src/ir/ast.rs
Normal file
172
src/ir/ast.rs
Normal file
@@ -0,0 +1,172 @@
|
||||
use internment::ArcIntern;
|
||||
use pretty::{DocAllocator, Pretty};
|
||||
|
||||
use crate::syntax::Location;
|
||||
|
||||
type Variable = ArcIntern<String>;
|
||||
|
||||
pub struct Program {
|
||||
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) -> pretty::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
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Statement {
|
||||
Binding(Location, Variable, Expression),
|
||||
Print(Location, Variable),
|
||||
}
|
||||
|
||||
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) -> pretty::DocBuilder<'a, D, A> {
|
||||
match self {
|
||||
Statement::Binding(_, var, expr) => allocator
|
||||
.text(var.as_ref().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.as_ref().to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Expression {
|
||||
Value(Location, Value),
|
||||
Reference(Location, Variable),
|
||||
Primitive(Location, Primitive, Vec<ValueOrRef>),
|
||||
}
|
||||
|
||||
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) -> pretty::DocBuilder<'a, D, A> {
|
||||
match self {
|
||||
Expression::Value(_, val) => val.pretty(allocator),
|
||||
Expression::Reference(_, var) => allocator.text(var.as_ref().to_string()),
|
||||
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()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum Primitive {
|
||||
Plus,
|
||||
Minus,
|
||||
Times,
|
||||
Divide,
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a str> for Primitive {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
"+" => Ok(Primitive::Plus),
|
||||
"-" => Ok(Primitive::Minus),
|
||||
"*" => Ok(Primitive::Times),
|
||||
"/" => Ok(Primitive::Divide),
|
||||
_ => Err(format!("Illegal primitive {}", value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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("/"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ValueOrRef {
|
||||
Value(Location, Value),
|
||||
Ref(Location, ArcIntern<String>),
|
||||
}
|
||||
|
||||
impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b ValueOrRef
|
||||
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()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Value {
|
||||
Number(Option<u8>, i64),
|
||||
}
|
||||
|
||||
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> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
73
src/ir/from_syntax.rs
Normal file
73
src/ir/from_syntax.rs
Normal file
@@ -0,0 +1,73 @@
|
||||
use internment::ArcIntern;
|
||||
|
||||
use crate::ir::ast as ir;
|
||||
use crate::syntax::ast as syntax;
|
||||
|
||||
impl From<syntax::Program> for ir::Program {
|
||||
fn from(mut value: syntax::Program) -> Self {
|
||||
ir::Program {
|
||||
statements: value.statements.drain(..).map(Into::into).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<syntax::Statement>> for ir::Program {
|
||||
fn from(mut value: Vec<syntax::Statement>) -> Self {
|
||||
ir::Program {
|
||||
statements: value.drain(..).map(Into::into).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<syntax::Statement> for ir::Statement {
|
||||
fn from(value: syntax::Statement) -> Self {
|
||||
match value {
|
||||
syntax::Statement::Binding(loc, name, expr) => {
|
||||
ir::Statement::Binding(loc, ArcIntern::from(name), ir::Expression::from(expr))
|
||||
}
|
||||
syntax::Statement::Print(loc, name) => ir::Statement::Print(loc, ArcIntern::from(name)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<syntax::Expression> for ir::Expression {
|
||||
fn from(value: syntax::Expression) -> Self {
|
||||
match value {
|
||||
syntax::Expression::Primitive(loc, name, mut exprs) => ir::Expression::Primitive(
|
||||
loc,
|
||||
ir::Primitive::try_from(name.as_str()).unwrap(),
|
||||
exprs.drain(..).map(Into::into).collect(),
|
||||
),
|
||||
syntax::Expression::Reference(loc, name) => {
|
||||
ir::Expression::Reference(loc, ArcIntern::from(name))
|
||||
}
|
||||
syntax::Expression::Value(loc, value) => {
|
||||
ir::Expression::Value(loc, ir::Value::from(value))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<syntax::Expression> for ir::ValueOrRef {
|
||||
fn from(value: syntax::Expression) -> Self {
|
||||
match value {
|
||||
syntax::Expression::Primitive(loc, _, _) => {
|
||||
panic!("{:?}: couldn't convert to valueorref", loc)
|
||||
}
|
||||
|
||||
syntax::Expression::Reference(loc, var) => {
|
||||
ir::ValueOrRef::Ref(loc, ArcIntern::new(var))
|
||||
}
|
||||
|
||||
syntax::Expression::Value(loc, val) => ir::ValueOrRef::Value(loc, val.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<syntax::Value> for ir::Value {
|
||||
fn from(x: syntax::Value) -> Self {
|
||||
match x {
|
||||
syntax::Value::Number(base, value) => ir::Value::Number(base, value),
|
||||
}
|
||||
}
|
||||
}
|
||||
36
src/ir/strings.rs
Normal file
36
src/ir/strings.rs
Normal file
@@ -0,0 +1,36 @@
|
||||
use super::ast::{Expression, Program, Statement};
|
||||
use internment::ArcIntern;
|
||||
use std::collections::HashSet;
|
||||
|
||||
impl Program {
|
||||
pub fn strings(&self) -> HashSet<ArcIntern<String>> {
|
||||
let mut result = HashSet::new();
|
||||
|
||||
for stmt in self.statements.iter() {
|
||||
stmt.register_strings(&mut result);
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl Statement {
|
||||
fn register_strings(&self, string_set: &mut HashSet<ArcIntern<String>>) {
|
||||
match self {
|
||||
Statement::Binding(_, name, expr) => {
|
||||
string_set.insert(name.clone());
|
||||
expr.register_strings(string_set);
|
||||
}
|
||||
|
||||
Statement::Print(_, name) => {
|
||||
string_set.insert(name.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Expression {
|
||||
fn register_strings(&self, _string_set: &mut HashSet<ArcIntern<String>>) {
|
||||
// nothing has a string in here, at the moment
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user