unit Test.Myc.Ast.Tuples; interface uses DUnitX.TestFramework, System.SysUtils, Myc.Ast.Environment, Myc.Ast.Script, Myc.Data.Value, Myc.Data.Scalar, Myc.Ast.Nodes, // For EAstException/EEvaluatorException if needed Myc.Ast.Evaluator; type [TestFixture] [IgnoreMemoryLeaks] TTestTuples = class private FEnv: TAstEnvironment; function Run(const Code: string): TDataValue; public [Setup] procedure Setup; [TearDown] procedure TearDown; // --- Basic Literals --- [Test] procedure Test_EmptyTuple; [Test] procedure Test_SingleElement; [Test] procedure Test_HomogeneousVector; [Test] procedure Test_HeterogeneousTuple; // --- Indexing (Get) --- [Test] [TestCase('Index0', '[10 20 30],0,10')] [TestCase('Index1', '[10 20 30],1,20')] [TestCase('Index2', '[10 20 30],2,30')] procedure Test_Indexing(const Script, IndexStr, ExpectedStr: string); [Test] procedure Test_Indexing_OutOfBounds; // --- Nesting (Matrices) --- [Test] procedure Test_Matrix_2x2; [Test] procedure Test_Jagged_Array; // Not a matrix, but a tuple of tuples // --- Complex Expressions inside Tuples --- [Test] procedure Test_Expressions_Inside_Tuple; [Test] procedure Test_Tuple_Inside_Block; // --- Variable Binding --- [Test] procedure Test_Def_And_Access; end; implementation uses Myc.Ast.Scope; // For IExecutionScope { TTestTuples } procedure TTestTuples.Setup; begin // Create a fresh standard environment for each test FEnv := TAstEnvironment.Construct(nil); FEnv.SetStandardMode; end; procedure TTestTuples.TearDown; begin // Interfaces represent the env, automatic cleanup handled by ARC FEnv := Default(TAstEnvironment); end; function TTestTuples.Run(const Code: string): TDataValue; begin // Parse, Compile, Link, Run pipeline var ast := TAstScript.Parse(Code); Result := FEnv.Run(ast); end; procedure TTestTuples.Test_EmptyTuple; begin var res := Run('[]'); Assert.AreEqual(TDataValueKind.vkTuple, res.Kind); Assert.AreEqual(0, res.AsTuple.Count); end; procedure TTestTuples.Test_SingleElement; begin var res := Run('[42]'); Assert.AreEqual(TDataValueKind.vkTuple, res.Kind); Assert.AreEqual(1, res.AsTuple.Count); Assert.AreEqual(42, res.AsTuple[0].AsScalar.Value.AsInt64); end; procedure TTestTuples.Test_HomogeneousVector; begin // [1 2 3 4] var res := Run('[1 2 3 4]'); Assert.AreEqual(TDataValueKind.vkTuple, res.Kind); Assert.AreEqual(4, res.AsTuple.Count); Assert.AreEqual(4, res.AsTuple[3].AsScalar.Value.AsInt64); end; procedure TTestTuples.Test_HeterogeneousTuple; begin // [1 "Text" 3.14] var res := Run('[1 "Text" 3.14]'); Assert.AreEqual(TDataValueKind.vkTuple, res.Kind); var tpl := res.AsTuple; Assert.AreEqual(3, tpl.Count); // Check types Assert.AreEqual(TDataValueKind.vkScalar, tpl[0].Kind); Assert.AreEqual(TDataValueKind.vkText, tpl[1].Kind); Assert.AreEqual(TDataValueKind.vkScalar, tpl[2].Kind); // Check values Assert.AreEqual(1, tpl[0].AsScalar.Value.AsInt64); Assert.AreEqual('Text', tpl[1].AsText); Assert.AreEqual(3.14, tpl[2].AsScalar.Value.AsDouble, 0.0001); end; procedure TTestTuples.Test_Indexing(const Script, IndexStr, ExpectedStr: string); begin // (get [10 20 30] 1) -> 20 var code := Format('(get %s %s)', [Script, IndexStr]); var res := Run(code); Assert.AreEqual(TDataValueKind.vkScalar, res.Kind); Assert.AreEqual(StrToInt64(ExpectedStr), res.AsScalar.Value.AsInt64); end; procedure TTestTuples.Test_Indexing_OutOfBounds; begin // Should throw exception Assert.WillRaise(procedure begin Run('(get [1 2] 5)'); end, EEvaluatorException); Assert.WillRaise(procedure begin Run('(get [1 2] -1)'); end, EEvaluatorException); end; procedure TTestTuples.Test_Matrix_2x2; begin // [[1 2] [3 4]] // This tests the parser's ability to handle nested brackets and the evaluator's reconstruction. var res := Run('[[1 2] [3 4]]'); Assert.AreEqual(TDataValueKind.vkTuple, res.Kind); var outer := res.AsTuple; Assert.AreEqual(2, outer.Count); var row0 := outer[0].AsTuple; var row1 := outer[1].AsTuple; Assert.AreEqual(1, row0[0].AsScalar.Value.AsInt64); Assert.AreEqual(2, row0[1].AsScalar.Value.AsInt64); Assert.AreEqual(3, row1[0].AsScalar.Value.AsInt64); Assert.AreEqual(4, row1[1].AsScalar.Value.AsInt64); end; procedure TTestTuples.Test_Jagged_Array; begin // [[1] [2 3]] -> Valid tuple of tuples, but not a matrix in strict sense (though runtime handles it as nested tuples) var res := Run('[[1] [2 3]]'); Assert.AreEqual(2, res.AsTuple.Count); Assert.AreEqual(1, res.AsTuple[0].AsTuple.Count); Assert.AreEqual(2, res.AsTuple[1].AsTuple.Count); end; procedure TTestTuples.Test_Expressions_Inside_Tuple; begin // [(+ 1 2) (* 10 2)] -> [3 20] var res := Run('[(+ 1 2) (* 10 2)]'); Assert.AreEqual(2, res.AsTuple.Count); Assert.AreEqual(3, res.AsTuple[0].AsScalar.Value.AsInt64); Assert.AreEqual(20, res.AsTuple[1].AsScalar.Value.AsInt64); end; procedure TTestTuples.Test_Tuple_Inside_Block; begin // (do [1 2] [3 4]) -> Should return the last tuple [3 4] var res := Run('(do [1 2] [3 4])'); Assert.AreEqual(TDataValueKind.vkTuple, res.Kind); Assert.AreEqual(3, res.AsTuple[0].AsScalar.Value.AsInt64); end; procedure TTestTuples.Test_Def_And_Access; begin // This mimics the case that previously crashed // (do (def x [10 20 30]) (get x 1)) var code := '(do ' + ' (def x [10 20 30]) ' + ' (get x 1) ' + ')'; var res := Run(code); Assert.AreEqual(TDataValueKind.vkScalar, res.Kind); Assert.AreEqual(20, res.AsScalar.Value.AsInt64); end; initialization TDUnitX.RegisterTestFixture(TTestTuples); end.