diff --git a/Rakefile b/Rakefile index 743fc21..ae7f27f 100644 --- a/Rakefile +++ b/Rakefile @@ -38,10 +38,47 @@ end desc 'Convert previous stage language into the current stage language' task :convert do - File.open('boot/stage18/cl.elna', 'w') do |current_stage| - File.readlines('boot/stage17/cl.elna').each do |line| + File.open('boot/stage19/cl.elna', 'w') do |current_stage| + File.readlines('boot/stage18/cl.elna').each do |line| + current_stage << "\tf();\n" if line.include? "if _compile() then" current_stage << line + if line == "type\n" + current_stage << <<-RECORD + Pair = record + first: Word; + second: Word + end; + R1 = record + field1: Word; + field2: [4]Word; + field3: Word + end; + R2 = record + field1: Word; + field3: Word; + field4: Word; + field5: Word; + field6: Word; + field2: Word + end; + + RECORD + end end + current_stage << <<~EPILOGUE + + proc f(); + var + v1: Word; + r: ^R1; + begin + r := malloc(#size(R1)); + + v1 := r^.field2[2]; + + printf("# %i\\n\\0", v1) + end; + EPILOGUE end end @@ -105,7 +142,10 @@ file 'build/build.ninja' => ['build'] do |t| build #{valid_stage}/cl: #{link} #{valid_stage}/cl.o NINJA end - f << "\ndefault build/valid/stage18/cl\n" + f << <<~NINJA + + default build/valid/#{STAGES.last}/cl + NINJA end end diff --git a/boot/stage17/cl.elna b/boot/stage17/cl.elna index 70c0348..ec0f656 100644 --- a/boot/stage17/cl.elna +++ b/boot/stage17/cl.elna @@ -2707,7 +2707,15 @@ begin if parser_node^.kind = ElnaTreeKind.dereference_expression then dereference_expression := parser_node; first_instruction := elna_tac_designator(dereference_expression^.pointer, symbol_table, is_address, operand_type, operand_value, operand_length); - is_address^ := 1 + if is_address^ = 1 then + last_instruction := _elna_tac_instruction_create(ElnaTacOperator.load); + _elna_tac_instruction_set_operand(last_instruction, 1, operand_type^, operand_value^, operand_length^); + _elna_tac_instruction_set_operand(last_instruction, 2, operand_type^, operand_value^, operand_length^); + + first_instruction := elna_instruction_list_concatenate(first_instruction, last_instruction) + else + is_address^ := 1 + end elsif parser_node^.kind = ElnaTreeKind.field_access_expression then field_access_expression := parser_node; expression_type := field_access_expression^.type_decoration; diff --git a/boot/stage18/cl.elna b/boot/stage18/cl.elna index 4783700..dc9fdb5 100644 --- a/boot/stage18/cl.elna +++ b/boot/stage18/cl.elna @@ -8,6 +8,7 @@ program; (* Stage 18 compiler. *) (* - Record fields can be aggregates themselves *) +(* - Static arrays. *) type (** @@ -92,7 +93,8 @@ type pointer_type_expression, array_type_expression, null, - trait_expression + trait_expression, + array_access_expression ); ElnaTreeNode = record kind: ElnaTreeKind @@ -196,6 +198,12 @@ type field: Word; length: Word end; + ElnaTreeArrayAccessExpression = record + kind: ElnaTreeKind; + type_decoration: ^ElnaType; + array: Word; + index: Word + end; ElnaTreeEnumerationTypeExpression = record kind: ElnaTreeKind; members: Word; @@ -2114,6 +2122,9 @@ begin elsif token_kind = ElnaLexerKind.dot then simple_expression := elna_parser_field_access_expression(simple_expression); goto elna_parser_designator_loop + elsif token_kind = ElnaLexerKind.left_square then + simple_expression := elna_parser_array_access_expression(simple_expression); + goto elna_parser_designator_loop elsif token_kind = ElnaLexerKind.left_paren then simple_expression := elna_parser_call(simple_expression); goto elna_parser_designator_loop @@ -2215,9 +2226,15 @@ begin first_instruction := elna_tac_designator(operand, symbol_table, @is_address, operand_type, operand_value, operand_length); if operator = '@' then - instruction := _elna_tac_instruction_create(ElnaTacOperator.get_address); - _elna_tac_instruction_set_operand(instruction, 1, ElnaTacOperand.pseudo, "$unary", 6); - _elna_tac_instruction_set_operand(instruction, 2, operand_type^, operand_value^, operand_length^) + if is_address then + instruction := _elna_tac_instruction_create(ElnaTacOperator.copy); + _elna_tac_instruction_set_operand(instruction, 1, ElnaTacOperand.pseudo, "$unary", 6); + _elna_tac_instruction_set_operand(instruction, 2, operand_type^, operand_value^, operand_length^) + else + instruction := _elna_tac_instruction_create(ElnaTacOperator.get_address); + _elna_tac_instruction_set_operand(instruction, 1, ElnaTacOperand.pseudo, "$unary", 6); + _elna_tac_instruction_set_operand(instruction, 2, operand_type^, operand_value^, operand_length^) + end elsif operator = '-' then instruction := _elna_tac_instruction_create(ElnaTacOperator.negate); _elna_tac_instruction_set_operand(instruction, 1, ElnaTacOperand.pseudo, "$unary", 6); @@ -2682,10 +2699,78 @@ begin return result end; +proc elna_parser_array_access_expression(array: Word); +var + token_kind: Word; + result: ^ElnaTreeArrayAccessExpression; +begin + _elna_lexer_skip_token(); + + result := malloc(#size(ElnaTreeArrayAccessExpression)); + + result^.kind := ElnaTreeKind.array_access_expression; + result^.type_decoration := nil; + result^.array := array; + result^.index := elna_parser_binary_expression(); + + _elna_lexer_read_token(@token_kind); + _elna_lexer_skip_token(); + + return result +end; + +proc elna_tac_dereference_expression(dereference_expression: ^ElnaTreeDereferenceExpression, symbol_table: Word, operand_type: Word, operand_value: Word, operand_length: Word); +var + result_instructions: Word; + load_instruction: Word; + is_address: Word; +begin + result_instructions := elna_tac_designator(dereference_expression^.pointer, symbol_table, @is_address, operand_type, operand_value, operand_length); + if is_address = true then + load_instruction := _elna_tac_instruction_create(ElnaTacOperator.load); + _elna_tac_instruction_set_operand(load_instruction, 1, operand_type^, operand_value^, operand_length^); + _elna_tac_instruction_set_operand(load_instruction, 2, operand_type^, operand_value^, operand_length^); + + result_instructions := elna_instruction_list_concatenate(result_instructions, load_instruction) + end; + return result_instructions +end; + proc elna_tac_designator(parser_node: ^ElnaTreeExpression, symbol_table: Word, is_address: Word, operand_type: Word, operand_value: Word, operand_length: Word); var field_access_expression: ^ElnaTreeFieldAccessExpression; - dereference_expression: ^ElnaTreeDereferenceExpression; + result_instructions: Word; + designator_base: ^ElnaTreeExpression; +begin + if parser_node^.kind = ElnaTreeKind.dereference_expression then + result_instructions := elna_tac_dereference_expression(parser_node, symbol_table, operand_type, operand_value, operand_length); + is_address^ := true + elsif parser_node^.kind = ElnaTreeKind.field_access_expression then + field_access_expression := parser_node; + designator_base := field_access_expression^.aggregate; + + if designator_base^.type_decoration = nil then + result_instructions := elna_tac_enumeration_value(field_access_expression, operand_type, operand_value, operand_length); + is_address^ := false + else + result_instructions := elna_tac_field_access_expression(field_access_expression, symbol_table, operand_type, operand_value, operand_length); + is_address^ := true + end; + elsif parser_node^.kind = ElnaTreeKind.array_access_expression then + result_instructions := elna_tac_array_access_expression(parser_node, symbol_table, operand_type, operand_value, operand_length); + is_address^ := true + elsif parser_node^.kind = ElnaTreeKind.call then + result_instructions := elna_tac_call(parser_node, symbol_table, operand_type, operand_value, operand_length); + is_address^ := false + else + result_instructions := elna_tac_simple_expression(parser_node, symbol_table, operand_type, operand_value, operand_length); + is_address^ := false + end; + return result_instructions +end; + +proc elna_tac_field_access_expression(field_access_expression: ^ElnaTreeFieldAccessExpression, symbol_table: Word, operand_type: Word, operand_value: Word, operand_length: Word); +var field_type: ^ElnaType; first_instruction: Word; last_instruction: Word; @@ -2694,55 +2779,81 @@ var field_count: Word; current_field: ^ElnaTypeField; field_offset: Word; + is_address: Word; begin - if parser_node^.kind = ElnaTreeKind.dereference_expression then - dereference_expression := parser_node; - first_instruction := elna_tac_designator(dereference_expression^.pointer, symbol_table, is_address, operand_type, operand_value, operand_length); - is_address^ := 1 - elsif parser_node^.kind = ElnaTreeKind.field_access_expression then - field_access_expression := parser_node; - designator_base := field_access_expression^.aggregate; - aggregate_type := designator_base^.type_decoration; + designator_base := field_access_expression^.aggregate; + aggregate_type := designator_base^.type_decoration; - if aggregate_type = nil then - first_instruction := elna_tac_enumeration_value(field_access_expression, operand_type, operand_value, operand_length); - is_address^ := 0 - else - first_instruction := elna_tac_designator(designator_base, symbol_table, is_address, operand_type, operand_value, operand_length); + first_instruction := elna_tac_designator(designator_base, symbol_table, @is_address, operand_type, operand_value, operand_length); - field_count := aggregate_type^.length; - current_field := aggregate_type^.members; - field_offset := 0; + field_count := aggregate_type^.length; + current_field := aggregate_type^.members; + field_offset := 0; - .elna_tac_designator_field; + .elna_tac_field_access_expression_field; - if string_compare(field_access_expression^.field, field_access_expression^.length, current_field^.name, current_field^.length) = 0 then - field_type := current_field^.field_type; - field_count := field_count - 1; - current_field := current_field + #size(ElnaTypeField); - field_offset := field_offset + field_type^.size; - goto elna_tac_designator_field - end; - last_instruction := _elna_tac_instruction_create(ElnaTacOperator.add); - _elna_tac_instruction_set_operand(last_instruction, 1, ElnaTacOperand.pseudo, "$unary", 6); - _elna_tac_instruction_set_operand(last_instruction, 2, ElnaTacOperand.immediate, field_offset, 0); - _elna_tac_instruction_set_operand(last_instruction, 3, operand_type^, operand_value^, operand_length^); - - operand_type^ := ElnaTacOperand.pseudo; - operand_value^ := "$unary"; - operand_length^ := 6; - - is_address^ := 1; - first_instruction := elna_instruction_list_concatenate(first_instruction, last_instruction) - end; - elsif parser_node^.kind = ElnaTreeKind.call then - first_instruction := elna_tac_call(parser_node, symbol_table, operand_type, operand_value, operand_length); - is_address^ := 0 - else - first_instruction := elna_tac_simple_expression(parser_node, symbol_table, operand_type, operand_value, operand_length); - is_address^ := 0 + if string_compare(field_access_expression^.field, field_access_expression^.length, current_field^.name, current_field^.length) = 0 then + field_type := current_field^.field_type; + field_count := field_count - 1; + current_field := current_field + #size(ElnaTypeField); + field_offset := field_offset + field_type^.size; + goto elna_tac_field_access_expression_field end; - return first_instruction + last_instruction := _elna_tac_instruction_create(ElnaTacOperator.add); + _elna_tac_instruction_set_operand(last_instruction, 1, ElnaTacOperand.pseudo, "$unary", 6); + _elna_tac_instruction_set_operand(last_instruction, 2, ElnaTacOperand.immediate, field_offset, 0); + _elna_tac_instruction_set_operand(last_instruction, 3, operand_type^, operand_value^, operand_length^); + + operand_type^ := ElnaTacOperand.pseudo; + operand_value^ := "$unary"; + operand_length^ := 6; + + return elna_instruction_list_concatenate(first_instruction, last_instruction) +end; + +proc elna_tac_array_access_expression(array_access_expression: ^ElnaTreeArrayAccessExpression, symbol_table: Word, operand_type: Word, operand_value: Word, operand_length: Word); +var + array_instructions: Word; + index_instructions: Word; + offset_instruction: Word; + add_instruction: Word; + is_address: Word; + offset_type: Word; + offset_value: Word; + offset_length: Word; + aggregate_type: ^ElnaTypeArray; + designator_base: ^ElnaTreeExpression; + element_type: ^ElnaType; +begin + designator_base := array_access_expression^.array; + aggregate_type := designator_base^.type_decoration; + element_type := aggregate_type^.base; + + index_instructions := elna_tac_binary_expression(array_access_expression^.index, symbol_table, operand_type, operand_value, operand_length); + + add_instruction := _elna_tac_instruction_create(ElnaTacOperator.subtract); + _elna_tac_instruction_set_operand(add_instruction, 1, ElnaTacOperand.pseudo, "$lhs", 4); + _elna_tac_instruction_set_operand(add_instruction, 2, operand_type^, operand_value^, operand_length^); + _elna_tac_instruction_set_operand(add_instruction, 3, ElnaTacOperand.immediate, 1, 0); + + offset_instruction := _elna_tac_instruction_create(ElnaTacOperator.multiply); + _elna_tac_instruction_set_operand(offset_instruction, 1, ElnaTacOperand.pseudo, "$lhs", 4); + _elna_tac_instruction_set_operand(offset_instruction, 2, ElnaTacOperand.pseudo, "$lhs", 4); + _elna_tac_instruction_set_operand(offset_instruction, 3, ElnaTacOperand.immediate, element_type^.size, 0); + + elna_instruction_list_concatenate(add_instruction, offset_instruction); + index_instructions := elna_instruction_list_concatenate(index_instructions, add_instruction); + + array_instructions := elna_tac_designator(array_access_expression^.array, symbol_table, @is_address, operand_type, operand_value, operand_length); + + add_instruction := _elna_tac_instruction_create(ElnaTacOperator.add); + _elna_tac_instruction_set_operand(add_instruction, 1, operand_type^, operand_value^, operand_length^); + _elna_tac_instruction_set_operand(add_instruction, 2, operand_type^, operand_value^, operand_length^); + _elna_tac_instruction_set_operand(add_instruction, 3, ElnaTacOperand.pseudo, "$lhs", 4); + + elna_instruction_list_concatenate(offset_instruction, array_instructions); + + return elna_instruction_list_concatenate(index_instructions, add_instruction) end; proc elna_parser_assign_statement(assignee: Word); @@ -4437,12 +4548,28 @@ begin parser_node^.type_decoration := base_type end; +proc elna_type_array_access_expression(parser_node: ^ElnaTreeArrayAccessExpression, symbol_table: Word); +var + aggregate_type: ^ElnaTypeArray; + base_expression: ^ElnaTreeExpression; +begin + base_expression := parser_node^.array; + + elna_type_designator(base_expression, symbol_table); + elna_type_binary_expression(parser_node^.index, symbol_table); + + aggregate_type := base_expression^.type_decoration; + parser_node^.type_decoration := aggregate_type^.base +end; + proc elna_type_designator(parser_node: ^ElnaTreeNode, symbol_table: Word); begin if parser_node^.kind = ElnaTreeKind.dereference_expression then elna_type_dereference_expression(parser_node, symbol_table) elsif parser_node^.kind = ElnaTreeKind.field_access_expression then elna_type_field_access_expression(parser_node, symbol_table) + elsif parser_node^.kind = ElnaTreeKind.array_access_expression then + elna_type_array_access_expression(parser_node, symbol_table) elsif parser_node^.kind = ElnaTreeKind.call then elna_type_call(parser_node, symbol_table) else