Define multidimentional arrays. Some bugs remain.
M jasm/assemble/assembler_impl/assembler_impl.h +28 -14
@@ 936,11 936,6 @@ private:
 		return type_hash;
 	}
 
-	/// Fill the offset type value based on the type reference.
-	/// This will generate new array types if necessary.
-	/// @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);

          
@@ 983,18 978,37 @@ private:
 	bool verify_and_define_object(bool generate, const ExpressionToken &expr, const TypeDescription &type_desc, const Value &value);
 
 	/// Parse and define data for one array. Adds data to section and increases the program counter.
-	/// @param array_length The known length of an array, or zero to be updated with the length when known.
-	const SyntaxToken *parse_define_array(bool generate, bool &success, const SyntaxToken *t, const TypeDescription &element_type, int32_t &array_length);
+	/// @param innermost_type_hash The type hash for the innermost type in the array
+	/// @param adjacent_type_hash The type hash that THIS array should be using (forced by an adjacent array index). Pass zero if undetermined.
+	/// @param return_type_hash The type hash that this array must be. The same as adjacent_type_hash if that isn't zero.
+	const SyntaxToken *parse_define_array(
+		bool generate
+		, bool &success
+		, const SyntaxToken *t
+		, uint64_t innermost_type_hash
+		, uint64_t adjacent_type_hash
+		, uint64_t &return_type_hash
+		, const std::vector<int32_t> &array_sizes
+		, size_t array_depth
+	);
 
 	/// Parse and define data for one object. Adds data to section and increases the program counter.
-	const SyntaxToken *parse_define_object(bool generate, bool &success, const SyntaxToken *t, const TypeDescription &type_desc);
+	const SyntaxToken *parse_define_object(
+		bool generate
+		, bool &success
+		, const SyntaxToken *t
+		, uint64_t innermost_type_hash
+	);
 
-	/// Parse data for one type and recurse until all containing data is parsed.
-	/// Data is verified and added to the section data.
-	/// @param array_updated_type Optional type pointer to update with length of parsed array. This is used for arrays without length.
-	/// @param type_desc Type we are going to parse.
-	/// @param array_length Length of array, if applicable, otherwise 0.
-	const SyntaxToken *parse_define_data(bool generate, const SyntaxToken *t, TypeDescription *array_updated_type, const TypeDescription &type_desc, int32_t array_length);
+	const SyntaxToken *parse_define_any(
+		bool generate
+		, bool &success
+		, const SyntaxToken *t
+		, uint64_t innermost_type_hash
+		, uint64_t &return_type_hash
+		, const std::vector<int32_t> &array_sizes
+		, size_t array_depth
+	);
 
 	/// Parse the loop body over a collection loop.
 	const SyntaxToken *parse_range_list_loop(bool generate, const SyntaxToken *t, RangedLoopContainer &loop_container, const RangedForLoopToken &loop_token, bool &early_return);

          
M jasm/assemble/assembler_impl/syntax_impl.cpp +177 -118
@@ 591,38 591,6 @@ bool Assembler::resolve_type_hash(bool g
 	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;
-
-	// map typenames to offset types for typenames in symbols
-	underlying_type_hash = map_fundamental_types_to_offset_types(underlying_type_hash);
-
-	// select the value type
-	if (type_reference.array_flag == ArrayFlag::ArrayWithSize || type_reference.array_flag == ArrayFlag::ArrayWithUnknownSize) {
-		// create a type description for this array length
-		uint64_t type_hash = add_array_type_description(underlying_type_hash, array_length);
-
-		// store array type
-		value.type = ValueType::ArrayOffset;
-		value.type_hash = type_hash;
-
-	} else if (type_reference.array_flag == ArrayFlag::Single) {
-		const TypeDescription &type_desc = find_type(underlying_type_hash);
-		value.type = type_desc.type;
-		value.type_hash = underlying_type_hash;
-	}
-
-	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;

          
@@ 807,19 775,35 @@ bool Assembler::define_single_fundamenta
 	return true;
 }
 
-const SyntaxToken *Assembler::parse_define_array(bool generate, bool &success, const SyntaxToken *t, const TypeDescription &element_type, int32_t &array_length)
+const SyntaxToken *Assembler::parse_define_array(
+	bool generate
+	, bool &success
+	, const SyntaxToken *t
+	, uint64_t innermost_type_hash
+	, uint64_t adjacent_type_hash
+	, uint64_t &return_type_hash
+	, const std::vector<int32_t> &array_sizes
+	, size_t array_depth
+)
 {
 	assert(_data_generation_depth == 1);
 
+	int32_t array_length = array_sizes[array_depth-1];
 	bool is_fixed_array = array_length > 0;
 
+	// pick size from an adjacent array
+	if (!is_fixed_array && adjacent_type_hash != 0) {
+		const TypeDescription &adjacent_type = find_type(adjacent_type_hash);
+		array_length = adjacent_type.array_size;
+		is_fixed_array = true;
+	}
+
 	// expecting a group for the array
 	if (t->type == SyntaxTokenType::Expression) {
 		if (generate) {
 			const ExpressionToken &expr = *static_cast<const ExpressionToken *>(t);
-			std::stringstream ss;
-			ss << "Expected array definition.";
-			report_error(expr.source_location, AssemblyErrorCodes::ExpectedArrayDefinition, ss.str());
+			std::string s("Expected array definition.");
+			report_error(expr.source_location, AssemblyErrorCodes::ExpectedArrayDefinition, s);
 		}
 		success = false;
 		return t;

          
@@ 831,9 815,8 @@ const SyntaxToken *Assembler::parse_defi
 
 	if (!is_fixed_array && group->repeated_pattern) {
 		if (generate) {
-			std::stringstream ss;
-			ss << "Array definition with repeating pattern must have specified size.";
-			report_error(group->source_location, AssemblyErrorCodes::DynamicArrayCantHaveRepeatingPattern, ss.str());
+			std::string s("Array definition with repeating pattern must have specified size.");
+			report_error(group->source_location, AssemblyErrorCodes::DynamicArrayCantHaveRepeatingPattern, s);
 		}
 		success = false;
 		return t;

          
@@ 848,71 831,115 @@ const SyntaxToken *Assembler::parse_defi
 
 	// loop until the end of the group
 	int32_t defined_array_elements = 0;
-	for(int i = 0; i < group->num_elements; ++i) {
-		assert(element_type.array_size == 0); // arrays of arrays is not supported yet
-
+	uint64_t child_type_hash = 0;
+	if (array_depth > 1) {
+		// array in array
+		for(int i = 0; i < group->num_elements; ++i) {
+			++defined_array_elements;
+
+			// check upper bounds on array in case it isn't a repeated pattern
+			if (!group->repeated_pattern && is_fixed_array && defined_array_elements > array_length) {
+				if (generate) {
+					const SourceLocation *location = nullptr;
+					if (t->type == SyntaxTokenType::Expression) {
+						const ExpressionToken &expr = *static_cast<const ExpressionToken *>(t);
+						location = &expr.source_location;
+					} else {
+						assert(t->type == SyntaxTokenType::DefineGroup);
+						const DefineGroupToken *token = static_cast<const DefineGroupToken *>(t);
+						location = &token->source_location;
+					}
+
+					std::stringstream ss;
+					ss << "Number of values in array exceeds array length.";
+					report_error(*location, AssemblyErrorCodes::NoOfValuesExceedArray, ss.str());
+				}
+				success = false;
+				return t;
+			}
+
+			t = parse_define_array(
+				generate
+				, success
+				, t
+				, innermost_type_hash
+				, child_type_hash
+				, child_type_hash
+				, array_sizes
+				, array_depth - 1
+			);
+			if (!success) {
+				return t;
+			}
+		}
+	} else {
+		child_type_hash = innermost_type_hash;
+		const TypeDescription &element_type = find_type(innermost_type_hash);
 		bool is_fundamental_type = element_type.type == ValueType::ByteOffset
 								|| element_type.type == ValueType::WordOffset
 								|| element_type.type == ValueType::LongOffset;
 
 		if (is_fundamental_type) {
-			if (t->type != SyntaxTokenType::Expression) {
-				assert(t->type == SyntaxTokenType::DefineGroup);
-				const DefineGroupToken *g = static_cast<const DefineGroupToken *>(t);
-				if (generate) {
-					std::stringstream ss;
-					ss << "Expected single value definition but found start of group.";
-					report_error(g->source_location, AssemblyErrorCodes::ExpectedSingleValueDefinition, ss.str());
+			for(int i = 0; i < group->num_elements; ++i) {
+				if (t->type != SyntaxTokenType::Expression) {
+					assert(t->type == SyntaxTokenType::DefineGroup);
+					const DefineGroupToken *g = static_cast<const DefineGroupToken *>(t);
+					if (generate) {
+						std::stringstream ss;
+						ss << "Expected single value definition but found start of group.";
+						report_error(g->source_location, AssemblyErrorCodes::ExpectedSingleValueDefinition, ss.str());
+					}
+					success = false;
+					return t;
 				}
-				success = false;
-				return t;
-			}
-
-			const ExpressionToken *expr = static_cast<const ExpressionToken *>(t);
-			const Value expr_value = follow_reference_or_value(evaluate_expression(generate, t));
-			t = consume_next_token();
-
-			// special case for arrays of fundamental types, which supports strings as input
-			if (is_string(expr_value)) {
-				// define a range of objects
-				std::string_view s = dereference_string(expr_value);
-				wchar_t wide = 0;
-				const char *source = s.data();
-				size_t source_size = s.size();
-				while(source_size != 0) {
-					core::utf8_to_wide(source, source_size, wide);
-					if (!define_single_fundamental_type(generate, element_type.type, static_cast<int32_t>(wide), expr->source_location)) {
+
+				const ExpressionToken *expr = static_cast<const ExpressionToken *>(t);
+				const Value expr_value = follow_reference_or_value(evaluate_expression(generate, t));
+				t = consume_next_token();
+
+				// special case for arrays of fundamental types, which supports strings as input
+				if (is_string(expr_value)) {
+					// define a range of objects
+					std::string_view s = dereference_string(expr_value);
+					wchar_t wide = 0;
+					const char *source = s.data();
+					size_t source_size = s.size();
+					while(source_size != 0) {
+						core::utf8_to_wide(source, source_size, wide);
+						if (!define_single_fundamental_type(generate, element_type.type, static_cast<int32_t>(wide), expr->source_location)) {
+							success = false;
+							return t;
+						}
+						++defined_array_elements;
+					}
+
+				} else {
+					// define one object
+					if (!verify_and_define_object(generate, *expr, element_type, expr_value)) {
 						success = false;
 						return t;
 					}
 					++defined_array_elements;
 				}
 
-			} else {
-				// define one object
-				if (!verify_and_define_object(generate, *expr, element_type, expr_value)) {
+				// check upper bounds on array in case it isn't a repeated pattern
+				if (!group->repeated_pattern && is_fixed_array && defined_array_elements > array_length) {
+					if (generate) {
+						std::stringstream ss;
+						ss << "Number of values in array exceeds array length.";
+						report_error(expr->source_location, AssemblyErrorCodes::NoOfValuesExceedArray, ss.str());
+					}
 					success = false;
 					return t;
 				}
-				++defined_array_elements;
 			}
-
-			// check upper bounds on array in case it isn't a repeated pattern
-			if (!group->repeated_pattern && is_fixed_array && defined_array_elements > array_length) {
-				if (generate) {
-					std::stringstream ss;
-					ss << "Number of values in array exceeds array length.";
-					report_error(expr->source_location, AssemblyErrorCodes::NoOfValuesExceedArray, ss.str());
-				}
+		} else if (element_type.type == ValueType::StructOffset) {
+			//for(int i = 0; i < group->num_elements; ++i) {
+				// call a special version of the parse object method which checks for group and all that
+				assert(false);
 				success = false;
 				return t;
-			}
-
-		} else if (element_type.type == ValueType::StructOffset) {
-			// call a special version of the parse object method which checks for group and all that
-			assert(false);
-			success = false;
-			return t;
+			//}
 		} else {
 			// this should never happen
 			assert(false);

          
@@ 920,8 947,10 @@ const SyntaxToken *Assembler::parse_defi
 			return t;
 		}
 	}
+	assert(child_type_hash != 0);
 
 	if (group->repeated_pattern) {
+		const TypeDescription &element_type = find_type(child_type_hash);
 		// the defined data may be longer than the repeated pattern so we begin by cropping the output
 		int32_t desired_size = array_length * element_type.byte_size;
 

          
@@ 963,11 992,10 @@ const SyntaxToken *Assembler::parse_defi
 		success = false;
 		return t;
 	}
-
-	if (!is_fixed_array) {
-		array_length = defined_array_elements;
-	}
 	
+	// create a type description for this array length
+	return_type_hash = add_array_type_description(child_type_hash, defined_array_elements);
+
 	success = true;
 	return t;
 }

          
@@ 1069,7 1097,12 @@ bool Assembler::verify_and_define_object
 	return true;
 }
 
-const SyntaxToken *Assembler::parse_define_object(bool generate, bool &success, const SyntaxToken *t, const TypeDescription &type_desc)
+const SyntaxToken *Assembler::parse_define_object(
+	bool generate
+	, bool &success
+	, const SyntaxToken *t
+	, uint64_t type_hash
+)
 {
 	// This isn't made for structs. This should only be called for fundamental types and structs will
 	// have to have their own parse object method.

          
@@ 1091,6 1124,7 @@ const SyntaxToken *Assembler::parse_defi
 	const Value expr_value = follow_reference_or_value(evaluate_expression(generate, t));
 	t = consume_next_token();
 
+	const TypeDescription &type_desc = find_type(type_hash);
 	if (!verify_and_define_object(generate, *expr, type_desc, expr_value)) {
 		success = false;
 		return t;

          
@@ 1100,6 1134,24 @@ const SyntaxToken *Assembler::parse_defi
 	return t;
 }
 
+const SyntaxToken *Assembler::parse_define_any(
+	bool generate
+	, bool &success
+	, const SyntaxToken *t
+	, uint64_t innermost_type_hash
+	, uint64_t &return_type_hash
+	, const std::vector<int32_t> &array_sizes
+	, size_t array_depth
+)
+{
+	if (array_depth > 0) {
+		return parse_define_array(generate, success, t, innermost_type_hash, 0, return_type_hash, array_sizes, array_depth);
+	} else {
+		return_type_hash = innermost_type_hash;
+		return parse_define_object(generate, success, t, innermost_type_hash);
+	}
+}
+
 const SyntaxToken *Assembler::parse_define(bool generate, const SyntaxToken *t, bool export_enabled)
 {
 	const DefineToken &define = *static_cast<const DefineToken *>(t);

          
@@ 1146,16 1198,24 @@ 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) {
-		bool success;
-		t = parse_array_length(generate, success, t, 0, array_length);
-		if (!success) {
-			set_unknown(value);
-			return skip_to_end_of_define(t);
+	// parse all array sizes
+	std::vector<int32_t> array_sizes;
+	if (define.type_reference.is_array) {
+		array_sizes.resize(define.type_reference.array_depth);
+		for(uint32_t index = 0; index < define.type_reference.array_depth; ++index) {
+			ArrayFlag flag = define.type_reference.array_flags[index];
+			if (flag == ArrayFlag::ArrayWithSize) {
+				bool success = false;
+				int32_t array_length = 0;
+				t = parse_array_length(generate, success, t, 0, array_length);
+				if (!success) {
+					set_unknown(value);
+					return skip_to_end_of_define(t);
+				}
+				array_sizes[index] = array_length;
+			} else {
+				array_sizes[index] = 0; // zero means the size hasn't been determined yet.
+			}
 		}
 	}
 

          
@@ 1168,7 1228,6 @@ const SyntaxToken *Assembler::parse_defi
 	// map type names in symbols to offset types
 	underlying_type_hash = map_fundamental_types_to_offset_types(underlying_type_hash);
 
-
 	// set the value base and offset (this will enable use of offsetof() and further offsetting
 	int32_t program_counter_before = _program_counter.integer_value;
 	value.offset_base = program_counter_before;

          
@@ 1177,24 1236,25 @@ const SyntaxToken *Assembler::parse_defi
 	auto &data = _section->generated_data();
 	size_t data_size_before = data.size();
 
-	bool is_array_type = define.type_reference.array_flag != ArrayFlag::Single;
-	bool success;
-	//t = parse_define_data(generate, success, t, array_updated_type, find_type(value.type_hash), is_array_type, array_length);
-	if (is_array_type)
-		t = parse_define_array(generate, success, t, find_type(underlying_type_hash), array_length);
-	else
-		t = parse_define_object(generate, success, t, find_type(underlying_type_hash));
-
-	if (!success) {
-		set_unknown(value);
-		return skip_to_end_of_define(t);
+	// Parse recursive from the outermost array and generate types on the way back when
+	// the size of each array has been determined. Fail as soon as mismatches in lengths
+	// are found.
+
+	uint64_t final_type_hash = 0;
+	{
+		bool success = false;
+		t = parse_define_any(generate, success, t, underlying_type_hash, final_type_hash, array_sizes, define.type_reference.array_depth);
+
+		if (!success) {
+			set_unknown(value);
+			return skip_to_end_of_define(t);
+		}
 	}
 
 	// fill the type description now when array length has been determined
-	if (!fill_offset_value(generate, define.type_reference, array_length, define.source_location, value)) {
-		set_unknown(value);
-		return skip_to_end_of_define(t);
-	}
+	const TypeDescription &type_desc = find_type(final_type_hash);
+	value.type = type_desc.type;
+	value.type_hash = type_desc.underlying_type_hash;
 
 	if (generate && _hex_source_writer != nullptr) {
 		uint32_t generated_size = static_cast<uint32_t>(data.size() - data_size_before);

          
@@ 1206,7 1266,6 @@ 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/syntax/syntax_parser.cpp +0 -1
@@ 1772,7 1772,6 @@ const Token *SyntaxParser::parse_define_
 			return t;
 		}
 	}
-
 }
 
 const Token *SyntaxParser::parse_reserve_statement(const Token *t)