🤷 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:
159
src/syntax/arbitrary.rs
Normal file
159
src/syntax/arbitrary.rs
Normal file
@@ -0,0 +1,159 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use crate::syntax::ast::{Expression, Program, Statement, Value};
|
||||
use crate::syntax::location::Location;
|
||||
use proptest::sample::select;
|
||||
use proptest::{
|
||||
prelude::{Arbitrary, BoxedStrategy, Strategy},
|
||||
strategy::{Just, Union},
|
||||
};
|
||||
|
||||
const VALID_VARIABLE_NAMES: &str = r"[a-z][a-zA-Z0-9_]*";
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Name(String);
|
||||
|
||||
impl Arbitrary for Name {
|
||||
type Parameters = ();
|
||||
type Strategy = BoxedStrategy<Self>;
|
||||
|
||||
fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
|
||||
VALID_VARIABLE_NAMES.prop_map(Name).boxed()
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for Program {
|
||||
type Parameters = ();
|
||||
type Strategy = BoxedStrategy<Self>;
|
||||
|
||||
fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
|
||||
let optionals = Vec::<Option<Name>>::arbitrary();
|
||||
|
||||
optionals
|
||||
.prop_flat_map(|mut possible_names| {
|
||||
let mut statements = Vec::new();
|
||||
let mut defined_variables: HashSet<String> = HashSet::new();
|
||||
|
||||
for possible_name in possible_names.drain(..) {
|
||||
match possible_name {
|
||||
None if defined_variables.is_empty() => continue,
|
||||
None => statements.push(
|
||||
Union::new(defined_variables.iter().map(|name| {
|
||||
Just(Statement::Print(Location::manufactured(), name.to_string()))
|
||||
}))
|
||||
.boxed(),
|
||||
),
|
||||
Some(new_name) => {
|
||||
let closures_name = new_name.0.clone();
|
||||
let retval =
|
||||
Expression::arbitrary_with(Some(defined_variables.clone()))
|
||||
.prop_map(move |exp| {
|
||||
Statement::Binding(
|
||||
Location::manufactured(),
|
||||
closures_name.clone(),
|
||||
exp,
|
||||
)
|
||||
})
|
||||
.boxed();
|
||||
|
||||
defined_variables.insert(new_name.0);
|
||||
statements.push(retval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
statements
|
||||
})
|
||||
.prop_map(|statements| Program { statements })
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for Statement {
|
||||
type Parameters = Option<HashSet<String>>;
|
||||
type Strategy = BoxedStrategy<Self>;
|
||||
|
||||
fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
|
||||
let duplicated_args = args.clone();
|
||||
let defined_variables = args.unwrap_or_default();
|
||||
|
||||
let binding_strategy = (
|
||||
VALID_VARIABLE_NAMES,
|
||||
Expression::arbitrary_with(duplicated_args),
|
||||
)
|
||||
.prop_map(|(name, exp)| Statement::Binding(Location::manufactured(), name, exp))
|
||||
.boxed();
|
||||
|
||||
if defined_variables.is_empty() {
|
||||
binding_strategy
|
||||
} else {
|
||||
let print_strategy = Union::new(
|
||||
defined_variables
|
||||
.iter()
|
||||
.map(|x| Just(Statement::Print(Location::manufactured(), x.to_string()))),
|
||||
)
|
||||
.boxed();
|
||||
|
||||
Union::new([binding_strategy, print_strategy]).boxed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for Expression {
|
||||
type Parameters = Option<HashSet<String>>;
|
||||
type Strategy = BoxedStrategy<Self>;
|
||||
|
||||
fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
|
||||
let defined_variables = args.unwrap_or_default();
|
||||
|
||||
let value_strategy = Value::arbitrary()
|
||||
.prop_map(move |x| Expression::Value(Location::manufactured(), x))
|
||||
.boxed();
|
||||
|
||||
let leaf_strategy = if defined_variables.is_empty() {
|
||||
value_strategy
|
||||
} else {
|
||||
let reference_strategy = Union::new(defined_variables.iter().map(|x| {
|
||||
Just(Expression::Reference(
|
||||
Location::manufactured(),
|
||||
x.to_owned(),
|
||||
))
|
||||
}))
|
||||
.boxed();
|
||||
Union::new([value_strategy, reference_strategy]).boxed()
|
||||
};
|
||||
|
||||
leaf_strategy
|
||||
.prop_recursive(3, 64, 2, move |inner| {
|
||||
(
|
||||
select(super::BINARY_OPERATORS),
|
||||
proptest::collection::vec(inner, 2),
|
||||
)
|
||||
.prop_map(move |(operator, exprs)| {
|
||||
Expression::Primitive(Location::manufactured(), operator.to_string(), exprs)
|
||||
})
|
||||
})
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for Value {
|
||||
type Parameters = ();
|
||||
type Strategy = BoxedStrategy<Self>;
|
||||
|
||||
fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
|
||||
let base_strategy = Union::new([
|
||||
Just(None::<u8>),
|
||||
Just(Some(2)),
|
||||
Just(Some(8)),
|
||||
Just(Some(10)),
|
||||
Just(Some(16)),
|
||||
]);
|
||||
|
||||
let value_strategy = i64::arbitrary();
|
||||
|
||||
(base_strategy, value_strategy)
|
||||
.prop_map(move |(base, value)| Value::Number(base, value))
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user