diff --git a/src/lexer/lexer.cpp b/src/lexer/lexer.cpp index 4690f58..c54644a 100644 --- a/src/lexer/lexer.cpp +++ b/src/lexer/lexer.cpp @@ -242,6 +242,60 @@ namespace Solstice { break; } + case '=': { + if (!buf.empty()) { + output.push_back(processToken(buf)); + buf.clear(); + } + + if (peek() && *peek() == '=') { + output.push_back(TT::Comparison_Equal); + consume(); + } else { + output.push_back(TT::Assign_Set); + } + break; + } + + case '/': { + if (!buf.empty()) { + output.push_back(processToken(buf)); + buf.clear(); + } + + if (peek() && *peek() == '/') { + consume(); + // comment + for (;;) { + auto next = consume(); + if (!next.has_value()) { + break; + } + if (*next == '\n') { + break; + } + } + } else if (peek() && *peek() == '*') { + consume(); + /* comment */ + for (;;) { + auto next = consume(); + if (!next.has_value()) { + throw std::runtime_error("unterminated multi-line comment"); + } + if (*next == '*') { + if (peek() && *peek() == '/') { + consume(); + break; + } + } + } + } else { + output.push_back(TT::Assign_Set); + } + break; + } + // all the delimiters case '<': case '>': @@ -251,7 +305,6 @@ namespace Solstice { case ')': case '+': case '*': - case '/': case ',': { if (!buf.empty()) { diff --git a/src/parser/parser.cpp b/src/parser/parser.cpp index c9ed430..6e9fef3 100644 --- a/src/parser/parser.cpp +++ b/src/parser/parser.cpp @@ -17,6 +17,9 @@ namespace Solstice { {TT::Comparison_NotEqual, NodeType::NotEqual}, {TT::Comparison_GreaterThan, NodeType::GreaterThan}, {TT::Comparison_LesserThan, NodeType::LesserThan}, + {TT::Assign_Type, NodeType::SetType}, + {TT::Assign_Set, NodeType::Set}, + {TT::Assign_Bind, NodeType::Bind}, }; std::optional Node::getLiteral() const { @@ -52,6 +55,8 @@ namespace Solstice { return "Bind"; case NodeType::Set: return "Set"; + case NodeType::SetType: + return "SetType"; case NodeType::Lambda: return "Lambda"; case NodeType::FunctionCall: @@ -383,6 +388,11 @@ namespace Solstice { case TT::Math_Multiply: case TT::Math_Divide: return Precedence::Multiply; + case TT::Comparison_Equal: + case TT::Comparison_NotEqual: + case TT::Comparison_GreaterThan: + case TT::Comparison_LesserThan: + return Precedence::Compare; case TT::Assign_Bind: case TT::Assign_Set: case TT::Assign_Type: @@ -417,6 +427,13 @@ namespace Solstice { case TT::Math_Subtract: case TT::Math_Multiply: case TT::Math_Divide: + case TT::Comparison_Equal: + case TT::Comparison_NotEqual: + case TT::Comparison_GreaterThan: + case TT::Comparison_LesserThan: + case TT::Assign_Set: + case TT::Assign_Bind: + case TT::Assign_Type: return parseExpr(next->type); case TT::OpenParen: return parseOpenParen(); diff --git a/src/parser/parser.hpp b/src/parser/parser.hpp index 10ac5c0..8674684 100644 --- a/src/parser/parser.hpp +++ b/src/parser/parser.hpp @@ -12,7 +12,7 @@ namespace Solstice { enum class NodeType { Root, Literal, Identifier, Expression, Tuple, CodeBlock, - FunctionBind, Bind, Set, + FunctionBind, Bind, Set, SetType, Lambda, FunctionCall, Add, Subtract, Multiply, Divide, Equal, NotEqual, GreaterThan, LesserThan diff --git a/src/typechecker/type.cpp b/src/typechecker/type.cpp index 4eb22ac..8627546 100644 --- a/src/typechecker/type.cpp +++ b/src/typechecker/type.cpp @@ -56,4 +56,15 @@ namespace Solstice { return left.left == right.left && left.right == right.right; } + std::optional PossibleType::getOnlyType() { + if (possiblities.size() != 1) { + return {}; + } + for (const auto& type : possiblities) { + // just return one + return type; + } + return {}; + } + } diff --git a/src/typechecker/type.hpp b/src/typechecker/type.hpp index 4eae77e..858fba0 100644 --- a/src/typechecker/type.hpp +++ b/src/typechecker/type.hpp @@ -30,11 +30,13 @@ namespace Solstice { std::nullopt_t > fields = std::nullopt; + bool isConstant = false; + std::optional> getObject() const; std::optional> getTuple() const; std::optional getArrayOrPointer() const; - Type() = delete; + Type() = default; Type(BaseType type) : type(type) {} }; @@ -106,6 +108,18 @@ namespace Solstice { PossibleType() = default; PossibleType(const std::unordered_set& possiblities) : possiblities(possiblities) {} + + std::optional getOnlyType(); + }; + + struct Function { + PossibleType returnType = {{BaseType::None}}; + std::vector argumentTypes = {}; + + Function() = default; + Function(PossibleType returnType) : returnType(returnType) {} + Function(PossibleType returnType, std::vector argumentTypes) + : returnType(returnType), argumentTypes(argumentTypes) {} }; } diff --git a/src/typechecker/typechecker.cpp b/src/typechecker/typechecker.cpp index 84c4f00..4f812c4 100644 --- a/src/typechecker/typechecker.cpp +++ b/src/typechecker/typechecker.cpp @@ -1,6 +1,7 @@ #include "typechecker.hpp" #include "type.hpp" #include +#include namespace Solstice { @@ -107,21 +108,84 @@ namespace Solstice { throw std::runtime_error("unknown variable " + *identifier); } - node.ptype = variables[*identifier]; + node.ptype = {{variables[*identifier]}}; } void TypeChecker::checkTupleNodeType(Node& node) { for (auto& child : node.children) { checkNodeType(child); } - - + // TODO implemnt tuple type checking } + void TypeChecker::checkBindNodeType(Node& node) { + auto id = node.children[0].getIdentifier(); + if (!id.has_value()) { + throw std::runtime_error("FIXME identifier node does not contain identifier"); + } + if (variables.find(*id) != variables.end()) { + throw std::runtime_error("Cannot overwrite existing bind/variable/function with new bind"); + } + + checkNodeType(node.children[1]); + auto nodeType = node.children[1].ptype.getOnlyType(); + if (!nodeType.has_value()) { + throw std::runtime_error("cannot assign ambiguous type to name"); + } + variables[*id] = *nodeType; + variables[*id].isConstant = true; + } + + void TypeChecker::checkFunctionBindNodeType(Node& node) { + auto id = node.children[0].getIdentifier(); + if (!id.has_value()) { + throw std::runtime_error("FIXME identifier node does not contain identifier"); + } + if (variables.find(*id) != variables.end()) { + throw std::runtime_error("Cannot overwrite existing bind/variable/function with new function"); + } + + checkNodeType(node.children[1]); + auto nodeType = node.children[1].ptype.getOnlyType(); + if (!nodeType.has_value()) { + throw std::runtime_error("cannot assign ambiguous type to name"); + } + variables[*id] = *nodeType; + variables[*id].isConstant = true; + } + + void TypeChecker::checkSetNodeType(Node& node) { + auto id = node.children[0].getIdentifier(); + if (!id.has_value()) { + throw std::runtime_error("FIXME identifier node does not contain identifier"); + } + if (variables.find(*id) != variables.end()) { + if (variables[*id].isConstant) { + throw std::runtime_error("cannot reassign existing bind"); + } + } + if (!inFunction) { + throw std::runtime_error("mutable variables may only be used in functions"); + } + + checkNodeType(node.children[1]); + auto nodeType = node.children[1].ptype.getOnlyType(); + if (!nodeType.has_value()) { + throw std::runtime_error("cannot assign ambiguous type to name"); + } + variables[*id] = *nodeType; + } + + void TypeChecker::checkCodeBlockType(Node& node) {} + + void TypeChecker::checkNodeType(Node& node) { switch (node.type) { case NodeType::Root: node.ptype = {{}}; // no possible types for root node + for (auto& child : node.children) { + checkNodeType(child); + } break; case NodeType::Literal: checkLiteralNodeType(node); @@ -135,6 +199,19 @@ namespace Solstice { case NodeType::Tuple: checkTupleNodeType(node); break; + case NodeType::Bind: + checkBindNodeType(node); + break; + case NodeType::FunctionBind: + checkFunctionBindNodeType(node); + break; + case NodeType::Set: + checkSetNodeType(node); + break; + case NodeType::CodeBlock: + checkCodeBlockType(node); + break; + } } diff --git a/src/typechecker/typechecker.hpp b/src/typechecker/typechecker.hpp index 494813a..f068e95 100644 --- a/src/typechecker/typechecker.hpp +++ b/src/typechecker/typechecker.hpp @@ -9,7 +9,7 @@ namespace Solstice { class TypeChecker { - std::unordered_map variables; + std::unordered_map variables; std::unordered_map types; std::unordered_map addOverloads; @@ -23,12 +23,20 @@ namespace Solstice { Node& input; + bool inFunction = false; + void initOverloads(); void checkLiteralNodeType(Node& node); void checkIdentifierNodeType(Node& node); void checkTupleNodeType(Node& node); + void checkBindNodeType(Node& node); + void checkFunctionBindNodeType(Node& node); + void checkSetNodeType(Node& node); + + void checkCodeBlockType(Node& node); + void checkNodeType(Node& node); public: