Document some of the IR; not much to say, actually.
This commit is contained in:
@@ -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),
|
||||||
|
|||||||
@@ -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)?),
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user