Type check binary expressions
This commit is contained in:
		
							
								
								
									
										2
									
								
								TODO
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								TODO
									
									
									
									
									
								
							| @@ -13,3 +13,5 @@ | ||||
| - Error message with an empty file wrongly says that a ")" is expected. | ||||
| - Support any expressions for constants. | ||||
| - Name analysis should fail if there are undefined symbols. | ||||
| - type_mismatch error should get second type argument for the | ||||
|   expected type. | ||||
|   | ||||
| @@ -199,14 +199,11 @@ namespace elna::riscv | ||||
|         ELFIO::relocation_section_accessor rela(writer, rel_sec); | ||||
|         auto _writer = std::make_shared<elfio_writer>(text_sec, ro_sec, syma, stra); | ||||
|  | ||||
|         // visitor _visitor{ _writer, table }; | ||||
|         // _visitor.visit(ast); | ||||
|         auto references = generate(intermediate_code_generator, table, _writer); | ||||
|  | ||||
|         syma.arrange_local_symbols(); | ||||
|  | ||||
|         for (auto& reference : references) | ||||
|         // for (auto& reference : _visitor.references) | ||||
|         { | ||||
|             ELFIO::Elf_Word relocated_symbol = lookup(syma, reference.name); | ||||
|  | ||||
|   | ||||
| @@ -134,7 +134,9 @@ namespace elna::source | ||||
|         enum class operation | ||||
|         { | ||||
|             dereference, | ||||
|             argument | ||||
|             argument, | ||||
|             arithmetic, | ||||
|             comparison | ||||
|         }; | ||||
|  | ||||
|         /** | ||||
|   | ||||
| @@ -82,6 +82,7 @@ namespace elna::source | ||||
|         void visit(integer_literal *literal) override; | ||||
|         void visit(boolean_literal *literal) override; | ||||
|         void visit(unary_expression *expression) override; | ||||
|         void visit(binary_expression *expression) override; | ||||
|         void visit(call_statement *statement) override; | ||||
|         void visit(constant_definition *definition) override; | ||||
|     }; | ||||
|   | ||||
| @@ -26,6 +26,9 @@ namespace elna::source | ||||
|          * \return The type size in bytes. | ||||
|          */ | ||||
|         virtual std::size_t size() const noexcept; | ||||
|  | ||||
|         friend bool operator==(const type& lhs, const type& rhs) noexcept; | ||||
|         friend bool operator!=(const type& lhs, const type& rhs) noexcept; | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
| @@ -43,6 +46,9 @@ namespace elna::source | ||||
|          * \param byte_size The type size in bytes. | ||||
|          */ | ||||
|         primitive_type(const std::string& type_name, const std::size_t byte_size); | ||||
|  | ||||
|         bool operator==(const primitive_type& that) const noexcept; | ||||
|         bool operator!=(const primitive_type& that) const noexcept; | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
| @@ -60,6 +66,9 @@ namespace elna::source | ||||
|          * \param byte_size The type size in bytes. | ||||
|          */ | ||||
|         pointer_type(std::shared_ptr<const type> base_type, const std::size_t byte_size); | ||||
|  | ||||
|         bool operator==(const pointer_type& that) const noexcept; | ||||
|         bool operator!=(const pointer_type& that) const noexcept; | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
| @@ -77,8 +86,14 @@ namespace elna::source | ||||
|          * \param byte_size Function pointer size. | ||||
|          */ | ||||
|         procedure_type(std::vector<std::shared_ptr<const type>> arguments, const std::size_t byte_size); | ||||
|  | ||||
|         bool operator==(const procedure_type& that) const noexcept; | ||||
|         bool operator!=(const procedure_type& that) const noexcept; | ||||
|     }; | ||||
|  | ||||
|     bool operator==(const type& lhs, const type& rhs) noexcept; | ||||
|     bool operator!=(const type& lhs, const type& rhs) noexcept; | ||||
|  | ||||
|     inline const primitive_type boolean_type{ "Boolean", 1 }; | ||||
|     inline const primitive_type int_type{ "Int", 4 }; | ||||
| } | ||||
|   | ||||
| @@ -5,7 +5,8 @@ | ||||
|   "main": "tools/index.js", | ||||
|   "type": "module", | ||||
|   "scripts": { | ||||
|     "cross": "node tools/cross.js" | ||||
|     "start": "node tools/cross.js", | ||||
| 	"test": "node tools/tester.js" | ||||
|   }, | ||||
|   "author": "Eugen Wissner <belka@caraus.de>", | ||||
|   "license": "MPL-2.0", | ||||
|   | ||||
| @@ -216,6 +216,10 @@ namespace elna::source | ||||
|                 return "«proc»"; | ||||
|             case type::comparison_operator: | ||||
|                 return "«comparison_operator»"; | ||||
|             case type::hat: | ||||
|                 return "«^»"; | ||||
|             case type::at: | ||||
|                 return "«@»"; | ||||
|         }; | ||||
|         assert(false); | ||||
|     } | ||||
|   | ||||
| @@ -196,6 +196,56 @@ namespace elna::source | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void type_analysis_visitor::visit(binary_expression *expression) | ||||
|     { | ||||
|         empty_visitor::visit(expression); | ||||
|  | ||||
|         switch (expression->operation()) | ||||
|         { | ||||
|             case binary_operator::sum: | ||||
|             case binary_operator::subtraction: | ||||
|             case binary_operator::multiplication: | ||||
|             case binary_operator::division: | ||||
|             case binary_operator::less: | ||||
|             case binary_operator::greater: | ||||
|             case binary_operator::less_equal: | ||||
|             case binary_operator::greater_equal: | ||||
|                 if (expression->lhs().data_type != nullptr && expression->rhs().data_type != nullptr) | ||||
|                 { | ||||
|                     auto lhs_type = std::dynamic_pointer_cast<const primitive_type>(expression->lhs().data_type); | ||||
|                     auto rhs_type = std::dynamic_pointer_cast<const primitive_type>(expression->rhs().data_type); | ||||
|  | ||||
|                     std::unique_ptr<type_mismatch> new_error; | ||||
|                     if (lhs_type == nullptr || *lhs_type != int_type) | ||||
|                     { | ||||
|                         new_error = std::make_unique<type_mismatch>(lhs_type, | ||||
|                                 type_mismatch::operation::arithmetic, this->filename, expression->lhs().position()); | ||||
|                     } | ||||
|                     if (rhs_type == nullptr || *rhs_type != int_type) | ||||
|                     { | ||||
|                         new_error = std::make_unique<type_mismatch>(rhs_type, | ||||
|                                 type_mismatch::operation::arithmetic, this->filename, expression->rhs().position()); | ||||
|                     } | ||||
|                     if (new_error != nullptr) | ||||
|                     { | ||||
|                         m_errors.push_back(std::move(new_error)); | ||||
|                     } | ||||
|                 } | ||||
|             break; | ||||
|             case binary_operator::equals: | ||||
|             case binary_operator::not_equals: | ||||
|                 if (expression->lhs().data_type != nullptr && expression->rhs().data_type != nullptr) | ||||
|                 { | ||||
|                     if (expression->lhs().data_type != expression->rhs().data_type) | ||||
|                     { | ||||
|                         auto new_error = std::make_unique<type_mismatch>(expression->rhs().data_type, | ||||
|                                 type_mismatch::operation::comparison, this->filename, expression->rhs().position()); | ||||
|                     } | ||||
|                 } | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void type_analysis_visitor::visit(call_statement *statement) | ||||
|     { | ||||
|         auto call_info = std::dynamic_pointer_cast<intrinsic_info>(this->table->lookup(statement->name())); | ||||
|   | ||||
| @@ -17,13 +17,80 @@ namespace elna::source | ||||
|    { | ||||
|    } | ||||
|  | ||||
|     bool primitive_type::operator==(const primitive_type& that) const noexcept | ||||
|     { | ||||
|         return this->type_name == that.type_name; | ||||
|     } | ||||
|  | ||||
|     bool primitive_type::operator!=(const primitive_type& that) const noexcept | ||||
|     { | ||||
|         return this->type_name != that.type_name; | ||||
|     } | ||||
|  | ||||
|     pointer_type::pointer_type(std::shared_ptr<const type> base_type, const std::size_t byte_size) | ||||
|         : type(byte_size), base_type(base_type) | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     bool pointer_type::operator==(const pointer_type& that) const noexcept | ||||
|     { | ||||
|         return this->base_type == that.base_type; | ||||
|     } | ||||
|  | ||||
|     bool pointer_type::operator!=(const pointer_type& that) const noexcept | ||||
|     { | ||||
|         return this->base_type != that.base_type; | ||||
|     } | ||||
|  | ||||
|     procedure_type::procedure_type(std::vector<std::shared_ptr<const type>> arguments, const std::size_t byte_size) | ||||
|         : arguments(std::move(arguments)), type(byte_size) | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     bool procedure_type::operator==(const procedure_type &that) const noexcept | ||||
|     { | ||||
|         return this->arguments == that.arguments; | ||||
|     } | ||||
|  | ||||
|     bool procedure_type::operator!=(const procedure_type &that) const noexcept | ||||
|     { | ||||
|         return this->arguments != that.arguments; | ||||
|     } | ||||
|  | ||||
|     bool operator==(const type& lhs, const type& rhs) noexcept | ||||
|     { | ||||
|         { | ||||
|             auto lhs_type = dynamic_cast<const primitive_type *>(&lhs); | ||||
|             auto rhs_type = dynamic_cast<const primitive_type *>(&rhs); | ||||
|  | ||||
|             if (lhs_type != nullptr && rhs_type != nullptr) | ||||
|             { | ||||
|                 return *lhs_type == *rhs_type; | ||||
|             } | ||||
|         } | ||||
|         { | ||||
|             auto lhs_type = dynamic_cast<const pointer_type *>(&lhs); | ||||
|             auto rhs_type = dynamic_cast<const pointer_type *>(&rhs); | ||||
|  | ||||
|             if (lhs_type != nullptr && rhs_type != nullptr) | ||||
|             { | ||||
|                 return *lhs_type == *rhs_type; | ||||
|             } | ||||
|         } | ||||
|         { | ||||
|             auto lhs_type = dynamic_cast<const procedure_type *>(&lhs); | ||||
|             auto rhs_type = dynamic_cast<const procedure_type *>(&rhs); | ||||
|  | ||||
|             if (lhs_type != nullptr && rhs_type != nullptr) | ||||
|             { | ||||
|                 return *lhs_type == *rhs_type; | ||||
|             } | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     bool operator!=(const type& lhs, const type& rhs) noexcept | ||||
|     { | ||||
|         return !(lhs == rhs); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -32,11 +32,13 @@ async function compileTest (parsedPath) { | ||||
|   }) | ||||
| } | ||||
|  | ||||
| function checkFailure (parsedPath, buildResult) { | ||||
| async function checkFailure (parsedPath, buildResult) { | ||||
|   const failureFile = path.resolve('./tests/failures', `${parsedPath.name}.txt`) | ||||
|  | ||||
|   await fs.access(failureFile) | ||||
|   const diffArguments = [ | ||||
|     '-u', '--color=always', | ||||
|     path.resolve('./tests/failures', `${parsedPath.name}.txt`), | ||||
|     '-' | ||||
|     failureFile, '-' | ||||
|   ] | ||||
|  | ||||
|   try { | ||||
| @@ -97,16 +99,22 @@ async function runVM(cpioImage) { | ||||
|  | ||||
| async function runInDirectory (directoryPath) { | ||||
|   let failed = 0 | ||||
|   const sources = await glob(path.join(directoryPath, '*.eln')) | ||||
|  | ||||
|   for (const testEntry of await glob(path.join(directoryPath, '*.eln'))) { | ||||
|   for (const testEntry of sources) { | ||||
|     const parsedPath = path.parse(testEntry) | ||||
|  | ||||
|     console.log(`Compiling ${parsedPath.base}.`) | ||||
|     const buildResult = await compileTest(parsedPath) | ||||
|  | ||||
|     if (buildResult.code !== 0) { | ||||
|       if (!(await checkFailure(parsedPath, buildResult))) { | ||||
|       try { | ||||
|         if (!(await checkFailure(parsedPath, buildResult))) { | ||||
|           ++failed | ||||
|         } | ||||
|       } catch (e) { | ||||
|         ++failed | ||||
|         console.log(buildResult.output) | ||||
|       } | ||||
|       continue | ||||
|     } | ||||
| @@ -119,7 +127,7 @@ async function runInDirectory (directoryPath) { | ||||
|   await runVM(cpioImage) | ||||
|  | ||||
|   return { | ||||
|     total: (await glob(path.join(directoryPath, 'failures/*'))).length, | ||||
|     total: sources.length, | ||||
|     failed, | ||||
|  | ||||
|     passed () { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user