📜 Add better documentation across the compiler. #3

Merged
acw merged 19 commits from acw/better-docs into develop 2023-05-13 12:34:48 -07:00
3 changed files with 29 additions and 3 deletions
Showing only changes of commit 3615d19485 - Show all commits

View File

@@ -1,3 +1,4 @@
use crate::syntax::Location;
use internment::ArcIntern; use internment::ArcIntern;
use pretty::{DocAllocator, Pretty}; use pretty::{DocAllocator, Pretty};
use proptest::{ use proptest::{
@@ -5,13 +6,18 @@ use proptest::{
strategy::{BoxedStrategy, Strategy}, strategy::{BoxedStrategy, Strategy},
}; };
use crate::syntax::Location; /// We're going to represent variables as interned strings.
///
/// These should be fast enough for comparison that it's OK, since it's going to end up
/// being pretty much the pointer to the string.
type Variable = ArcIntern<String>; type Variable = ArcIntern<String>;
/// The representation of a program within our IR. For now, this is exactly one file.
#[derive(Debug)] #[derive(Debug)]
pub struct Program { pub struct Program {
pub statements: Vec<Statement>, // For now, a program is just a vector of statements. In the future, we'll probably
// extend this to include a bunch of other information, but for now: just a list.
pub(crate) statements: Vec<Statement>,
} }
impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Program impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Program
@@ -23,6 +29,8 @@ where
let mut result = allocator.nil(); let mut result = allocator.nil();
for stmt in self.statements.iter() { for stmt in self.statements.iter() {
// there's probably a better way to do this, rather than constantly
// adding to the end, but this works.
result = result result = result
.append(stmt.pretty(allocator)) .append(stmt.pretty(allocator))
.append(allocator.text(";")) .append(allocator.text(";"))
@@ -44,6 +52,10 @@ impl Arbitrary for Program {
} }
} }
/// The representation of a statement in the language.
///
/// For now, this is either a binding site (`x = 4`) or a print statement
/// (`print x`). Someday, though, more!
#[derive(Debug)] #[derive(Debug)]
pub enum Statement { pub enum Statement {
Binding(Location, Variable, Expression), Binding(Location, Variable, Expression),
@@ -71,6 +83,7 @@ where
} }
} }
/// The representation of an expression.
#[derive(Debug)] #[derive(Debug)]
pub enum Expression { pub enum Expression {
Value(Location, Value), Value(Location, Value),

View File

@@ -4,6 +4,10 @@ use crate::ir::{Expression, Program, Statement};
use super::{Primitive, ValueOrRef}; use super::{Primitive, ValueOrRef};
impl Program { impl Program {
/// Evaluate the program, returning either an error or a string containing everything
/// the program printed out.
///
/// The print outs will be newline separated, with one print out per line.
pub fn eval(&self) -> Result<String, EvalError> { pub fn eval(&self) -> Result<String, EvalError> {
let mut env = EvalEnvironment::empty(); let mut env = EvalEnvironment::empty();
let mut stdout = String::new(); let mut stdout = String::new();
@@ -39,6 +43,9 @@ impl Expression {
Expression::Primitive(_, op, args) => { Expression::Primitive(_, op, args) => {
let mut arg_values = Vec::with_capacity(args.len()); let mut arg_values = Vec::with_capacity(args.len());
// we implement primitive operations by first evaluating each of the
// arguments to the function, and then gathering up all the values
// produced.
for arg in args.iter() { for arg in args.iter() {
match arg { match arg {
ValueOrRef::Ref(_, n) => arg_values.push(env.lookup(n.clone())?), ValueOrRef::Ref(_, n) => arg_values.push(env.lookup(n.clone())?),
@@ -48,6 +55,8 @@ impl Expression {
} }
} }
// and then finally we call `calculate` to run them. trust me, it's nice
// to not have to deal with all the nonsense hidden under `calculate`.
match op { match op {
Primitive::Plus => Ok(Value::calculate("+", arg_values)?), Primitive::Plus => Ok(Value::calculate("+", arg_values)?),
Primitive::Minus => Ok(Value::calculate("-", arg_values)?), Primitive::Minus => Ok(Value::calculate("-", arg_values)?),

View File

@@ -3,6 +3,10 @@ use internment::ArcIntern;
use std::collections::HashSet; use std::collections::HashSet;
impl Program { impl Program {
/// Get the complete list of strings used within the program.
///
/// For the purposes of this function, strings are the variables used in
/// `print` statements.
pub fn strings(&self) -> HashSet<ArcIntern<String>> { pub fn strings(&self) -> HashSet<ArcIntern<String>> {
let mut result = HashSet::new(); let mut result = HashSet::new();