diff --git a/Makefile b/Makefile index c90b75c..31a1664 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ LDFLAGS = -lgroundvm BUILD_DIR = build SRC_DIR = src -SRCS = $(SRC_DIR)/main.cpp $(SRC_DIR)/argparser.cpp $(SRC_DIR)/lexer.cpp $(SRC_DIR)/parser.cpp +SRCS = $(SRC_DIR)/main.cpp $(SRC_DIR)/argparser.cpp $(SRC_DIR)/lexer.cpp $(SRC_DIR)/parser.cpp $(SRC_DIR)/error.cpp OBJS = $(patsubst $(SRC_DIR)/%.cpp, $(BUILD_DIR)/%.o, $(SRCS)) TARGET = solstice @@ -23,4 +23,4 @@ $(BUILD_DIR): clean: rm -rf $(BUILD_DIR) $(TARGET) -.PHONY: all clean \ No newline at end of file +.PHONY: all clean diff --git a/src/error.cpp b/src/error.cpp new file mode 100644 index 0000000..0ce3d05 --- /dev/null +++ b/src/error.cpp @@ -0,0 +1,18 @@ +#include "error.h" +#include +#include + +namespace Solstice { + namespace Error { + [[noreturn]] void syntaxError(std::string what) { + std::cout << "\033[31mSyntax error\n"; + std::cout << "What: " << what << "\n"; + exit(1); + } + [[noreturn]] void typingError(std::string what) { + std::cout << "\033[31mTyping error\n"; + std::cout << "What: " << what << "\n"; + exit(1); + } + } +} diff --git a/src/error.h b/src/error.h new file mode 100644 index 0000000..726fe51 --- /dev/null +++ b/src/error.h @@ -0,0 +1,8 @@ +#include + +namespace Solstice { + namespace Error { + [[noreturn]] void syntaxError(std::string what); + [[noreturn]] void typingError(std::string what); + } +} diff --git a/src/parser.cpp b/src/parser.cpp index d2c6ced..0a125fa 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1,8 +1,8 @@ #include "parser.h" +#include "error.h" #include #include #include -#include #define parseOneToken(token) Parser({token.value()}).parse().children[0] @@ -13,6 +13,56 @@ namespace Solstice { return id.rfind("tmp_", 0) == 0; } + std::map variables; + + SolDataType checkNodeReturnType(SolNode i) { + switch (i.nodeType) { + case SolNodeType::Identifier: { + if (variables.find(i.outputId) != variables.end()) { + return variables[i.outputId]; + } else { + Error::syntaxError("Unknown variable"); + } + break; + } + case SolNodeType::Equal: + case SolNodeType::Inequal: + case SolNodeType::Greater: + case SolNodeType::Lesser: + case SolNodeType::EqGreater: + case SolNodeType::EqLesser: + return SolDataType::Bool; + break; + case SolNodeType::Add: + { + if (checkNodeReturnType(i.children[0]) == SolDataType::String && checkNodeReturnType(i.children[1]) == SolDataType::String) { + return SolDataType::String; + } + } + case SolNodeType::Subtract: + case SolNodeType::Multiply: + case SolNodeType::Divide: + { + if (checkNodeReturnType(i.children[0]) == SolDataType::Double) { + return SolDataType::Double; + } + if (checkNodeReturnType(i.children[1]) == SolDataType::Double) { + return SolDataType::Double; + } + return SolDataType::Int; + break; + } + case SolNodeType::Puts: + case SolNodeType::If: + case SolNodeType::While: + case SolNodeType::CodeBlock: + default: + return SolDataType::None; + break; + } + return SolDataType::None; + } + // SolData Implementation SolData::SolData(int64_t in) : data(in), type(SolDataType::Int) {} SolData::SolData(double in) : data(in), type(SolDataType::Double) {} @@ -359,6 +409,7 @@ namespace Solstice { break; } case SolNodeType::If: { + ensure(children[0], SolDataType::Bool); auto conditionCode = children[0].generateCode(); code.insert(code.end(), conditionCode.begin(), conditionCode.end()); outputId = "tmp_" + std::to_string(tmpIdIterator++); @@ -390,6 +441,7 @@ namespace Solstice { break; } case SolNodeType::While: { + ensure(children[0], SolDataType::Bool); SolGroundCodeBlock startLabelBlock; std::string startLabelIdString = "whilestart_" + std::to_string(labelIterator++); std::string endLabelIdString = "whileend_" + std::to_string(labelIterator); @@ -439,6 +491,11 @@ namespace Solstice { break; } case SolNodeType::Set: { + if (variables.find(children[0].outputId) != variables.end()) { + if (variables[children[0].outputId] != checkNodeReturnType(children[1])) { + Error::typingError("Cannot change type of this variable"); + } + } SolGroundCodeBlock codeBlock; GroundInstruction setInstruction = groundCreateInstruction(SET); groundAddReferenceToInstruction(&setInstruction, groundCreateReference(DIRREF, children[0].outputId.data())); @@ -446,6 +503,8 @@ namespace Solstice { codeBlock.code.push_back(setInstruction); if (isTemp(children[1].outputId)) codeBlock.toBeDropped.push_back(children[1].outputId); code.push_back(codeBlock); + // Make sure we know what the variable type is + variables[children[0].outputId] = children[1].data.type; break; } case SolNodeType::FunctionCall: { diff --git a/src/parser.h b/src/parser.h index 85e103e..b57ed2f 100644 --- a/src/parser.h +++ b/src/parser.h @@ -2,11 +2,22 @@ #define PARSER_H #include +#include #include #include #include #include +#define ensure(node, datatype) \ + if (checkNodeReturnType(node) != datatype) { \ + Error::typingError("Expected " #datatype); \ + } + +#define ensure2(node, datatype1, datatype2) \ + if (checkNodeReturnType(node) != datatype1 && checkNodeReturnType(node) != datatype2) { \ + Error::typingError("Expected either " #datatype1 " or " #datatype2); \ + } + namespace Solstice { // External variables referenced in parser logic @@ -15,14 +26,17 @@ namespace Solstice { namespace Parser { + enum class SolNodeType { - Add, Subtract, Equal, Inequal, Greater, Lesser, EqGreater, EqLesser, Set, While, If, Value, Identifier, None, Root, CodeBlock, CodeBlockStart, CodeBlockEnd, FunctionCall, Expression, BracketStart, BracketEnd, Puts + Add, Subtract, Multiply, Divide, Equal, Inequal, Greater, Lesser, EqGreater, EqLesser, Set, While, If, Value, Identifier, None, Root, CodeBlock, CodeBlockStart, CodeBlockEnd, FunctionCall, Expression, BracketStart, BracketEnd, Puts }; enum class SolDataType { Int, String, Double, Bool, Char, None }; + extern std::map variables; + class SolNode; class SolGroundCodeBlock { @@ -85,6 +99,7 @@ namespace Solstice { }; GroundProgram assembleProgram(SolNode& rootNode); + SolDataType checkNodeReturnType(SolNode in); } // namespace Parser }