Pass value tests
This commit is contained in:
parent
ca04103372
commit
86ce1dc3af
@ -55,19 +55,27 @@ use crate::{
|
|||||||
error::rw_lock_error::RwLockError, Identifier, Type, TypeDefinition, Value,
|
error::rw_lock_error::RwLockError, Identifier, Type, TypeDefinition, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub enum ContextMode {
|
||||||
|
AllowGarbage,
|
||||||
|
RemoveGarbage,
|
||||||
|
}
|
||||||
|
|
||||||
/// An execution context stores that variable and type data during the
|
/// An execution context stores that variable and type data during the
|
||||||
/// [Interpreter]'s abstraction and execution process.
|
/// [Interpreter]'s abstraction and execution process.
|
||||||
///
|
///
|
||||||
/// See the [module-level docs][self] for more info.
|
/// See the [module-level docs][self] for more info.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
|
mode: ContextMode,
|
||||||
inner: Arc<RwLock<BTreeMap<Identifier, (ValueData, UsageCounter)>>>,
|
inner: Arc<RwLock<BTreeMap<Identifier, (ValueData, UsageCounter)>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Context {
|
impl Context {
|
||||||
/// Return a new, empty Context.
|
/// Return a new, empty Context.
|
||||||
pub fn new() -> Self {
|
pub fn new(mode: ContextMode) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
mode,
|
||||||
inner: Arc::new(RwLock::new(BTreeMap::new())),
|
inner: Arc::new(RwLock::new(BTreeMap::new())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -88,6 +96,7 @@ impl Context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ok(Context {
|
Ok(Context {
|
||||||
|
mode: other.mode.clone(),
|
||||||
inner: Arc::new(RwLock::new(new_variables)),
|
inner: Arc::new(RwLock::new(new_variables)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -215,7 +224,7 @@ impl Context {
|
|||||||
|
|
||||||
let (allowances, runtime_uses) = counter.get_counts()?;
|
let (allowances, runtime_uses) = counter.get_counts()?;
|
||||||
|
|
||||||
if allowances == runtime_uses {
|
if self.mode == ContextMode::RemoveGarbage && allowances == runtime_uses {
|
||||||
self.unset(identifier)?;
|
self.unset(identifier)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -326,7 +335,7 @@ impl Context {
|
|||||||
|
|
||||||
impl Default for Context {
|
impl Default for Context {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Context::new()
|
Context::new(ContextMode::RemoveGarbage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -386,7 +395,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn drops_variables() {
|
fn drops_variables() {
|
||||||
let context = Context::new();
|
let context = Context::default();
|
||||||
|
|
||||||
interpret_with_context(
|
interpret_with_context(
|
||||||
"
|
"
|
||||||
|
@ -37,14 +37,14 @@
|
|||||||
//! ```
|
//! ```
|
||||||
use tree_sitter::{Parser, Tree as SyntaxTree};
|
use tree_sitter::{Parser, Tree as SyntaxTree};
|
||||||
|
|
||||||
use crate::{language, AbstractTree, Context, Error, Format, Root, Value};
|
use crate::{language, AbstractTree, Context, ContextMode, Error, Format, Root, Value};
|
||||||
|
|
||||||
/// Interpret the given source code. Returns the value of last statement or the
|
/// Interpret the given source code. Returns the value of last statement or the
|
||||||
/// first error encountered.
|
/// first error encountered.
|
||||||
///
|
///
|
||||||
/// See the [module-level docs][self] for more info.
|
/// See the [module-level docs][self] for more info.
|
||||||
pub fn interpret(source: &str) -> Result<Value, Error> {
|
pub fn interpret(source: &str) -> Result<Value, Error> {
|
||||||
interpret_with_context(source, Context::new())
|
interpret_with_context(source, Context::new(ContextMode::RemoveGarbage))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Interpret the given source code with the given context.
|
/// Interpret the given source code with the given context.
|
||||||
@ -152,6 +152,6 @@ impl Interpreter {
|
|||||||
|
|
||||||
impl Default for Interpreter {
|
impl Default for Interpreter {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Interpreter::new(Context::new())
|
Interpreter::new(Context::default())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,11 +5,7 @@
|
|||||||
//! You can use this library externally by calling either of the "interpret"
|
//! You can use this library externally by calling either of the "interpret"
|
||||||
//! functions or by constructing your own Interpreter.
|
//! functions or by constructing your own Interpreter.
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
abstract_tree::*,
|
abstract_tree::*, built_in_functions::BuiltInFunction, context::*, error::Error, interpret::*,
|
||||||
built_in_functions::BuiltInFunction,
|
|
||||||
context::{Context, ValueData},
|
|
||||||
error::Error,
|
|
||||||
interpret::*,
|
|
||||||
value::*,
|
value::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
27
src/main.rs
27
src/main.rs
@ -11,7 +11,8 @@ use reedline::{
|
|||||||
use std::{borrow::Cow, fs::read_to_string, path::PathBuf, process::Command};
|
use std::{borrow::Cow, fs::read_to_string, path::PathBuf, process::Command};
|
||||||
|
|
||||||
use dust_lang::{
|
use dust_lang::{
|
||||||
built_in_values::all_built_in_values, Context, Error, Identifier, Interpreter, Value, ValueData,
|
built_in_values::all_built_in_values, Context, ContextMode, Error, Interpreter, Value,
|
||||||
|
ValueData,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Command-line arguments to be parsed.
|
/// Command-line arguments to be parsed.
|
||||||
@ -22,14 +23,6 @@ struct Args {
|
|||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
command: Option<String>,
|
command: Option<String>,
|
||||||
|
|
||||||
/// Data to assign to the "input" variable.
|
|
||||||
#[arg(short, long)]
|
|
||||||
input: Option<String>,
|
|
||||||
|
|
||||||
/// File whose contents will be assigned to the "input" variable.
|
|
||||||
#[arg(short = 'p', long)]
|
|
||||||
input_path: Option<String>,
|
|
||||||
|
|
||||||
/// Command for alternate functionality besides running the source.
|
/// Command for alternate functionality besides running the source.
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
cli_command: Option<CliCommand>,
|
cli_command: Option<CliCommand>,
|
||||||
@ -51,21 +44,7 @@ fn main() {
|
|||||||
env_logger::init();
|
env_logger::init();
|
||||||
|
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
let context = Context::new();
|
let context = Context::new(ContextMode::AllowGarbage);
|
||||||
|
|
||||||
if let Some(input) = args.input {
|
|
||||||
context
|
|
||||||
.set_value(Identifier::new("input"), Value::string(input))
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(path) = args.input_path {
|
|
||||||
let file_contents = read_to_string(path).unwrap();
|
|
||||||
|
|
||||||
context
|
|
||||||
.set_value(Identifier::new("input"), Value::string(file_contents))
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
if args.path.is_none() && args.command.is_none() {
|
if args.path.is_none() && args.command.is_none() {
|
||||||
let run_shell_result = run_shell(context);
|
let run_shell_result = run_shell(context);
|
||||||
|
@ -86,21 +86,19 @@ impl Eq for List {}
|
|||||||
|
|
||||||
impl PartialEq for List {
|
impl PartialEq for List {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
if let Ok(left) = self.items() {
|
if let (Ok(left), Ok(right)) = (self.items(), other.items()) {
|
||||||
if let Ok(right) = other.items() {
|
if left.len() != right.len() {
|
||||||
if left.len() != right.len() {
|
return false;
|
||||||
return false;
|
} else {
|
||||||
} else {
|
for (i, j) in left.iter().zip(right.iter()) {
|
||||||
for (i, j) in left.iter().zip(right.iter()) {
|
if i != j {
|
||||||
if i != j {
|
return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
false
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,12 +110,10 @@ impl PartialOrd for List {
|
|||||||
|
|
||||||
impl Ord for List {
|
impl Ord for List {
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
if let Ok(left) = self.items() {
|
if let (Ok(left), Ok(right)) = (self.items(), other.items()) {
|
||||||
if let Ok(right) = other.items() {
|
left.cmp(&right)
|
||||||
return left.cmp(&right);
|
} else {
|
||||||
}
|
Ordering::Equal
|
||||||
}
|
}
|
||||||
|
|
||||||
Ordering::Equal
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
built_in_values::BuiltInValue,
|
built_in_values::BuiltInValue,
|
||||||
error::{rw_lock_error::RwLockError, RuntimeError, ValidationError},
|
error::{rw_lock_error::RwLockError, RuntimeError, ValidationError},
|
||||||
Identifier, SourcePosition, Type, TypeSpecification,
|
Identifier, SourcePosition, Type,
|
||||||
};
|
};
|
||||||
|
|
||||||
use serde::{
|
use serde::{
|
||||||
@ -93,18 +93,7 @@ impl Value {
|
|||||||
Type::List(Box::new(Type::Any))
|
Type::List(Box::new(Type::Any))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Value::Map(map) => {
|
Value::Map(_) => Type::Map,
|
||||||
let mut identifier_types = Vec::new();
|
|
||||||
|
|
||||||
for (key, value) in map.inner() {
|
|
||||||
identifier_types.push((
|
|
||||||
Identifier::new(key.inner()),
|
|
||||||
TypeSpecification::new(value.r#type()?),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
Type::Map
|
|
||||||
}
|
|
||||||
Value::Function(function) => function.r#type().clone(),
|
Value::Function(function) => function.r#type().clone(),
|
||||||
Value::String(_) => Type::String,
|
Value::String(_) => Type::String,
|
||||||
Value::Float(_) => Type::Float,
|
Value::Float(_) => Type::Float,
|
||||||
@ -263,7 +252,9 @@ impl Value {
|
|||||||
(Value::Float(left), Value::Float(right)) => Ok(Value::Float(left + right)),
|
(Value::Float(left), Value::Float(right)) => Ok(Value::Float(left + right)),
|
||||||
(Value::Float(left), Value::Integer(right)) => Ok(Value::Float(left + right as f64)),
|
(Value::Float(left), Value::Integer(right)) => Ok(Value::Float(left + right as f64)),
|
||||||
(Value::Integer(left), Value::Float(right)) => Ok(Value::Float((left as f64) + right)),
|
(Value::Integer(left), Value::Float(right)) => Ok(Value::Float((left as f64) + right)),
|
||||||
(Value::Integer(left), Value::Integer(right)) => Ok(Value::Integer(left + right)),
|
(Value::Integer(left), Value::Integer(right)) => {
|
||||||
|
Ok(Value::Integer(left.saturating_add(right)))
|
||||||
|
}
|
||||||
(Value::List(list), value) | (value, Value::List(list)) => {
|
(Value::List(list), value) | (value, Value::List(list)) => {
|
||||||
list.items_mut()?.push(value);
|
list.items_mut()?.push(value);
|
||||||
|
|
||||||
@ -284,7 +275,9 @@ impl Value {
|
|||||||
(Value::Float(left), Value::Float(right)) => Ok(Value::Float(left - right)),
|
(Value::Float(left), Value::Float(right)) => Ok(Value::Float(left - right)),
|
||||||
(Value::Float(left), Value::Integer(right)) => Ok(Value::Float(left - right as f64)),
|
(Value::Float(left), Value::Integer(right)) => Ok(Value::Float(left - right as f64)),
|
||||||
(Value::Integer(left), Value::Float(right)) => Ok(Value::Float(left as f64 - right)),
|
(Value::Integer(left), Value::Float(right)) => Ok(Value::Float(left as f64 - right)),
|
||||||
(Value::Integer(left), Value::Integer(right)) => Ok(Value::Integer(left - right)),
|
(Value::Integer(left), Value::Integer(right)) => {
|
||||||
|
Ok(Value::Integer(left.saturating_sub(right)))
|
||||||
|
}
|
||||||
(left, right) => Err(ValidationError::CannotSubtract {
|
(left, right) => Err(ValidationError::CannotSubtract {
|
||||||
left,
|
left,
|
||||||
right,
|
right,
|
||||||
|
@ -2,7 +2,7 @@ use dust_lang::*;
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn format_simple_program() {
|
fn format_simple_program() {
|
||||||
let mut interpreter = Interpreter::new(Context::new());
|
let mut interpreter = Interpreter::new(Context::default());
|
||||||
|
|
||||||
assert_eq!(interpreter.format("x=1"), Ok("x = 1\n".to_string()));
|
assert_eq!(interpreter.format("x=1"), Ok("x = 1\n".to_string()));
|
||||||
}
|
}
|
||||||
@ -16,7 +16,7 @@ const FORMATTED_BLOCK: &str = "{
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn format_block() {
|
fn format_block() {
|
||||||
let mut interpreter = Interpreter::new(Context::new());
|
let mut interpreter = Interpreter::new(Context::default());
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
interpreter.format("{1 2 3}"),
|
interpreter.format("{1 2 3}"),
|
||||||
@ -34,7 +34,7 @@ const FORMATTED_MAP: &str = "{
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn format_map() {
|
fn format_map() {
|
||||||
let mut interpreter = Interpreter::new(Context::new());
|
let mut interpreter = Interpreter::new(Context::default());
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
interpreter.format("{{x=1 y <int> = 2}}"),
|
interpreter.format("{{x=1 y <int> = 2}}"),
|
||||||
@ -49,7 +49,7 @@ const FORMATTED_FUNCTION: &str = "(x <int>) <num> {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn format_function() {
|
fn format_function() {
|
||||||
let mut interpreter = Interpreter::new(Context::new());
|
let mut interpreter = Interpreter::new(Context::default());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
interpreter.format("( x< int > )<num>{x/2}"),
|
interpreter.format("( x< int > )<num>{x/2}"),
|
||||||
Ok(FORMATTED_FUNCTION.to_string())
|
Ok(FORMATTED_FUNCTION.to_string())
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use dust_lang::*;
|
use dust_lang::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn empty() {
|
fn none() {
|
||||||
assert_eq!(interpret("x = 9"), Ok(Value::none()));
|
assert_eq!(interpret("x = 9"), Ok(Value::none()));
|
||||||
assert_eq!(interpret("x = 1 + 1"), Ok(Value::none()));
|
assert_eq!(interpret("x = 1 + 1"), Ok(Value::none()));
|
||||||
}
|
}
|
||||||
@ -14,14 +14,14 @@ fn integer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn integer_overflow() {
|
fn integer_saturation() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
interpret("9223372036854775807 + 1"),
|
interpret("9223372036854775807 + 1"),
|
||||||
Ok(Value::Integer(i64::MIN))
|
Ok(Value::Integer(i64::MAX))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
interpret("-9223372036854775808 - 1"),
|
interpret("-9223372036854775808 - 1"),
|
||||||
Ok(Value::Integer(i64::MAX))
|
Ok(Value::Integer(i64::MIN))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,6 +37,18 @@ fn float() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn float_saturation() {
|
||||||
|
assert_eq!(
|
||||||
|
interpret("1.7976931348623157e308 + 1"),
|
||||||
|
Ok(Value::Float(f64::MAX))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
interpret("-1.7976931348623157e308 - 1"),
|
||||||
|
Ok(Value::Float(f64::MIN))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn string() {
|
fn string() {
|
||||||
assert_eq!(interpret("\"one\""), Ok(Value::string("one".to_string())));
|
assert_eq!(interpret("\"one\""), Ok(Value::string("one".to_string())));
|
||||||
|
Loading…
Reference in New Issue
Block a user