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. | - Error message with an empty file wrongly says that a ")" is expected. | ||||||
| - Support any expressions for constants. | - Support any expressions for constants. | ||||||
| - Name analysis should fail if there are undefined symbols. | - 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); |         ELFIO::relocation_section_accessor rela(writer, rel_sec); | ||||||
|         auto _writer = std::make_shared<elfio_writer>(text_sec, ro_sec, syma, stra); |         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); |         auto references = generate(intermediate_code_generator, table, _writer); | ||||||
|  |  | ||||||
|         syma.arrange_local_symbols(); |         syma.arrange_local_symbols(); | ||||||
|  |  | ||||||
|         for (auto& reference : references) |         for (auto& reference : references) | ||||||
|         // for (auto& reference : _visitor.references) |  | ||||||
|         { |         { | ||||||
|             ELFIO::Elf_Word relocated_symbol = lookup(syma, reference.name); |             ELFIO::Elf_Word relocated_symbol = lookup(syma, reference.name); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -134,7 +134,9 @@ namespace elna::source | |||||||
|         enum class operation |         enum class operation | ||||||
|         { |         { | ||||||
|             dereference, |             dereference, | ||||||
|             argument |             argument, | ||||||
|  |             arithmetic, | ||||||
|  |             comparison | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         /** |         /** | ||||||
|   | |||||||
| @@ -82,6 +82,7 @@ namespace elna::source | |||||||
|         void visit(integer_literal *literal) override; |         void visit(integer_literal *literal) override; | ||||||
|         void visit(boolean_literal *literal) override; |         void visit(boolean_literal *literal) override; | ||||||
|         void visit(unary_expression *expression) override; |         void visit(unary_expression *expression) override; | ||||||
|  |         void visit(binary_expression *expression) override; | ||||||
|         void visit(call_statement *statement) override; |         void visit(call_statement *statement) override; | ||||||
|         void visit(constant_definition *definition) override; |         void visit(constant_definition *definition) override; | ||||||
|     }; |     }; | ||||||
|   | |||||||
| @@ -26,6 +26,9 @@ namespace elna::source | |||||||
|          * \return The type size in bytes. |          * \return The type size in bytes. | ||||||
|          */ |          */ | ||||||
|         virtual std::size_t size() const noexcept; |         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. |          * \param byte_size The type size in bytes. | ||||||
|          */ |          */ | ||||||
|         primitive_type(const std::string& type_name, const std::size_t byte_size); |         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. |          * \param byte_size The type size in bytes. | ||||||
|          */ |          */ | ||||||
|         pointer_type(std::shared_ptr<const type> base_type, const std::size_t byte_size); |         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. |          * \param byte_size Function pointer size. | ||||||
|          */ |          */ | ||||||
|         procedure_type(std::vector<std::shared_ptr<const type>> arguments, const std::size_t byte_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 boolean_type{ "Boolean", 1 }; | ||||||
|     inline const primitive_type int_type{ "Int", 4 }; |     inline const primitive_type int_type{ "Int", 4 }; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -5,7 +5,8 @@ | |||||||
|   "main": "tools/index.js", |   "main": "tools/index.js", | ||||||
|   "type": "module", |   "type": "module", | ||||||
|   "scripts": { |   "scripts": { | ||||||
|     "cross": "node tools/cross.js" |     "start": "node tools/cross.js", | ||||||
|  | 	"test": "node tools/tester.js" | ||||||
|   }, |   }, | ||||||
|   "author": "Eugen Wissner <belka@caraus.de>", |   "author": "Eugen Wissner <belka@caraus.de>", | ||||||
|   "license": "MPL-2.0", |   "license": "MPL-2.0", | ||||||
|   | |||||||
| @@ -216,6 +216,10 @@ namespace elna::source | |||||||
|                 return "«proc»"; |                 return "«proc»"; | ||||||
|             case type::comparison_operator: |             case type::comparison_operator: | ||||||
|                 return "«comparison_operator»"; |                 return "«comparison_operator»"; | ||||||
|  |             case type::hat: | ||||||
|  |                 return "«^»"; | ||||||
|  |             case type::at: | ||||||
|  |                 return "«@»"; | ||||||
|         }; |         }; | ||||||
|         assert(false); |         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) |     void type_analysis_visitor::visit(call_statement *statement) | ||||||
|     { |     { | ||||||
|         auto call_info = std::dynamic_pointer_cast<intrinsic_info>(this->table->lookup(statement->name())); |         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) |     pointer_type::pointer_type(std::shared_ptr<const type> base_type, const std::size_t byte_size) | ||||||
|         : type(byte_size), base_type(base_type) |         : 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) |     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) |         : 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 = [ |   const diffArguments = [ | ||||||
|     '-u', '--color=always', |     '-u', '--color=always', | ||||||
|     path.resolve('./tests/failures', `${parsedPath.name}.txt`), |     failureFile, '-' | ||||||
|     '-' |  | ||||||
|   ] |   ] | ||||||
|  |  | ||||||
|   try { |   try { | ||||||
| @@ -97,17 +99,23 @@ async function runVM(cpioImage) { | |||||||
|  |  | ||||||
| async function runInDirectory (directoryPath) { | async function runInDirectory (directoryPath) { | ||||||
|   let failed = 0 |   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) |     const parsedPath = path.parse(testEntry) | ||||||
|  |  | ||||||
|     console.log(`Compiling ${parsedPath.base}.`) |     console.log(`Compiling ${parsedPath.base}.`) | ||||||
|     const buildResult = await compileTest(parsedPath) |     const buildResult = await compileTest(parsedPath) | ||||||
|  |  | ||||||
|     if (buildResult.code !== 0) { |     if (buildResult.code !== 0) { | ||||||
|  |       try { | ||||||
|         if (!(await checkFailure(parsedPath, buildResult))) { |         if (!(await checkFailure(parsedPath, buildResult))) { | ||||||
|           ++failed |           ++failed | ||||||
|         } |         } | ||||||
|  |       } catch (e) { | ||||||
|  |         ++failed | ||||||
|  |         console.log(buildResult.output) | ||||||
|  |       } | ||||||
|       continue |       continue | ||||||
|     } |     } | ||||||
|     buildTest(parsedPath) |     buildTest(parsedPath) | ||||||
| @@ -119,7 +127,7 @@ async function runInDirectory (directoryPath) { | |||||||
|   await runVM(cpioImage) |   await runVM(cpioImage) | ||||||
|  |  | ||||||
|   return { |   return { | ||||||
|     total: (await glob(path.join(directoryPath, 'failures/*'))).length, |     total: sources.length, | ||||||
|     failed, |     failed, | ||||||
|  |  | ||||||
|     passed () { |     passed () { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user