M jasm/assemble/assembler_impl/assembler_impl.cpp +7 -1
@@ 434,7 434,13 @@ uint64_t Assembler::add_array_type_descr
fill_type_array_operators(type);
fill_type_integer_operators(type);
- return add_type_to_type_map(type, p);
+ uint64_t hash = type.hash(0);
+ if (_current_pass.type_lookup.has(hash)) {
+ _current_pass.types.rewind(p);
+ } else {
+ hash = add_type_to_type_map(type, p);
+ }
+ return hash;
}
void Assembler::setup_predefined_constants(bool generate)
M jasm/assemble/assembler_impl/assembler_impl.h +4 -0
@@ 941,6 941,10 @@ private:
/// @param array_length Only valid if @a type_reference refers to an array.
bool fill_offset_value(bool generate, const TypeReference &type_reference, int32_t array_length, const SourceLocation &source_location, Value &value);
+ /// Generate a type from a type reference structure. This can reuse an existing type or create new ones.
+ /// @return True if successful.
+ bool generate_offset_type(bool generate, const TypeReference &type_reference, const std::vector<int32_t> array_lengths, const SourceLocation &source_location, ValueType &type, uint64_t &type_hash);
+
/// Create a new value for a symbol and if the name is unique it will also be mapped to the value.
/// Non unique values will be dangling but that is not a problem really. It simplifies the code that
/// can parse as if the value was going to be filled. Get a reference to the new value using _current_pass_values.back().
M jasm/assemble/assembler_impl/syntax_impl.cpp +61 -13
@@ 582,18 582,19 @@ bool Assembler::resolve_type_hash(bool g
// set underlying_type_hash to it
assert(false);
throw Exception("not implemented");
- } else {
- type_hash = symbol_value.type_hash;
}
+ type_hash = symbol_value.type_hash;
return true;
}
UNREACHABLE_CODE(throw Exception("internal error"));
}
+// TODO: REMOVE
bool Assembler::fill_offset_value(bool generate, const TypeReference &type_reference, int32_t array_length, const SourceLocation &source_location, Value &value)
{
+ /*
uint64_t underlying_type_hash;
if (!resolve_type_hash(generate, type_reference, source_location, underlying_type_hash))
return false;
@@ 617,6 618,36 @@ bool Assembler::fill_offset_value(bool g
}
return true;
+ */
+ assert(false);
+ return false;
+}
+
+bool Assembler::generate_offset_type(bool generate, const TypeReference &type_reference, const std::vector<int32_t> array_lengths, const SourceLocation &source_location, ValueType &type, uint64_t &type_hash)
+{
+ uint64_t underlying_type_hash;
+ if (!resolve_type_hash(generate, type_reference, source_location, underlying_type_hash))
+ return false;
+
+ // map typenames to offset types for typenames in symbols
+ underlying_type_hash = map_fundamental_types_to_offset_types(underlying_type_hash);
+
+ if (!type_reference.is_array) {
+ const TypeDescription &type_desc = find_type(underlying_type_hash);
+ type = type_desc.type;
+ type_hash = underlying_type_hash;
+ return true;
+ }
+
+ // it's an array, so loop and generate types as deep as the arrays go
+ type = ValueType::ArrayOffset;
+ type_hash = underlying_type_hash;
+ for(uint32_t depth = 0; depth < type_reference.array_depth; ++depth)
+ {
+ int32_t array_length = array_lengths[depth];
+ type_hash = add_array_type_description(type_hash, array_length);
+ }
+ return true;
}
const SyntaxToken *Assembler::parse_array_length(bool generate, bool &success, const SyntaxToken *t, int32_t min_length, int32_t &array_length)
@@ 683,23 714,37 @@ const SyntaxToken *Assembler::parse_rese
if (export_enabled)
value.set_is_public(true);
- // determine array length, if applicable
- int32_t array_length = 1;
- if (reserve.type_reference.array_flag == ArrayFlag::ArrayWithSize) {
- bool success;
- t = parse_array_length(generate, success, t, 0, array_length);
- if (!success) {
- set_unknown(value);
- return t;
+ // parse all array sizes
+ std::vector<int32_t> array_sizes;
+ bool size_parse_success = true;
+ if (reserve.type_reference.is_array) {
+ array_sizes.resize(reserve.type_reference.array_depth);
+ for(uint32_t index = 0; index < reserve.type_reference.array_depth; ++index) {
+ ArrayFlag flag = reserve.type_reference.array_flags[index];
+ assert(flag == ArrayFlag::ArrayWithSize); // all reserved sizes should be known
+ if (flag == ArrayFlag::ArrayWithSize) {
+ bool success = false;
+ int32_t array_length = 0;
+ t = parse_array_length(generate, success, t, 0, array_length);
+ if (success) {
+ array_sizes[index] = array_length;
+ } else {
+ size_parse_success = false;
+ }
+ }
}
}
+ if (!size_parse_success) {
+ set_unknown(value);
+ return t;
+ }
+
// set the value base and offset (this will enable use of offsetof() and further offsetting
value.offset_base = _program_counter.integer_value;
value.offset = 0;
- assert(reserve.type_reference.array_flag != ArrayFlag::ArrayWithUnknownSize); // this is not allowed in reservations
- if (!fill_offset_value(generate, reserve.type_reference, array_length, reserve.source_location, value)) {
+ if (!generate_offset_type(generate, reserve.type_reference, array_sizes, reserve.source_location, value.type, value.type_hash)) {
set_unknown(value);
return t;
}
@@ 1101,6 1146,8 @@ const SyntaxToken *Assembler::parse_defi
// Groups are used for arrays and structs. A string is a form of multi byte entry
// so that will require a group.
+ assert(false); // TODO: Fix
+/*
// parse the size of the array if that is specified
int32_t array_length = 0;
if (define.type_reference.array_flag == ArrayFlag::ArrayWithSize) {
@@ 1159,7 1206,8 @@ const SyntaxToken *Assembler::parse_defi
constexpr bool increase = true;
_hex_source_writer->write_data(static_cast<uint32_t>(program_counter_before), &data[data_size_before], increase, generated_size, define.source_location.file_index, define.source_location.row, end_line);
}
-
+ */
+
t = skip_to_end_of_define(t);
return t;
}
M jasm/exceptions/error_codes.h +1 -0
@@ 89,6 89,7 @@ enum class AssemblyErrorCodes
ModulesNotAllowedInMacros,
IncludeNotAllowedInMacros,
IncludeNotAllowedInSubroutine,
+ TooManyNestedArrays,
// assembler
OperatorNotSupportingType = 3000,
M jasm/syntax/syntax_parser.cpp +19 -9
@@ 1544,29 1544,39 @@ const Token *SyntaxParser::parse_type_re
}
// check if the type is an array
- t = skip_whitespaces(t);
- if (is_operator(t, OperatorType::LeftBracket)) {
+ reference.array_depth = 0;
+ while(true) {
+ t = skip_whitespaces(t);
+ if (!is_operator(t, OperatorType::LeftBracket)) {
+ break;
+ }
+
+ if (reference.array_depth == max_array_depth) {
+ std::string msg("Too many nested arrays");
+ throw AssemblyException(_source_files, t->source_location, AssemblyErrorCodes::TooManyNestedArrays, msg);
+ }
+
// We have an array so we must parse the size of the array now.
// Some type references don't need a size so we check for empty bracket contents as well.
t = skip_whitespaces(consume_next_token());
if (is_operator(t, OperatorType::RightBracket)) {
if (array_size_is_required) {
- std::stringstream ss;
- ss << "Array declaration must have a known size";
- throw AssemblyException(_source_files, t->source_location, AssemblyErrorCodes::ArrayDeclarationMustHaveSize, ss.str());
+ std::string msg("Array declaration must have a known size");
+ throw AssemblyException(_source_files, t->source_location, AssemblyErrorCodes::ArrayDeclarationMustHaveSize, msg);
}
- reference.array_flag = ArrayFlag::ArrayWithUnknownSize;
+ reference.array_flags[reference.array_depth] = ArrayFlag::ArrayWithUnknownSize;
t = consume_next_token();
} else {
- reference.array_flag = ArrayFlag::ArrayWithSize;
+ reference.array_flags[reference.array_depth] = ArrayFlag::ArrayWithSize;
constexpr bool end_at_unmatched_parenthesis = true;
constexpr bool end_at_newline = false;
t = parse_and_output_expression(t, end_at_unmatched_parenthesis, end_at_newline);
t = parse_operator(t, OperatorType::RightBracket);
}
- } else {
- reference.array_flag = ArrayFlag::Single;
+
+ ++reference.array_depth;
}
+ reference.is_array = reference.array_depth > 0;
return t;
}
M jasm/syntax/syntax_tokens.h +8 -3
@@ 444,7 444,6 @@ enum class DataType : uint8_t
enum ArrayFlag : uint8_t
{
- Single,
ArrayWithSize,
ArrayWithUnknownSize,
};
@@ 452,13 451,19 @@ enum ArrayFlag : uint8_t
struct TypeReference
{
DataType type; ///< Type of the instance.
- ArrayFlag array_flag; ///< Tells if the instance is an array of @a type.
+ bool is_array; ///< Tells if the instance is an array of @a type.
bool global; ///< Used with DataType::Symbol. True if the symbol name is a global name.
- uint8_t padding;
+ uint8_t array_depth; ///< Used when @a is_array is true.
+ // 8 byte aligned
+
+ static_assert((max_array_depth & 7) == 0, "max depth must be multiple of 8");
+ ArrayFlag array_flags[max_array_depth]; ///< The array size known/unknown state for each array, starting with the innermost.
+ // 8 byte aligned
uint64_t type_name; ///< Used with DataType::Symbol. Symbol for the type.
// 8 byte aligned
+
uint64_t type_namespace_handle; ///< Used with DataType::Symbol. Handle to namespaces for the type.
// 8 byte aligned
};
M jasm/tokenize/types.h +2 -0
@@ 13,6 13,8 @@ enum class TypenameType : uint8_t
NumTypes,
};
+constexpr uint8_t max_array_depth = 16;
+
std::string_view to_string(TypenameType type);
/// @}
A => jasm/unit_tests/results/test_reserve_multiarray_and_access.bin +0 -0
A => jasm/unit_tests/results/test_reserve_multiarray_must_have_size.stdout +1 -0
@@ 0,0 1,1 @@
+unit_tests/test_reserve_multiarray_must_have_size.asm(10,18) : Error 2020 : Array declaration must have a known size
A => jasm/unit_tests/results/test_reserve_multiarray_offsetof.bin +0 -0
A => jasm/unit_tests/results/test_reserve_multiarray_too_deep.stdout +1 -0
@@ 0,0 1,1 @@
+unit_tests/test_reserve_multiarray_too_deep.asm(10,69) : Error 2056 : Too many nested arrays
A => jasm/unit_tests/test_reserve_multiarray_and_access.asm +21 -0
@@ 0,0 1,21 @@
+// assembler command line arguments: 6502 [-v0]
+processor "6502"
+
+section code, "main", $8000
+{
+ lda b[0]
+ lda b[1]
+ lda b[0][0]
+ lda b[0][1]
+ lda b[0][2]
+ lda b[0][3]
+ lda b[1][0]
+ lda b[1][1]
+ lda b[1][2]
+ lda b[1][3]
+}
+
+section bss, "variables", $1000
+{
+ reserve byte[4][2] b
+}
No newline at end of file
A => jasm/unit_tests/test_reserve_multiarray_must_have_size.asm +11 -0
@@ 0,0 1,11 @@
+// assembler command line arguments: 6502 [-v0]
+processor "6502"
+
+section code, "main", $8000
+{
+}
+
+section bss, "variables", $1000
+{
+ reserve byte[4][] b
+}
No newline at end of file
A => jasm/unit_tests/test_reserve_multiarray_offsetof.asm +17 -0
@@ 0,0 1,17 @@
+// assembler command line arguments: 6502 [-v0]
+processor "6502"
+
+section code, "main", $8000
+{
+ lda #offsetof(b[0])
+ lda #offsetof(b[1])
+ lda #offsetof(b[0][0])
+ lda #offsetof(b[0][2])
+ lda #offsetof(b[1][0])
+ lda #offsetof(b[1][2])
+}
+
+section bss, "variables", $1000
+{
+ reserve byte[4][2] b
+}
No newline at end of file
A => jasm/unit_tests/test_reserve_multiarray_too_deep.asm +11 -0
@@ 0,0 1,11 @@
+// assembler command line arguments: 6502 [-v0]
+processor "6502"
+
+section code, "main", $8000
+{
+}
+
+section bss, "variables", $1000
+{
+ reserve byte[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17] b
+}
No newline at end of file
M jasm/utility/token_chain.h +8 -1
@@ 48,13 48,20 @@ public:
/// Calculate a hash to compare token chains with each other.
uint64_t hash() const;
- TokenReadPosition position() const {
+ TokenReadPosition position() const
+ {
TokenReadPosition p;
p.u.parts.buffer_index = static_cast<uint32_t>(_buffers.size()) - 1;
p.u.parts.buffer_position = static_cast<uint32_t>(_buffers.back().size());
return p;
}
+ void rewind(const TokenReadPosition &position)
+ {
+ _buffers.resize(position.u.parts.buffer_index + 1);
+ _buffers[position.u.parts.buffer_index].resize(position.u.parts.buffer_position);
+ }
+
private:
static constexpr int _alignment = 4;