diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index aa23c38..e587dcb 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -9,3 +9,6 @@ target_include_directories(step1_test PRIVATE src) add_executable(step2_test tests/step2_test.cpp) target_include_directories(step2_test PRIVATE src) + +add_executable(step3_test tests/step3_test.cpp) +target_include_directories(step3_test PRIVATE src) diff --git a/editor/src/ast/Expression.h b/editor/src/ast/Expression.h new file mode 100644 index 0000000..a0437f1 --- /dev/null +++ b/editor/src/ast/Expression.h @@ -0,0 +1,107 @@ +#pragma once +#include "ASTNode.h" + +class Expression : public ASTNode { +public: + Expression() { conceptType = "Expression"; } +}; + +class BinaryOperation : public Expression { +public: + std::string op; + BinaryOperation() { conceptType = "BinaryOperation"; } + BinaryOperation(const std::string& id, const std::string& op) : op(op) { + this->id = id; + this->conceptType = "BinaryOperation"; + } + // children: left (1), right (1) via setChild +}; + +class UnaryOperation : public Expression { +public: + std::string op; + UnaryOperation() { conceptType = "UnaryOperation"; } + // children: operand (1) via setChild("operand", ...) +}; + +class FunctionCall : public Expression { +public: + std::string functionName; + FunctionCall() { conceptType = "FunctionCall"; } + // children: arguments (0..n) via addChild("arguments", ...) +}; + +class VariableReference : public Expression { +public: + std::string variableName; + VariableReference() { conceptType = "VariableReference"; } + VariableReference(const std::string& id, const std::string& varName) + : variableName(varName) { + this->id = id; + this->conceptType = "VariableReference"; + } +}; + +class IntegerLiteral : public Expression { +public: + int value = 0; + IntegerLiteral() { conceptType = "IntegerLiteral"; } + IntegerLiteral(const std::string& id, int val) : value(val) { + this->id = id; + this->conceptType = "IntegerLiteral"; + } +}; + +class FloatLiteral : public Expression { +public: + std::string value; + FloatLiteral() { conceptType = "FloatLiteral"; } + FloatLiteral(const std::string& id, const std::string& val) : value(val) { + this->id = id; + this->conceptType = "FloatLiteral"; + } +}; + +class StringLiteral : public Expression { +public: + std::string value; + StringLiteral() { conceptType = "StringLiteral"; } + StringLiteral(const std::string& id, const std::string& val) : value(val) { + this->id = id; + this->conceptType = "StringLiteral"; + } +}; + +class BooleanLiteral : public Expression { +public: + bool value = false; + BooleanLiteral() { conceptType = "BooleanLiteral"; } + BooleanLiteral(const std::string& id, bool val) : value(val) { + this->id = id; + this->conceptType = "BooleanLiteral"; + } +}; + +class NullLiteral : public Expression { +public: + NullLiteral() { conceptType = "NullLiteral"; } +}; + +class ListLiteral : public Expression { +public: + ListLiteral() { conceptType = "ListLiteral"; } + // children: elements (0..n) via addChild("elements", ...) +}; + +class IndexAccess : public Expression { +public: + IndexAccess() { conceptType = "IndexAccess"; } + // children: target (1), index (1) via setChild +}; + +class MemberAccess : public Expression { +public: + std::string memberName; + MemberAccess() { conceptType = "MemberAccess"; } + // children: target (1) via setChild("target", ...) +}; diff --git a/editor/src/ast/Parameter.h b/editor/src/ast/Parameter.h new file mode 100644 index 0000000..f30d30a --- /dev/null +++ b/editor/src/ast/Parameter.h @@ -0,0 +1,14 @@ +#pragma once +#include "ASTNode.h" + +class Parameter : public ASTNode { +public: + std::string name; + Parameter() { conceptType = "Parameter"; } + Parameter(const std::string& id, const std::string& name) : name(name) { + this->id = id; + this->conceptType = "Parameter"; + } + // children: type (1) via setChild("type", ...) + // children: defaultValue (0..1) via setChild("defaultValue", ...) +}; diff --git a/editor/src/ast/Statement.h b/editor/src/ast/Statement.h new file mode 100644 index 0000000..acae56b --- /dev/null +++ b/editor/src/ast/Statement.h @@ -0,0 +1,55 @@ +#pragma once +#include "ASTNode.h" + +class Statement : public ASTNode { +public: + Statement() { conceptType = "Statement"; } +}; + +class Block : public Statement { +public: + Block() { conceptType = "Block"; } + // children: statements (0..n) via addChild("statements", ...) +}; + +class Assignment : public Statement { +public: + Assignment() { conceptType = "Assignment"; } + // children: target (1) via setChild("target", ...) + // children: value (1) via setChild("value", ...) +}; + +class IfStatement : public Statement { +public: + IfStatement() { conceptType = "IfStatement"; } + // children: condition (1) via setChild("condition", ...) + // children: thenBranch (0..n) via addChild("thenBranch", ...) + // children: elseBranch (0..n) via addChild("elseBranch", ...) +}; + +class WhileLoop : public Statement { +public: + WhileLoop() { conceptType = "WhileLoop"; } + // children: condition (1) via setChild("condition", ...) + // children: body (0..n) via addChild("body", ...) +}; + +class ForLoop : public Statement { +public: + std::string iteratorName; + ForLoop() { conceptType = "ForLoop"; } + // children: iterable (1) via setChild("iterable", ...) + // children: body (0..n) via addChild("body", ...) +}; + +class Return : public Statement { +public: + Return() { conceptType = "Return"; } + // children: value (0..1) via setChild("value", ...) +}; + +class ExpressionStatement : public Statement { +public: + ExpressionStatement() { conceptType = "ExpressionStatement"; } + // children: expression (1) via setChild("expression", ...) +}; diff --git a/editor/src/ast/Type.h b/editor/src/ast/Type.h new file mode 100644 index 0000000..a36d58c --- /dev/null +++ b/editor/src/ast/Type.h @@ -0,0 +1,64 @@ +#pragma once +#include "ASTNode.h" + +class Type : public ASTNode { +public: + Type() { conceptType = "Type"; } +}; + +class PrimitiveType : public Type { +public: + std::string kind; + PrimitiveType() { conceptType = "PrimitiveType"; } + PrimitiveType(const std::string& id, const std::string& kind) : kind(kind) { + this->id = id; + this->conceptType = "PrimitiveType"; + } +}; + +class ListType : public Type { +public: + ListType() { conceptType = "ListType"; } + // children: elementType (1) via setChild("elementType", ...) +}; + +class SetType : public Type { +public: + SetType() { conceptType = "SetType"; } + // children: elementType (1) via setChild("elementType", ...) +}; + +class MapType : public Type { +public: + MapType() { conceptType = "MapType"; } + // children: keyType (1), valueType (1) via setChild +}; + +class TupleType : public Type { +public: + TupleType() { conceptType = "TupleType"; } + // children: elementTypes (0..n) via addChild("elementTypes", ...) +}; + +class ArrayType : public Type { +public: + ArrayType() { conceptType = "ArrayType"; } + // children: elementType (1) via setChild("elementType", ...) + // children: size (1) via setChild("size", ...) [Expression] +}; + +class OptionalType : public Type { +public: + OptionalType() { conceptType = "OptionalType"; } + // children: innerType (1) via setChild("innerType", ...) +}; + +class CustomType : public Type { +public: + std::string typeName; + CustomType() { conceptType = "CustomType"; } + CustomType(const std::string& id, const std::string& name) : typeName(name) { + this->id = id; + this->conceptType = "CustomType"; + } +}; diff --git a/editor/tests/step3_test.cpp b/editor/tests/step3_test.cpp new file mode 100644 index 0000000..6e9f402 --- /dev/null +++ b/editor/tests/step3_test.cpp @@ -0,0 +1,188 @@ +// Step 3: Build the Calculator example from Phase1Test.mps as a C++ object graph. +// +// Calculator (Module, targetLanguage=cpp) +// ├── Variable "PI" → type: PrimitiveType("float") +// ├── Function "add" +// │ ├── returnType: PrimitiveType("int") +// │ ├── parameters: Parameter("x", int), Parameter("y", int) +// │ └── body: +// │ ├── Assignment { target: VarRef("result"), value: BinOp("+", VarRef("x"), VarRef("y")) } +// │ └── Return { value: VarRef("result") } +// └── Function "multiply" +// ├── returnType: PrimitiveType("int") +// ├── parameters: Parameter("a", int), Parameter("b", int) +// └── body: +// └── Return { value: BinOp("*", VarRef("a"), VarRef("b")) } + +#include "../src/ast/Module.h" +#include "../src/ast/Function.h" +#include "../src/ast/Variable.h" +#include "../src/ast/Parameter.h" +#include "../src/ast/Statement.h" +#include "../src/ast/Expression.h" +#include "../src/ast/Type.h" +#include +#include + +int main() { + // --- Build the tree (matching Phase1Test.mps IDs) --- + + // Root + Module calc("Calc_M001", "Calculator", "cpp"); + + // Variable PI + Variable pi("Calc_V001", "PI"); + PrimitiveType piType("Calc_VT001", "float"); + pi.setChild("type", &piType); + calc.addChild("variables", &pi); + + // Function: add + Function add("Calc_F001", "add"); + PrimitiveType addRet("Calc_RT001", "int"); + add.setChild("returnType", &addRet); + + Parameter px("Calc_P001", "x"); + PrimitiveType pxType("Calc_PT001", "int"); + px.setChild("type", &pxType); + add.addChild("parameters", &px); + + Parameter py("Calc_P002", "y"); + PrimitiveType pyType("Calc_PT002", "int"); + py.setChild("type", &pyType); + add.addChild("parameters", &py); + + // add body: Assignment(result = x + y), then Return(result) + Assignment assign; + assign.id = "Calc_A001"; + VariableReference assignTarget("c_VR_result", "result"); + assign.setChild("target", &assignTarget); + + BinaryOperation addOp("Calc_E001", "+"); + VariableReference vrX("Calc_VR_x", "x"); + VariableReference vrY("Calc_VR_y", "y"); + addOp.setChild("left", &vrX); + addOp.setChild("right", &vrY); + assign.setChild("value", &addOp); + + Return addReturn; + addReturn.id = "Calc_R001"; + VariableReference vrRetResult("c_VR_return", "result"); + addReturn.setChild("value", &vrRetResult); + + add.addChild("body", &assign); + add.addChild("body", &addReturn); + + calc.addChild("functions", &add); + + // Function: multiply + Function multiply("Calc_F002", "multiply"); + PrimitiveType mulRet("Calc_RT002", "int"); + multiply.setChild("returnType", &mulRet); + + Parameter pa("Calc_P003", "a"); + PrimitiveType paType("Calc_PT003", "int"); + pa.setChild("type", &paType); + multiply.addChild("parameters", &pa); + + Parameter pb("Calc_P004", "b"); + PrimitiveType pbType("Calc_PT004", "int"); + pb.setChild("type", &pbType); + multiply.addChild("parameters", &pb); + + Return mulReturn; + mulReturn.id = "Calc_R002"; + BinaryOperation mulOp("Calc_E005", "*"); + VariableReference vrA("Calc_VR_a", "a"); + VariableReference vrB("Calc_VR_b", "b"); + mulOp.setChild("left", &vrA); + mulOp.setChild("right", &vrB); + mulReturn.setChild("value", &mulOp); + + multiply.addChild("body", &mulReturn); + + calc.addChild("functions", &multiply); + + // --- Verify the tree --- + + // Module level + assert(calc.name == "Calculator"); + assert(calc.targetLanguage == "cpp"); + assert(calc.getChildren("functions").size() == 2); + assert(calc.getChildren("variables").size() == 1); + + // Variable PI + auto* v = static_cast(calc.getChildren("variables")[0]); + assert(v->name == "PI"); + assert(v->parent == &calc); + auto* vt = static_cast(v->getChild("type")); + assert(vt != nullptr); + assert(vt->kind == "float"); + assert(vt->parent == v); + + // Function: add + auto* fn1 = static_cast(calc.getChildren("functions")[0]); + assert(fn1->name == "add"); + assert(fn1->parent == &calc); + + auto* rt1 = static_cast(fn1->getChild("returnType")); + assert(rt1->kind == "int"); + + const auto& params1 = fn1->getChildren("parameters"); + assert(params1.size() == 2); + assert(static_cast(params1[0])->name == "x"); + assert(static_cast(params1[1])->name == "y"); + // Param types + auto* pxt = static_cast(params1[0]->getChild("type")); + assert(pxt->kind == "int"); + assert(pxt->parent == params1[0]); + + const auto& body1 = fn1->getChildren("body"); + assert(body1.size() == 2); + + // First statement: Assignment + auto* a1 = static_cast(body1[0]); + assert(a1->conceptType == "Assignment"); + assert(a1->parent == fn1); + auto* aTgt = static_cast(a1->getChild("target")); + assert(aTgt->variableName == "result"); + auto* aVal = static_cast(a1->getChild("value")); + assert(aVal->op == "+"); + auto* aLeft = static_cast(aVal->getChild("left")); + auto* aRight = static_cast(aVal->getChild("right")); + assert(aLeft->variableName == "x"); + assert(aRight->variableName == "y"); + // Walk up: expression → assignment → function → module + assert(aLeft->parent == aVal); + assert(aVal->parent == a1); + assert(a1->parent == fn1); + assert(fn1->parent == &calc); + + // Second statement: Return + auto* r1 = static_cast(body1[1]); + assert(r1->conceptType == "Return"); + auto* rv = static_cast(r1->getChild("value")); + assert(rv->variableName == "result"); + + // Function: multiply + auto* fn2 = static_cast(calc.getChildren("functions")[1]); + assert(fn2->name == "multiply"); + + const auto& params2 = fn2->getChildren("parameters"); + assert(params2.size() == 2); + assert(static_cast(params2[0])->name == "a"); + assert(static_cast(params2[1])->name == "b"); + + const auto& body2 = fn2->getChildren("body"); + assert(body2.size() == 1); + auto* r2 = static_cast(body2[0]); + auto* mulExpr = static_cast(r2->getChild("value")); + assert(mulExpr->op == "*"); + assert(static_cast(mulExpr->getChild("left"))->variableName == "a"); + assert(static_cast(mulExpr->getChild("right"))->variableName == "b"); + + // Full tree depth: varref → binop → return → function → module + assert(vrB.parent->parent->parent->parent == &calc); + + std::cout << "Step 3: PASS — Calculator model (27 nodes) built and verified" << std::endl; + return 0; +}