Type check binary expressions
This commit is contained in:
parent
83c16e7ad9
commit
7845c700d8
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 () {
|
||||
|
Loading…
Reference in New Issue
Block a user