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<Scope>,
631}
632
633struct Scope {
634 variable_start: usize,
635 variables: HashMap<String, usize>,
636}
637
638impl<T: TypeSet> StackFrame<T> {
639 fn new() -> StackFrame<T> {
640 StackFrame {
641 start_addr: 0,
642 variables: vec![],
643 scopes: vec![Scope {
644 variable_start: 0,
645 variables: HashMap::new(),
646 }],
647 }
648 }
649
650 fn next_address(&self) -> usize {
651 self.start_addr + self.variables.len()
652 }
653
654 fn reset(&mut self, start_addr: usize) {
655 self.start_addr = start_addr;
656 self.variables.clear();
657 self.scopes.truncate(1);
658 self.scopes[0].variable_start = 0;
659 self.scopes[0].variables.clear();
660 }
661
662 fn declare(&mut self, variable: &str, value: TypedValue<T>) -> usize {
663 let index = self.variables.len();
664 self.variables.push(value);
665 self.scopes
666 .last_mut()
667 .unwrap()
668 .variables
669 .insert(variable.to_string(), index);
670 index + self.start_addr
671 }
672
673 fn lookup_index(&self, name: &str) -> Option<usize> {
674 for scope in self.scopes.iter().rev() {
675 if let Some(idx) = scope.variables.get(name) {
676 return Some(*idx);
677 }
678 }
679 None
680 }
681
682 fn store(&mut self, variable: &str, value: &TypedValue<T>) -> bool {
683 if let Some(idx) = self.lookup_index(variable) {
684 self.variables.get_mut(idx).unwrap().clone_from(value);
685 true
686 } else {
687 false
688 }
689 }
690
691 fn lookup_by_address(&mut self, address: usize) -> Result<&mut TypedValue<T>, Box<str>> {
692 self.variables
693 .get_mut(address - self.start_addr)
694 .ok_or_else(|| format!("Invalid address {address}").into_boxed_str())
695 }
696
697 fn lookup_by_name<'s>(&'s mut self, variable: &str) -> Option<(usize, &'s mut TypedValue<T>)> {
698 let index = self.lookup_index(variable)?;
699 let address = index + self.start_addr;
700
701 Some((address, self.variables.get_mut(index).unwrap()))
702 }
703
704 fn open_scope(&mut self) {
705 self.scopes.push(Scope {
706 variable_start: self.variables.len(),
707 variables: HashMap::new(),
708 });
709 }
710
711 fn close_scope(&mut self) {
712 let scope = self.scopes.pop().unwrap();
713 self.variables.truncate(scope.variable_start);
714 }
715}
716
717struct ProgramData<'ctx, T: TypeSet> {
718 source: &'ctx str,
719 program: Program<T::Parser>,
720 program_functions: HashMap<&'ctx str, usize>,
721 program_structs: HashMap<&'ctx str, usize>,
723 functions: RefCell<HashMap<&'ctx str, ExprFn<'ctx, T>>>,
725}
726
727impl<'ctx> Default for Context<'ctx, DefaultTypeSet> {
728 fn default() -> Self {
729 Self::new()
730 }
731}
732
733pub struct Context<'ctx, T = DefaultTypeSet>
735where
736 T: TypeSet,
737{
738 program: Rc<ProgramData<'ctx, T>>,
739 stack: Vec<StackFrame<T>>,
743 frame_pool: Vec<StackFrame<T>>,
745 initializers: HashMap<&'ctx str, InitializerState>,
747 type_context: T,
748}
749
750impl<'ctx> Context<'ctx, DefaultTypeSet> {
751 pub fn new() -> Self {
753 Self::new_with_types()
754 }
755
756 pub fn parse(source: &'ctx str) -> Result<Self, ExpressionError<'ctx>> {
758 Self::parse_with_types(source)
759 }
760}
761
762const GLOBAL_VARIABLE: usize = usize::MAX - usize::MAX / 2;
763
764impl<'ctx, T> Context<'ctx, T>
765where
766 T: TypeSet,
767{
768 pub fn new_with_types() -> Self {
775 Self::new_from_program("", Program { items: vec![] })
776 }
777
778 pub fn parse_with_types(source: &'ctx str) -> Result<Self, ExpressionError<'ctx>> {
785 let program = parse::<T::Parser>(source).map_err(|e| ExpressionError {
786 error: EvalError {
787 message: format!("Failed to parse program: {e}").into_boxed_str(),
788 location: e.location,
789 },
790 source,
791 })?;
792
793 Ok(Self::new_from_program(source, program))
794 }
795
796 pub fn new_from_program(source: &'ctx str, program: Program<T::Parser>) -> Self {
798 let mut program_functions = HashMap::new();
799 let mut program_structs = HashMap::new();
800 let mut initializers = HashMap::new();
801 for (idx, item) in program.items.iter().enumerate() {
803 match item {
804 ast::Item::Function(function) => {
805 program_functions.insert(function.name.source(source), idx);
806 }
807 ast::Item::GlobalVariable(global_variable) => {
808 initializers.insert(
809 global_variable.identifier.source(source),
810 InitializerState::Unevaluated(idx),
811 );
812 }
813 ast::Item::Struct(struct_def) => {
814 program_structs.insert(struct_def.name.source(source), idx);
815 }
816 ast::Item::ExternFunction(_) => {}
817 }
818 }
819 Self {
820 program: Rc::new(ProgramData {
821 source,
822 program,
823 program_functions,
824 program_structs,
825 functions: RefCell::new(HashMap::new()),
826 }),
827 stack: vec![StackFrame::new()],
828 frame_pool: Vec::new(),
829 type_context: T::default(),
830 initializers,
831 }
832 }
833
834 fn evaluate_any_function_impl(
835 &mut self,
836 function_name: &Function<T::Parser>,
837 args: &[TypedValue<T>],
838 ) -> Result<TypedValue<T>, EvalError> {
839 let source = self.program.clone().source;
840
841 let start_addr = self
842 .stack
843 .last()
844 .expect("The global scope must always be present")
845 .next_address();
846 let mut stack_frame = self.frame_pool.pop().unwrap_or_else(StackFrame::new);
847 stack_frame.reset(start_addr);
848 self.stack.push(stack_frame);
849
850 let mut visitor = ExpressionVisitor::<Self, T> {
851 context: self,
852 source,
853 _marker: std::marker::PhantomData,
854 };
855
856 let result = visitor.visit_function(function_name, args);
857
858 let stack_frame = self.stack.pop().unwrap();
859 self.frame_pool.push(stack_frame);
860
861 result
862 }
863
864 pub fn evaluate<'s, V>(&'s mut self, source: &'s str) -> Result<V::Output, ExpressionError<'s>>
878 where
879 V: LoadOwned<T>,
880 {
881 let expression =
882 parser::parse_expression::<T::Parser>(source).map_err(|e| ExpressionError {
883 error: EvalError {
884 message: format!("Parser error: {e}").into_boxed_str(),
885 location: e.location,
886 },
887 source,
888 })?;
889
890 self.evaluate_parsed::<V>(source, &expression)
891 }
892
893 pub fn evaluate_parsed<'s, V>(
910 &'s mut self,
911 source: &'s str,
912 expression: &Expression<T::Parser>,
913 ) -> Result<V::Output, ExpressionError<'s>>
914 where
915 V: LoadOwned<T>,
916 {
917 self.evaluate_impl::<V>(source, expression)
918 .map_err(|error| ExpressionError { error, source })
919 }
920
921 fn evaluate_impl<V>(
922 &mut self,
923 source: &str,
924 expression: &Expression<T::Parser>,
925 ) -> Result<V::Output, EvalError>
926 where
927 V: LoadOwned<T>,
928 {
929 let mut visitor = ExpressionVisitor::<Self, T> {
930 context: self,
931 source,
932 _marker: std::marker::PhantomData,
933 };
934 let result = visitor.visit_expression(expression)?;
935 let result_ty = result.type_of();
936 V::load_owned(self.type_context(), &result).ok_or_else(|| EvalError {
937 message: format!(
938 "Expression evaluates to {result_ty}, which cannot be converted to {}",
939 std::any::type_name::<V>()
940 )
941 .into_boxed_str(),
942 location: expression.location(),
943 })
944 }
945
946 pub fn add_variable<V>(&mut self, name: &'ctx str, value: V)
968 where
969 V: LoadStore<T>,
970 {
971 let stored = value.store(self.type_context());
972 self.stack[0].declare(name, stored);
973 }
974
975 pub fn add_function<F, A>(&mut self, name: &'ctx str, func: F)
987 where
988 F: DynFunction<A, T> + 'ctx,
989 {
990 self.program
991 .functions
992 .borrow_mut()
993 .insert(name, ExprFn::new(func));
994 }
995
996 fn lookup(&mut self, variable: &str) -> Option<(usize, TypedValue<T>)> {
997 if self.stack.len() > 1 {
998 let frame = self.stack.last_mut().unwrap();
999 if let Some((index, var)) = frame.lookup_by_name(variable) {
1000 return Some((index, var.clone()));
1002 }
1003 }
1004
1005 {
1006 let global_frame = &mut self.stack[0];
1007 if let Some((index, var)) = global_frame.lookup_by_name(variable) {
1008 return Some((index | GLOBAL_VARIABLE, var.clone()));
1010 }
1011 }
1012
1013 let state = self.initializers.get_mut(variable)?;
1015 let InitializerState::Unevaluated(idx) =
1016 std::mem::replace(state, InitializerState::Evaluating)
1017 else {
1018 return None;
1019 };
1020
1021 let program = self.program.clone();
1023 let Some(Item::GlobalVariable(global)) = program.program.items.get(idx) else {
1024 return None;
1025 };
1026
1027 let value = self
1028 .evaluate_parsed::<TypedValue<T>>(self.program.source, &global.initializer)
1029 .ok()?;
1030
1031 let global_frame = &mut self.stack[0];
1032 let index = global_frame.declare(variable, value.clone());
1033
1034 Some((index | GLOBAL_VARIABLE, value))
1035 }
1036
1037 fn lookup_address_raw(&mut self, address: usize) -> Result<&mut TypedValue<T>, Box<str>> {
1039 if address & GLOBAL_VARIABLE != 0 {
1040 return self.stack[0].lookup_by_address(address & !GLOBAL_VARIABLE);
1041 }
1042
1043 for frame in self.stack.iter_mut().rev() {
1044 if frame.start_addr <= address {
1045 return frame.lookup_by_address(address);
1046 }
1047 }
1048
1049 Err(format!("Not a valid memory address: {address}").into_boxed_str())
1050 }
1051
1052 fn resolve_place_mut(&mut self, place: &Place) -> Result<&mut TypedValue<T>, Box<str>> {
1054 let mut current = self.lookup_address_raw(place.root)?;
1055 for field in place.path.iter() {
1056 let TypedValue::Struct(structure) = current else {
1057 return Err(
1058 format!("Cannot access field `{field}` of a non-struct value").into_boxed_str(),
1059 );
1060 };
1061 let struct_name = structure.name().to_string();
1062 current = structure.fields_mut().get_mut(&**field).ok_or_else(|| {
1063 format!("Struct `{struct_name}` has no field `{field}`").into_boxed_str()
1064 })?;
1065 }
1066 Ok(current)
1067 }
1068}
1069
1070impl<T> ExprContext<T> for Context<'_, T>
1071where
1072 T: TypeSet,
1073{
1074 fn type_context(&mut self) -> &mut T {
1075 &mut self.type_context
1076 }
1077
1078 fn try_load_variable(&mut self, variable: &str) -> Option<TypedValue<T>> {
1080 self.lookup(variable).map(|(_idx, var)| var)
1081 }
1082
1083 fn place_of_variable(&mut self, variable: &str) -> Result<Place, Box<str>> {
1084 let root = self
1085 .lookup(variable)
1086 .map(|(address, _var)| address)
1087 .ok_or_else(|| format!("Variable not found: {variable}").into_boxed_str())?;
1088 Ok(Place {
1089 root,
1090 path: Box::new([]),
1091 })
1092 }
1093
1094 fn load_place(&mut self, place: &Place) -> Result<TypedValue<T>, Box<str>> {
1095 self.resolve_place_mut(place).map(|v| v.clone())
1096 }
1097
1098 fn store_place(&mut self, place: &Place, value: &TypedValue<T>) -> Result<(), Box<str>> {
1099 let slot = self.resolve_place_mut(place)?;
1100 slot.clone_from(value);
1101 Ok(())
1102 }
1103
1104 fn struct_fields(&self, struct_name: &str) -> Option<Vec<(Box<str>, Box<str>)>> {
1105 let idx = *self.program.program_structs.get(struct_name)?;
1106 let Some(Item::Struct(struct_def)) = self.program.program.items.get(idx) else {
1107 return None;
1108 };
1109 let source = self.program.source;
1110 Some(
1111 struct_def
1112 .fields
1113 .iter()
1114 .map(|field| {
1115 (
1116 Box::from(field.name.source(source)),
1117 Box::from(field.field_type.type_name.source(source)),
1118 )
1119 })
1120 .collect(),
1121 )
1122 }
1123
1124 fn declare(&mut self, variable: &str, value: TypedValue<T>) {
1126 self.stack.last_mut().unwrap().declare(variable, value);
1127 }
1128
1129 fn assign_variable(&mut self, variable: &str, value: &TypedValue<T>) -> Result<(), Box<str>> {
1131 if self.stack.last_mut().unwrap().store(variable, value) {
1132 return Ok(());
1133 }
1134 if self.stack[0].store(variable, value) {
1135 return Ok(());
1136 }
1137
1138 Err(format!("Variable not found: {variable}").into_boxed_str())
1139 }
1140
1141 fn call_function(
1142 &mut self,
1143 function_name: &str,
1144 args: &[TypedValue<T>],
1145 ) -> Result<TypedValue<T>, FunctionCallError> {
1146 let program = self.program.clone();
1147 let Some(fn_item) = self.program.program_functions.get(function_name) else {
1148 return match program.functions.borrow().get(function_name) {
1150 Some(func) => func.call(self.type_context(), args),
1151 None => Err(FunctionCallError::FunctionNotFound),
1152 };
1153 };
1154
1155 let Some(ast::Item::Function(function)) = program.program.items.get(*fn_item) else {
1157 return Err(FunctionCallError::FunctionNotFound);
1158 };
1159 self.evaluate_any_function_impl(function, args)
1160 .map_err(|err| {
1161 FunctionCallError::Other(
1162 format!(
1163 "{:?}",
1164 ExpressionError {
1165 source: self.program.source,
1166 error: err,
1167 }
1168 )
1169 .into_boxed_str(),
1170 )
1171 })
1172 }
1173
1174 fn open_scope(&mut self) {
1176 self.stack.last_mut().unwrap().open_scope();
1178 }
1179
1180 fn close_scope(&mut self) {
1182 self.stack.last_mut().unwrap().close_scope();
1184 }
1185}
1186
1187#[macro_export]
1188#[doc(hidden)]
1189macro_rules! for_all_tuples {
1190 ($pat:tt => $code:tt;) => {
1191 macro_rules! inner { $pat => $code; }
1192
1193 inner!();
1194 inner!(V1);
1195 inner!(V1, V2);
1196 inner!(V1, V2, V3);
1197 inner!(V1, V2, V3, V4);
1198 inner!(V1, V2, V3, V4, V5);
1199 inner!(V1, V2, V3, V4, V5, V6);
1200 inner!(V1, V2, V3, V4, V5, V6, V7);
1201 inner!(V1, V2, V3, V4, V5, V6, V7, V8);
1202 inner!(V1, V2, V3, V4, V5, V6, V7, V8, V9);
1203 inner!(V1, V2, V3, V4, V5, V6, V7, V8, V9, V10);
1204 };
1205}
1206
1207#[cfg(test)]
1208mod test {
1209 use std::path::Path;
1210
1211 use super::*;
1212
1213 fn strip_ansi(s: impl AsRef<str>) -> String {
1214 use ansi_parser::AnsiParser;
1215 fn text_block(output: ansi_parser::Output<'_>) -> Option<&str> {
1216 match output {
1217 ansi_parser::Output::TextBlock(text) => Some(text),
1218 _ => None,
1219 }
1220 }
1221
1222 s.as_ref()
1223 .ansi_parse()
1224 .filter_map(text_block)
1225 .collect::<String>()
1226 }
1227
1228 #[test]
1229 fn test_evaluating_exprs() {
1230 let mut ctx = Context::new();
1231
1232 ctx.add_variable::<i64>("signed", 30);
1233 ctx.add_variable::<u64>("value", 30);
1234 ctx.add_function("func", |v: u64| 2 * v);
1235 ctx.add_function("func2", |v1: u64, v2: u64| v1 + v2);
1236 ctx.add_function("five", || "five");
1237 ctx.add_function("is_five", |num: &str| num == "five");
1238 ctx.add_function("concatenate", |a: &str, b: &str| format!("{a}{b}"));
1239
1240 assert_eq!(ctx.evaluate::<bool>("value / 5 == 6"), Ok(true));
1241 assert_eq!(ctx.evaluate::<bool>("five() == \"five\""), Ok(true));
1242 assert_eq!(
1243 ctx.evaluate::<bool>("is_five(five()) != is_five(\"six\")"),
1244 Ok(true)
1245 );
1246 assert_eq!(ctx.evaluate::<u64>("func(20) / 5"), Ok(8));
1247 assert_eq!(
1248 ctx.evaluate::<TypedValue>("func(20) / 5"),
1249 Ok(TypedValue::Int(8))
1250 );
1251 assert_eq!(ctx.evaluate::<u64>("func2(20, 20) / 5"), Ok(8));
1252 assert_eq!(ctx.evaluate::<bool>("true & false"), Ok(false));
1253 assert_eq!(ctx.evaluate::<bool>("!true"), Ok(false));
1254 assert_eq!(ctx.evaluate::<bool>("false | false"), Ok(false));
1255 assert_eq!(ctx.evaluate::<bool>("true ^ true"), Ok(false));
1256 assert_eq!(ctx.evaluate::<u64>("!0x1111"), Ok(0xFFFF_FFFF_FFFF_EEEE));
1257 assert_eq!(
1258 ctx.evaluate::<String>("concatenate(five(), \"six\")"),
1259 Ok(String::from("fivesix"))
1260 );
1261 assert_eq!(ctx.evaluate::<bool>("signed * 2 == 60"), Ok(true));
1262 assert_eq!(ctx.evaluate::<i64>("*&signed"), Ok(30));
1263 }
1264
1265 #[test]
1266 fn test_context_is_mutable() {
1267 let mut ctx = Context::new();
1268
1269 ctx.add_variable::<u64>("value", 30);
1270
1271 ctx.evaluate::<()>("value = 5").unwrap();
1272 assert_eq!(ctx.evaluate::<bool>("value == 5"), Ok(true));
1273 }
1274
1275 #[test]
1276 fn closing_scope_releases_its_variables() {
1277 let mut frame = StackFrame::<DefaultTypeSet>::new();
1278 frame.declare("outer", TypedValue::Int(1));
1279 frame.open_scope();
1280 frame.declare("inner", TypedValue::Int(2));
1281
1282 frame.close_scope();
1283
1284 assert_eq!(frame.variables.len(), 1);
1285 assert_eq!(frame.lookup_index("outer"), Some(0));
1286 assert_eq!(frame.lookup_index("inner"), None);
1287 }
1288
1289 #[test]
1290 fn test_evaluating_exprs_with_u32() {
1291 let mut ctx = Context::<TypeSet32>::new_with_types();
1292
1293 ctx.add_variable::<u32>("value", 30);
1294 ctx.add_function("func", |v: u32| 2 * v);
1295 ctx.add_function("func2", |v1: u32, v2: u32| v1 + v2);
1296
1297 assert_eq!(ctx.evaluate::<bool>("value / 5 == 6"), Ok(true));
1298 assert_eq!(ctx.evaluate::<u32>("func(20) / 5"), Ok(8));
1299 assert_eq!(ctx.evaluate::<u32>("func2(20, 20) / 5"), Ok(8));
1300 }
1301
1302 #[test]
1303 fn test_evaluating_exprs_with_u128() {
1304 let mut ctx = Context::<TypeSet128>::new_with_types();
1305
1306 ctx.add_variable::<u128>("value", 30);
1307 ctx.add_function("func", |v: u128| 2 * v);
1308 ctx.add_function("func2", |v1: u128, v2: u128| v1 + v2);
1309
1310 assert_eq!(ctx.evaluate::<bool>("value / 5 == 6"), Ok(true));
1311 assert_eq!(ctx.evaluate::<u128>("func(20) / 5"), Ok(8));
1312 assert_eq!(ctx.evaluate::<u128>("func2(20, 20) / 5"), Ok(8));
1313 }
1314
1315 #[test]
1316 fn test_evaluate_function() {
1317 let mut ctx =
1318 Context::parse("fn multiply_with_global(a: int) -> int { return a * global; }")
1319 .unwrap();
1320
1321 ctx.add_variable::<u64>("global", 3);
1322
1323 assert_eq!(
1324 ctx.evaluate::<bool>("multiply_with_global(2) == 6"),
1325 Ok(true)
1326 );
1327 assert!(
1328 ctx.evaluate::<bool>("multiply_with_global(\"2\") == 6")
1329 .is_err()
1330 );
1331 }
1332
1333 #[test]
1334 fn run_eval_tests() {
1335 fn filter(path: &Path) -> bool {
1336 let Ok(env) = std::env::var("TEST_FILTER") else {
1337 return path.is_dir() || path.extension().map_or(false, |ext| ext == "sm");
1339 };
1340
1341 Path::new(&env) == path
1342 }
1343
1344 fn walk(dir: &Path, on_file: &impl Fn(&Path)) {
1345 for entry in std::fs::read_dir(dir)
1346 .unwrap_or_else(|_| panic!("Folder not found: {}", dir.display()))
1347 .flatten()
1348 {
1349 let path = entry.path();
1350
1351 if !filter(&path) {
1352 continue;
1353 }
1354
1355 if path.is_file() {
1356 on_file(&path);
1357 } else {
1358 walk(&path, on_file);
1359 }
1360 }
1361 }
1362
1363 fn run_eval_test(path: &Path) {
1364 type Types = WithIterator<DefaultTypeSet>;
1365
1366 fn parse(source: &str) -> Context<'_, Types> {
1367 let mut context = Context::<Types>::parse_with_types(source).unwrap();
1368
1369 context.add_function("add_from_rust", |a: u64, b: u64| -> i64 { (a + b) as i64 });
1370 context.add_function("assert", |a: bool| a); context.add_function("reverse", |s: &str| s.chars().rev().collect::<String>());
1372 context.add_function("range", |a: u64, b: u64| {
1373 SomniIterator::new((a..b).map(TypedValue::<DefaultTypeSet>::Int))
1374 });
1375
1376 context
1377 }
1378
1379 let test_name = path.file_stem().unwrap();
1380 let parent = path.parent().unwrap().canonicalize().unwrap();
1381 let vm_error = parent.join(test_name).join("stderr");
1382 let expr_error = parent.join(test_name).join("stderr_expr");
1383 let source = std::fs::read_to_string(path).unwrap();
1384
1385 let expressions = source
1386 .lines()
1387 .filter_map(|line| line.trim().strip_prefix("//@"))
1388 .collect::<Vec<_>>();
1389
1390 let mut context = parse(&source);
1391 let fail_expected = std::fs::exists(&expr_error).unwrap_or(false)
1392 || std::fs::exists(&vm_error).unwrap_or(false);
1393
1394 let blessed = std::env::var("BLESS").as_deref() == Ok("1");
1395
1396 for expression in &expressions {
1397 let expression = if let Some(e) = expression.strip_prefix('+') {
1398 e.trim()
1400 } else {
1401 context = parse(&source);
1403 expression
1404 };
1405 println!("Running `{expression}`");
1406 match context.evaluate::<TypedValue<Types>>(expression) {
1407 Ok(_) if fail_expected => {
1408 panic!(
1409 "Expected {} to fail evaluating, but it succeeded",
1410 path.display()
1411 )
1412 }
1413 Ok(value) => assert_eq!(
1414 value,
1415 TypedValue::Bool(true),
1416 "{}: Expression `{expression}` evaluated to {value:?}",
1417 path.display()
1418 ),
1419 Err(e) if fail_expected => {
1420 let error = strip_ansi(format!("{e:?}"));
1421 if blessed {
1422 std::fs::write(&expr_error, error).unwrap();
1423 } else {
1424 let expected_error = std::fs::read_to_string(&expr_error).unwrap();
1425 pretty_assertions::assert_eq!(strip_ansi(expected_error), error);
1426 }
1427 }
1428 Err(e) => panic!("{}: {e:?}", path.display()),
1429 };
1430 }
1431 }
1432
1433 walk("../tests/eval".as_ref(), &|path| {
1434 run_eval_test(path);
1435 });
1436 }
1437
1438 #[test]
1439 fn test_struct_literals_and_field_access() {
1440 let program = r#"
1441struct Point { x: int, y: int }
1442
1443fn make() -> Point {
1444 return Point { x: 3, y: 4 };
1445}
1446
1447fn sum_sq() -> int {
1448 var p = make();
1449 return p.x * p.x + p.y * p.y;
1450}
1451"#;
1452 let mut ctx = Context::parse(program).unwrap();
1453 assert_eq!(ctx.evaluate::<bool>("sum_sq() == 25"), Ok(true));
1454 }
1455
1456 #[test]
1457 fn test_struct_field_write() {
1458 let program = r#"
1459struct Point { x: int, y: int }
1460
1461fn moved() -> int {
1462 var p = Point { x: 1, y: 2 };
1463 p.x = 10;
1464 p.y = p.y + 5;
1465 return p.x + p.y;
1466}
1467"#;
1468 let mut ctx = Context::parse(program).unwrap();
1469 assert_eq!(ctx.evaluate::<bool>("moved() == 17"), Ok(true));
1470 }
1471
1472 #[test]
1473 fn test_nested_struct() {
1474 let program = r#"
1475struct Point { x: int, y: int }
1476struct Line { start: Point, end: Point }
1477
1478fn build() -> int {
1479 var l = Line { start: Point { x: 1, y: 2 }, end: Point { x: 3, y: 4 } };
1480 l.end.x = 30;
1481 return l.start.x + l.end.x;
1482}
1483"#;
1484 let mut ctx = Context::parse(program).unwrap();
1485 assert_eq!(ctx.evaluate::<bool>("build() == 31"), Ok(true));
1486 }
1487
1488 #[test]
1489 fn test_struct_pass_by_reference_and_autoderef() {
1490 let program = r#"
1491struct Point { x: int, y: int }
1492
1493fn scale(p: &Point, factor: int) {
1494 p.x = p.x * factor;
1495 p.y = p.y * factor;
1496}
1497
1498fn run() -> int {
1499 var p = Point { x: 2, y: 3 };
1500 scale(&p, 4);
1501 return p.x + p.y;
1502}
1503"#;
1504 let mut ctx = Context::parse(program).unwrap();
1505 assert_eq!(ctx.evaluate::<bool>("run() == 20"), Ok(true));
1506 }
1507
1508 #[test]
1509 fn test_reference_to_field() {
1510 let program = r#"
1511struct Point { x: int, y: int }
1512
1513fn double(v: &int) {
1514 *v = *v * 2;
1515}
1516
1517fn run() -> int {
1518 var p = Point { x: 5, y: 6 };
1519 double(&p.x);
1520 return p.x;
1521}
1522"#;
1523 let mut ctx = Context::parse(program).unwrap();
1524 assert_eq!(ctx.evaluate::<bool>("run() == 10"), Ok(true));
1525 }
1526
1527 #[test]
1528 fn test_struct_equality() {
1529 let program = r#"
1530struct Point { x: int, y: int }
1531
1532fn a() -> Point { return Point { x: 1, y: 2 }; }
1533fn b() -> Point { return Point { x: 1, y: 2 }; }
1534fn c() -> Point { return Point { x: 1, y: 9 }; }
1535"#;
1536 let mut ctx = Context::parse(program).unwrap();
1537 assert_eq!(ctx.evaluate::<bool>("a() == b()"), Ok(true));
1538 assert_eq!(ctx.evaluate::<bool>("a() != c()"), Ok(true));
1539 assert_eq!(ctx.evaluate::<bool>("a() == c()"), Ok(false));
1540 }
1541
1542 #[test]
1543 fn test_struct_boundary_and_macro() {
1544 let program = r#"
1545struct Point { x: int, y: int }
1546"#;
1547 let mut ctx = Context::parse(program).unwrap();
1548 ctx.add_function("origin_distance_sq", |p: SomniStruct| -> i64 {
1549 let TypedValue::Int(x) = p.fields()["x"] else {
1550 panic!("x not an int")
1551 };
1552 let TypedValue::Int(y) = p.fields()["y"] else {
1553 panic!("y not an int")
1554 };
1555 (x * x + y * y) as i64
1556 });
1557
1558 let tc = ctx.type_context();
1560 let point = somni_struct!(tc, Point { x: 3u64, y: 4u64 });
1561 assert_eq!(point.name(), "Point");
1562
1563 assert_eq!(
1564 ctx.evaluate::<bool>("origin_distance_sq(Point { x: 3, y: 4 }) == 25"),
1565 Ok(true)
1566 );
1567 }
1568
1569 #[test]
1570 fn test_unknown_struct_is_error() {
1571 let mut ctx = Context::new();
1572 assert!(ctx.evaluate::<TypedValue>("Nope { x: 1 }").is_err());
1573 }
1574
1575 #[test]
1576 fn test_eval_error() {
1577 let mut ctx = Context::new();
1578
1579 ctx.add_function("func", |v1: u64, v2: u64| v1 + v2);
1580
1581 let err = ctx
1582 .evaluate::<u64>("func(20, true)")
1583 .expect_err("Expected expression to return an error");
1584
1585 pretty_assertions::assert_eq!(
1586 strip_ansi(format!("\n{err:?}")),
1587 r#"
1588Evaluation error
1589 ---> at line 1 column 10
1590 |
15911 | func(20, true)
1592 | ^^^^ func expects argument 1 to be u64, got bool"#,
1593 );
1594 }
1595}