Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(remap): compile-time program result type checking #4902

Merged
merged 30 commits into from Nov 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
c811d28
add ValueKind enum
JeanMertz Nov 5, 2020
af109a7
add ResolveKind enum
JeanMertz Nov 5, 2020
2b37bbe
add CompilerState
JeanMertz Nov 5, 2020
58b9e40
add Expression::resolves_to
JeanMertz Nov 5, 2020
17634a4
add option to constraint Program value kind
JeanMertz Nov 6, 2020
8739cc9
rename ResolveKind to ValueConstraint
JeanMertz Nov 6, 2020
03824a0
introduce "TypeCheck" type
JeanMertz Nov 6, 2020
64c4357
infallible program must not have any fallible expressions
JeanMertz Nov 6, 2020
f92b859
block is fallible if any expression is fallible
JeanMertz Nov 6, 2020
e3142cc
force boolean return value for remap condition
JeanMertz Nov 6, 2020
b41e586
store path type-checks in correct store
JeanMertz Nov 6, 2020
d476aeb
cleanup
JeanMertz Nov 6, 2020
7b6a988
add type_check tests
JeanMertz Nov 7, 2020
a557fd2
rename TypeCheck to TypeDef
JeanMertz Nov 7, 2020
4ae205e
rename State to ProgramState
JeanMertz Nov 7, 2020
df68194
impl Clone for remap::Error
JeanMertz Nov 10, 2020
7b837ee
add as_*, try_*, and unwrap_* funcs to Value
JeanMertz Nov 10, 2020
3eea108
add as_*_mut func to Value
JeanMertz Nov 10, 2020
7089514
fix remap condition
JeanMertz Nov 11, 2020
c9179ea
expand TypeDef impl
JeanMertz Nov 11, 2020
9d6d2f2
improve module structure
JeanMertz Nov 11, 2020
282b164
properly format program error
JeanMertz Nov 11, 2020
cf90d9a
configure remap transform to accept any return value
JeanMertz Nov 11, 2020
98d1e06
remove function error cases
JeanMertz Nov 11, 2020
4446083
add type definitions to functions
JeanMertz Nov 12, 2020
01ef918
add function type definition tests
JeanMertz Nov 12, 2020
3a7e485
cleanup
JeanMertz Nov 12, 2020
1c7be2a
temporarily allow `None` values in conditions
JeanMertz Nov 12, 2020
8c067d8
clippy
JeanMertz Nov 12, 2020
c4c762a
update tests
JeanMertz Nov 12, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 12 additions & 3 deletions lib/remap-lang/src/error.rs
@@ -1,12 +1,15 @@
use crate::{expression, function, parser::Rule, value};
use crate::{expression, function, parser::Rule, program, value};
use std::error::Error as StdError;
use std::fmt;

#[derive(thiserror::Error, Debug, PartialEq)]
#[derive(thiserror::Error, Clone, Debug, PartialEq)]
pub enum Error {
#[error("parser error: {0}")]
Parser(String),

#[error("program error")]
Program(#[from] program::Error),

#[error("unexpected token sequence")]
Rule(#[from] Rule),

Expand Down Expand Up @@ -107,7 +110,7 @@ impl fmt::Display for Rule {
}
}

#[derive(Debug)]
#[derive(Debug, PartialEq)]
pub struct RemapError(pub(crate) Error);

impl StdError for RemapError {
Expand All @@ -130,6 +133,12 @@ impl fmt::Display for RemapError {
}
}

impl From<Error> for RemapError {
fn from(error: Error) -> Self {
RemapError(error)
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
99 changes: 78 additions & 21 deletions lib/remap-lang/src/expression.rs
@@ -1,29 +1,28 @@
use crate::{Object, Result, State, Value};
use crate::{state, Object, Result, TypeDef, Value};

pub(super) mod arithmetic;
pub(super) mod assignment;
mod arithmetic;
mod assignment;
mod block;
pub(super) mod function;
pub(super) mod if_statement;
pub(crate) mod function;
mod if_statement;
mod literal;
mod noop;
pub(super) mod not;
pub(super) mod path;
pub(super) mod variable;

pub(super) use arithmetic::Arithmetic;
pub(super) use assignment::{Assignment, Target};
pub(super) use block::Block;
pub(super) use function::Function;
pub(super) use if_statement::IfStatement;
pub(super) use not::Not;
pub(super) use variable::Variable;

mod not;
pub(crate) mod path;
mod variable;

pub use arithmetic::Arithmetic;
pub use assignment::{Assignment, Target};
pub use block::Block;
pub use function::Function;
pub use if_statement::IfStatement;
pub use literal::Literal;
pub use noop::Noop;
pub use not::Not;
pub use path::Path;
pub use variable::Variable;

#[derive(thiserror::Error, Debug, PartialEq)]
#[derive(thiserror::Error, Clone, Debug, PartialEq)]
pub enum Error {
#[error("expected expression, got none")]
Missing,
Expand All @@ -48,7 +47,9 @@ pub enum Error {
}

pub trait Expression: Send + Sync + std::fmt::Debug + dyn_clone::DynClone {
fn execute(&self, state: &mut State, object: &mut dyn Object) -> Result<Option<Value>>;
fn execute(&self, state: &mut state::Program, object: &mut dyn Object)
-> Result<Option<Value>>;
fn type_def(&self, state: &state::Compiler) -> TypeDef;
}

dyn_clone::clone_trait_object!(Expression);
Expand All @@ -66,16 +67,22 @@ macro_rules! expression_dispatch {
/// Any expression that stores other expressions internally will still
/// have to box this enum, to avoid infinite recursion.
#[derive(Debug, Clone)]
pub(crate) enum Expr {
pub enum Expr {
$($expr($expr)),+
}

impl Expression for Expr {
fn execute(&self, state: &mut State, object: &mut dyn Object) -> Result<Option<Value>> {
fn execute(&self, state: &mut state::Program, object: &mut dyn Object) -> Result<Option<Value>> {
match self {
$(Expr::$expr(expression) => expression.execute(state, object)),+
}
}

fn type_def(&self, state: &state::Compiler) -> TypeDef {
match self {
$(Expr::$expr(expression) => expression.type_def(state)),+
}
}
}

$(
Expand All @@ -100,3 +107,53 @@ expression_dispatch![
Path,
Variable,
];

#[cfg(test)]
mod tests {
use crate::value;

#[test]
fn test_contains() {
use value::Constraint::*;
use value::Kind::*;

let cases = vec![
(true, Any, Any),
(true, Any, Exact(String)),
(true, Any, Exact(Integer)),
(true, Any, OneOf(vec![Float, Boolean])),
(true, Any, OneOf(vec![Map])),
(true, Exact(String), Exact(String)),
(true, Exact(String), OneOf(vec![String])),
(false, Exact(String), Exact(Array)),
(false, Exact(String), OneOf(vec![Integer])),
(false, Exact(String), OneOf(vec![Integer, Float])),
];

for (expect, this, other) in cases {
assert_eq!(this.contains(&other), expect);
}
}

#[test]
fn test_merge() {
use value::Constraint::*;
use value::Kind::*;

let cases = vec![
(Any, Any, Any),
(Any, OneOf(vec![Integer, String]), Any),
(OneOf(vec![Integer, Float]), Exact(Integer), Exact(Float)),
(Exact(Integer), Exact(Integer), Exact(Integer)),
(
OneOf(vec![String, Integer, Float, Boolean]),
OneOf(vec![Integer, String]),
OneOf(vec![Float, Boolean]),
),
];

for (expect, this, other) in cases {
assert_eq!(this.merge(&other), expect);
}
}
}