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::{Place, Reference, SomniStruct, TypedValue};
133pub use visitor::ExpressionVisitor;
134
135#[doc(hidden)]
137pub use indexmap;
138
139#[macro_export]
155macro_rules! somni_struct {
156 ($ctx:ident, $name:ident { $($field:ident : $value:expr),* $(,)? }) => {{
157 let $ctx: &mut _ = $ctx;
158 let mut fields = $crate::indexmap::IndexMap::new();
159 $(
160 let value = $crate::value::LoadStore::store(&$value, $ctx);
161 fields.insert(::std::boxed::Box::<str>::from(stringify!($field)), value);
162 )*
163 $crate::SomniStruct::new(
164 ::std::boxed::Box::<str>::from(stringify!($name)),
165 fields,
166 )
167 }};
168}
169
170use std::{
171 cell::RefCell,
172 collections::HashMap,
173 fmt::{Debug, Display},
174 rc::Rc,
175};
176
177use somni_parser::{
178 Location,
179 ast::{self, Expression, Function, Item, Program},
180 parser::{self, TypeSet as ParserTypeSet, parse},
181};
182
183use crate::{
184 error::MarkInSource,
185 function::ExprFn,
186 value::{LoadOwned, LoadStore, ValueType},
187};
188
189pub use somni_parser::parser::{DefaultTypeSet, TypeSet32, TypeSet128};
190
191pub trait TypeSet: Sized + Default + Debug + 'static {
195 type Parser: ParserTypeSet<Integer = Self::Integer, Float = Self::Float>;
197
198 type Integer: Copy + ValueType<NegateOutput: LoadStore<Self>> + LoadStore<Self>;
200
201 type SignedInteger: Copy + ValueType<NegateOutput: LoadStore<Self>> + LoadStore<Self>;
203
204 type Float: Copy + ValueType<NegateOutput: LoadStore<Self>> + LoadStore<Self>;
206
207 type String: ValueType<NegateOutput: LoadStore<Self>> + LoadStore<Self>;
209
210 type Iterator: Clone + PartialEq + Debug;
216
217 fn to_signed(v: Self::Integer) -> Result<Self::SignedInteger, OperatorError>;
219
220 fn to_usize(v: Self::Integer) -> Result<usize, OperatorError>;
222
223 fn int_from_usize(v: usize) -> Self::Integer;
225
226 fn load_string<'s>(&'s self, str: &'s Self::String) -> &'s str;
228
229 fn store_string(&mut self, str: &str) -> Self::String;
231
232 fn iter_has_next(&self, iter: &Self::Iterator) -> bool;
234
235 fn iter_next(&self, iter: &Self::Iterator) -> Option<TypedValue<Self>>;
238}
239
240#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
245pub enum NoIterator {}
246
247for_each! {
248 (($name:ident, $signed:ty)) in [(DefaultTypeSet, i64), (TypeSet32, i32), (TypeSet128, i128)] => {
249 impl TypeSet for $name {
250 type Parser = Self;
251
252 type Integer = <Self::Parser as ParserTypeSet>::Integer;
253 type SignedInteger = $signed;
254 type Float = <Self::Parser as ParserTypeSet>::Float;
255 type String = Box<str>;
256 type Iterator = NoIterator;
257
258 fn to_signed(v: Self::Integer) -> Result<Self::SignedInteger, OperatorError> {
259 <$signed>::try_from(v).map_err(|_| OperatorError::RuntimeError)
260 }
261
262 fn to_usize(v: Self::Integer) -> Result<usize, OperatorError> {
263 usize::try_from(v).map_err(|_| OperatorError::RuntimeError)
264 }
265
266 fn int_from_usize(v: usize) -> Self::Integer {
267 Self::Integer::try_from(v).unwrap()
268 }
269
270 fn load_string<'s>(&'s self, str: &'s Self::String) -> &'s str {
271 str
272 }
273
274 fn store_string(&mut self, str: &str) -> Self::String {
275 str.to_string().into_boxed_str()
276 }
277
278 fn iter_has_next(&self, iter: &Self::Iterator) -> bool {
279 match *iter {}
280 }
281
282 fn iter_next(&self, iter: &Self::Iterator) -> Option<TypedValue<Self>> {
283 match *iter {}
284 }
285 }
286 };
287}
288
289#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
291pub enum OperatorError {
292 TypeError,
294 RuntimeError,
296}
297
298impl Display for OperatorError {
299 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
300 let message = match self {
301 OperatorError::TypeError => "Type error",
302 OperatorError::RuntimeError => "Runtime error",
303 };
304
305 f.write_str(message)
306 }
307}
308
309macro_rules! dispatch_binary {
310 ($method:ident) => {
311 pub(crate) fn $method(ctx: &mut T, lhs: Self, rhs: Self) -> Result<Self, OperatorError> {
312 let result = match (lhs, rhs) {
313 (Self::Bool(value), Self::Bool(other)) => {
314 ValueType::$method(value, other)?.store(ctx)
315 }
316 (Self::Int(value), Self::Int(other)) => {
317 ValueType::$method(value, other)?.store(ctx)
318 }
319 (Self::SignedInt(value), Self::SignedInt(other)) => {
320 ValueType::$method(value, other)?.store(ctx)
321 }
322 (Self::MaybeSignedInt(value), Self::MaybeSignedInt(other)) => {
323 match ValueType::$method(value, other)?.store(ctx) {
324 Self::Int(v) => Self::MaybeSignedInt(v),
325 other => other,
326 }
327 }
328 (Self::Float(value), Self::Float(other)) => {
329 ValueType::$method(value, other)?.store(ctx)
330 }
331 (Self::String(value), Self::String(other)) => {
332 ValueType::$method(value, other)?.store(ctx)
333 }
334 (Self::Int(value), Self::MaybeSignedInt(other)) => {
335 ValueType::$method(value, other)?.store(ctx)
336 }
337 (Self::MaybeSignedInt(value), Self::Int(other)) => {
338 ValueType::$method(value, other)?.store(ctx)
339 }
340 (Self::SignedInt(value), Self::MaybeSignedInt(other)) => {
341 ValueType::$method(value, T::to_signed(other)?)?.store(ctx)
342 }
343 (Self::MaybeSignedInt(value), Self::SignedInt(other)) => {
344 ValueType::$method(T::to_signed(value)?, other)?.store(ctx)
345 }
346 _ => return Err(OperatorError::TypeError),
347 };
348
349 Ok(result)
350 }
351 };
352}
353
354macro_rules! dispatch_unary {
355 ($method:ident) => {
356 pub(crate) fn $method(ctx: &mut T, operand: Self) -> Result<Self, OperatorError> {
357 match operand {
358 Self::Bool(value) => Ok(ValueType::$method(value)?.store(ctx)),
359 Self::Int(value) | Self::MaybeSignedInt(value) => {
360 Ok(ValueType::$method(value)?.store(ctx))
361 }
362 Self::SignedInt(value) => Ok(ValueType::$method(value)?.store(ctx)),
363 Self::Float(value) => Ok(ValueType::$method(value)?.store(ctx)),
364 Self::String(value) => Ok(ValueType::$method(value)?.store(ctx)),
365 _ => return Err(OperatorError::TypeError),
366 }
367 }
368 };
369}
370
371impl<T> TypedValue<T>
372where
373 T: TypeSet,
374{
375 dispatch_binary!(equals);
376 dispatch_binary!(less_than);
377 dispatch_binary!(less_than_or_equal);
378 dispatch_binary!(not_equals);
379 dispatch_binary!(bitwise_or);
380 dispatch_binary!(bitwise_xor);
381 dispatch_binary!(bitwise_and);
382 dispatch_binary!(shift_left);
383 dispatch_binary!(shift_right);
384 dispatch_binary!(add);
385 dispatch_binary!(subtract);
386 dispatch_binary!(multiply);
387 dispatch_binary!(divide);
388 dispatch_binary!(modulo);
389 dispatch_unary!(not);
390 dispatch_unary!(negate);
391}
392
393pub trait ExprContext<T = DefaultTypeSet>
395where
396 T: TypeSet,
397{
398 fn type_context(&mut self) -> &mut T;
400
401 fn try_load_variable(&mut self, variable: &str) -> Option<TypedValue<T>>;
403
404 fn declare(&mut self, variable: &str, value: TypedValue<T>);
406
407 fn assign_variable(&mut self, variable: &str, value: &TypedValue<T>) -> Result<(), Box<str>>;
409
410 fn place_of_variable(&mut self, variable: &str) -> Result<Place, Box<str>>;
412
413 fn load_place(&mut self, place: &Place) -> Result<TypedValue<T>, Box<str>>;
415
416 fn store_place(&mut self, place: &Place, value: &TypedValue<T>) -> Result<(), Box<str>>;
418
419 fn struct_fields(&self, struct_name: &str) -> Option<Vec<(Box<str>, Box<str>)>>;
425
426 fn open_scope(&mut self);
428
429 fn close_scope(&mut self);
431
432 fn call_function(
434 &mut self,
435 function_name: &str,
436 args: &[TypedValue<T>],
437 ) -> Result<TypedValue<T>, FunctionCallError>;
438}
439
440#[derive(Clone, Debug, PartialEq)]
442pub struct EvalError {
443 pub message: Box<str>,
445 pub location: Location,
447}
448
449impl Display for EvalError {
450 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
451 write!(f, "Evaluation error: {}", self.message)
452 }
453}
454
455#[derive(Clone, PartialEq)]
476pub struct ExpressionError<'s> {
477 error: EvalError,
478 source: &'s str,
479}
480
481impl ExpressionError<'_> {
482 pub fn into_inner(self) -> EvalError {
484 self.error
485 }
486}
487
488impl Debug for ExpressionError<'_> {
489 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
490 let marked = MarkInSource(
491 self.source,
492 self.error.location,
493 "Evaluation error",
494 &self.error.message,
495 );
496 marked.fmt(f)
497 }
498}
499
500#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
502pub enum Type {
503 Void,
505 MaybeSignedInt,
507 Int,
509 SignedInt,
511 Float,
513 Bool,
515 String,
517 Iter,
520 Struct,
523 Ref(RefPointee),
526}
527
528#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
534pub enum RefPointee {
535 Void,
537 Int,
539 SignedInt,
541 Float,
543 Bool,
545 String,
547 Iter,
549 Struct,
551}
552
553impl RefPointee {
554 pub fn from_type(ty: Type) -> Option<Self> {
559 Some(match ty {
560 Type::Void => RefPointee::Void,
561 Type::Int | Type::MaybeSignedInt => RefPointee::Int,
562 Type::SignedInt => RefPointee::SignedInt,
563 Type::Float => RefPointee::Float,
564 Type::Bool => RefPointee::Bool,
565 Type::String => RefPointee::String,
566 Type::Iter => RefPointee::Iter,
567 Type::Struct => RefPointee::Struct,
568 Type::Ref(_) => return None,
569 })
570 }
571}
572
573impl Display for RefPointee {
574 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
575 match self {
576 RefPointee::Void => write!(f, "void"),
577 RefPointee::Int => write!(f, "int"),
578 RefPointee::SignedInt => write!(f, "signed"),
579 RefPointee::Float => write!(f, "float"),
580 RefPointee::Bool => write!(f, "bool"),
581 RefPointee::String => write!(f, "string"),
582 RefPointee::Iter => write!(f, "iter"),
583 RefPointee::Struct => write!(f, "struct"),
584 }
585 }
586}
587
588impl Type {
589 fn from_name(source: &str) -> Result<Self, Box<str>> {
590 match source {
591 "int" => Ok(Type::Int),
592 "signed" => Ok(Type::SignedInt),
593 "float" => Ok(Type::Float),
594 "bool" => Ok(Type::Bool),
595 "string" => Ok(Type::String),
596 "iter" => Ok(Type::Iter),
597 other => Err(format!("Unknown type `{other}`").into_boxed_str()),
598 }
599 }
600}
601
602impl Display for Type {
603 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
604 match self {
605 Type::Void => write!(f, "void"),
606 Type::MaybeSignedInt => write!(f, "{{int/signed}}"),
607 Type::Int => write!(f, "int"),
608 Type::SignedInt => write!(f, "signed"),
609 Type::Bool => write!(f, "bool"),
610 Type::String => write!(f, "string"),
611 Type::Float => write!(f, "float"),
612 Type::Iter => write!(f, "iter"),
613 Type::Struct => write!(f, "struct"),
614 Type::Ref(pointee) => write!(f, "&{pointee}"),
615 }
616 }
617}
618
619enum InitializerState {
621 Unevaluated(usize),
623 Evaluating,
625}
626
627struct StackFrame<T: TypeSet> {
628 start_addr: usize,
629 variables: Vec<TypedValue<T>>,
630 scopes: Vec<HashMap<String, usize>>,
631}
632
633impl<T: TypeSet> StackFrame<T> {
634 fn new() -> StackFrame<T> {
635 StackFrame {
636 start_addr: 0,
637 variables: vec![],
638 scopes: vec![HashMap::new()],
639 }
640 }
641
642 fn next_call_frame(&self) -> StackFrame<T> {
643 StackFrame {
644 start_addr: self.start_addr + self.variables.len(),
645 variables: vec![],
646 scopes: vec![HashMap::new()],
647 }
648 }
649
650 fn declare(&mut self, variable: &str, value: TypedValue<T>) -> usize {
651 let index = self.variables.len();
652 self.variables.push(value);
653 self.scopes
654 .last_mut()
655 .unwrap()
656 .insert(variable.to_string(), index);
657 index + self.start_addr
658 }
659
660 fn lookup_index(&self, name: &str) -> Option<usize> {
661 for scope in self.scopes.iter().rev() {
662 if let Some(idx) = scope.get(name) {
663 return Some(*idx);
664 }
665 }
666 None
667 }
668
669 fn store(&mut self, variable: &str, value: &TypedValue<T>) -> bool {
670 if let Some(idx) = self.lookup_index(variable) {
671 self.variables.get_mut(idx).unwrap().clone_from(value);
672 true
673 } else {
674 false
675 }
676 }
677
678 fn lookup_by_address(&mut self, address: usize) -> Result<&mut TypedValue<T>, Box<str>> {
679 self.variables
680 .get_mut(address - self.start_addr)
681 .ok_or_else(|| format!("Invalid address {address}").into_boxed_str())
682 }
683
684 fn lookup_by_name<'s>(&'s mut self, variable: &str) -> Option<(usize, &'s mut TypedValue<T>)> {
685 let index = self.lookup_index(variable)?;
686 let address = index + self.start_addr;
687
688 Some((address, self.variables.get_mut(index).unwrap()))
689 }
690
691 fn open_scope(&mut self) {
692 self.scopes.push(HashMap::new());
693 }
694
695 fn close_scope(&mut self) {
696 self.scopes.pop().unwrap();
697 }
698}
699
700struct ProgramData<'ctx, T: TypeSet> {
701 source: &'ctx str,
702 program: Program<T::Parser>,
703 program_functions: HashMap<&'ctx str, usize>,
704 program_structs: HashMap<&'ctx str, usize>,
706 functions: RefCell<HashMap<&'ctx str, ExprFn<'ctx, T>>>,
708}
709
710impl<'ctx> Default for Context<'ctx, DefaultTypeSet> {
711 fn default() -> Self {
712 Self::new()
713 }
714}
715
716pub struct Context<'ctx, T = DefaultTypeSet>
718where
719 T: TypeSet,
720{
721 program: Rc<ProgramData<'ctx, T>>,
722 stack: Vec<StackFrame<T>>,
726 initializers: HashMap<&'ctx str, InitializerState>,
728 type_context: T,
729}
730
731impl<'ctx> Context<'ctx, DefaultTypeSet> {
732 pub fn new() -> Self {
734 Self::new_with_types()
735 }
736
737 pub fn parse(source: &'ctx str) -> Result<Self, ExpressionError<'ctx>> {
739 Self::parse_with_types(source)
740 }
741}
742
743const GLOBAL_VARIABLE: usize = usize::MAX - usize::MAX / 2;
744
745impl<'ctx, T> Context<'ctx, T>
746where
747 T: TypeSet,
748{
749 pub fn new_with_types() -> Self {
756 Self::new_from_program("", Program { items: vec![] })
757 }
758
759 pub fn parse_with_types(source: &'ctx str) -> Result<Self, ExpressionError<'ctx>> {
766 let program = parse::<T::Parser>(source).map_err(|e| ExpressionError {
767 error: EvalError {
768 message: format!("Failed to parse program: {e}").into_boxed_str(),
769 location: e.location,
770 },
771 source,
772 })?;
773
774 Ok(Self::new_from_program(source, program))
775 }
776
777 pub fn new_from_program(source: &'ctx str, program: Program<T::Parser>) -> Self {
779 let mut program_functions = HashMap::new();
780 let mut program_structs = HashMap::new();
781 let mut initializers = HashMap::new();
782 for (idx, item) in program.items.iter().enumerate() {
784 match item {
785 ast::Item::Function(function) => {
786 program_functions.insert(function.name.source(source), idx);
787 }
788 ast::Item::GlobalVariable(global_variable) => {
789 initializers.insert(
790 global_variable.identifier.source(source),
791 InitializerState::Unevaluated(idx),
792 );
793 }
794 ast::Item::Struct(struct_def) => {
795 program_structs.insert(struct_def.name.source(source), idx);
796 }
797 ast::Item::ExternFunction(_) => {}
798 }
799 }
800 Self {
801 program: Rc::new(ProgramData {
802 source,
803 program,
804 program_functions,
805 program_structs,
806 functions: RefCell::new(HashMap::new()),
807 }),
808 stack: vec![StackFrame::new()],
809 type_context: T::default(),
810 initializers,
811 }
812 }
813
814 fn evaluate_any_function_impl(
815 &mut self,
816 function_name: &Function<T::Parser>,
817 args: &[TypedValue<T>],
818 ) -> Result<TypedValue<T>, EvalError> {
819 let source = self.program.clone().source;
820
821 let stack_frame = self
822 .stack
823 .last()
824 .expect("The global scope must always be present")
825 .next_call_frame();
826 self.stack.push(stack_frame);
827
828 let mut visitor = ExpressionVisitor::<Self, T> {
829 context: self,
830 source,
831 _marker: std::marker::PhantomData,
832 };
833
834 let result = visitor.visit_function(function_name, args);
835
836 self.stack.pop();
837
838 result
839 }
840
841 pub fn evaluate<'s, V>(&'s mut self, source: &'s str) -> Result<V::Output, ExpressionError<'s>>
855 where
856 V: LoadOwned<T>,
857 {
858 let expression =
859 parser::parse_expression::<T::Parser>(source).map_err(|e| ExpressionError {
860 error: EvalError {
861 message: format!("Parser error: {e}").into_boxed_str(),
862 location: e.location,
863 },
864 source,
865 })?;
866
867 self.evaluate_parsed::<V>(source, &expression)
868 }
869
870 pub fn evaluate_parsed<'s, V>(
887 &'s mut self,
888 source: &'s str,
889 expression: &Expression<T::Parser>,
890 ) -> Result<V::Output, ExpressionError<'s>>
891 where
892 V: LoadOwned<T>,
893 {
894 self.evaluate_impl::<V>(source, expression)
895 .map_err(|error| ExpressionError { error, source })
896 }
897
898 fn evaluate_impl<V>(
899 &mut self,
900 source: &str,
901 expression: &Expression<T::Parser>,
902 ) -> Result<V::Output, EvalError>
903 where
904 V: LoadOwned<T>,
905 {
906 let mut visitor = ExpressionVisitor::<Self, T> {
907 context: self,
908 source,
909 _marker: std::marker::PhantomData,
910 };
911 let result = visitor.visit_expression(expression)?;
912 let result_ty = result.type_of();
913 V::load_owned(self.type_context(), &result).ok_or_else(|| EvalError {
914 message: format!(
915 "Expression evaluates to {result_ty}, which cannot be converted to {}",
916 std::any::type_name::<V>()
917 )
918 .into_boxed_str(),
919 location: expression.location(),
920 })
921 }
922
923 pub fn add_variable<V>(&mut self, name: &'ctx str, value: V)
945 where
946 V: LoadStore<T>,
947 {
948 let stored = value.store(self.type_context());
949 self.stack[0].declare(name, stored);
950 }
951
952 pub fn add_function<F, A>(&mut self, name: &'ctx str, func: F)
964 where
965 F: DynFunction<A, T> + 'ctx,
966 {
967 self.program
968 .functions
969 .borrow_mut()
970 .insert(name, ExprFn::new(func));
971 }
972
973 fn lookup(&mut self, variable: &str) -> Option<(usize, TypedValue<T>)> {
974 if self.stack.len() > 1 {
975 let frame = self.stack.last_mut().unwrap();
976 if let Some((index, var)) = frame.lookup_by_name(variable) {
977 return Some((index, var.clone()));
979 }
980 }
981
982 {
983 let global_frame = &mut self.stack[0];
984 if let Some((index, var)) = global_frame.lookup_by_name(variable) {
985 return Some((index | GLOBAL_VARIABLE, var.clone()));
987 }
988 }
989
990 let state = self.initializers.get_mut(variable)?;
992 let InitializerState::Unevaluated(idx) =
993 std::mem::replace(state, InitializerState::Evaluating)
994 else {
995 return None;
996 };
997
998 let program = self.program.clone();
1000 let Some(Item::GlobalVariable(global)) = program.program.items.get(idx) else {
1001 return None;
1002 };
1003
1004 let value = self
1005 .evaluate_parsed::<TypedValue<T>>(self.program.source, &global.initializer)
1006 .ok()?;
1007
1008 let global_frame = &mut self.stack[0];
1009 let index = global_frame.declare(variable, value.clone());
1010
1011 Some((index | GLOBAL_VARIABLE, value))
1012 }
1013
1014 fn lookup_address_raw(&mut self, address: usize) -> Result<&mut TypedValue<T>, Box<str>> {
1016 if address & GLOBAL_VARIABLE != 0 {
1017 return self.stack[0].lookup_by_address(address & !GLOBAL_VARIABLE);
1018 }
1019
1020 for frame in self.stack.iter_mut().rev() {
1021 if frame.start_addr <= address {
1022 return frame.lookup_by_address(address);
1023 }
1024 }
1025
1026 Err(format!("Not a valid memory address: {address}").into_boxed_str())
1027 }
1028
1029 fn resolve_place_mut(&mut self, place: &Place) -> Result<&mut TypedValue<T>, Box<str>> {
1031 let mut current = self.lookup_address_raw(place.root)?;
1032 for field in place.path.iter() {
1033 let TypedValue::Struct(structure) = current else {
1034 return Err(
1035 format!("Cannot access field `{field}` of a non-struct value").into_boxed_str(),
1036 );
1037 };
1038 let struct_name = structure.name().to_string();
1039 current = structure.fields_mut().get_mut(&**field).ok_or_else(|| {
1040 format!("Struct `{struct_name}` has no field `{field}`").into_boxed_str()
1041 })?;
1042 }
1043 Ok(current)
1044 }
1045}
1046
1047impl<T> ExprContext<T> for Context<'_, T>
1048where
1049 T: TypeSet,
1050{
1051 fn type_context(&mut self) -> &mut T {
1052 &mut self.type_context
1053 }
1054
1055 fn try_load_variable(&mut self, variable: &str) -> Option<TypedValue<T>> {
1057 self.lookup(variable).map(|(_idx, var)| var)
1058 }
1059
1060 fn place_of_variable(&mut self, variable: &str) -> Result<Place, Box<str>> {
1061 let root = self
1062 .lookup(variable)
1063 .map(|(address, _var)| address)
1064 .ok_or_else(|| format!("Variable not found: {variable}").into_boxed_str())?;
1065 Ok(Place {
1066 root,
1067 path: Box::new([]),
1068 })
1069 }
1070
1071 fn load_place(&mut self, place: &Place) -> Result<TypedValue<T>, Box<str>> {
1072 self.resolve_place_mut(place).map(|v| v.clone())
1073 }
1074
1075 fn store_place(&mut self, place: &Place, value: &TypedValue<T>) -> Result<(), Box<str>> {
1076 let slot = self.resolve_place_mut(place)?;
1077 slot.clone_from(value);
1078 Ok(())
1079 }
1080
1081 fn struct_fields(&self, struct_name: &str) -> Option<Vec<(Box<str>, Box<str>)>> {
1082 let idx = *self.program.program_structs.get(struct_name)?;
1083 let Some(Item::Struct(struct_def)) = self.program.program.items.get(idx) else {
1084 return None;
1085 };
1086 let source = self.program.source;
1087 Some(
1088 struct_def
1089 .fields
1090 .iter()
1091 .map(|field| {
1092 (
1093 Box::from(field.name.source(source)),
1094 Box::from(field.field_type.type_name.source(source)),
1095 )
1096 })
1097 .collect(),
1098 )
1099 }
1100
1101 fn declare(&mut self, variable: &str, value: TypedValue<T>) {
1103 self.stack.last_mut().unwrap().declare(variable, value);
1104 }
1105
1106 fn assign_variable(&mut self, variable: &str, value: &TypedValue<T>) -> Result<(), Box<str>> {
1108 if self.stack.last_mut().unwrap().store(variable, value) {
1109 return Ok(());
1110 }
1111 if self.stack[0].store(variable, value) {
1112 return Ok(());
1113 }
1114
1115 Err(format!("Variable not found: {variable}").into_boxed_str())
1116 }
1117
1118 fn call_function(
1119 &mut self,
1120 function_name: &str,
1121 args: &[TypedValue<T>],
1122 ) -> Result<TypedValue<T>, FunctionCallError> {
1123 let program = self.program.clone();
1124 let Some(fn_item) = self.program.program_functions.get(function_name) else {
1125 return match program.functions.borrow().get(function_name) {
1127 Some(func) => func.call(self.type_context(), args),
1128 None => Err(FunctionCallError::FunctionNotFound),
1129 };
1130 };
1131
1132 let Some(ast::Item::Function(function)) = program.program.items.get(*fn_item) else {
1134 return Err(FunctionCallError::FunctionNotFound);
1135 };
1136 self.evaluate_any_function_impl(function, args)
1137 .map_err(|err| {
1138 FunctionCallError::Other(
1139 format!(
1140 "{:?}",
1141 ExpressionError {
1142 source: self.program.source,
1143 error: err,
1144 }
1145 )
1146 .into_boxed_str(),
1147 )
1148 })
1149 }
1150
1151 fn open_scope(&mut self) {
1153 self.stack.last_mut().unwrap().open_scope();
1155 }
1156
1157 fn close_scope(&mut self) {
1159 self.stack.last_mut().unwrap().close_scope();
1161 }
1162}
1163
1164#[macro_export]
1165#[doc(hidden)]
1166macro_rules! for_all_tuples {
1167 ($pat:tt => $code:tt;) => {
1168 macro_rules! inner { $pat => $code; }
1169
1170 inner!();
1171 inner!(V1);
1172 inner!(V1, V2);
1173 inner!(V1, V2, V3);
1174 inner!(V1, V2, V3, V4);
1175 inner!(V1, V2, V3, V4, V5);
1176 inner!(V1, V2, V3, V4, V5, V6);
1177 inner!(V1, V2, V3, V4, V5, V6, V7);
1178 inner!(V1, V2, V3, V4, V5, V6, V7, V8);
1179 inner!(V1, V2, V3, V4, V5, V6, V7, V8, V9);
1180 inner!(V1, V2, V3, V4, V5, V6, V7, V8, V9, V10);
1181 };
1182}
1183
1184#[cfg(test)]
1185mod test {
1186 use std::path::Path;
1187
1188 use super::*;
1189
1190 fn strip_ansi(s: impl AsRef<str>) -> String {
1191 use ansi_parser::AnsiParser;
1192 fn text_block(output: ansi_parser::Output<'_>) -> Option<&str> {
1193 match output {
1194 ansi_parser::Output::TextBlock(text) => Some(text),
1195 _ => None,
1196 }
1197 }
1198
1199 s.as_ref()
1200 .ansi_parse()
1201 .filter_map(text_block)
1202 .collect::<String>()
1203 }
1204
1205 #[test]
1206 fn test_evaluating_exprs() {
1207 let mut ctx = Context::new();
1208
1209 ctx.add_variable::<i64>("signed", 30);
1210 ctx.add_variable::<u64>("value", 30);
1211 ctx.add_function("func", |v: u64| 2 * v);
1212 ctx.add_function("func2", |v1: u64, v2: u64| v1 + v2);
1213 ctx.add_function("five", || "five");
1214 ctx.add_function("is_five", |num: &str| num == "five");
1215 ctx.add_function("concatenate", |a: &str, b: &str| format!("{a}{b}"));
1216
1217 assert_eq!(ctx.evaluate::<bool>("value / 5 == 6"), Ok(true));
1218 assert_eq!(ctx.evaluate::<bool>("five() == \"five\""), Ok(true));
1219 assert_eq!(
1220 ctx.evaluate::<bool>("is_five(five()) != is_five(\"six\")"),
1221 Ok(true)
1222 );
1223 assert_eq!(ctx.evaluate::<u64>("func(20) / 5"), Ok(8));
1224 assert_eq!(
1225 ctx.evaluate::<TypedValue>("func(20) / 5"),
1226 Ok(TypedValue::Int(8))
1227 );
1228 assert_eq!(ctx.evaluate::<u64>("func2(20, 20) / 5"), Ok(8));
1229 assert_eq!(ctx.evaluate::<bool>("true & false"), Ok(false));
1230 assert_eq!(ctx.evaluate::<bool>("!true"), Ok(false));
1231 assert_eq!(ctx.evaluate::<bool>("false | false"), Ok(false));
1232 assert_eq!(ctx.evaluate::<bool>("true ^ true"), Ok(false));
1233 assert_eq!(ctx.evaluate::<u64>("!0x1111"), Ok(0xFFFF_FFFF_FFFF_EEEE));
1234 assert_eq!(
1235 ctx.evaluate::<String>("concatenate(five(), \"six\")"),
1236 Ok(String::from("fivesix"))
1237 );
1238 assert_eq!(ctx.evaluate::<bool>("signed * 2 == 60"), Ok(true));
1239 assert_eq!(ctx.evaluate::<i64>("*&signed"), Ok(30));
1240 }
1241
1242 #[test]
1243 fn test_context_is_mutable() {
1244 let mut ctx = Context::new();
1245
1246 ctx.add_variable::<u64>("value", 30);
1247
1248 ctx.evaluate::<()>("value = 5").unwrap();
1249 assert_eq!(ctx.evaluate::<bool>("value == 5"), Ok(true));
1250 }
1251
1252 #[test]
1253 fn test_evaluating_exprs_with_u32() {
1254 let mut ctx = Context::<TypeSet32>::new_with_types();
1255
1256 ctx.add_variable::<u32>("value", 30);
1257 ctx.add_function("func", |v: u32| 2 * v);
1258 ctx.add_function("func2", |v1: u32, v2: u32| v1 + v2);
1259
1260 assert_eq!(ctx.evaluate::<bool>("value / 5 == 6"), Ok(true));
1261 assert_eq!(ctx.evaluate::<u32>("func(20) / 5"), Ok(8));
1262 assert_eq!(ctx.evaluate::<u32>("func2(20, 20) / 5"), Ok(8));
1263 }
1264
1265 #[test]
1266 fn test_evaluating_exprs_with_u128() {
1267 let mut ctx = Context::<TypeSet128>::new_with_types();
1268
1269 ctx.add_variable::<u128>("value", 30);
1270 ctx.add_function("func", |v: u128| 2 * v);
1271 ctx.add_function("func2", |v1: u128, v2: u128| v1 + v2);
1272
1273 assert_eq!(ctx.evaluate::<bool>("value / 5 == 6"), Ok(true));
1274 assert_eq!(ctx.evaluate::<u128>("func(20) / 5"), Ok(8));
1275 assert_eq!(ctx.evaluate::<u128>("func2(20, 20) / 5"), Ok(8));
1276 }
1277
1278 #[test]
1279 fn test_evaluate_function() {
1280 let mut ctx =
1281 Context::parse("fn multiply_with_global(a: int) -> int { return a * global; }")
1282 .unwrap();
1283
1284 ctx.add_variable::<u64>("global", 3);
1285
1286 assert_eq!(
1287 ctx.evaluate::<bool>("multiply_with_global(2) == 6"),
1288 Ok(true)
1289 );
1290 assert!(
1291 ctx.evaluate::<bool>("multiply_with_global(\"2\") == 6")
1292 .is_err()
1293 );
1294 }
1295
1296 #[test]
1297 fn run_eval_tests() {
1298 fn filter(path: &Path) -> bool {
1299 let Ok(env) = std::env::var("TEST_FILTER") else {
1300 return path.is_dir() || path.extension().map_or(false, |ext| ext == "sm");
1302 };
1303
1304 Path::new(&env) == path
1305 }
1306
1307 fn walk(dir: &Path, on_file: &impl Fn(&Path)) {
1308 for entry in std::fs::read_dir(dir)
1309 .unwrap_or_else(|_| panic!("Folder not found: {}", dir.display()))
1310 .flatten()
1311 {
1312 let path = entry.path();
1313
1314 if !filter(&path) {
1315 continue;
1316 }
1317
1318 if path.is_file() {
1319 on_file(&path);
1320 } else {
1321 walk(&path, on_file);
1322 }
1323 }
1324 }
1325
1326 fn run_eval_test(path: &Path) {
1327 type Types = WithIterator<DefaultTypeSet>;
1328
1329 fn parse(source: &str) -> Context<'_, Types> {
1330 let mut context = Context::<Types>::parse_with_types(source).unwrap();
1331
1332 context.add_function("add_from_rust", |a: u64, b: u64| -> i64 { (a + b) as i64 });
1333 context.add_function("assert", |a: bool| a); context.add_function("reverse", |s: &str| s.chars().rev().collect::<String>());
1335 context.add_function("range", |a: u64, b: u64| {
1336 SomniIterator::new((a..b).map(TypedValue::<DefaultTypeSet>::Int))
1337 });
1338
1339 context
1340 }
1341
1342 let test_name = path.file_stem().unwrap();
1343 let parent = path.parent().unwrap().canonicalize().unwrap();
1344 let vm_error = parent.join(test_name).join("stderr");
1345 let expr_error = parent.join(test_name).join("stderr_expr");
1346 let source = std::fs::read_to_string(path).unwrap();
1347
1348 let expressions = source
1349 .lines()
1350 .filter_map(|line| line.trim().strip_prefix("//@"))
1351 .collect::<Vec<_>>();
1352
1353 let mut context = parse(&source);
1354 let fail_expected = std::fs::exists(&expr_error).unwrap_or(false)
1355 || std::fs::exists(&vm_error).unwrap_or(false);
1356
1357 let blessed = std::env::var("BLESS").as_deref() == Ok("1");
1358
1359 for expression in &expressions {
1360 let expression = if let Some(e) = expression.strip_prefix('+') {
1361 e.trim()
1363 } else {
1364 context = parse(&source);
1366 expression
1367 };
1368 println!("Running `{expression}`");
1369 match context.evaluate::<TypedValue<Types>>(expression) {
1370 Ok(_) if fail_expected => {
1371 panic!(
1372 "Expected {} to fail evaluating, but it succeeded",
1373 path.display()
1374 )
1375 }
1376 Ok(value) => assert_eq!(
1377 value,
1378 TypedValue::Bool(true),
1379 "{}: Expression `{expression}` evaluated to {value:?}",
1380 path.display()
1381 ),
1382 Err(e) if fail_expected => {
1383 let error = strip_ansi(format!("{e:?}"));
1384 if blessed {
1385 std::fs::write(&expr_error, error).unwrap();
1386 } else {
1387 let expected_error = std::fs::read_to_string(&expr_error).unwrap();
1388 pretty_assertions::assert_eq!(strip_ansi(expected_error), error);
1389 }
1390 }
1391 Err(e) => panic!("{}: {e:?}", path.display()),
1392 };
1393 }
1394 }
1395
1396 walk("../tests/eval".as_ref(), &|path| {
1397 run_eval_test(path);
1398 });
1399 }
1400
1401 #[test]
1402 fn test_struct_literals_and_field_access() {
1403 let program = r#"
1404struct Point { x: int, y: int }
1405
1406fn make() -> Point {
1407 return Point { x: 3, y: 4 };
1408}
1409
1410fn sum_sq() -> int {
1411 var p = make();
1412 return p.x * p.x + p.y * p.y;
1413}
1414"#;
1415 let mut ctx = Context::parse(program).unwrap();
1416 assert_eq!(ctx.evaluate::<bool>("sum_sq() == 25"), Ok(true));
1417 }
1418
1419 #[test]
1420 fn test_struct_field_write() {
1421 let program = r#"
1422struct Point { x: int, y: int }
1423
1424fn moved() -> int {
1425 var p = Point { x: 1, y: 2 };
1426 p.x = 10;
1427 p.y = p.y + 5;
1428 return p.x + p.y;
1429}
1430"#;
1431 let mut ctx = Context::parse(program).unwrap();
1432 assert_eq!(ctx.evaluate::<bool>("moved() == 17"), Ok(true));
1433 }
1434
1435 #[test]
1436 fn test_nested_struct() {
1437 let program = r#"
1438struct Point { x: int, y: int }
1439struct Line { start: Point, end: Point }
1440
1441fn build() -> int {
1442 var l = Line { start: Point { x: 1, y: 2 }, end: Point { x: 3, y: 4 } };
1443 l.end.x = 30;
1444 return l.start.x + l.end.x;
1445}
1446"#;
1447 let mut ctx = Context::parse(program).unwrap();
1448 assert_eq!(ctx.evaluate::<bool>("build() == 31"), Ok(true));
1449 }
1450
1451 #[test]
1452 fn test_struct_pass_by_reference_and_autoderef() {
1453 let program = r#"
1454struct Point { x: int, y: int }
1455
1456fn scale(p: &Point, factor: int) {
1457 p.x = p.x * factor;
1458 p.y = p.y * factor;
1459}
1460
1461fn run() -> int {
1462 var p = Point { x: 2, y: 3 };
1463 scale(&p, 4);
1464 return p.x + p.y;
1465}
1466"#;
1467 let mut ctx = Context::parse(program).unwrap();
1468 assert_eq!(ctx.evaluate::<bool>("run() == 20"), Ok(true));
1469 }
1470
1471 #[test]
1472 fn test_reference_to_field() {
1473 let program = r#"
1474struct Point { x: int, y: int }
1475
1476fn double(v: &int) {
1477 *v = *v * 2;
1478}
1479
1480fn run() -> int {
1481 var p = Point { x: 5, y: 6 };
1482 double(&p.x);
1483 return p.x;
1484}
1485"#;
1486 let mut ctx = Context::parse(program).unwrap();
1487 assert_eq!(ctx.evaluate::<bool>("run() == 10"), Ok(true));
1488 }
1489
1490 #[test]
1491 fn test_struct_equality() {
1492 let program = r#"
1493struct Point { x: int, y: int }
1494
1495fn a() -> Point { return Point { x: 1, y: 2 }; }
1496fn b() -> Point { return Point { x: 1, y: 2 }; }
1497fn c() -> Point { return Point { x: 1, y: 9 }; }
1498"#;
1499 let mut ctx = Context::parse(program).unwrap();
1500 assert_eq!(ctx.evaluate::<bool>("a() == b()"), Ok(true));
1501 assert_eq!(ctx.evaluate::<bool>("a() != c()"), Ok(true));
1502 assert_eq!(ctx.evaluate::<bool>("a() == c()"), Ok(false));
1503 }
1504
1505 #[test]
1506 fn test_struct_boundary_and_macro() {
1507 let program = r#"
1508struct Point { x: int, y: int }
1509"#;
1510 let mut ctx = Context::parse(program).unwrap();
1511 ctx.add_function("origin_distance_sq", |p: SomniStruct| -> i64 {
1512 let TypedValue::Int(x) = p.fields()["x"] else {
1513 panic!("x not an int")
1514 };
1515 let TypedValue::Int(y) = p.fields()["y"] else {
1516 panic!("y not an int")
1517 };
1518 (x * x + y * y) as i64
1519 });
1520
1521 let tc = ctx.type_context();
1523 let point = somni_struct!(tc, Point { x: 3u64, y: 4u64 });
1524 assert_eq!(point.name(), "Point");
1525
1526 assert_eq!(
1527 ctx.evaluate::<bool>("origin_distance_sq(Point { x: 3, y: 4 }) == 25"),
1528 Ok(true)
1529 );
1530 }
1531
1532 #[test]
1533 fn test_unknown_struct_is_error() {
1534 let mut ctx = Context::new();
1535 assert!(ctx.evaluate::<TypedValue>("Nope { x: 1 }").is_err());
1536 }
1537
1538 #[test]
1539 fn test_eval_error() {
1540 let mut ctx = Context::new();
1541
1542 ctx.add_function("func", |v1: u64, v2: u64| v1 + v2);
1543
1544 let err = ctx
1545 .evaluate::<u64>("func(20, true)")
1546 .expect_err("Expected expression to return an error");
1547
1548 pretty_assertions::assert_eq!(
1549 strip_ansi(format!("\n{err:?}")),
1550 r#"
1551Evaluation error
1552 ---> at line 1 column 10
1553 |
15541 | func(20, true)
1555 | ^^^^ func expects argument 1 to be u64, got bool"#,
1556 );
1557 }
1558}