Type check binary expressions

This commit is contained in:
Eugen Wissner 2024-05-28 23:39:04 +02:00
parent 83c16e7ad9
commit 7845c700d8
Signed by: belka
GPG Key ID: A27FDC1E8EE902C0
10 changed files with 158 additions and 11 deletions

2
TODO
View File

@ -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.

View File

@ -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);

View File

@ -134,7 +134,9 @@ namespace elna::source
enum class operation
{
dereference,
argument
argument,
arithmetic,
comparison
};
/**

View File

@ -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;
};

View File

@ -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 };
}

View File

@ -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",

View File

@ -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);
}

View File

@ -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()));

View File

@ -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);
}
}

View File

@ -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 () {