1#![warn(missing_docs)]
100
101macro_rules! for_each {
102 ($(($pattern:tt) in [$( ($($choice:tt)*) ),*] => $code:tt;)*) => {
104 $(
105 macro_rules! inner { $pattern => $code; }
106
107 $(
108 inner!( $($choice)* );
109 )*
110 )*
111 };
112 ($($pattern:tt in [$($choice:ty),*] => $code:tt;)*) => {
114 $(
115 macro_rules! inner { $pattern => $code; }
116
117 $(
118 inner!($choice);
119 )*
120 )*
121 };
122}
123
124pub mod error;
125pub mod function;
126pub mod iter;
127pub mod value;
128mod visitor;
129
130pub use function::{DynFunction, FunctionCallError};
131pub use iter::{SomniIterator, WithIterator};
132pub use value::TypedValue;
133pub use visitor::ExpressionVisitor;
134
135use std::{
136 cell::RefCell,
137 collections::HashMap,
138 fmt::{Debug, Display},
139 rc::Rc,
140};
141
142use somni_parser::{
143 ast::{self, Expression, Function, Item, Program},
144 parser::{self, parse, TypeSet as ParserTypeSet},
145 Location,
146};
147
148use crate::{
149 error::MarkInSource,
150 function::ExprFn,
151 value::{LoadOwned, LoadStore, ValueType},
152};
153
154pub use somni_parser::parser::{DefaultTypeSet, TypeSet128, TypeSet32};
155
156pub trait TypeSet: Sized + Default + Debug + 'static {
160 type Parser: ParserTypeSet<Integer = Self::Integer, Float = Self::Float>;
162
163 type Integer: Copy + ValueType<NegateOutput: LoadStore<Self>> + LoadStore<Self>;
165
166 type SignedInteger: Copy + ValueType<NegateOutput: LoadStore<Self>> + LoadStore<Self>;
168
169 type Float: Copy + ValueType<NegateOutput: LoadStore<Self>> + LoadStore<Self>;
171
172 type String: ValueType<NegateOutput: LoadStore<Self>> + LoadStore<Self>;
174
175 type Iterator: Clone + PartialEq + Debug;
181
182 fn to_signed(v: Self::Integer) -> Result<Self::SignedInteger, OperatorError>;
184
185 fn to_usize(v: Self::Integer) -> Result<usize, OperatorError>;
187
188 fn int_from_usize(v: usize) -> Self::Integer;
190
191 fn load_string<'s>(&'s self, str: &'s Self::String) -> &'s str;
193
194 fn store_string(&mut self, str: &str) -> Self::String;
196
197 fn iter_has_next(&self, iter: &Self::Iterator) -> bool;
199
200 fn iter_next(&self, iter: &Self::Iterator) -> Option<TypedValue<Self>>;
203}
204
205#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
210pub enum NoIterator {}
211
212for_each! {
213 (($name:ident, $signed:ty)) in [(DefaultTypeSet, i64), (TypeSet32, i32), (TypeSet128, i128)] => {
214 impl TypeSet for $name {
215 type Parser = Self;
216
217 type Integer = <Self::Parser as ParserTypeSet>::Integer;
218 type SignedInteger = $signed;
219 type Float = <Self::Parser as ParserTypeSet>::Float;
220 type String = Box<str>;
221 type Iterator = NoIterator;
222
223 fn to_signed(v: Self::Integer) -> Result<Self::SignedInteger, OperatorError> {
224 <$signed>::try_from(v).map_err(|_| OperatorError::RuntimeError)
225 }
226
227 fn to_usize(v: Self::Integer) -> Result<usize, OperatorError> {
228 usize::try_from(v).map_err(|_| OperatorError::RuntimeError)
229 }
230
231 fn int_from_usize(v: usize) -> Self::Integer {
232 Self::Integer::try_from(v).unwrap()
233 }
234
235 fn load_string<'s>(&'s self, str: &'s Self::String) -> &'s str {
236 str
237 }
238
239 fn store_string(&mut self, str: &str) -> Self::String {
240 str.to_string().into_boxed_str()
241 }
242
243 fn iter_has_next(&self, iter: &Self::Iterator) -> bool {
244 match *iter {}
245 }
246
247 fn iter_next(&self, iter: &Self::Iterator) -> Option<TypedValue<Self>> {
248 match *iter {}
249 }
250 }
251 };
252}
253
254#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
256pub enum OperatorError {
257 TypeError,
259 RuntimeError,
261}
262
263impl Display for OperatorError {
264 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
265 let message = match self {
266 OperatorError::TypeError => "Type error",
267 OperatorError::RuntimeError => "Runtime error",
268 };
269
270 f.write_str(message)
271 }
272}
273
274macro_rules! dispatch_binary {
275 ($method:ident) => {
276 pub(crate) fn $method(ctx: &mut T, lhs: Self, rhs: Self) -> Result<Self, OperatorError> {
277 let result = match (lhs, rhs) {
278 (Self::Bool(value), Self::Bool(other)) => {
279 ValueType::$method(value, other)?.store(ctx)
280 }
281 (Self::Int(value), Self::Int(other)) => {
282 ValueType::$method(value, other)?.store(ctx)
283 }
284 (Self::SignedInt(value), Self::SignedInt(other)) => {
285 ValueType::$method(value, other)?.store(ctx)
286 }
287 (Self::MaybeSignedInt(value), Self::MaybeSignedInt(other)) => {
288 match ValueType::$method(value, other)?.store(ctx) {
289 Self::Int(v) => Self::MaybeSignedInt(v),
290 other => other,
291 }
292 }
293 (Self::Float(value), Self::Float(other)) => {
294 ValueType::$method(value, other)?.store(ctx)
295 }
296 (Self::String(value), Self::String(other)) => {
297 ValueType::$method(value, other)?.store(ctx)
298 }
299 (Self::Int(value), Self::MaybeSignedInt(other)) => {
300 ValueType::$method(value, other)?.store(ctx)
301 }
302 (Self::MaybeSignedInt(value), Self::Int(other)) => {
303 ValueType::$method(value, other)?.store(ctx)
304 }
305 (Self::SignedInt(value), Self::MaybeSignedInt(other)) => {
306 ValueType::$method(value, T::to_signed(other)?)?.store(ctx)
307 }
308 (Self::MaybeSignedInt(value), Self::SignedInt(other)) => {
309 ValueType::$method(T::to_signed(value)?, other)?.store(ctx)
310 }
311 _ => return Err(OperatorError::TypeError),
312 };
313
314 Ok(result)
315 }
316 };
317}
318
319macro_rules! dispatch_unary {
320 ($method:ident) => {
321 pub(crate) fn $method(ctx: &mut T, operand: Self) -> Result<Self, OperatorError> {
322 match operand {
323 Self::Bool(value) => Ok(ValueType::$method(value)?.store(ctx)),
324 Self::Int(value) | Self::MaybeSignedInt(value) => {
325 Ok(ValueType::$method(value)?.store(ctx))
326 }
327 Self::SignedInt(value) => Ok(ValueType::$method(value)?.store(ctx)),
328 Self::Float(value) => Ok(ValueType::$method(value)?.store(ctx)),
329 Self::String(value) => Ok(ValueType::$method(value)?.store(ctx)),
330 _ => return Err(OperatorError::TypeError),
331 }
332 }
333 };
334}
335
336impl<T> TypedValue<T>
337where
338 T: TypeSet,
339{
340 dispatch_binary!(equals);
341 dispatch_binary!(less_than);
342 dispatch_binary!(less_than_or_equal);
343 dispatch_binary!(not_equals);
344 dispatch_binary!(bitwise_or);
345 dispatch_binary!(bitwise_xor);
346 dispatch_binary!(bitwise_and);
347 dispatch_binary!(shift_left);
348 dispatch_binary!(shift_right);
349 dispatch_binary!(add);
350 dispatch_binary!(subtract);
351 dispatch_binary!(multiply);
352 dispatch_binary!(divide);
353 dispatch_binary!(modulo);
354 dispatch_unary!(not);
355 dispatch_unary!(negate);
356}
357
358pub trait ExprContext<T = DefaultTypeSet>
360where
361 T: TypeSet,
362{
363 fn type_context(&mut self) -> &mut T;
365
366 fn try_load_variable(&mut self, variable: &str) -> Option<TypedValue<T>>;
368
369 fn declare(&mut self, variable: &str, value: TypedValue<T>);
371
372 fn assign_variable(&mut self, variable: &str, value: &TypedValue<T>) -> Result<(), Box<str>>;
374
375 fn at_address(&mut self, address: TypedValue<T>) -> Result<TypedValue<T>, Box<str>>;
377
378 fn assign_address(
380 &mut self,
381 address: TypedValue<T>,
382 value: &TypedValue<T>,
383 ) -> Result<(), Box<str>>;
384
385 fn address_of(&mut self, variable: &str) -> TypedValue<T>;
387
388 fn open_scope(&mut self);
390
391 fn close_scope(&mut self);
393
394 fn call_function(
396 &mut self,
397 function_name: &str,
398 args: &[TypedValue<T>],
399 ) -> Result<TypedValue<T>, FunctionCallError>;
400}
401
402#[derive(Clone, Debug, PartialEq)]
404pub struct EvalError {
405 pub message: Box<str>,
407 pub location: Location,
409}
410
411impl Display for EvalError {
412 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
413 write!(f, "Evaluation error: {}", self.message)
414 }
415}
416
417#[derive(Clone, PartialEq)]
438pub struct ExpressionError<'s> {
439 error: EvalError,
440 source: &'s str,
441}
442
443impl ExpressionError<'_> {
444 pub fn into_inner(self) -> EvalError {
446 self.error
447 }
448}
449
450impl Debug for ExpressionError<'_> {
451 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
452 let marked = MarkInSource(
453 self.source,
454 self.error.location,
455 "Evaluation error",
456 &self.error.message,
457 );
458 marked.fmt(f)
459 }
460}
461
462#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
464pub enum Type {
465 Void,
467 MaybeSignedInt,
469 Int,
471 SignedInt,
473 Float,
475 Bool,
477 String,
479 Iter,
482}
483impl Type {
484 fn from_name(source: &str) -> Result<Self, Box<str>> {
485 match source {
486 "int" => Ok(Type::Int),
487 "signed" => Ok(Type::SignedInt),
488 "float" => Ok(Type::Float),
489 "bool" => Ok(Type::Bool),
490 "string" => Ok(Type::String),
491 "iter" => Ok(Type::Iter),
492 other => Err(format!("Unknown type `{other}`").into_boxed_str()),
493 }
494 }
495}
496
497impl Display for Type {
498 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
499 match self {
500 Type::Void => write!(f, "void"),
501 Type::MaybeSignedInt => write!(f, "{{int/signed}}"),
502 Type::Int => write!(f, "int"),
503 Type::SignedInt => write!(f, "signed"),
504 Type::Bool => write!(f, "bool"),
505 Type::String => write!(f, "string"),
506 Type::Float => write!(f, "float"),
507 Type::Iter => write!(f, "iter"),
508 }
509 }
510}
511
512enum InitializerState {
514 Unevaluated(usize),
516 Evaluating,
518}
519
520struct StackFrame<T: TypeSet> {
521 start_addr: usize,
522 variables: Vec<TypedValue<T>>,
523 scopes: Vec<HashMap<String, usize>>,
524}
525
526impl<T: TypeSet> StackFrame<T> {
527 fn new() -> StackFrame<T> {
528 StackFrame {
529 start_addr: 0,
530 variables: vec![],
531 scopes: vec![HashMap::new()],
532 }
533 }
534
535 fn next_call_frame(&self) -> StackFrame<T> {
536 StackFrame {
537 start_addr: self.start_addr + self.variables.len(),
538 variables: vec![],
539 scopes: vec![HashMap::new()],
540 }
541 }
542
543 fn declare(&mut self, variable: &str, value: TypedValue<T>) -> usize {
544 let index = self.variables.len();
545 self.variables.push(value);
546 self.scopes
547 .last_mut()
548 .unwrap()
549 .insert(variable.to_string(), index);
550 index + self.start_addr
551 }
552
553 fn lookup_index(&self, name: &str) -> Option<usize> {
554 for scope in self.scopes.iter().rev() {
555 if let Some(idx) = scope.get(name) {
556 return Some(*idx);
557 }
558 }
559 None
560 }
561
562 fn store(&mut self, variable: &str, value: &TypedValue<T>) -> bool {
563 if let Some(idx) = self.lookup_index(variable) {
564 self.variables.get_mut(idx).unwrap().clone_from(value);
565 true
566 } else {
567 false
568 }
569 }
570
571 fn lookup_by_address(&mut self, address: usize) -> Result<&mut TypedValue<T>, Box<str>> {
572 self.variables
573 .get_mut(address - self.start_addr)
574 .ok_or_else(|| format!("Invalid address {address}").into_boxed_str())
575 }
576
577 fn lookup_by_name<'s>(&'s mut self, variable: &str) -> Option<(usize, &'s mut TypedValue<T>)> {
578 let index = self.lookup_index(variable)?;
579 let address = index + self.start_addr;
580
581 Some((address, self.variables.get_mut(index).unwrap()))
582 }
583
584 fn open_scope(&mut self) {
585 self.scopes.push(HashMap::new());
586 }
587
588 fn close_scope(&mut self) {
589 self.scopes.pop().unwrap();
590 }
591}
592
593struct ProgramData<'ctx, T: TypeSet> {
594 source: &'ctx str,
595 program: Program<T::Parser>,
596 program_functions: HashMap<&'ctx str, usize>,
597 functions: RefCell<HashMap<&'ctx str, ExprFn<'ctx, T>>>,
599}
600
601impl<'ctx> Default for Context<'ctx, DefaultTypeSet> {
602 fn default() -> Self {
603 Self::new()
604 }
605}
606
607pub struct Context<'ctx, T = DefaultTypeSet>
609where
610 T: TypeSet,
611{
612 program: Rc<ProgramData<'ctx, T>>,
613 stack: Vec<StackFrame<T>>,
617 initializers: HashMap<&'ctx str, InitializerState>,
619 type_context: T,
620}
621
622impl<'ctx> Context<'ctx, DefaultTypeSet> {
623 pub fn new() -> Self {
625 Self::new_with_types()
626 }
627
628 pub fn parse(source: &'ctx str) -> Result<Self, ExpressionError<'ctx>> {
630 Self::parse_with_types(source)
631 }
632}
633
634const GLOBAL_VARIABLE: usize = usize::MAX - usize::MAX / 2;
635
636impl<'ctx, T> Context<'ctx, T>
637where
638 T: TypeSet,
639{
640 pub fn new_with_types() -> Self {
647 Self::new_from_program("", Program { items: vec![] })
648 }
649
650 pub fn parse_with_types(source: &'ctx str) -> Result<Self, ExpressionError<'ctx>> {
657 let program = parse::<T::Parser>(source).map_err(|e| ExpressionError {
658 error: EvalError {
659 message: format!("Failed to parse program: {e}").into_boxed_str(),
660 location: e.location,
661 },
662 source,
663 })?;
664
665 Ok(Self::new_from_program(source, program))
666 }
667
668 pub fn new_from_program(source: &'ctx str, program: Program<T::Parser>) -> Self {
670 let mut program_functions = HashMap::new();
671 let mut initializers = HashMap::new();
672 for (idx, item) in program.items.iter().enumerate() {
674 match item {
675 ast::Item::Function(function) => {
676 program_functions.insert(function.name.source(source), idx);
677 }
678 ast::Item::GlobalVariable(global_variable) => {
679 initializers.insert(
680 global_variable.identifier.source(source),
681 InitializerState::Unevaluated(idx),
682 );
683 }
684 ast::Item::ExternFunction(_) => {}
685 }
686 }
687 Self {
688 program: Rc::new(ProgramData {
689 source,
690 program,
691 program_functions,
692 functions: RefCell::new(HashMap::new()),
693 }),
694 stack: vec![StackFrame::new()],
695 type_context: T::default(),
696 initializers,
697 }
698 }
699
700 fn evaluate_any_function_impl(
701 &mut self,
702 function_name: &Function<T::Parser>,
703 args: &[TypedValue<T>],
704 ) -> Result<TypedValue<T>, EvalError> {
705 let source = self.program.clone().source;
706
707 let stack_frame = self
708 .stack
709 .last()
710 .expect("The global scope must always be present")
711 .next_call_frame();
712 self.stack.push(stack_frame);
713
714 let mut visitor = ExpressionVisitor::<Self, T> {
715 context: self,
716 source,
717 _marker: std::marker::PhantomData,
718 };
719
720 let result = visitor.visit_function(function_name, args);
721
722 self.stack.pop();
723
724 result
725 }
726
727 pub fn evaluate<'s, V>(&'s mut self, source: &'s str) -> Result<V::Output, ExpressionError<'s>>
741 where
742 V: LoadOwned<T>,
743 {
744 let expression =
745 parser::parse_expression::<T::Parser>(source).map_err(|e| ExpressionError {
746 error: EvalError {
747 message: format!("Parser error: {e}").into_boxed_str(),
748 location: e.location,
749 },
750 source,
751 })?;
752
753 self.evaluate_parsed::<V>(source, &expression)
754 }
755
756 pub fn evaluate_parsed<'s, V>(
773 &'s mut self,
774 source: &'s str,
775 expression: &Expression<T::Parser>,
776 ) -> Result<V::Output, ExpressionError<'s>>
777 where
778 V: LoadOwned<T>,
779 {
780 self.evaluate_impl::<V>(source, expression)
781 .map_err(|error| ExpressionError { error, source })
782 }
783
784 fn evaluate_impl<V>(
785 &mut self,
786 source: &str,
787 expression: &Expression<T::Parser>,
788 ) -> Result<V::Output, EvalError>
789 where
790 V: LoadOwned<T>,
791 {
792 let mut visitor = ExpressionVisitor::<Self, T> {
793 context: self,
794 source,
795 _marker: std::marker::PhantomData,
796 };
797 let result = visitor.visit_expression(expression)?;
798 let result_ty = result.type_of();
799 V::load_owned(self.type_context(), &result).ok_or_else(|| EvalError {
800 message: format!(
801 "Expression evaluates to {result_ty}, which cannot be converted to {}",
802 std::any::type_name::<V>()
803 )
804 .into_boxed_str(),
805 location: expression.location(),
806 })
807 }
808
809 pub fn add_variable<V>(&mut self, name: &'ctx str, value: V)
831 where
832 V: LoadStore<T>,
833 {
834 let stored = value.store(self.type_context());
835 self.stack[0].declare(name, stored);
836 }
837
838 pub fn add_function<F, A>(&mut self, name: &'ctx str, func: F)
850 where
851 F: DynFunction<A, T> + 'ctx,
852 {
853 self.program
854 .functions
855 .borrow_mut()
856 .insert(name, ExprFn::new(func));
857 }
858
859 fn lookup(&mut self, variable: &str) -> Option<(usize, TypedValue<T>)> {
860 if self.stack.len() > 1 {
861 let frame = self.stack.last_mut().unwrap();
862 if let Some((index, var)) = frame.lookup_by_name(variable) {
863 return Some((index, var.clone()));
865 }
866 }
867
868 {
869 let global_frame = &mut self.stack[0];
870 if let Some((index, var)) = global_frame.lookup_by_name(variable) {
871 return Some((index | GLOBAL_VARIABLE, var.clone()));
873 }
874 }
875
876 let state = self.initializers.get_mut(variable)?;
878 let InitializerState::Unevaluated(idx) =
879 std::mem::replace(state, InitializerState::Evaluating)
880 else {
881 return None;
882 };
883
884 let program = self.program.clone();
886 let Some(Item::GlobalVariable(global)) = program.program.items.get(idx) else {
887 return None;
888 };
889
890 let value = self
891 .evaluate_parsed::<TypedValue<T>>(self.program.source, &global.initializer)
892 .ok()?;
893
894 let global_frame = &mut self.stack[0];
895 let index = global_frame.declare(variable, value.clone());
896
897 Some((index | GLOBAL_VARIABLE, value))
898 }
899
900 fn lookup_address(&mut self, address: TypedValue<T>) -> Result<&mut TypedValue<T>, Box<str>> {
901 let TypedValue::Int(address) = address else {
902 return Err(format!("Expected address, got {address:?}").into_boxed_str());
903 };
904
905 let address = T::to_usize(address)
906 .map_err(|_| format!("Invalid address: {address:?}").into_boxed_str())?;
907
908 if address & GLOBAL_VARIABLE != 0 {
909 return self.stack[0].lookup_by_address(address & !GLOBAL_VARIABLE);
910 }
911
912 for frame in self.stack.iter_mut().rev() {
913 if frame.start_addr <= address {
914 return frame.lookup_by_address(address);
915 }
916 }
917
918 Err(format!("Not a valid memory address: {address}").into_boxed_str())
919 }
920}
921
922impl<T> ExprContext<T> for Context<'_, T>
923where
924 T: TypeSet,
925{
926 fn type_context(&mut self) -> &mut T {
927 &mut self.type_context
928 }
929
930 fn try_load_variable(&mut self, variable: &str) -> Option<TypedValue<T>> {
932 self.lookup(variable).map(|(_idx, var)| var)
933 }
934
935 fn address_of(&mut self, variable: &str) -> TypedValue<T> {
936 let address = self
937 .lookup(variable)
938 .map(|(address, _var)| address)
939 .unwrap();
940 TypedValue::Int(T::int_from_usize(address))
941 }
942
943 fn declare(&mut self, variable: &str, value: TypedValue<T>) {
945 self.stack.last_mut().unwrap().declare(variable, value);
946 }
947
948 fn assign_variable(&mut self, variable: &str, value: &TypedValue<T>) -> Result<(), Box<str>> {
950 if self.stack.last_mut().unwrap().store(variable, value) {
951 return Ok(());
952 }
953 if self.stack[0].store(variable, value) {
954 return Ok(());
955 }
956
957 Err(format!("Variable not found: {variable}").into_boxed_str())
958 }
959
960 fn at_address(&mut self, address: TypedValue<T>) -> Result<TypedValue<T>, Box<str>> {
961 self.lookup_address(address).cloned()
962 }
963
964 fn assign_address(
965 &mut self,
966 address: TypedValue<T>,
967 value: &TypedValue<T>,
968 ) -> Result<(), Box<str>> {
969 let v = self.lookup_address(address)?;
970 v.clone_from(value);
971 Ok(())
972 }
973
974 fn call_function(
975 &mut self,
976 function_name: &str,
977 args: &[TypedValue<T>],
978 ) -> Result<TypedValue<T>, FunctionCallError> {
979 let program = self.program.clone();
980 let Some(fn_item) = self.program.program_functions.get(function_name) else {
981 return match program.functions.borrow().get(function_name) {
983 Some(func) => func.call(self.type_context(), args),
984 None => Err(FunctionCallError::FunctionNotFound),
985 };
986 };
987
988 let Some(ast::Item::Function(function)) = program.program.items.get(*fn_item) else {
990 return Err(FunctionCallError::FunctionNotFound);
991 };
992 self.evaluate_any_function_impl(function, args)
993 .map_err(|err| {
994 FunctionCallError::Other(
995 format!(
996 "{:?}",
997 ExpressionError {
998 source: self.program.source,
999 error: err,
1000 }
1001 )
1002 .into_boxed_str(),
1003 )
1004 })
1005 }
1006
1007 fn open_scope(&mut self) {
1009 self.stack.last_mut().unwrap().open_scope();
1011 }
1012
1013 fn close_scope(&mut self) {
1015 self.stack.last_mut().unwrap().close_scope();
1017 }
1018}
1019
1020#[macro_export]
1021#[doc(hidden)]
1022macro_rules! for_all_tuples {
1023 ($pat:tt => $code:tt;) => {
1024 macro_rules! inner { $pat => $code; }
1025
1026 inner!();
1027 inner!(V1);
1028 inner!(V1, V2);
1029 inner!(V1, V2, V3);
1030 inner!(V1, V2, V3, V4);
1031 inner!(V1, V2, V3, V4, V5);
1032 inner!(V1, V2, V3, V4, V5, V6);
1033 inner!(V1, V2, V3, V4, V5, V6, V7);
1034 inner!(V1, V2, V3, V4, V5, V6, V7, V8);
1035 inner!(V1, V2, V3, V4, V5, V6, V7, V8, V9);
1036 inner!(V1, V2, V3, V4, V5, V6, V7, V8, V9, V10);
1037 };
1038}
1039
1040#[cfg(test)]
1041mod test {
1042 use std::path::Path;
1043
1044 use super::*;
1045
1046 fn strip_ansi(s: impl AsRef<str>) -> String {
1047 use ansi_parser::AnsiParser;
1048 fn text_block(output: ansi_parser::Output<'_>) -> Option<&str> {
1049 match output {
1050 ansi_parser::Output::TextBlock(text) => Some(text),
1051 _ => None,
1052 }
1053 }
1054
1055 s.as_ref()
1056 .ansi_parse()
1057 .filter_map(text_block)
1058 .collect::<String>()
1059 }
1060
1061 #[test]
1062 fn test_evaluating_exprs() {
1063 let mut ctx = Context::new();
1064
1065 ctx.add_variable::<i64>("signed", 30);
1066 ctx.add_variable::<u64>("value", 30);
1067 ctx.add_function("func", |v: u64| 2 * v);
1068 ctx.add_function("func2", |v1: u64, v2: u64| v1 + v2);
1069 ctx.add_function("five", || "five");
1070 ctx.add_function("is_five", |num: &str| num == "five");
1071 ctx.add_function("concatenate", |a: &str, b: &str| format!("{a}{b}"));
1072
1073 assert_eq!(ctx.evaluate::<bool>("value / 5 == 6"), Ok(true));
1074 assert_eq!(ctx.evaluate::<bool>("five() == \"five\""), Ok(true));
1075 assert_eq!(
1076 ctx.evaluate::<bool>("is_five(five()) != is_five(\"six\")"),
1077 Ok(true)
1078 );
1079 assert_eq!(ctx.evaluate::<u64>("func(20) / 5"), Ok(8));
1080 assert_eq!(
1081 ctx.evaluate::<TypedValue>("func(20) / 5"),
1082 Ok(TypedValue::Int(8))
1083 );
1084 assert_eq!(ctx.evaluate::<u64>("func2(20, 20) / 5"), Ok(8));
1085 assert_eq!(ctx.evaluate::<bool>("true & false"), Ok(false));
1086 assert_eq!(ctx.evaluate::<bool>("!true"), Ok(false));
1087 assert_eq!(ctx.evaluate::<bool>("false | false"), Ok(false));
1088 assert_eq!(ctx.evaluate::<bool>("true ^ true"), Ok(false));
1089 assert_eq!(ctx.evaluate::<u64>("!0x1111"), Ok(0xFFFF_FFFF_FFFF_EEEE));
1090 assert_eq!(
1091 ctx.evaluate::<String>("concatenate(five(), \"six\")"),
1092 Ok(String::from("fivesix"))
1093 );
1094 assert_eq!(ctx.evaluate::<bool>("signed * 2 == 60"), Ok(true));
1095 assert_eq!(ctx.evaluate::<i64>("*&signed"), Ok(30));
1096 }
1097
1098 #[test]
1099 fn test_context_is_mutable() {
1100 let mut ctx = Context::new();
1101
1102 ctx.add_variable::<u64>("value", 30);
1103
1104 ctx.evaluate::<()>("value = 5").unwrap();
1105 assert_eq!(ctx.evaluate::<bool>("value == 5"), Ok(true));
1106 }
1107
1108 #[test]
1109 fn test_evaluating_exprs_with_u32() {
1110 let mut ctx = Context::<TypeSet32>::new_with_types();
1111
1112 ctx.add_variable::<u32>("value", 30);
1113 ctx.add_function("func", |v: u32| 2 * v);
1114 ctx.add_function("func2", |v1: u32, v2: u32| v1 + v2);
1115
1116 assert_eq!(ctx.evaluate::<bool>("value / 5 == 6"), Ok(true));
1117 assert_eq!(ctx.evaluate::<u32>("func(20) / 5"), Ok(8));
1118 assert_eq!(ctx.evaluate::<u32>("func2(20, 20) / 5"), Ok(8));
1119 }
1120
1121 #[test]
1122 fn test_evaluating_exprs_with_u128() {
1123 let mut ctx = Context::<TypeSet128>::new_with_types();
1124
1125 ctx.add_variable::<u128>("value", 30);
1126 ctx.add_function("func", |v: u128| 2 * v);
1127 ctx.add_function("func2", |v1: u128, v2: u128| v1 + v2);
1128
1129 assert_eq!(ctx.evaluate::<bool>("value / 5 == 6"), Ok(true));
1130 assert_eq!(ctx.evaluate::<u128>("func(20) / 5"), Ok(8));
1131 assert_eq!(ctx.evaluate::<u128>("func2(20, 20) / 5"), Ok(8));
1132 }
1133
1134 #[test]
1135 fn test_evaluate_function() {
1136 let mut ctx =
1137 Context::parse("fn multiply_with_global(a: int) -> int { return a * global; }")
1138 .unwrap();
1139
1140 ctx.add_variable::<u64>("global", 3);
1141
1142 assert_eq!(
1143 ctx.evaluate::<bool>("multiply_with_global(2) == 6"),
1144 Ok(true)
1145 );
1146 assert!(ctx
1147 .evaluate::<bool>("multiply_with_global(\"2\") == 6")
1148 .is_err());
1149 }
1150
1151 #[test]
1152 fn run_eval_tests() {
1153 fn filter(path: &Path) -> bool {
1154 let Ok(env) = std::env::var("TEST_FILTER") else {
1155 return path.is_dir() || path.extension().map_or(false, |ext| ext == "sm");
1157 };
1158
1159 Path::new(&env) == path
1160 }
1161
1162 fn walk(dir: &Path, on_file: &impl Fn(&Path)) {
1163 for entry in std::fs::read_dir(dir)
1164 .unwrap_or_else(|_| panic!("Folder not found: {}", dir.display()))
1165 .flatten()
1166 {
1167 let path = entry.path();
1168
1169 if !filter(&path) {
1170 continue;
1171 }
1172
1173 if path.is_file() {
1174 on_file(&path);
1175 } else {
1176 walk(&path, on_file);
1177 }
1178 }
1179 }
1180
1181 fn run_eval_test(path: &Path) {
1182 type Types = WithIterator<DefaultTypeSet>;
1183
1184 fn parse(source: &str) -> Context<'_, Types> {
1185 let mut context = Context::<Types>::parse_with_types(source).unwrap();
1186
1187 context.add_function("add_from_rust", |a: u64, b: u64| -> i64 { (a + b) as i64 });
1188 context.add_function("assert", |a: bool| a); context.add_function("reverse", |s: &str| s.chars().rev().collect::<String>());
1190 context.add_function("range", |a: u64, b: u64| {
1191 SomniIterator::new((a..b).map(TypedValue::<DefaultTypeSet>::Int))
1192 });
1193
1194 context
1195 }
1196
1197 let test_name = path.file_stem().unwrap();
1198 let parent = path.parent().unwrap().canonicalize().unwrap();
1199 let vm_error = parent.join(test_name).join("stderr");
1200 let expr_error = parent.join(test_name).join("stderr_expr");
1201 let source = std::fs::read_to_string(path).unwrap();
1202
1203 let expressions = source
1204 .lines()
1205 .filter_map(|line| line.trim().strip_prefix("//@"))
1206 .collect::<Vec<_>>();
1207
1208 let mut context = parse(&source);
1209 let fail_expected = std::fs::exists(&expr_error).unwrap_or(false)
1210 || std::fs::exists(&vm_error).unwrap_or(false);
1211
1212 let blessed = std::env::var("BLESS").as_deref() == Ok("1");
1213
1214 for expression in &expressions {
1215 let expression = if let Some(e) = expression.strip_prefix('+') {
1216 e.trim()
1218 } else {
1219 context = parse(&source);
1221 expression
1222 };
1223 println!("Running `{expression}`");
1224 match context.evaluate::<TypedValue<Types>>(expression) {
1225 Ok(_) if fail_expected => {
1226 panic!(
1227 "Expected {} to fail evaluating, but it succeeded",
1228 path.display()
1229 )
1230 }
1231 Ok(value) => assert_eq!(
1232 value,
1233 TypedValue::Bool(true),
1234 "{}: Expression `{expression}` evaluated to {value:?}",
1235 path.display()
1236 ),
1237 Err(e) if fail_expected => {
1238 let error = strip_ansi(format!("{e:?}"));
1239 if blessed {
1240 std::fs::write(&expr_error, error).unwrap();
1241 } else {
1242 let expected_error = std::fs::read_to_string(&expr_error).unwrap();
1243 pretty_assertions::assert_eq!(strip_ansi(expected_error), error);
1244 }
1245 }
1246 Err(e) => panic!("{}: {e:?}", path.display()),
1247 };
1248 }
1249 }
1250
1251 walk("../tests/eval".as_ref(), &|path| {
1252 run_eval_test(path);
1253 });
1254 }
1255
1256 #[test]
1257 fn test_eval_error() {
1258 let mut ctx = Context::new();
1259
1260 ctx.add_function("func", |v1: u64, v2: u64| v1 + v2);
1261
1262 let err = ctx
1263 .evaluate::<u64>("func(20, true)")
1264 .expect_err("Expected expression to return an error");
1265
1266 pretty_assertions::assert_eq!(
1267 strip_ansi(format!("\n{err:?}")),
1268 r#"
1269Evaluation error
1270 ---> at line 1 column 10
1271 |
12721 | func(20, true)
1273 | ^^^^ func expects argument 1 to be u64, got bool"#,
1274 );
1275 }
1276}