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.
|
- 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,16 +99,22 @@ 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) {
|
||||||
if (!(await checkFailure(parsedPath, buildResult))) {
|
try {
|
||||||
|
if (!(await checkFailure(parsedPath, buildResult))) {
|
||||||
|
++failed
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
++failed
|
++failed
|
||||||
|
console.log(buildResult.output)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -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 () {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user