From 010d155f5d2534c85ee3700161f616a1f2acbf36 Mon Sep 17 00:00:00 2001 From: Maxwell Jeffress Date: Sun, 25 Jan 2026 17:40:07 +1100 Subject: [PATCH] Start work on structs --- .gitignore | 1 + src/lexer.cpp | 1 + src/parser.cpp | 171 +++++++++++++++++++++++++++++++++++++++++++++- src/parser.h | 2 +- tests/struct.sols | 3 + 5 files changed, 174 insertions(+), 4 deletions(-) create mode 100644 tests/struct.sols diff --git a/.gitignore b/.gitignore index 995fa49..6ac323e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ solstice build +.*_solsbuild diff --git a/src/lexer.cpp b/src/lexer.cpp index ac13ec8..f7f01f8 100644 --- a/src/lexer.cpp +++ b/src/lexer.cpp @@ -100,6 +100,7 @@ namespace Solstice { } // tokens which are not followed by anything case '\n': + case ':': { if (!buf.empty()) { addToken(buf); diff --git a/src/parser.cpp b/src/parser.cpp index e5ba11f..69320c9 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -216,7 +216,7 @@ namespace Solstice { code = parsedStdlib.generateCode(); } } - if (nodeType != SolNodeType::If && nodeType != SolNodeType::While) for (auto& child : children) { + if (nodeType != SolNodeType::If && nodeType != SolNodeType::While && nodeType != SolNodeType::Struct) for (auto& child : children) { auto childCode = child.generateCode(); code.insert(code.end(), childCode.begin(), childCode.end()); } @@ -749,6 +749,51 @@ namespace Solstice { code.push_back(codeBlock); break; } + case SolNodeType::Struct: { + //SolGroundCodeBlock preProcessCodeBlock; + SolGroundCodeBlock codeBlock; + GroundInstruction gi = groundCreateInstruction(STRUCT); + + // struct -name + groundAddReferenceToInstruction(&gi, groundCreateReference(TYPEREF, copyString(outputId))); + codeBlock.code.push_back(gi); + + // contents of struct + for (SolNode& child : children) { + if (child.nodeType == SolNodeType::Identifier) { + gi = groundCreateInstruction(SET); + groundAddReferenceToInstruction(&gi, groundCreateReference(DIRREF, copyString(child.outputId))); + + SolNode& valNode = child.children[0]; + if (valNode.nodeType == SolNodeType::Value) { + GroundValue gv; + switch (valNode.data.type) { + case SolDataType::String: gv = groundCreateValue(STRING, copyString(valNode.data.getString().value())); break; + case SolDataType::Int: gv = groundCreateValue(INT, valNode.data.getInt().value()); break; + case SolDataType::Double: gv = groundCreateValue(DOUBLE, valNode.data.getDouble().value()); break; + case SolDataType::Char: gv = groundCreateValue(CHAR, valNode.data.getChar().value()); break; + case SolDataType::Bool: gv = groundCreateValue(BOOL, valNode.data.getBool().value()); break; + default: Error::syntaxError("Struct member must be assigned a value or identifier", valNode.line, valNode.lineContent); break; + } + groundAddValueToInstruction(&gi, gv); + } else if (child.nodeType == SolNodeType::Identifier) { + groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, copyString(valNode.outputId))); + } else { + Error::syntaxError("Struct member must be assigned a value or identifier", valNode.line, valNode.lineContent); + } + codeBlock.code.push_back(gi); + } + if (child.nodeType == SolNodeType::FunctionDef) { + auto fnBlock = child.generateCode(); + for (const auto& code : fnBlock) { + codeBlock.code.insert(codeBlock.code.end(), code.code.begin(), code.code.end()); + } + } + } + codeBlock.code.push_back(groundCreateInstruction(ENDSTRUCT)); + code.push_back(codeBlock); + break; + } default: {} } return code; @@ -849,7 +894,7 @@ namespace Solstice { if (in == "/") { return SolNodeType::Divide; } - if (in == "=") { + if (in == "=" || in == ":") { return SolNodeType::Set; } if (in == "==") { @@ -888,6 +933,12 @@ namespace Solstice { if (in == "ground") { return SolNodeType::InlineGround; } + if (in == "struct") { + return SolNodeType::Struct; + } + if (in == "new") { + return SolNodeType::New; + } if (in == "{") { return SolNodeType::CodeBlockStart; } @@ -1437,7 +1488,6 @@ namespace Solstice { SolNode groundNode(SolNodeType::InlineGround); consume(); // some funny token consume(); // { - consume(); // new line token while (auto tokenopt = consume()) { if (tokenopt.value().value == "}") { break; @@ -1447,6 +1497,121 @@ namespace Solstice { rootNode.addNode(groundNode); break; } + case SolNodeType::Struct: { + SolNode structNode(SolNodeType::Struct); + structNode.line = tokenObj.line; + structNode.lineContent = tokenObj.lineContent; + + auto nameopt = consume(); + if (nameopt) { + structNode.outputId = nameopt.value().value; + } + + while (peek() && peek().value().value == "\n") { + consume(); + } + + auto openBrace = consume(); + if (!openBrace || openBrace.value().value != "{") { + Error::syntaxError("Expected '{' after struct name", tokenObj.line, tokenObj.lineContent); + } + + std::vector tokens; + int count = 1; + while (auto tokenopt = consume()) { + if (tokenopt.value().value == "{") { + count++; + } + if (tokenopt.value().value == "}") { + count--; + } + if (count < 1) { + break; + } + tokens.push_back(tokenopt.value()); + } + + if (count != 0) { + Error::syntaxError("Unclosed struct body", tokenObj.line, tokenObj.lineContent); + } + + for (size_t i = 0; i < tokens.size(); i++) { + Token token = tokens[i]; + + if (token.value == "\n") { + continue; + } + + if (getNodeType(token.value) == SolNodeType::Identifier) { + if (i + 2 >= tokens.size()) { + Error::syntaxError("Expecting a value after the identifier", token.line, token.lineContent); + continue; + } + if (getNodeType(tokens[i + 1].value) != SolNodeType::Set) { + Error::syntaxError("Expected a set keyword ('=' or ':')", token.line, token.lineContent); + continue; + } + if (getNodeType(tokens[i + 2].value) != SolNodeType::Identifier && + getNodeType(tokens[i + 2].value) != SolNodeType::Value) { + Error::syntaxError("Expected a value or identifier for default value", token.line, token.lineContent); + continue; + } + + SolNode idNode(SolNodeType::Identifier); + idNode.outputId = token.value; + idNode.line = token.line; + idNode.lineContent = token.lineContent; + SolNode valueNode = Parser({tokens[i + 2]}).parse().children[0]; + idNode.addNode(valueNode); + structNode.addNode(idNode); + i += 2; + continue; + } + + else if (getNodeType(token.value) == SolNodeType::FunctionDef) { + std::vector functionTokens; + functionTokens.push_back(token); + int funcCount = 0; + i++; + + while (i < tokens.size()) { + functionTokens.push_back(tokens[i]); + + if (tokens[i].value == "{") { + funcCount++; + } + if (tokens[i].value == "}") { + funcCount--; + if (funcCount == 0) { + break; + } + } + i++; + } + + if (funcCount != 0) { + Error::syntaxError("Unclosed function in struct", token.line, token.lineContent); + } + + SolNode functionNode = Parser(functionTokens).parse().children[0]; + structNode.addNode(functionNode); + } else { + Error::syntaxError("Structs only support set statements and function definition", token.line, token.lineContent); + } + } + + rootNode.addNode(structNode); + break; + } + + case SolNodeType::New: { + SolNode putsNode(SolNodeType::New); + putsNode.line = tokenObj.line; + putsNode.lineContent = tokenObj.lineContent; + auto typeopt = consume(); + rootNode.addNode(putsNode); + break; + } } } return rootNode; diff --git a/src/parser.h b/src/parser.h index 99e8369..7609d0e 100644 --- a/src/parser.h +++ b/src/parser.h @@ -57,7 +57,7 @@ namespace Solstice { enum class SolNodeType { - Add, Subtract, Multiply, Divide, Equal, Inequal, Greater, Lesser, EqGreater, EqLesser, Set, While, If, Value, Identifier, None, Root, CodeBlock, CodeBlockStart, CodeBlockEnd, FunctionDef, FunctionCall, Expression, BracketStart, BracketEnd, Puts, Return, InlineGround + Add, Subtract, Multiply, Divide, Equal, Inequal, Greater, Lesser, EqGreater, EqLesser, Set, While, If, Value, Identifier, None, Root, CodeBlock, CodeBlockStart, CodeBlockEnd, FunctionDef, FunctionCall, Expression, BracketStart, BracketEnd, Puts, Return, InlineGround, Struct, New }; enum class SolDataType { diff --git a/tests/struct.sols b/tests/struct.sols new file mode 100644 index 0000000..431e620 --- /dev/null +++ b/tests/struct.sols @@ -0,0 +1,3 @@ +struct dingus { + x = 5 +}