Wire functions through everything, with some unimplemented, and add a basic scoped map.

This commit is contained in:
2023-10-07 11:06:28 +02:00
parent eba5227ebc
commit 736d27953f
18 changed files with 392 additions and 97 deletions

View File

@@ -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,

View File

@@ -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.

View File

@@ -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);

View File

@@ -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.

View File

@@ -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,

View File

@@ -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);