diff --git a/include/filc/grammar/DumpVisitor.h b/include/filc/grammar/DumpVisitor.h index 236337f..dfe3597 100644 --- a/include/filc/grammar/DumpVisitor.h +++ b/include/filc/grammar/DumpVisitor.h @@ -52,6 +52,12 @@ class DumpVisitor final: public Visitor { auto visitAssignation(Assignation *assignation) -> void override; + auto visitPointer(Pointer *pointer) -> void override; + + auto visitPointerDereferencing(PointerDereferencing *pointer) -> void override; + + auto visitVariableAddress(VariableAddress *address) -> void override; + private: std::ostream &_out; int _indent_level; diff --git a/include/filc/grammar/Type.h b/include/filc/grammar/Type.h index d4b3783..88a0d57 100644 --- a/include/filc/grammar/Type.h +++ b/include/filc/grammar/Type.h @@ -41,11 +41,13 @@ class AbstractType { auto setLLVMType(llvm::Type *type) -> void; - [[nodiscard]] auto getLLVMType() const -> llvm::Type *; + [[nodiscard]] auto getLLVMType(llvm::LLVMContext *context) -> llvm::Type *; protected: AbstractType() = default; + virtual auto generateLLVMType(llvm::LLVMContext *context) -> void = 0; + private: llvm::Type *_llvm_type = nullptr; }; @@ -60,6 +62,8 @@ class Type final : public AbstractType { [[nodiscard]] auto toDisplay() const noexcept -> std::string override; + auto generateLLVMType(llvm::LLVMContext *context) -> void override; + private: std::string _name; }; @@ -74,6 +78,10 @@ class PointerType final : public AbstractType { [[nodiscard]] auto toDisplay() const noexcept -> std::string override; + [[nodiscard]] auto getPointedType() const noexcept -> std::shared_ptr; + + auto generateLLVMType(llvm::LLVMContext *context) -> void override; + private: std::shared_ptr _pointed_type; }; @@ -88,6 +96,8 @@ class AliasType final : public AbstractType { [[nodiscard]] auto toDisplay() const noexcept -> std::string override; + auto generateLLVMType(llvm::LLVMContext *context) -> void override; + private: std::string _name; std::shared_ptr _aliased_type; diff --git a/include/filc/grammar/Visitor.h b/include/filc/grammar/Visitor.h index c309cbf..a6d6d8b 100644 --- a/include/filc/grammar/Visitor.h +++ b/include/filc/grammar/Visitor.h @@ -53,6 +53,12 @@ template class Visitor { virtual auto visitAssignation(Assignation *assignation) -> Return = 0; + virtual auto visitPointer(Pointer *pointer) -> Return = 0; + + virtual auto visitPointerDereferencing(PointerDereferencing *pointer) -> Return = 0; + + virtual auto visitVariableAddress(VariableAddress *address) -> Return = 0; + protected: Visitor() = default; }; diff --git a/include/filc/grammar/ast.h b/include/filc/grammar/ast.h index da54951..5b24f80 100644 --- a/include/filc/grammar/ast.h +++ b/include/filc/grammar/ast.h @@ -49,6 +49,12 @@ class Identifier; class BinaryCalcul; class Assignation; + +class Pointer; + +class PointerDereferencing; + +class VariableAddress; } #endif // FILC_AST_H diff --git a/include/filc/grammar/pointer/Pointer.h b/include/filc/grammar/pointer/Pointer.h new file mode 100644 index 0000000..553dbdb --- /dev/null +++ b/include/filc/grammar/pointer/Pointer.h @@ -0,0 +1,81 @@ +/** + * MIT License + * + * Copyright (c) 2024-Present Kevin Traini + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef FILC_POINTER_H +#define FILC_POINTER_H + +#include "filc/grammar/expression/Expression.h" + +#include +#include + +namespace filc { +class Pointer final : public Expression { + public: + Pointer(std::string type_name, const std::shared_ptr &value); + + [[nodiscard]] auto getTypeName() const -> std::string; + + [[nodiscard]] auto getValue() const -> std::shared_ptr; + + [[nodiscard]] auto getPointedType() const -> std::shared_ptr; + + auto acceptVoidVisitor(Visitor *visitor) -> void override; + + auto acceptIRVisitor(Visitor *visitor) -> llvm::Value * override; + + private: + std::string _type_name; + std::shared_ptr _value; +}; + +class PointerDereferencing final : public Expression { + public: + explicit PointerDereferencing(std::string name); + + [[nodiscard]] auto getName() const -> std::string; + + auto acceptVoidVisitor(Visitor *visitor) -> void override; + + auto acceptIRVisitor(Visitor *visitor) -> llvm::Value * override; + + private: + std::string _name; +}; + +class VariableAddress final : public Expression { + public: + explicit VariableAddress(std::string name); + + [[nodiscard]] auto getName() const -> std::string; + + auto acceptVoidVisitor(Visitor *visitor) -> void override; + + auto acceptIRVisitor(Visitor *visitor) -> llvm::Value * override; + + private: + std::string _name; +}; +} // namespace filc + +#endif // FILC_POINTER_H diff --git a/include/filc/llvm/GeneratorContext.h b/include/filc/llvm/GeneratorContext.h new file mode 100644 index 0000000..5d968e0 --- /dev/null +++ b/include/filc/llvm/GeneratorContext.h @@ -0,0 +1,46 @@ +/** + * MIT License + * + * Copyright (c) 2024-Present Kevin Traini + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef GENERATORCONTEXT_H +#define GENERATORCONTEXT_H + +#include +#include +#include + +namespace filc { +class GeneratorContext { + public: + GeneratorContext(); + + auto setValue(const std::string &name, llvm::Value *value) -> void; + + [[nodiscard]] auto getValue(const std::string &name) const -> llvm::Value *; + + private: + std::map _values; +}; +} // namespace filc + +#endif // GENERATORCONTEXT_H diff --git a/include/filc/llvm/IRGenerator.h b/include/filc/llvm/IRGenerator.h index 40642bc..93e950c 100644 --- a/include/filc/llvm/IRGenerator.h +++ b/include/filc/llvm/IRGenerator.h @@ -26,6 +26,7 @@ #include "filc/grammar/Visitor.h" #include "filc/validation/Environment.h" +#include "filc/llvm/GeneratorContext.h" #include #include #include @@ -61,10 +62,17 @@ class IRGenerator final: public Visitor { auto visitAssignation(Assignation *assignation) -> llvm::Value * override; + auto visitPointer(Pointer *pointer) -> llvm::Value * override; + + auto visitPointerDereferencing(PointerDereferencing *pointer) -> llvm::Value * override; + + auto visitVariableAddress(VariableAddress *address) -> llvm::Value * override; + private: std::unique_ptr _llvm_context; std::unique_ptr _module; std::unique_ptr> _builder; + GeneratorContext _context; }; } diff --git a/include/filc/validation/Environment.h b/include/filc/validation/Environment.h index df6e80e..d1b7456 100644 --- a/include/filc/validation/Environment.h +++ b/include/filc/validation/Environment.h @@ -48,6 +48,8 @@ class Environment { auto addName(const Name &name) -> void; + auto setName(const Name &name) -> void; + private: std::map> _types; std::map _names; diff --git a/include/filc/validation/Name.h b/include/filc/validation/Name.h index 982ec92..3214dca 100644 --- a/include/filc/validation/Name.h +++ b/include/filc/validation/Name.h @@ -33,16 +33,21 @@ class Name { public: Name(); - Name(bool constant, std::string name, std::shared_ptr type); + Name(bool constant, std::string name, std::shared_ptr type, bool has_value); [[nodiscard]] auto isConstant() const -> bool; + [[nodiscard]] auto hasValue() const -> bool; + [[nodiscard]] auto getName() const -> const std::string&; [[nodiscard]] auto getType() const -> std::shared_ptr; + auto hasValue(bool has_value) -> void; + private: bool _constant; + bool _has_value; std::string _name; std::shared_ptr _type; }; diff --git a/include/filc/validation/ValidationVisitor.h b/include/filc/validation/ValidationVisitor.h index 51759cd..82c11ca 100644 --- a/include/filc/validation/ValidationVisitor.h +++ b/include/filc/validation/ValidationVisitor.h @@ -90,6 +90,12 @@ class ValidationVisitor final : public Visitor { auto visitAssignation(Assignation *assignation) -> void override; + auto visitPointer(Pointer *pointer) -> void override; + + auto visitPointerDereferencing(PointerDereferencing *pointer) -> void override; + + auto visitVariableAddress(VariableAddress *address) -> void override; + private: std::unique_ptr _context; std::unique_ptr _environment; diff --git a/src/grammar/DumpVisitor.cpp b/src/grammar/DumpVisitor.cpp index 8cfa484..600ce24 100644 --- a/src/grammar/DumpVisitor.cpp +++ b/src/grammar/DumpVisitor.cpp @@ -27,6 +27,7 @@ #include "filc/grammar/calcul/Calcul.h" #include "filc/grammar/identifier/Identifier.h" #include "filc/grammar/literal/Literal.h" +#include "filc/grammar/pointer/Pointer.h" #include "filc/grammar/program/Program.h" #include "filc/grammar/variable/Variable.h" @@ -144,6 +145,24 @@ auto DumpVisitor::visitAssignation(Assignation *assignation) -> void { _indent_level--; } +auto DumpVisitor::visitPointer(Pointer *pointer) -> void { + printIdent(); + _out << "[Pointer:" << pointer->getTypeName() << "]\n"; + _indent_level++; + pointer->getValue()->acceptVoidVisitor(this); + _indent_level--; +} + +auto DumpVisitor::visitPointerDereferencing(PointerDereferencing *pointer) -> void { + printIdent(); + _out << "[PointerDereferencing:" << pointer->getName() << "]\n"; +} + +auto DumpVisitor::visitVariableAddress(VariableAddress *address) -> void { + printIdent(); + _out << "[VariableAddress:" << address->getName() << "]\n"; +} + auto DumpVisitor::printIdent() const -> void { _out << std::string(_indent_level, '\t'); } diff --git a/src/grammar/FilLexer.g4 b/src/grammar/FilLexer.g4 index 577c543..fe462fb 100644 --- a/src/grammar/FilLexer.g4 +++ b/src/grammar/FilLexer.g4 @@ -28,6 +28,7 @@ VAR: 'var'; VAL: 'val'; TRUE: 'true'; FALSE: 'false'; +NEW: 'new'; // Operators EQ: '='; @@ -48,6 +49,7 @@ GT: '>'; GTE: '>='; LPAREN: '('; RPAREN: ')'; +AMP: '&'; // Assignation operators PLUS_EQ: '+='; diff --git a/src/grammar/FilParser.g4 b/src/grammar/FilParser.g4 index f3cb63e..b769328 100644 --- a/src/grammar/FilParser.g4 +++ b/src/grammar/FilParser.g4 @@ -35,6 +35,7 @@ options { #include "filc/grammar/calcul/Calcul.h" #include "filc/grammar/identifier/Identifier.h" #include "filc/grammar/assignation/Assignation.h" +#include "filc/grammar/pointer/Pointer.h" #include #include } @@ -63,6 +64,12 @@ expression returns[std::shared_ptr tree] | i=IDENTIFIER { $tree = std::make_shared($i.text); } + | p=pointer { + $tree = $p.tree; + } + | po=pointer_operation { + $tree = $po.tree; + } // === Binary calcul === | el3=expression op3=MOD er3=expression { @@ -135,20 +142,22 @@ number returns[std::shared_ptr tree] variable_declaration returns[std::shared_ptr tree] @init { bool is_constant = true; - std::string type; + std::string type_name; std::shared_ptr value = nullptr; } @after { - $tree = std::make_shared(is_constant, $name.text, type, value); + $tree = std::make_shared(is_constant, $name.text, type_name, value); } : (VAL | VAR { is_constant = false; - }) name=IDENTIFIER (COLON type=IDENTIFIER { - type = $type.text; + }) name=IDENTIFIER (COLON type { + type_name = $type.text; })? (EQ value=expression { value = $value.tree; })?; +type : IDENTIFIER STAR?; + assignation returns[std::shared_ptr tree] : i1=IDENTIFIER EQ e1=expression { $tree = std::make_shared($i1.text, $e1.tree); @@ -158,3 +167,16 @@ assignation returns[std::shared_ptr tree] calcul->setPosition(filc::Position($op, $e2.stop)); $tree = std::make_shared($i2.text, calcul); }; + +pointer returns[std::shared_ptr tree] + : NEW t=IDENTIFIER LPAREN e=expression RPAREN { + $tree = std::make_shared($t.text, $e.tree); + }; + +pointer_operation returns[std::shared_ptr tree] + : STAR i=IDENTIFIER { + $tree = std::make_shared($i.text); + } + | AMP i=IDENTIFIER { + $tree = std::make_shared($i.text); + }; diff --git a/src/grammar/Type.cpp b/src/grammar/Type.cpp index 0a8bbd5..6575310 100644 --- a/src/grammar/Type.cpp +++ b/src/grammar/Type.cpp @@ -23,6 +23,7 @@ */ #include "filc/grammar/Type.h" +#include #include using namespace filc; @@ -31,7 +32,11 @@ auto AbstractType::setLLVMType(llvm::Type *type) -> void { _llvm_type = type; } -auto AbstractType::getLLVMType() const -> llvm::Type * { +auto AbstractType::getLLVMType(llvm::LLVMContext *context) -> llvm::Type * { + if (_llvm_type == nullptr) { + generateLLVMType(context); + } + return _llvm_type; } @@ -49,6 +54,10 @@ auto Type::toDisplay() const noexcept -> std::string { return getName(); } +auto Type::generateLLVMType(llvm::LLVMContext *context) -> void { + throw std::logic_error("Should not be called for scalar types"); +} + PointerType::PointerType(std::shared_ptr pointed_type): _pointed_type(std::move(pointed_type)) {} auto PointerType::getName() const noexcept -> std::string { @@ -66,6 +75,14 @@ auto PointerType::toDisplay() const noexcept -> std::string { return getName(); } +auto PointerType::getPointedType() const noexcept -> std::shared_ptr { + return _pointed_type; +} + +auto PointerType::generateLLVMType(llvm::LLVMContext *context) -> void { + setLLVMType(llvm::PointerType::getUnqual(_pointed_type->getLLVMType(context))); +} + AliasType::AliasType(std::string name, std::shared_ptr aliased_type) : _name(std::move(name)), _aliased_type(std::move(aliased_type)) {} @@ -81,6 +98,10 @@ auto AliasType::toDisplay() const noexcept -> std::string { return getDisplayName() + " aka " + getName(); } +auto AliasType::generateLLVMType(llvm::LLVMContext *context) -> void { + throw std::logic_error("Should not be called for scalar alias types"); +} + auto operator==(const std::shared_ptr &a, const std::shared_ptr &b) -> bool { return a->getName() == b->getName(); } diff --git a/src/grammar/pointer/Pointer.cpp b/src/grammar/pointer/Pointer.cpp new file mode 100644 index 0000000..c28414d --- /dev/null +++ b/src/grammar/pointer/Pointer.cpp @@ -0,0 +1,54 @@ +/** + * MIT License + * + * Copyright (c) 2024-Present Kevin Traini + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "filc/grammar/pointer/Pointer.h" + +using namespace filc; + +Pointer::Pointer(std::string type_name, const std::shared_ptr &value) + : _type_name(std::move(type_name)), _value(value) {} + +auto Pointer::getTypeName() const -> std::string { + return _type_name; +} + +auto Pointer::getValue() const -> std::shared_ptr { + return _value; +} + +auto Pointer::getPointedType() const -> std::shared_ptr { + const auto type = std::dynamic_pointer_cast(getType()); + if (type == nullptr) { + return nullptr; + } + + return type->getPointedType(); +} + +auto Pointer::acceptVoidVisitor(Visitor *visitor) -> void { + visitor->visitPointer(this); +} + +auto Pointer::acceptIRVisitor(Visitor *visitor) -> llvm::Value * { + return visitor->visitPointer(this); +} diff --git a/src/grammar/pointer/PointerDereferencing.cpp b/src/grammar/pointer/PointerDereferencing.cpp new file mode 100644 index 0000000..c60152e --- /dev/null +++ b/src/grammar/pointer/PointerDereferencing.cpp @@ -0,0 +1,42 @@ +/** + * MIT License + * + * Copyright (c) 2024-Present Kevin Traini + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "filc/grammar/pointer/Pointer.h" + +#include + +using namespace filc; + +PointerDereferencing::PointerDereferencing(std::string name): _name(std::move(name)) {} + +auto PointerDereferencing::getName() const -> std::string { + return _name; +} + +auto PointerDereferencing::acceptVoidVisitor(Visitor *visitor) -> void { + visitor->visitPointerDereferencing(this); +} + +auto PointerDereferencing::acceptIRVisitor(Visitor *visitor) -> llvm::Value * { + return visitor->visitPointerDereferencing(this); +} diff --git a/src/grammar/pointer/VariableAddress.cpp b/src/grammar/pointer/VariableAddress.cpp new file mode 100644 index 0000000..8627485 --- /dev/null +++ b/src/grammar/pointer/VariableAddress.cpp @@ -0,0 +1,42 @@ +/** + * MIT License + * + * Copyright (c) 2024-Present Kevin Traini + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "filc/grammar/pointer/Pointer.h" + +#include + +using namespace filc; + +VariableAddress::VariableAddress(std::string name): _name(std::move(name)) {} + +auto VariableAddress::getName() const -> std::string { + return _name; +} + +auto VariableAddress::acceptVoidVisitor(Visitor *visitor) -> void { + visitor->visitVariableAddress(this); +} + +auto VariableAddress::acceptIRVisitor(Visitor *visitor) -> llvm::Value * { + return visitor->visitVariableAddress(this); +} diff --git a/src/llvm/GeneratorContext.cpp b/src/llvm/GeneratorContext.cpp new file mode 100644 index 0000000..6dc2884 --- /dev/null +++ b/src/llvm/GeneratorContext.cpp @@ -0,0 +1,39 @@ +/** + * MIT License + * + * Copyright (c) 2024-Present Kevin Traini + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "filc/llvm/GeneratorContext.h" + +using namespace filc; + +GeneratorContext::GeneratorContext() = default; + +auto GeneratorContext::setValue(const std::string &name, llvm::Value *value) -> void { + _values[name] = value; +} + +auto GeneratorContext::getValue(const std::string &name) const -> llvm::Value * { + if (_values.find(name) != _values.end()) { + return _values.at(name); + } + return nullptr; +} diff --git a/src/llvm/IRGenerator.cpp b/src/llvm/IRGenerator.cpp index 9a1e66e..7025205 100644 --- a/src/llvm/IRGenerator.cpp +++ b/src/llvm/IRGenerator.cpp @@ -27,6 +27,7 @@ #include "filc/grammar/calcul/Calcul.h" #include "filc/grammar/identifier/Identifier.h" #include "filc/grammar/literal/Literal.h" +#include "filc/grammar/pointer/Pointer.h" #include "filc/grammar/program/Program.h" #include "filc/grammar/variable/Variable.h" #include "filc/llvm/CalculBuilder.h" @@ -150,24 +151,23 @@ auto IRGenerator::visitStringLiteral(StringLiteral *literal) -> llvm::Value * { } auto IRGenerator::visitVariableDeclaration(VariableDeclaration *variable) -> llvm::Value * { - const auto global = new llvm::GlobalVariable( - variable->getType()->getLLVMType(), variable->isConstant(), llvm::GlobalValue::InternalLinkage - ); - global->setName(variable->getName()); - _module->insertGlobalVariable(global); - if (variable->getValue() != nullptr) { const auto value = variable->getValue()->acceptIRVisitor(this); - global->setInitializer((llvm::Constant *) value); + _context.setValue(variable->getName(), value); return value; } - return global; + _context.setValue(variable->getName(), nullptr); + + return nullptr; } auto IRGenerator::visitIdentifier(Identifier *identifier) -> llvm::Value * { - const auto variable = _module->getNamedGlobal(identifier->getName()); - return _builder->CreateLoad(variable->getValueType(), variable); + const auto value = _context.getValue(identifier->getName()); + if (value == nullptr) { + throw std::logic_error("Tried to access to a variable without a value set"); + } + return value; } auto IRGenerator::visitBinaryCalcul(BinaryCalcul *calcul) -> llvm::Value * { @@ -176,8 +176,31 @@ auto IRGenerator::visitBinaryCalcul(BinaryCalcul *calcul) -> llvm::Value * { } auto IRGenerator::visitAssignation(Assignation *assignation) -> llvm::Value * { - const auto variable = _module->getNamedGlobal(assignation->getIdentifier()); - const auto value = assignation->getValue()->acceptIRVisitor(this); - _builder->CreateStore(value, variable); + const auto value = assignation->getValue()->acceptIRVisitor(this); + _context.setValue(assignation->getIdentifier(), value); return value; } + +auto IRGenerator::visitPointer(Pointer *pointer) -> llvm::Value * { + const auto alloca = _builder->CreateAlloca(pointer->getPointedType()->getLLVMType(_llvm_context.get())); + _builder->CreateStore(pointer->getValue()->acceptIRVisitor(this), alloca); + + return alloca; +} + +auto IRGenerator::visitPointerDereferencing(PointerDereferencing *pointer) -> llvm::Value * { + const auto pointer_value = _context.getValue(pointer->getName()); + if (pointer_value == nullptr) { + throw std::logic_error("Tried to access to a variable without a value set"); + } + + return _builder->CreateLoad(pointer->getType()->getLLVMType(_llvm_context.get()), pointer_value); +} + +auto IRGenerator::visitVariableAddress(VariableAddress *address) -> llvm::Value * { + const auto value = _context.getValue(address->getName()); + const auto alloca = _builder->CreateAlloca(value->getType()); + _builder->CreateStore(value, alloca); + + return alloca; +} diff --git a/src/validation/Environment.cpp b/src/validation/Environment.cpp index ee5e22b..e479f68 100644 --- a/src/validation/Environment.cpp +++ b/src/validation/Environment.cpp @@ -105,9 +105,16 @@ auto Environment::getName(const std::string &name) const -> const Name & { return _names.at(name); } -auto Environment::addName(const filc::Name &name) -> void { +auto Environment::addName(const Name &name) -> void { if (hasName(name.getName())) { throw std::logic_error("Environment already have name " + name.getName()); } _names[name.getName()] = name; } + +auto Environment::setName(const Name &name) -> void { + if (! hasName(name.getName())) { + throw std::logic_error("Cannot change a name which does not exists: " + name.getName()); + } + _names[name.getName()] = name; +} diff --git a/src/validation/Name.cpp b/src/validation/Name.cpp index 190447e..ae066ae 100644 --- a/src/validation/Name.cpp +++ b/src/validation/Name.cpp @@ -27,15 +27,19 @@ using namespace filc; -Name::Name(): _constant(true) {} +Name::Name(): _constant(true), _has_value(false) {} -Name::Name(const bool constant, std::string name, std::shared_ptr type) - : _constant(constant), _name(std::move(name)), _type(std::move(type)) {} +Name::Name(const bool constant, std::string name, std::shared_ptr type, const bool has_value) + : _constant(constant), _has_value(has_value), _name(std::move(name)), _type(std::move(type)) {} auto Name::isConstant() const -> bool { return _constant; } +auto Name::hasValue() const -> bool { + return _has_value; +} + auto Name::getName() const -> const std::string & { return _name; } @@ -43,3 +47,7 @@ auto Name::getName() const -> const std::string & { auto Name::getType() const -> std::shared_ptr { return _type; } + +auto Name::hasValue(const bool has_value) -> void { + _has_value = has_value; +} diff --git a/src/validation/ValidationVisitor.cpp b/src/validation/ValidationVisitor.cpp index 91f3537..c1063b1 100644 --- a/src/validation/ValidationVisitor.cpp +++ b/src/validation/ValidationVisitor.cpp @@ -27,12 +27,13 @@ #include "filc/grammar/calcul/Calcul.h" #include "filc/grammar/identifier/Identifier.h" #include "filc/grammar/literal/Literal.h" +#include "filc/grammar/pointer/Pointer.h" #include "filc/grammar/program/Program.h" #include "filc/grammar/variable/Variable.h" #include "filc/utils/Message.h" #include "filc/validation/CalculValidator.h" -#include +#include using namespace filc; @@ -196,7 +197,9 @@ auto ValidationVisitor::visitVariableDeclaration(VariableDeclaration *variable) } variable->setType(variable_type); - _environment->addName(Name(variable->isConstant(), variable->getName(), variable_type)); + _environment->addName( + Name(variable->isConstant(), variable->getName(), variable_type, variable->getValue() != nullptr) + ); } auto ValidationVisitor::visitIdentifier(Identifier *identifier) -> void { @@ -206,6 +209,13 @@ auto ValidationVisitor::visitIdentifier(Identifier *identifier) -> void { } const auto name = _environment->getName(identifier->getName()); + if (! name.hasValue()) { + displayError( + "Variable " + identifier->getName() + " has no value, please set one before accessing it", + identifier->getPosition() + ); + return; + } identifier->setType(name.getType()); if (! _context->has("return") || ! _context->get("return")) { @@ -255,7 +265,7 @@ auto ValidationVisitor::visitAssignation(Assignation *assignation) -> void { ); return; } - const auto name = _environment->getName(assignation->getIdentifier()); + auto name = _environment->getName(assignation->getIdentifier()); if (name.isConstant()) { displayError("Cannot modify a constant", assignation->getPosition()); return; @@ -279,5 +289,87 @@ auto ValidationVisitor::visitAssignation(Assignation *assignation) -> void { return; } + name.hasValue(true); + _environment->setName(name); assignation->setType(name.getType()); } + +auto ValidationVisitor::visitPointer(Pointer *pointer) -> void { + if (! _environment->hasType(pointer->getTypeName())) { + displayError("Unknown type: " + pointer->getTypeName(), pointer->getPosition()); + return; + } + const auto pointed_type = _environment->getType(pointer->getTypeName()); + + std::shared_ptr pointer_type = nullptr; + if (_environment->hasType(pointer->getTypeName() + "*")) { + pointer_type = _environment->getType(pointer->getTypeName() + "*"); + } else { + pointer_type = std::make_shared(pointed_type); + _environment->addType(pointer_type); + } + + _context->stack(); + _context->set("return", true); + pointer->getValue()->acceptVoidVisitor(this); + _context->unstack(); + + const auto value_type = pointer->getValue()->getType(); + if (value_type->getName() != pointed_type->getName()) { + displayError( + "Cannot assign a value of type " + value_type->toDisplay() + " to a pointer to type " + + pointed_type->toDisplay(), + pointer->getPosition() + ); + return; + } + + pointer->setType(pointer_type); + + if (! _context->has("return") || ! _context->get("return")) { + displayWarning("Value not used", pointer->getPosition()); + } +} + +auto ValidationVisitor::visitPointerDereferencing(PointerDereferencing *pointer) -> void { + if (! _environment->hasName(pointer->getName())) { + displayError("Unknown name, don't know what it refers to: " + pointer->getName(), pointer->getPosition()); + return; + } + + const auto name = _environment->getName(pointer->getName()); + const auto type = std::dynamic_pointer_cast(name.getType()); + if (type == nullptr) { + displayError("Cannot dereference a variable which is not a pointer", pointer->getPosition()); + return; + } + + pointer->setType(type->getPointedType()); + + if (! _context->has("return") || ! _context->get("return")) { + displayWarning("Value not used", pointer->getPosition()); + } +} + +auto ValidationVisitor::visitVariableAddress(VariableAddress *address) -> void { + if (! _environment->hasName(address->getName())) { + displayError("Unknown name, don't know what it refers to: " + address->getName(), address->getPosition()); + return; + } + + const auto name = _environment->getName(address->getName()); + const auto pointed_type = name.getType(); + std::shared_ptr type = nullptr; + if (_environment->hasType(pointed_type->getName() + "*")) { + type = _environment->getType(pointed_type->getName() + "*"); + } else { + type = std::make_shared(pointed_type); + _environment->addType(type); + } + + address->setType(type); + + if (! _context->has("return") || ! _context->get("return")) { + displayWarning("Value not used", address->getPosition()); + } +} diff --git a/tests/e2e/llvm_ir.cpp b/tests/e2e/llvm_ir.cpp index 5081b3b..50665b3 100644 --- a/tests/e2e/llvm_ir.cpp +++ b/tests/e2e/llvm_ir.cpp @@ -56,3 +56,11 @@ TEST(ir_dump, calcul_program) { TEST(ir_dump, variable_program) { ASSERT_EQ(2, getProgramResult("val foo = 2\nfoo")); } + +TEST(ir_dump, pointer_program) { + ASSERT_EQ(3, getProgramResult("val foo = new i32(3);*foo")); +} + +TEST(ir_dump, address_program) { + ASSERT_EQ(4, getProgramResult("val foo = 4;val bar = &foo;*bar")); +} diff --git a/tests/unit/grammar/DumpVisitorTest.cpp b/tests/unit/grammar/DumpVisitorTest.cpp index 09b73b8..415e300 100644 --- a/tests/unit/grammar/DumpVisitorTest.cpp +++ b/tests/unit/grammar/DumpVisitorTest.cpp @@ -62,7 +62,7 @@ TEST(DumpVisitor, dump) { std::stringstream ss; auto visitor = filc::DumpVisitor(ss); program->acceptVoidVisitor(&visitor); - std::string result(std::istreambuf_iterator(ss), {}); + const std::string result(std::istreambuf_iterator(ss), {}); ASSERT_STREQ("=== Begin AST dump ===\n=== End AST dump ===\n", result.c_str()); } @@ -117,7 +117,7 @@ TEST(DumpVisitor, visitCharacterLiteral_Classic) { filc::DumpVisitor visitor(ss); filc::CharacterLiteral literal('a'); visitor.visitCharacterLiteral(&literal); - std::string dump(std::istreambuf_iterator(ss), {}); + const std::string dump(std::istreambuf_iterator(ss), {}); ASSERT_STREQ("[Character:'a']\n", dump.c_str()); } @@ -185,3 +185,22 @@ TEST(DumpVisitor, Assignation) { ASSERT_STREQ("[Assignation:foo]", dump[0].c_str()); ASSERT_STREQ("\t[Character:'a']", dump[1].c_str()); } + +TEST(DumpVisitor, Pointer) { + const auto dump = dumpProgram("new i32(3)"); + ASSERT_THAT(dump, SizeIs(2)); + ASSERT_STREQ("[Pointer:i32]", dump[0].c_str()); + ASSERT_STREQ("\t[Integer:3]", dump[1].c_str()); +} + +TEST(DumpVisitor, PointerDereferencing) { + const auto dump = dumpProgram("*foo"); + ASSERT_THAT(dump, SizeIs(1)); + ASSERT_STREQ("[PointerDereferencing:foo]", dump[0].c_str()); +} + +TEST(DumpVisitor, VariableAddress) { + const auto dump = dumpProgram("&foo"); + ASSERT_THAT(dump, SizeIs(1)); + ASSERT_STREQ("[VariableAddress:foo]", dump[0].c_str()); +} diff --git a/tests/unit/grammar/TypeTest.cpp b/tests/unit/grammar/TypeTest.cpp index 487bb37..62e991d 100644 --- a/tests/unit/grammar/TypeTest.cpp +++ b/tests/unit/grammar/TypeTest.cpp @@ -25,31 +25,36 @@ #include TEST(Type, getName) { - filc::Type type("my_type"); + const filc::Type type("my_type"); ASSERT_STREQ("my_type", type.getName().c_str()); } TEST(Type, getDisplayName) { - filc::Type type("another_type"); + const filc::Type type("another_type"); ASSERT_STREQ("another_type", type.getDisplayName().c_str()); } TEST(PointerType, getName) { - filc::PointerType type(std::make_shared("int")); + const filc::PointerType type(std::make_shared("int")); ASSERT_STREQ("int*", type.getName().c_str()); } TEST(PointerType, getDisplayName) { - filc::PointerType type(std::make_shared("int")); + const filc::PointerType type(std::make_shared("int")); ASSERT_STREQ("int*", type.getDisplayName().c_str()); } +TEST(PonterType, getPointedType) { + const filc::PointerType type(std::make_shared("int")); + ASSERT_STREQ("int", type.getPointedType()->getDisplayName().c_str()); +} + TEST(AliasType, getName) { - filc::AliasType type("char", std::make_shared("u8")); + const filc::AliasType type("char", std::make_shared("u8")); ASSERT_STREQ("u8", type.getName().c_str()); } TEST(AliasType, getDisplayName) { - filc::AliasType type("char", std::make_shared("u8")); + const filc::AliasType type("char", std::make_shared("u8")); ASSERT_STREQ("char", type.getDisplayName().c_str()); } diff --git a/tests/unit/grammar/pointer/PointerDereferencingTest.cpp b/tests/unit/grammar/pointer/PointerDereferencingTest.cpp new file mode 100644 index 0000000..74599c1 --- /dev/null +++ b/tests/unit/grammar/pointer/PointerDereferencingTest.cpp @@ -0,0 +1,39 @@ +/** + * MIT License + * + * Copyright (c) 2024-Present Kevin Traini + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "test_tools.h" + +#include +#include +#include + +using namespace ::testing; + +TEST(PointerDereferencing, parsing) { + const auto program = parseString("*foo"); + const auto expressions = program->getExpressions(); + ASSERT_THAT(expressions, SizeIs(1)); + const auto pointer_dereferencing = std::dynamic_pointer_cast(expressions[0]); + ASSERT_NE(nullptr, pointer_dereferencing); + ASSERT_STREQ("foo", pointer_dereferencing->getName().c_str()); +} diff --git a/tests/unit/grammar/pointer/PointerTest.cpp b/tests/unit/grammar/pointer/PointerTest.cpp new file mode 100644 index 0000000..6754e8a --- /dev/null +++ b/tests/unit/grammar/pointer/PointerTest.cpp @@ -0,0 +1,44 @@ +/** + * MIT License + * + * Copyright (c) 2024-Present Kevin Traini + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "test_tools.h" + +#include +#include +#include +#include + +using namespace ::testing; + +TEST(Pointer, parsing) { + const auto program = parseString("new i32(3)"); + const auto expressions = program->getExpressions(); + ASSERT_THAT(expressions, SizeIs(1)); + const auto pointer = std::dynamic_pointer_cast(expressions[0]); + ASSERT_NE(nullptr, pointer); + ASSERT_STREQ("i32", pointer->getTypeName().c_str()); + + const auto value = std::dynamic_pointer_cast(pointer->getValue()); + ASSERT_NE(nullptr, value); + ASSERT_EQ(3, value->getValue()); +} diff --git a/tests/unit/grammar/pointer/VariableAddressTest.cpp b/tests/unit/grammar/pointer/VariableAddressTest.cpp new file mode 100644 index 0000000..c2c6e49 --- /dev/null +++ b/tests/unit/grammar/pointer/VariableAddressTest.cpp @@ -0,0 +1,39 @@ +/** + * MIT License + * + * Copyright (c) 2024-Present Kevin Traini + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "test_tools.h" + +#include +#include +#include + +using namespace ::testing; + +TEST(VariableAddress, parsing) { + const auto program = parseString("&foo"); + const auto expressions = program->getExpressions(); + ASSERT_THAT(expressions, SizeIs(1)); + const auto variable_address = std::dynamic_pointer_cast(expressions[0]); + ASSERT_NE(nullptr, variable_address); + ASSERT_STREQ("foo", variable_address->getName().c_str()); +} diff --git a/tests/unit/llvm/IRGeneratorTest.cpp b/tests/unit/llvm/IRGeneratorTest.cpp index 01805e7..7802097 100644 --- a/tests/unit/llvm/IRGeneratorTest.cpp +++ b/tests/unit/llvm/IRGeneratorTest.cpp @@ -22,13 +22,9 @@ * SOFTWARE. */ #include "filc/grammar/assignation/Assignation.h" -#include "filc/grammar/calcul/Calcul.h" -#include "filc/grammar/identifier/Identifier.h" -#include "filc/grammar/variable/Variable.h" #include "filc/validation/ValidationVisitor.h" #include "test_tools.h" -#include #include #include #include @@ -105,7 +101,7 @@ TEST(IRGenerator, variableDeclaration_value) { TEST(IRGenerator, variableDeclaration_identifier) { const auto ir = getIR("val foo = 2\nfoo"); - ASSERT_THAT(ir, HasSubstr("ret i32 %0")); // Returns the register in which it loads the constant + ASSERT_THAT(ir, HasSubstr("ret i32 2")); // Returns the value directly } TEST(IRGenerator, calcul_integer) { @@ -120,3 +116,21 @@ TEST(IRGenerator, assignation_notThrow) { const auto ir = getIR("var bar = 3\nbar = 0"); ASSERT_THAT(ir, HasSubstr("ret i32 0")); } + +TEST(IRGenerator, pointer_notThrow) { + const auto ir = getIR("val foo = new i32(3);foo;0"); + ASSERT_THAT(ir, HasSubstr("alloca i32")); + ASSERT_THAT(ir, HasSubstr("store i32 3, ptr %")); +} + +TEST(IRGenerator, pointerDereferencing_notThrow) { + const auto ir = getIR("val foo = new i32(0);*foo"); + ASSERT_THAT(ir, HasSubstr("ret i32 %1")); // Register %1 contains pointed value +} + +TEST(IRGenerator, variableAddress_notThrow) { + const auto ir = getIR("val foo = 0;val bar = &foo;*bar"); + ASSERT_THAT(ir, HasSubstr("%0 = alloca i32")); + ASSERT_THAT(ir, HasSubstr("store i32 0, ptr %0")); // bar = &foo + ASSERT_THAT(ir, HasSubstr("ret i32 %1")); // Register %1 is *foo +} diff --git a/tests/unit/test_tools.cpp b/tests/unit/test_tools.cpp index b9e5032..7c1732f 100644 --- a/tests/unit/test_tools.cpp +++ b/tests/unit/test_tools.cpp @@ -120,6 +120,20 @@ auto PrinterVisitor::visitAssignation(filc::Assignation *assignation) -> void { assignation->getValue()->acceptVoidVisitor(this); } +auto PrinterVisitor::visitPointer(filc::Pointer *pointer) -> void { + _out << "new " << pointer->getTypeName() << "("; + pointer->getValue()->acceptVoidVisitor(this); + _out << ")"; +} + +auto PrinterVisitor::visitPointerDereferencing(filc::PointerDereferencing *pointer) -> void { + _out << "*" << pointer->getName(); +} + +auto PrinterVisitor::visitVariableAddress(filc::VariableAddress *address) -> void { + _out << "&" << address->getName(); +} + TokenSourceStub::TokenSourceStub(std::string filename): _filename(std::move(filename)) {} auto TokenSourceStub::nextToken() -> std::unique_ptr { diff --git a/tests/unit/test_tools.h b/tests/unit/test_tools.h index a09062f..6fd7901 100644 --- a/tests/unit/test_tools.h +++ b/tests/unit/test_tools.h @@ -64,6 +64,12 @@ class PrinterVisitor final: public filc::Visitor { auto visitAssignation(filc::Assignation *assignation) -> void override; + auto visitPointer(filc::Pointer *pointer) -> void override; + + auto visitPointerDereferencing(filc::PointerDereferencing *pointer) -> void override; + + auto visitVariableAddress(filc::VariableAddress *address) -> void override; + private: std::stringstream _out; }; diff --git a/tests/unit/validation/EnvironmentTest.cpp b/tests/unit/validation/EnvironmentTest.cpp index 5b74cdf..4719d4a 100644 --- a/tests/unit/validation/EnvironmentTest.cpp +++ b/tests/unit/validation/EnvironmentTest.cpp @@ -60,10 +60,25 @@ TEST(Environment, Name) { filc::Environment env; ASSERT_FALSE(env.hasName("my_name")); ASSERT_THROW(env.getName("my_name"), std::logic_error); - env.addName(filc::Name(false, "my_name", env.getType("i32"))); - ASSERT_THROW(env.addName(filc::Name(true, "my_name", env.getType("i64"))), std::logic_error); + env.addName(filc::Name(false, "my_name", env.getType("i32"), true)); + ASSERT_THROW(env.addName(filc::Name(true, "my_name", env.getType("i64"), true)), std::logic_error); ASSERT_TRUE(env.hasName("my_name")); ASSERT_FALSE(env.getName("my_name").isConstant()); ASSERT_STREQ("my_name", env.getName("my_name").getName().c_str()); ASSERT_STREQ("i32", env.getName("my_name").getType()->getName().c_str()); } + +TEST(Environment, setName) { + filc::Environment env; + env.addName(filc::Name(false, "my_name", env.getType("i32"), true)); + ASSERT_TRUE(env.hasName("my_name")); + ASSERT_FALSE(env.getName("my_name").isConstant()); + ASSERT_STREQ("my_name", env.getName("my_name").getName().c_str()); + ASSERT_STREQ("i32", env.getName("my_name").getType()->getName().c_str()); + ASSERT_TRUE(env.getName("my_name").hasValue()); + auto name = env.getName("my_name"); + name.hasValue(false); + ASSERT_TRUE(env.getName("my_name").hasValue()); + env.setName(name); + ASSERT_FALSE(env.getName("my_name").hasValue()); +} diff --git a/tests/unit/validation/NameTest.cpp b/tests/unit/validation/NameTest.cpp index cd54f4f..db585e4 100644 --- a/tests/unit/validation/NameTest.cpp +++ b/tests/unit/validation/NameTest.cpp @@ -25,15 +25,26 @@ #include TEST(Name, defaultConstructor) { - filc::Name name; + const filc::Name name; ASSERT_TRUE(name.isConstant()); ASSERT_STREQ("", name.getName().c_str()); ASSERT_EQ(nullptr, name.getType()); + ASSERT_FALSE(name.hasValue()); } TEST(Name, constructor) { - filc::Name name(false, "my_var", std::make_shared("i32")); + const filc::Name name(false, "my_var", std::make_shared("i32"), true); ASSERT_FALSE(name.isConstant()); ASSERT_STREQ("my_var", name.getName().c_str()); ASSERT_STREQ("i32", name.getType()->getName().c_str()); + ASSERT_TRUE(name.hasValue()); +} + +TEST(Name, setHasValue) { + filc::Name name(false, "my_var", std::make_shared("i32"), false); + ASSERT_FALSE(name.hasValue()); + name.hasValue(true); + ASSERT_TRUE(name.hasValue()); + name.hasValue(false); + ASSERT_FALSE(name.hasValue()); } diff --git a/tests/unit/validation/ValidationVisitorTest.cpp b/tests/unit/validation/ValidationVisitorTest.cpp index 184eaf4..1b2f0d9 100644 --- a/tests/unit/validation/ValidationVisitorTest.cpp +++ b/tests/unit/validation/ValidationVisitorTest.cpp @@ -39,7 +39,7 @@ TEST(ValidationVisitor, program_valid) { VISITOR; const auto program = parseString("0"); program->acceptVoidVisitor(&visitor); - ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), IsEmpty()); + ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), IsEmpty()); ASSERT_FALSE(visitor.hasError()); } @@ -47,9 +47,7 @@ TEST(ValidationVisitor, program_invalid) { VISITOR; const auto program = parseString("3.4"); program->acceptVoidVisitor(&visitor); - ASSERT_THAT( - std::string(std::istreambuf_iterator(ss), {}), HasSubstr("Expected type int aka i32 but got f64") - ); + ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), HasSubstr("Expected type int aka i32 but got f64")); ASSERT_TRUE(visitor.hasError()); } @@ -57,7 +55,7 @@ TEST(ValidationVisitor, program_finish_u8) { VISITOR; const auto program = parseString("val foo: u8 = 2\nfoo"); program->acceptVoidVisitor(&visitor); - ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), IsEmpty()); + ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), IsEmpty()); ASSERT_FALSE(visitor.hasError()); } @@ -65,7 +63,7 @@ TEST(ValidationVisitor, program_finish_bool) { VISITOR; const auto program = parseString("true"); program->acceptVoidVisitor(&visitor); - ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), IsEmpty()); + ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), IsEmpty()); ASSERT_FALSE(visitor.hasError()); } @@ -73,7 +71,7 @@ TEST(ValidationVisitor, boolean) { VISITOR; const auto program = parseString("true\n0"); program->acceptVoidVisitor(&visitor); - ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), HasSubstr("Boolean value not used")); + ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), HasSubstr("Boolean value not used")); ASSERT_FALSE(visitor.hasError()); } @@ -81,7 +79,7 @@ TEST(ValidationVisitor, integer) { VISITOR; const auto program = parseString("2\n0"); program->acceptVoidVisitor(&visitor); - ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), HasSubstr("Integer value not used")); + ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), HasSubstr("Integer value not used")); ASSERT_FALSE(visitor.hasError()); ASSERT_STREQ("int", program->getExpressions()[0]->getType()->getDisplayName().c_str()); } @@ -90,7 +88,7 @@ TEST(ValidationVisitor, float) { VISITOR; const auto program = parseString("3.14\n0"); program->acceptVoidVisitor(&visitor); - ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), HasSubstr("Float value not used")); + ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), HasSubstr("Float value not used")); ASSERT_FALSE(visitor.hasError()); ASSERT_STREQ("f64", program->getExpressions()[0]->getType()->getDisplayName().c_str()); } @@ -99,7 +97,7 @@ TEST(ValidationVisitor, character) { VISITOR; const auto program = parseString("'a'\n0"); program->acceptVoidVisitor(&visitor); - ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), HasSubstr("Character value not used")); + ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), HasSubstr("Character value not used")); ASSERT_FALSE(visitor.hasError()); ASSERT_STREQ("char", program->getExpressions()[0]->getType()->getDisplayName().c_str()); } @@ -108,7 +106,7 @@ TEST(ValidationVisitor, string) { VISITOR; const auto program = parseString("\"hello\"\n0"); program->acceptVoidVisitor(&visitor); - ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), HasSubstr("String value not used")); + ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), HasSubstr("String value not used")); ASSERT_FALSE(visitor.hasError()); ASSERT_STREQ("char*", program->getExpressions()[0]->getType()->getDisplayName().c_str()); } @@ -117,7 +115,7 @@ TEST(ValidationVisitor, variable_alreadyDefined) { VISITOR; const auto program = parseString("val my_constant = 2\nval my_constant = 3"); program->acceptVoidVisitor(&visitor); - ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), HasSubstr("my_constant is already defined")); + ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), HasSubstr("my_constant is already defined")); ASSERT_TRUE(visitor.hasError()); ASSERT_STREQ("int", program->getExpressions()[0]->getType()->getDisplayName().c_str()); ASSERT_EQ(nullptr, program->getExpressions()[1]->getType()); @@ -128,7 +126,7 @@ TEST(ValidationVisitor, variable_constantWithoutValue) { const auto program = parseString("val my_constant"); program->acceptVoidVisitor(&visitor); ASSERT_THAT( - std::string(std::istreambuf_iterator(ss), {}), + std::string(std::istreambuf_iterator(ss), {}), HasSubstr("When declaring a constant, you must provide it a value") ); ASSERT_TRUE(visitor.hasError()); @@ -139,7 +137,7 @@ TEST(ValidationVisitor, variable_unknowType) { VISITOR; const auto program = parseString("var my_var: foo"); program->acceptVoidVisitor(&visitor); - ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), HasSubstr("Unknown type: foo")); + ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), HasSubstr("Unknown type: foo")); ASSERT_TRUE(visitor.hasError()); ASSERT_EQ(nullptr, program->getExpressions()[0]->getType()); } @@ -149,7 +147,7 @@ TEST(ValidationVisitor, variable_differentValueType) { const auto program = parseString("var my_var: i32 = 'a'"); program->acceptVoidVisitor(&visitor); ASSERT_THAT( - std::string(std::istreambuf_iterator(ss), {}), + std::string(std::istreambuf_iterator(ss), {}), HasSubstr("Cannot assign value of type char aka u8 to a variable of type i32") ); ASSERT_TRUE(visitor.hasError()); @@ -160,7 +158,7 @@ TEST(ValidationVisitor, variable_assignAliasType) { VISITOR; const auto program = parseString("var my_var: u8 = 'a'\n0"); program->acceptVoidVisitor(&visitor); - ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), IsEmpty()); + ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), IsEmpty()); ASSERT_FALSE(visitor.hasError()); ASSERT_STREQ("u8", program->getExpressions()[0]->getType()->getDisplayName().c_str()); } @@ -170,7 +168,7 @@ TEST(ValidationVisitor, variable_withoutTypeAndValue) { const auto program = parseString("var my_var"); program->acceptVoidVisitor(&visitor); ASSERT_THAT( - std::string(std::istreambuf_iterator(ss), {}), + std::string(std::istreambuf_iterator(ss), {}), HasSubstr("When declaring a variable, you must provide at least a type or a value") ); ASSERT_TRUE(visitor.hasError()); @@ -181,7 +179,7 @@ TEST(ValidationVisitor, variable_valid) { VISITOR; const auto program = parseString("val foo: i32 = 45"); program->acceptVoidVisitor(&visitor); - ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), IsEmpty()); + ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), IsEmpty()); ASSERT_FALSE(visitor.hasError()); ASSERT_STREQ("i32", program->getExpressions()[0]->getType()->getDisplayName().c_str()); } @@ -190,7 +188,7 @@ TEST(ValidationVisitor, variable_castInteger) { VISITOR; const auto program = parseString("val bar: u8 = 4\n0"); program->acceptVoidVisitor(&visitor); - ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), IsEmpty()); + ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), IsEmpty()); ASSERT_FALSE(visitor.hasError()); ASSERT_STREQ("u8", program->getExpressions()[0]->getType()->getDisplayName().c_str()); } @@ -200,18 +198,38 @@ TEST(ValidationVisitor, identifier_nonExisting) { const auto program = parseString("bar"); program->acceptVoidVisitor(&visitor); ASSERT_THAT( - std::string(std::istreambuf_iterator(ss), {}), - HasSubstr("Unknown name, don't know what it refers to: bar") + std::string(std::istreambuf_iterator(ss), {}), HasSubstr("Unknown name, don't know what it refers to: bar") ); ASSERT_TRUE(visitor.hasError()); ASSERT_EQ(nullptr, program->getExpressions()[0]->getType()); } +TEST(ValidationVisitor, identifier_noValue) { + VISITOR; + const auto program = parseString("var foo : i32\nfoo"); + program->acceptVoidVisitor(&visitor); + ASSERT_THAT( + std::string(std::istreambuf_iterator(ss), {}), + HasSubstr("Variable foo has no value, please set one before accessing it") + ); + ASSERT_TRUE(visitor.hasError()); + ASSERT_EQ(nullptr, program->getExpressions()[1]->getType()); +} + +TEST(ValidationVisitor, identifier_validAssignation) { + VISITOR; + const auto program = parseString("var foo : i32\nfoo = 2\nfoo"); + program->acceptVoidVisitor(&visitor); + ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), IsEmpty()); + ASSERT_FALSE(visitor.hasError()); + ASSERT_STREQ("i32", program->getExpressions()[2]->getType()->getDisplayName().c_str()); +} + TEST(ValidationVisitor, identifier_valid) { VISITOR; const auto program = parseString("val foo = 1\nfoo"); program->acceptVoidVisitor(&visitor); - ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), IsEmpty()); + ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), IsEmpty()); ASSERT_FALSE(visitor.hasError()); ASSERT_STREQ("int", program->getExpressions()[1]->getType()->getDisplayName().c_str()); } @@ -221,7 +239,7 @@ TEST(ValidationVisitor, calcul_invalidLeft) { const auto program = parseString("(val foo) + 2"); program->acceptVoidVisitor(&visitor); ASSERT_THAT( - std::string(std::istreambuf_iterator(ss), {}), + std::string(std::istreambuf_iterator(ss), {}), HasSubstr("When declaring a constant, you must provide it a value") ); ASSERT_TRUE(visitor.hasError()); @@ -233,7 +251,7 @@ TEST(ValidationVisitor, calcul_invalidRight) { const auto program = parseString("2 + (val foo)"); program->acceptVoidVisitor(&visitor); ASSERT_THAT( - std::string(std::istreambuf_iterator(ss), {}), + std::string(std::istreambuf_iterator(ss), {}), HasSubstr("When declaring a constant, you must provide it a value") ); ASSERT_TRUE(visitor.hasError()); @@ -245,7 +263,7 @@ TEST(ValidationVisitor, calcul_invalid) { const auto program = parseString("'a' && 3"); program->acceptVoidVisitor(&visitor); ASSERT_THAT( - std::string(std::istreambuf_iterator(ss), {}), + std::string(std::istreambuf_iterator(ss), {}), HasSubstr("You cannot use operator && with char aka u8 and int aka i32") ); ASSERT_TRUE(visitor.hasError()); @@ -256,7 +274,7 @@ TEST(ValidationVisitor, calcul_valid) { VISITOR; const auto program = parseString("2 + 2"); program->acceptVoidVisitor(&visitor); - ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), IsEmpty()); + ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), IsEmpty()); ASSERT_FALSE(visitor.hasError()); ASSERT_STREQ("int", program->getExpressions()[0]->getType()->getDisplayName().c_str()); } @@ -266,8 +284,7 @@ TEST(ValidationVisitor, assignation_nonExisting) { const auto program = parseString("foo = 3"); program->acceptVoidVisitor(&visitor); ASSERT_THAT( - std::string(std::istreambuf_iterator(ss), {}), - HasSubstr("Unknown name, don't know what it refers to: foo") + std::string(std::istreambuf_iterator(ss), {}), HasSubstr("Unknown name, don't know what it refers to: foo") ); ASSERT_TRUE(visitor.hasError()); ASSERT_EQ(nullptr, program->getExpressions()[0]->getType()); @@ -277,7 +294,7 @@ TEST(ValidationVisitor, assignation_constant) { VISITOR; const auto program = parseString("val foo = 3\nfoo = 4"); program->acceptVoidVisitor(&visitor); - ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), HasSubstr("Cannot modify a constant")); + ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), HasSubstr("Cannot modify a constant")); ASSERT_TRUE(visitor.hasError()); ASSERT_EQ(nullptr, program->getExpressions()[1]->getType()); } @@ -287,7 +304,7 @@ TEST(ValidationVisitor, assignation_differentType) { const auto program = parseString("var foo = 3\nfoo = false"); program->acceptVoidVisitor(&visitor); ASSERT_THAT( - std::string(std::istreambuf_iterator(ss), {}), + std::string(std::istreambuf_iterator(ss), {}), HasSubstr("Cannot assign value of type bool to a variable of type int aka i32") ); ASSERT_TRUE(visitor.hasError()); @@ -298,7 +315,7 @@ TEST(ValidationVisitor, assignation_valid) { VISITOR; const auto program = parseString("var foo = 3\nfoo = 2"); program->acceptVoidVisitor(&visitor); - ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), IsEmpty()); + ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), IsEmpty()); ASSERT_FALSE(visitor.hasError()); ASSERT_STREQ("int", program->getExpressions()[1]->getType()->getDisplayName().c_str()); } @@ -307,7 +324,95 @@ TEST(ValidationVisitor, assignation_castInteger) { VISITOR; const auto program = parseString("var foo: u8 = 3\nfoo = 2\n0"); program->acceptVoidVisitor(&visitor); - ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), IsEmpty()); + ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), IsEmpty()); ASSERT_FALSE(visitor.hasError()); ASSERT_STREQ("u8", program->getExpressions()[1]->getType()->getDisplayName().c_str()); } + +TEST(ValidationVisitor, pointer_unknownType) { + VISITOR; + const auto program = parseString("new bla(2)"); + program->acceptVoidVisitor(&visitor); + ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), HasSubstr("Unknown type: bla")); + ASSERT_TRUE(visitor.hasError()); + ASSERT_EQ(nullptr, program->getExpressions()[0]->getType()); +} + +TEST(ValidationVisitor, pointer_invalidValueType) { + VISITOR; + const auto program = parseString("new i32(true)"); + program->acceptVoidVisitor(&visitor); + ASSERT_THAT( + std::string(std::istreambuf_iterator(ss), {}), + HasSubstr("Cannot assign a value of type bool to a pointer to type i32") + ); + ASSERT_TRUE(visitor.hasError()); + ASSERT_EQ(nullptr, program->getExpressions()[0]->getType()); +} + +TEST(ValidationVisitor, pointer_notUsed) { + VISITOR; + const auto program = parseString("new i32(3);0"); + program->acceptVoidVisitor(&visitor); + ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), HasSubstr("Value not used")); + ASSERT_FALSE(visitor.hasError()); + ASSERT_STREQ("i32*", program->getExpressions()[0]->getType()->getName().c_str()); +} + +TEST(ValidationVisitor, pointer_valid) { + VISITOR; + const auto program = parseString("val foo = new i32(3);0"); + program->acceptVoidVisitor(&visitor); + ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), IsEmpty()); + ASSERT_FALSE(visitor.hasError()); + ASSERT_STREQ("i32*", program->getExpressions()[0]->getType()->getName().c_str()); +} + +TEST(ValidationVisitor, pointerDereferencing_unknown) { + VISITOR; + const auto program = parseString("*foo"); + program->acceptVoidVisitor(&visitor); + ASSERT_THAT( + std::string(std::istreambuf_iterator(ss), {}), HasSubstr("Unknown name, don't know what it refers to: foo") + ); + ASSERT_TRUE(visitor.hasError()); +} + +TEST(ValidationVisitor, pointerDereferencing_notAPointer) { + VISITOR; + const auto program = parseString("val foo = 'a';*foo"); + program->acceptVoidVisitor(&visitor); + ASSERT_THAT( + std::string(std::istreambuf_iterator(ss), {}), HasSubstr("Cannot dereference a variable which is not a pointer") + ); + ASSERT_TRUE(visitor.hasError()); +} + +TEST(ValidationVisitor, pointerDereferencing_valid) { + VISITOR; + const auto program = parseString("val foo = new i32(3);*foo"); + program->acceptVoidVisitor(&visitor); + ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), IsEmpty()); + ASSERT_FALSE(visitor.hasError()); + ASSERT_STREQ("i32", program->getExpressions()[1]->getType()->getName().c_str()); +} + +TEST(ValidationVisitor, variableAddress_unknown) { + VISITOR; + const auto program = parseString("&foo"); + program->acceptVoidVisitor(&visitor); + ASSERT_THAT( + std::string(std::istreambuf_iterator(ss), {}), HasSubstr("Unknown name, don't know what it refers to: foo") + ); + ASSERT_TRUE(visitor.hasError()); +} + +TEST(ValidationVisitor, variableAddress_valid) { + VISITOR; + const auto program = parseString("val foo = 2;val bar = &foo;*bar"); + program->acceptVoidVisitor(&visitor); + ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), IsEmpty()); + ASSERT_FALSE(visitor.hasError()); + ASSERT_STREQ("i32*", program->getExpressions()[1]->getType()->getName().c_str()); + ASSERT_STREQ("i32", program->getExpressions()[2]->getType()->getName().c_str()); +}