Tuples
This commit is contained in:
@@ -0,0 +1,214 @@
|
||||
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<Int64>(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<Int64>(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<Int64>(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<Int64>(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<Int64>(1, row0[0].AsScalar.Value.AsInt64);
|
||||
Assert.AreEqual<Int64>(2, row0[1].AsScalar.Value.AsInt64);
|
||||
Assert.AreEqual<Int64>(3, row1[0].AsScalar.Value.AsInt64);
|
||||
Assert.AreEqual<Int64>(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<Int64>(3, res.AsTuple[0].AsScalar.Value.AsInt64);
|
||||
Assert.AreEqual<Int64>(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<Int64>(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<Int64>(20, res.AsScalar.Value.AsInt64);
|
||||
end;
|
||||
|
||||
initialization
|
||||
TDUnitX.RegisterTestFixture(TTestTuples);
|
||||
|
||||
end.
|
||||
+2
-1
@@ -38,7 +38,8 @@ uses
|
||||
Test.Myc.Ast.Pipes in 'AST\Test.Myc.Ast.Pipes.pas',
|
||||
Test.Myc.Ast.Integration in 'Test.Myc.Ast.Integration.pas',
|
||||
Demo.Finance in 'Demo.Finance.pas',
|
||||
Myc.Data.Tuple in '..\Src\Data\Myc.Data.Tuple.pas';
|
||||
Myc.Data.Tuple in '..\Src\Data\Myc.Data.Tuple.pas',
|
||||
Test.Myc.Ast.Tuples in 'AST\Test.Myc.Ast.Tuples.pas';
|
||||
|
||||
{ keep comment here to protect the following conditional from being removed by the IDE when adding a unit }
|
||||
{$IFNDEF TESTINSIGHT}
|
||||
|
||||
@@ -140,6 +140,7 @@ $(PreBuildEvent)]]></PreBuildEvent>
|
||||
<DCCReference Include="Test.Myc.Ast.Integration.pas"/>
|
||||
<DCCReference Include="Demo.Finance.pas"/>
|
||||
<DCCReference Include="..\Src\Data\Myc.Data.Tuple.pas"/>
|
||||
<DCCReference Include="AST\Test.Myc.Ast.Tuples.pas"/>
|
||||
<BuildConfiguration Include="Base">
|
||||
<Key>Base</Key>
|
||||
</BuildConfiguration>
|
||||
|
||||
Reference in New Issue
Block a user