Wire functions through everything, with some unimplemented, and add a basic scoped map.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
use crate::syntax::ast::{ConstantType, Expression, Name, Program, Statement, Value};
|
||||
use crate::syntax::ast::{ConstantType, Expression, Name, Program, Statement, TopLevel, Value};
|
||||
use crate::syntax::location::Location;
|
||||
use proptest::sample::select;
|
||||
use proptest::{
|
||||
@@ -57,38 +57,40 @@ impl Arbitrary for Program {
|
||||
|
||||
fn arbitrary_with(genenv: Self::Parameters) -> Self::Strategy {
|
||||
proptest::collection::vec(
|
||||
ProgramStatementInfo::arbitrary(),
|
||||
ProgramTopLevelInfo::arbitrary(),
|
||||
genenv.block_length.clone(),
|
||||
)
|
||||
.prop_flat_map(move |mut items| {
|
||||
let mut statements = Vec::new();
|
||||
.prop_flat_map(move |mut ptlis| {
|
||||
let mut items = Vec::new();
|
||||
let mut genenv = genenv.clone();
|
||||
|
||||
for psi in items.drain(..) {
|
||||
for psi in ptlis.drain(..) {
|
||||
if genenv.bindings.is_empty() || psi.should_be_binding {
|
||||
genenv.return_type = psi.binding_type;
|
||||
let expr = Expression::arbitrary_with(genenv.clone());
|
||||
genenv.bindings.insert(psi.name.clone(), psi.binding_type);
|
||||
statements.push(
|
||||
items.push(
|
||||
expr.prop_map(move |expr| {
|
||||
Statement::Binding(Location::manufactured(), psi.name.clone(), expr)
|
||||
TopLevel::Statement(Statement::Binding(
|
||||
Location::manufactured(),
|
||||
psi.name.clone(),
|
||||
expr,
|
||||
))
|
||||
})
|
||||
.boxed(),
|
||||
);
|
||||
} else {
|
||||
let printers = genenv.bindings.keys().map(|n| {
|
||||
Just(Statement::Print(
|
||||
Just(TopLevel::Statement(Statement::Print(
|
||||
Location::manufactured(),
|
||||
Name::manufactured(n),
|
||||
))
|
||||
)))
|
||||
});
|
||||
statements.push(Union::new(printers).boxed());
|
||||
items.push(Union::new(printers).boxed());
|
||||
}
|
||||
}
|
||||
|
||||
statements
|
||||
.prop_map(|statements| Program { statements })
|
||||
.boxed()
|
||||
items.prop_map(|items| Program { items }).boxed()
|
||||
})
|
||||
.boxed()
|
||||
}
|
||||
@@ -104,13 +106,13 @@ impl Arbitrary for Name {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ProgramStatementInfo {
|
||||
struct ProgramTopLevelInfo {
|
||||
should_be_binding: bool,
|
||||
name: Name,
|
||||
binding_type: ConstantType,
|
||||
}
|
||||
|
||||
impl Arbitrary for ProgramStatementInfo {
|
||||
impl Arbitrary for ProgramTopLevelInfo {
|
||||
type Parameters = ();
|
||||
type Strategy = BoxedStrategy<Self>;
|
||||
|
||||
@@ -121,7 +123,7 @@ impl Arbitrary for ProgramStatementInfo {
|
||||
ConstantType::arbitrary(),
|
||||
)
|
||||
.prop_map(
|
||||
|(should_be_binding, name, binding_type)| ProgramStatementInfo {
|
||||
|(should_be_binding, name, binding_type)| ProgramTopLevelInfo {
|
||||
should_be_binding,
|
||||
name,
|
||||
binding_type,
|
||||
|
||||
@@ -16,7 +16,18 @@ use crate::syntax::Location;
|
||||
/// `validate` and it comes back without errors.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Program {
|
||||
pub statements: Vec<Statement>,
|
||||
pub items: Vec<TopLevel>,
|
||||
}
|
||||
|
||||
/// A thing that can sit at the top level of a file.
|
||||
///
|
||||
/// For the moment, these are statements and functions. Other things
|
||||
/// will likely be added in the future, but for now: just statements
|
||||
/// and functions
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum TopLevel {
|
||||
Statement(Statement),
|
||||
Function(Name, Vec<Name>, Expression),
|
||||
}
|
||||
|
||||
/// A Name.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use internment::ArcIntern;
|
||||
|
||||
use crate::eval::{EvalEnvironment, EvalError, PrimitiveType, Value};
|
||||
use crate::syntax::{ConstantType, Expression, Program, Statement};
|
||||
use crate::syntax::{ConstantType, Expression, Program, Statement, TopLevel};
|
||||
use std::str::FromStr;
|
||||
|
||||
impl Program {
|
||||
@@ -19,16 +19,19 @@ impl Program {
|
||||
let mut env = EvalEnvironment::empty();
|
||||
let mut stdout = String::new();
|
||||
|
||||
for stmt in self.statements.iter() {
|
||||
// at this point, evaluation is pretty simple. just walk through each
|
||||
// statement, in order, and record printouts as we come to them.
|
||||
for stmt in self.items.iter() {
|
||||
match stmt {
|
||||
Statement::Binding(_, name, value) => {
|
||||
TopLevel::Function(_name, _arg_names, _body) => {
|
||||
unimplemented!()
|
||||
}
|
||||
// at this point, evaluation is pretty simple. just walk through each
|
||||
// statement, in order, and record printouts as we come to them.
|
||||
TopLevel::Statement(Statement::Binding(_, name, value)) => {
|
||||
let actual_value = value.eval(&env)?;
|
||||
env = env.extend(name.clone().intern(), actual_value);
|
||||
}
|
||||
|
||||
Statement::Print(_, name) => {
|
||||
TopLevel::Statement(Statement::Print(_, name)) => {
|
||||
let value = env.lookup(name.clone().intern())?;
|
||||
let line = format!("{} = {}\n", name, value);
|
||||
stdout.push_str(&line);
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
//! eventually want to leave lalrpop behind.)
|
||||
//!
|
||||
use crate::syntax::{LexerError, Location};
|
||||
use crate::syntax::ast::{Program,Statement,Expression,Value,Name};
|
||||
use crate::syntax::ast::{Program,TopLevel,Statement,Expression,Value,Name};
|
||||
use crate::syntax::tokens::{ConstantType, Token};
|
||||
use internment::ArcIntern;
|
||||
|
||||
@@ -57,21 +57,25 @@ extern {
|
||||
|
||||
pub Program: Program = {
|
||||
// a program is just a set of statements
|
||||
<stmts:ProgramTopLevel> => Program {
|
||||
statements: stmts
|
||||
<items:ProgramTopLevel> => Program {
|
||||
items
|
||||
}
|
||||
}
|
||||
|
||||
ProgramTopLevel: Vec<Statement> = {
|
||||
<rest: ProgramTopLevel> Function => unimplemented!(),
|
||||
<mut rest: ProgramTopLevel> <next:Statement> => {
|
||||
rest.push(next);
|
||||
ProgramTopLevel: Vec<TopLevel> = {
|
||||
<mut rest: ProgramTopLevel> <t:TopLevel> => {
|
||||
rest.push(t);
|
||||
rest
|
||||
},
|
||||
=> Vec::new(),
|
||||
}
|
||||
|
||||
Function: () = {
|
||||
pub TopLevel: TopLevel = {
|
||||
<f:Function> => f,
|
||||
<s:Statement> => TopLevel::Statement(s),
|
||||
}
|
||||
|
||||
Function: TopLevel = {
|
||||
"function" "(" Arguments OptionalComma ")" Expression => unimplemented!(),
|
||||
}
|
||||
|
||||
@@ -123,7 +127,7 @@ Statements: Vec<Statement> = {
|
||||
}
|
||||
}
|
||||
|
||||
pub Statement: Statement = {
|
||||
Statement: Statement = {
|
||||
// A statement can be a variable binding. Note, here, that we use this
|
||||
// funny @L thing to get the source location before the variable, so that
|
||||
// we can say that this statement spans across everything.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::syntax::ast::{Expression, Program, Statement, Value};
|
||||
use pretty::{DocAllocator, DocBuilder, Pretty};
|
||||
|
||||
use super::ConstantType;
|
||||
use super::{ConstantType, TopLevel};
|
||||
|
||||
impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Program
|
||||
where
|
||||
@@ -11,9 +11,9 @@ where
|
||||
fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> {
|
||||
let mut result = allocator.nil();
|
||||
|
||||
for stmt in self.statements.iter() {
|
||||
for tl in self.items.iter() {
|
||||
result = result
|
||||
.append(stmt.pretty(allocator))
|
||||
.append(tl.pretty(allocator))
|
||||
.append(allocator.text(";"))
|
||||
.append(allocator.hardline());
|
||||
}
|
||||
@@ -22,6 +22,32 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b TopLevel
|
||||
where
|
||||
A: 'a,
|
||||
D: ?Sized + DocAllocator<'a, A>,
|
||||
{
|
||||
fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> {
|
||||
match self {
|
||||
TopLevel::Statement(stmt) => stmt.pretty(allocator),
|
||||
TopLevel::Function(name, arg_names, body) => allocator
|
||||
.text("function")
|
||||
.append(allocator.space())
|
||||
.append(allocator.text(name.to_string()))
|
||||
.append(
|
||||
allocator
|
||||
.intersperse(
|
||||
arg_names.iter().map(|x| allocator.text(x.to_string())),
|
||||
CommaSep {},
|
||||
)
|
||||
.parens(),
|
||||
)
|
||||
.append(allocator.space())
|
||||
.append(body.pretty(allocator)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Statement
|
||||
where
|
||||
A: 'a,
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
use crate::{
|
||||
eval::PrimitiveType,
|
||||
syntax::{Expression, Location, Program, Statement},
|
||||
syntax::{Expression, Location, Program, Statement, TopLevel},
|
||||
util::scoped_map::ScopedMap,
|
||||
};
|
||||
use codespan_reporting::diagnostic::Diagnostic;
|
||||
use std::{collections::HashMap, str::FromStr};
|
||||
use std::str::FromStr;
|
||||
|
||||
/// An error we found while validating the input program.
|
||||
///
|
||||
@@ -65,7 +66,7 @@ impl Program {
|
||||
/// example, and generates warnings for things that are inadvisable but not
|
||||
/// actually a problem.
|
||||
pub fn validate(&self) -> (Vec<Error>, Vec<Warning>) {
|
||||
let mut bound_variables = HashMap::new();
|
||||
let mut bound_variables = ScopedMap::new();
|
||||
self.validate_with_bindings(&mut bound_variables)
|
||||
}
|
||||
|
||||
@@ -76,13 +77,13 @@ impl Program {
|
||||
/// actually a problem.
|
||||
pub fn validate_with_bindings(
|
||||
&self,
|
||||
bound_variables: &mut HashMap<String, Location>,
|
||||
bound_variables: &mut ScopedMap<String, Location>,
|
||||
) -> (Vec<Error>, Vec<Warning>) {
|
||||
let mut errors = vec![];
|
||||
let mut warnings = vec![];
|
||||
|
||||
for stmt in self.statements.iter() {
|
||||
let (mut new_errors, mut new_warnings) = stmt.validate(bound_variables);
|
||||
for stmt in self.items.iter() {
|
||||
let (mut new_errors, mut new_warnings) = stmt.validate_with_bindings(bound_variables);
|
||||
errors.append(&mut new_errors);
|
||||
warnings.append(&mut new_warnings);
|
||||
}
|
||||
@@ -91,6 +92,44 @@ impl Program {
|
||||
}
|
||||
}
|
||||
|
||||
impl TopLevel {
|
||||
/// Validate that the top level item makes semantic sense, not just syntactic
|
||||
/// sense.
|
||||
///
|
||||
/// This checks for things like references to variables that don't exist, for
|
||||
/// example, and generates warnings for thins that are inadvisable but not
|
||||
/// actually a problem.
|
||||
pub fn validate(&self) -> (Vec<Error>, Vec<Warning>) {
|
||||
let mut bound_variables = ScopedMap::new();
|
||||
self.validate_with_bindings(&mut bound_variables)
|
||||
}
|
||||
|
||||
/// Validate that the top level item makes semantic sense, not just syntactic
|
||||
/// sense.
|
||||
///
|
||||
/// This checks for things like references to variables that don't exist, for
|
||||
/// example, and generates warnings for thins that are inadvisable but not
|
||||
/// actually a problem.
|
||||
pub fn validate_with_bindings(
|
||||
&self,
|
||||
bound_variables: &mut ScopedMap<String, Location>,
|
||||
) -> (Vec<Error>, Vec<Warning>) {
|
||||
match self {
|
||||
TopLevel::Function(name, arguments, body) => {
|
||||
bound_variables.new_scope();
|
||||
bound_variables.insert(name.name.clone(), name.location.clone());
|
||||
for arg in arguments.iter() {
|
||||
bound_variables.insert(arg.name.clone(), arg.location.clone());
|
||||
}
|
||||
let result = body.validate(&bound_variables);
|
||||
bound_variables.release_scope();
|
||||
result
|
||||
}
|
||||
TopLevel::Statement(stmt) => stmt.validate(bound_variables),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Statement {
|
||||
/// Validate that the statement makes semantic sense, not just syntactic sense.
|
||||
///
|
||||
@@ -103,7 +142,7 @@ impl Statement {
|
||||
/// and warnings.
|
||||
fn validate(
|
||||
&self,
|
||||
bound_variables: &mut HashMap<String, Location>,
|
||||
bound_variables: &mut ScopedMap<String, Location>,
|
||||
) -> (Vec<Error>, Vec<Warning>) {
|
||||
let mut errors = vec![];
|
||||
let mut warnings = vec![];
|
||||
@@ -139,7 +178,7 @@ impl Statement {
|
||||
}
|
||||
|
||||
impl Expression {
|
||||
fn validate(&self, variable_map: &HashMap<String, Location>) -> (Vec<Error>, Vec<Warning>) {
|
||||
fn validate(&self, variable_map: &ScopedMap<String, Location>) -> (Vec<Error>, Vec<Warning>) {
|
||||
match self {
|
||||
Expression::Value(_, _) => (vec![], vec![]),
|
||||
Expression::Reference(_, var) if variable_map.contains_key(var) => (vec![], vec![]),
|
||||
@@ -174,14 +213,14 @@ impl Expression {
|
||||
|
||||
#[test]
|
||||
fn cast_checks_are_reasonable() {
|
||||
let good_stmt = Statement::parse(0, "x = <u16>4u8;").expect("valid test case");
|
||||
let (good_errs, good_warns) = good_stmt.validate(&mut HashMap::new());
|
||||
let good_stmt = TopLevel::parse(0, "x = <u16>4u8;").expect("valid test case");
|
||||
let (good_errs, good_warns) = good_stmt.validate();
|
||||
|
||||
assert!(good_errs.is_empty());
|
||||
assert!(good_warns.is_empty());
|
||||
|
||||
let bad_stmt = Statement::parse(0, "x = <apple>4u8;").expect("valid test case");
|
||||
let (bad_errs, bad_warns) = bad_stmt.validate(&mut HashMap::new());
|
||||
let bad_stmt = TopLevel::parse(0, "x = <apple>4u8;").expect("valid test case");
|
||||
let (bad_errs, bad_warns) = bad_stmt.validate();
|
||||
|
||||
assert!(bad_warns.is_empty());
|
||||
assert_eq!(bad_errs.len(), 1);
|
||||
|
||||
Reference in New Issue
Block a user