From 16a9985880aed5f65a4884ea037e31f34d602746 Mon Sep 17 00:00:00 2001 From: vajexal Date: Tue, 11 Jun 2024 13:21:06 +0300 Subject: [PATCH] constants --- readme.md | 3 +- src/lexer.l | 2 +- src/parser.y | 10 +++++-- src/pipes/type_inferrer.cpp | 19 ++++++++++++- src/pipes/type_inferrer.h | 5 ++++ src/type.cpp | 10 +++++-- src/type.h | 3 ++ tests/statement_test.cpp | 56 +++++++++++++++++++++++++++++++++++++ tests/tour_test.cpp | 3 +- 9 files changed, 103 insertions(+), 8 deletions(-) diff --git a/readme.md b/readme.md index 7ba7044..5966cd8 100644 --- a/readme.md +++ b/readme.md @@ -81,7 +81,8 @@ fn types() void { []Foo arr = [new Foo(), new Foo()] } -auto PI = 3.14 // globals +auto qux = 123 // globals +const auto PI = 3.14 // consts fn flow() void { // will print "then" diff --git a/src/lexer.l b/src/lexer.l index 0b10ea1..3a54276 100644 --- a/src/lexer.l +++ b/src/lexer.l @@ -32,10 +32,10 @@ yy::parser::symbol_type yylex(Driver &driver) { "string" { return yy::parser::make_STRING_TYPE(driver.location); } "void" { return yy::parser::make_VOID_TYPE(driver.location); } "auto" { return yy::parser::make_AUTO_TYPE(driver.location); } + "const" { return yy::parser::make_CONST(driver.location); } "false" { return yy::parser::make_BOOL(false, driver.location); } "true" { return yy::parser::make_BOOL(true, driver.location); } "fn" { return yy::parser::make_FN(driver.location); } - "var" { return yy::parser::make_VAR(driver.location); } "case" { return yy::parser::make_CASE(driver.location); } "default" { return yy::parser::make_DEFAULT(driver.location); } "if" { return yy::parser::make_IF(driver.location); } diff --git a/src/parser.y b/src/parser.y index cd1fc54..2b3e923 100644 --- a/src/parser.y +++ b/src/parser.y @@ -40,8 +40,8 @@ %token STRING_TYPE "string" %token VOID_TYPE "void" %token AUTO_TYPE "auto" +%token CONST "const" %token FN "fn" -%token VAR "var" %token IDENTIFIER %token CASE "case" %token DEFAULT "default" @@ -95,6 +95,7 @@ %nterm array_type %nterm return_type %nterm var_decl +%nterm id_decl %nterm identifier %nterm static_identifier %nterm scalar @@ -243,6 +244,11 @@ type { $$ = std::move($1); } ; var_decl: +CONST id_decl { $2->type.makeConst(); $$ = $2; } +| id_decl { $$ = $1; } +; + +id_decl: type IDENTIFIER '=' expr { $$ = new DeclNode(std::move($1), std::move($2), $4); } | AUTO_TYPE IDENTIFIER '=' expr { $$ = new DeclNode(Type::autoTy(), std::move($2), $4); } | type IDENTIFIER { $$ = new DeclNode(std::move($1), std::move($2)); } @@ -380,7 +386,7 @@ COMMENT { $$ = new CommentNode(std::move($1)); } ; prop_decl: -access_modifier optional_static var_decl { $$ = new PropDeclNode($3, $1, $2); } +access_modifier optional_static id_decl { $$ = new PropDeclNode($3, $1, $2); } ; method_def: diff --git a/src/pipes/type_inferrer.cpp b/src/pipes/type_inferrer.cpp index 5413a1e..ecbc1fb 100644 --- a/src/pipes/type_inferrer.cpp +++ b/src/pipes/type_inferrer.cpp @@ -280,9 +280,14 @@ namespace X::Pipes { } Type TypeInferrer::infer(AssignNode *node) { - auto exprType = node->expr->infer(*this); auto varType = getVarType(node->name); + if (varType.isConst()) { + throw ModifyConstException(); + } + + auto exprType = node->expr->infer(*this); + if (!canCastTo(exprType, varType)) { throw InvalidTypeException(); } @@ -609,6 +614,10 @@ namespace X::Pipes { throw InvalidTypeException(); } + if (arrType.isConst()) { + throw ModifyConstException(); + } + auto idxType = node->idx->infer(*this); if (!idxType.is(Type::TypeID::INT)) { throw InvalidTypeException(); @@ -628,6 +637,10 @@ namespace X::Pipes { throw InvalidTypeException(); } + if (arrType.isConst()) { + throw ModifyConstException(); + } + auto exprType = node->expr->infer(*this); if (!canCastTo(exprType, *arrType.getSubtype())) { throw InvalidTypeException(); @@ -679,6 +692,10 @@ namespace X::Pipes { checkLvalueTypeIsValid(exprType); + if (type.isConst()) { + exprType.makeConst(); + } + node->type = exprType; return; diff --git a/src/pipes/type_inferrer.h b/src/pipes/type_inferrer.h index f796b18..da62877 100644 --- a/src/pipes/type_inferrer.h +++ b/src/pipes/type_inferrer.h @@ -107,4 +107,9 @@ namespace X::Pipes { public: InvalidTypeException() : TypeInferrerException("invalid type") {} }; + + class ModifyConstException : public TypeInferrerException { + public: + ModifyConstException() : TypeInferrerException("can't modify const") {} + }; } diff --git a/src/type.cpp b/src/type.cpp index 58655fc..8247a1a 100644 --- a/src/type.cpp +++ b/src/type.cpp @@ -1,7 +1,7 @@ #include "type.h" namespace X { - Type::Type(const Type &type) : id(type.id) { + Type::Type(const Type &type) : id(type.id), constant(type.constant) { if (type.className) { className = type.className.value(); } @@ -11,14 +11,16 @@ namespace X { } } - Type::Type(Type &&type) : id(type.id), className(std::move(type.className)), subtype(type.subtype) { + Type::Type(Type &&type) : id(type.id), className(std::move(type.className)), subtype(type.subtype), constant(type.constant) { type.id = TypeID::VOID; type.className = std::nullopt; type.subtype = nullptr; + type.constant = false; } Type &Type::operator=(const Type &type) { id = type.id; + constant = type.constant; if (type.className) { className = type.className.value(); @@ -120,6 +122,10 @@ namespace X { } std::ostream &operator<<(std::ostream &out, const Type &type) { + if (type.isConst()) { + out << "const "; + } + switch (type.getTypeID()) { case Type::TypeID::INT: return out << "int"; diff --git a/src/type.h b/src/type.h index ede2731..f1f3b3e 100644 --- a/src/type.h +++ b/src/type.h @@ -48,6 +48,7 @@ namespace X { TypeID id; std::optional className; Type *subtype = nullptr; + bool constant; Type(TypeID id) : id(id) {} @@ -69,6 +70,8 @@ namespace X { TypeID getTypeID() const { return id; } const std::string &getClassName() const { return className.value(); } Type *getSubtype() const { return subtype; } + void makeConst() { constant = true; } + bool isConst() const { return constant; } bool is(TypeID typeId) const { return id == typeId; } diff --git a/tests/statement_test.cpp b/tests/statement_test.cpp index 79cd1f7..4f2b026 100644 --- a/tests/statement_test.cpp +++ b/tests/statement_test.cpp @@ -1,6 +1,7 @@ #include "compiler_test_helper.h" #include "codegen/codegen.h" +#include "pipes/type_inferrer.h" class StatementTest : public CompilerTest { }; @@ -75,3 +76,58 @@ fn main() void { } )code", "3"); } + +TEST_F(StatementTest, consts) { + checkProgram(R"code( +const auto x = 10 + +fn main() void { + const int y = 20 + + println(x + y) +} +)code", "30"); +} + +TEST_P(StatementTest, modifyConst) { + auto [code, exceptionMessage] = GetParam(); + + try { + compiler.compile(code); + FAIL() << "expected ModifyConstException"; + } catch (const Pipes::ModifyConstException &e) { + ASSERT_EQ(e.what(), exceptionMessage); + } +} + +INSTANTIATE_TEST_SUITE_P(Code, StatementTest, testing::Values( + std::make_pair( + R"code( +const auto x = 10 + +fn main() void { + x = 20 +} +)code", "can't modify const"), + std::make_pair( + R"code( +fn main() void { + const int x = 10 + x = 20 +} +)code", "can't modify const"), + std::make_pair( + R"code( +fn main() void { + const auto a = [1, 2, 3] + a[] = 4 +} +)code", "can't modify const"), + std::make_pair( + R"code( +fn main() void { + const auto a = [1, 2, 3] + a[1] = 4 +} +)code", "can't modify const") +)); diff --git a/tests/tour_test.cpp b/tests/tour_test.cpp index 120975c..9161615 100644 --- a/tests/tour_test.cpp +++ b/tests/tour_test.cpp @@ -81,7 +81,8 @@ fn types() void { []Foo arr = [new Foo(), new Foo()] } -auto PI = 3.14 // globals +auto qux = 123 // globals +const auto PI = 3.14 // consts fn flow() void { // will print "then"