(* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/. *) module import common, sys.posix.cstdlib, sys.posix.cstdio, sys.posix.cstring, lexer, ast, symbol, diagnostic type ElnaTacOperator* = ( get_address, add, load, store, proc_call, copy, copy_to_offset, copy_from_offset, subtract, divide, remainder, multiply, _xor, _or, and, less_than, greater_than, less_or_equal, greater_or_equal, equal, not_equal, negate, complement, jump, jump_if_zero, jump_if_not_zero, label, _return, add_ptr, nop ) ElnaTacKind* = (list, label, constant, variable, ignore) ElnaTacOperand* = record kind: ElnaTacKind; name: []const Word8; constant: Word; length: Word; list_operands: ^ElnaTacOperand end ElnaTacInstruction* = record(ElnaListNode) operator: ElnaTacOperator; operands: [4]ElnaTacOperand end ElnaTacProcedure* = record next: ^ElnaTacProcedure; name: []const Word8; body: ElnaList; parameters: ^[]const Word8; count: Word; symbol_table: ^ElnaSymbolTable end ElnaTacStaticVariable* = record next: ^ElnaTacStaticVariable; name: []const Word8; size: Word end ElnaTacStringConstant* = record next: ^ElnaTacStringConstant; name: []const Word8; value: []const Word8 end ElnaInstructionModule* = record strings: ^ElnaTacStringConstant; data: ^ElnaTacStaticVariable; code: ^ElnaTacProcedure end ElnaTacOperandType = (plain_object, dereferenced_pointer, sub_object) ElnaTacOperandResult = record kind: ElnaTacOperandType; offset: Word end var pseudo_counter: Word := 0u label_counter: Word := 0u string_counter: Word := 0u module_strings_first: ^ElnaTacStringConstant := nil module_strings_last: ^ElnaTacStringConstant := nil proc elna_tac_instruction_create*(operator: ElnaTacOperator) -> ^ElnaTacInstruction var result: ^ElnaTacInstruction begin result := malloc#[ElnaTacInstruction](#size(ElnaTacInstruction)); result^.next := nil; result^.operator := operator; result^.operands[1].kind := ElnaTacKind.ignore; result^.operands[2].kind := ElnaTacKind.ignore; result^.operands[3].kind := ElnaTacKind.ignore; result^.operands[4].kind := ElnaTacKind.ignore return result proc elna_tac_instruction_set_operand*(this: ^ElnaTacInstruction; n: Word; kind: ElnaTacKind; name: []const Word8; constant: Word; length: Word) begin this^.operands[n].kind := kind; this^.operands[n].name := name; this^.operands[n].constant := constant; this^.operands[n].length := length; this^.operands[n].list_operands := nil return proc elna_tac_instruction_set_op*(this: ^ElnaTacInstruction; n: Word; op: ^ElnaTacOperand) begin this^.operands[n] := op^ return proc elna_tac_instruction_set_const*(this: ^ElnaTacInstruction; n: Word; value: Word; length: Word) begin this^.operands[n].kind := ElnaTacKind.constant; this^.operands[n].name := ""; this^.operands[n].constant := value; this^.operands[n].length := length; this^.operands[n].list_operands := nil return proc elna_tac_instruction_set_label*(this: ^ElnaTacInstruction; n: Word; label_name: []const Word8) begin this^.operands[n].kind := ElnaTacKind.label; this^.operands[n].name := label_name; this^.operands[n].constant := 0u; this^.operands[n].length := label_name.length; this^.operands[n].list_operands := nil return proc elna_tac_make_variable*(operand: ^ElnaTacOperand; symbol_table: ^ElnaSymbolTable; variable_type: ^ElnaType) var buffer: ^Word8 name_slice: []const Word8 temporary_info: ^ElnaSymbolTemporaryInfo len: Int begin pseudo_counter := pseudo_counter + 1u; buffer := malloc#[Word8](16u); len := sprintf(buffer, "t%u\0".ptr, pseudo_counter); name_slice := buffer[1u to cast(len: Word)]; operand^.kind := ElnaTacKind.variable; operand^.name := name_slice; operand^.constant := 0u; operand^.length := name_slice.length; operand^.list_operands := nil; temporary_info := elna_symbol_temporary_info_create(1u, variable_type); elna_symbol_table_enter(symbol_table, name_slice, cast(temporary_info: ^ElnaSymbolInfo)) return proc elna_tac_label*() -> []const Word8 var buffer: ^Word8 result: []const Word8 len: Int begin label_counter := label_counter + 1u; buffer := malloc#[Word8](16u); len := sprintf(buffer, "l%u\0".ptr, label_counter); result := buffer[1u to cast(len: Word)] return result proc elna_tac_integer_literal(integer_literal_node: ^ElnaTreeIntegerLiteral; operand: ^ElnaTacOperand) begin operand^.kind := ElnaTacKind.constant; operand^.name := ""; operand^.constant := integer_literal_node^.value; operand^.length := word_type^.size; operand^.list_operands := nil return proc elna_tac_boolean_literal(boolean_literal_node: ^ElnaTreeBooleanLiteral; operand: ^ElnaTacOperand) begin operand^.kind := ElnaTacKind.constant; operand^.name := ""; if boolean_literal_node^.value then operand^.constant := 1u else operand^.constant := 0u end; operand^.length := bool_type^.size; operand^.list_operands := nil return proc elna_tac_nil_literal(operand: ^ElnaTacOperand) begin operand^.kind := ElnaTacKind.constant; operand^.name := ""; operand^.constant := 0u; operand^.length := 8u; operand^.list_operands := nil return proc elna_tac_character_literal(character_literal_node: ^ElnaTreeCharacterLiteral; operand: ^ElnaTacOperand) begin operand^.kind := ElnaTacKind.constant; operand^.name := ""; operand^.constant := cast(character_literal_node^.value: Word); operand^.length := char_type^.size; operand^.list_operands := nil return proc elna_tac_variable_expression(variable_expression: ^ElnaTreeVariableExpression; operand: ^ElnaTacOperand) begin operand^.kind := ElnaTacKind.variable; operand^.name := variable_expression^.name; operand^.constant := 0u; operand^.length := variable_expression^.name.length; operand^.list_operands := nil return proc elna_tac_trait_expression(trait_node: ^ElnaTreeTraitExpression; operand: ^ElnaTacOperand) var symbol_info: ^ElnaSymbolInfo type_info: ^ElnaSymbolTypeInfo parser_node: ^ElnaTreeNamedTypeExpression begin parser_node := cast(trait_node^.argument: ^ElnaTreeNamedTypeExpression); symbol_info := elna_symbol_table_lookup(symbol_table_global, parser_node^.name); operand^.kind := ElnaTacKind.constant; operand^.name := ""; operand^.length := 8u; operand^.list_operands := nil; if symbol_info <> nil & symbol_info^.kind = ElnaSymbolInfoKind.type_info then type_info := cast(symbol_info: ^ElnaSymbolTypeInfo); operand^.constant := type_info^._type^.size else operand^.constant := 0u end return proc elna_tac_string_literal(instructions: ^ElnaList; string_literal_node: ^ElnaTreeStringLiteral; symbol_table: ^ElnaSymbolTable; operand: ^ElnaTacOperand) var string_entry: ^ElnaTacStringConstant label_buf: ^Word8 label_slice: []const Word8 label_len: Int instruction: ^ElnaTacInstruction address: ElnaTacOperand begin string_counter := string_counter + 1u; label_buf := malloc#[Word8](16u); label_len := sprintf(label_buf, "str_%u\0".ptr, string_counter); label_slice := label_buf[1u to cast(label_len: Word)]; string_entry := malloc#[ElnaTacStringConstant](#size(ElnaTacStringConstant)); string_entry^.next := nil; string_entry^.name := label_slice; string_entry^.value := string_literal_node^.value; if module_strings_first = nil then module_strings_first := string_entry else module_strings_last^.next := string_entry end; module_strings_last := string_entry; elna_tac_make_variable(operand, symbol_table, cast(string_type: ^ElnaType)); elna_tac_make_variable(@address, symbol_table, pointer_type); instruction := elna_tac_instruction_create(ElnaTacOperator.get_address); elna_tac_instruction_set_operand(instruction, 1u, ElnaTacKind.variable, label_slice, 0u, label_slice.length); elna_tac_instruction_set_op(instruction, 2u, @address); elna_list_append(instructions, cast(instruction: ^ElnaListNode)); instruction := elna_tac_instruction_create(ElnaTacOperator.copy_to_offset); elna_tac_instruction_set_op(instruction, 1u, @address); elna_tac_instruction_set_op(instruction, 2u, operand); elna_tac_instruction_set_const(instruction, 3u, 0u, 8u); elna_list_append(instructions, cast(instruction: ^ElnaListNode)); instruction := elna_tac_instruction_create(ElnaTacOperator.copy_to_offset); elna_tac_instruction_set_const(instruction, 1u, string_literal_node^.value.length, 8u); elna_tac_instruction_set_op(instruction, 2u, operand); elna_tac_instruction_set_const(instruction, 3u, 8u, 8u); elna_list_append(instructions, cast(instruction: ^ElnaListNode)) return proc elna_tac_copy_address(instructions: ^ElnaList; operand_result: ^ElnaTacOperandResult; from, dest: ^ElnaTacOperand) var instruction: ^ElnaTacInstruction begin if operand_result^.kind = ElnaTacOperandType.dereferenced_pointer then dest^ := from^ elsif operand_result^.kind = ElnaTacOperandType.sub_object then instruction := elna_tac_instruction_create(ElnaTacOperator.get_address); elna_tac_instruction_set_op(instruction, 1u, from); elna_tac_instruction_set_op(instruction, 2u, dest); elna_list_append(instructions, cast(instruction: ^ElnaListNode)); instruction := elna_tac_instruction_create(ElnaTacOperator.add); elna_tac_instruction_set_const(instruction, 1u, operand_result^.offset, 4u); elna_tac_instruction_set_op(instruction, 2u, dest); elna_tac_instruction_set_op(instruction, 3u, dest); elna_list_append(instructions, cast(instruction: ^ElnaListNode)) else instruction := elna_tac_instruction_create(ElnaTacOperator.get_address); elna_tac_instruction_set_op(instruction, 1u, from); elna_tac_instruction_set_op(instruction, 2u, dest); elna_list_append(instructions, cast(instruction: ^ElnaListNode)) end return proc elna_tac_simple_expression(instructions: ^ElnaList; parser_node: ^ElnaTreeExpression; symbol_table: ^ElnaSymbolTable; operand: ^ElnaTacOperand) begin case parser_node^.kind of ElnaTreeKind.integer_literal: elna_tac_integer_literal(cast(parser_node: ^ElnaTreeIntegerLiteral), operand) | ElnaTreeKind.character_literal: elna_tac_character_literal(cast(parser_node: ^ElnaTreeCharacterLiteral), operand) | ElnaTreeKind.string_literal: elna_tac_string_literal(instructions, cast(parser_node: ^ElnaTreeStringLiteral), symbol_table, operand) | ElnaTreeKind.boolean_literal: elna_tac_boolean_literal(cast(parser_node: ^ElnaTreeBooleanLiteral), operand) | ElnaTreeKind.null: elna_tac_nil_literal(operand) | ElnaTreeKind.variable_expression: elna_tac_variable_expression(cast(parser_node: ^ElnaTreeVariableExpression), operand) | ElnaTreeKind.trait_expression: elna_tac_trait_expression(cast(parser_node: ^ElnaTreeTraitExpression), operand) | ElnaTreeKind._cast: elna_tac_binary_expression(instructions, (cast(parser_node: ^ElnaTreeCastExpression))^.expression, symbol_table, operand) else end return proc elna_tac_unary_expression(instructions: ^ElnaList; parser_node: ^ElnaTreeExpression; symbol_table: ^ElnaSymbolTable; operand: ^ElnaTacOperand) var operator: ElnaLexerKind unary_operand: ^ElnaTreeExpression unary_node: ^ElnaTreeUnaryExpression operand_result: ElnaTacOperandResult instruction: ^ElnaTacInstruction base: ElnaTacOperand has_unary: Bool := false begin if parser_node^.kind = ElnaTreeKind.unary_expression then unary_node := cast(parser_node: ^ElnaTreeUnaryExpression); operator := unary_node^.operator; unary_operand := unary_node^.operand; has_unary := true else unary_operand := parser_node end; elna_tac_designator(instructions, unary_operand, symbol_table, @operand_result, @base); if has_unary & operator = ElnaLexerKind.at then elna_tac_make_variable(operand, symbol_table, word_type); elna_tac_copy_address(instructions, @operand_result, @base, operand) elsif has_unary & operator = ElnaLexerKind.minus then elna_tac_make_variable(operand, symbol_table, parser_node^.type_decoration); instruction := elna_tac_instruction_create(ElnaTacOperator.negate); elna_tac_instruction_set_op(instruction, 1u, @base); elna_tac_instruction_set_op(instruction, 2u, operand); elna_list_append(instructions, cast(instruction: ^ElnaListNode)) elsif has_unary & operator = ElnaLexerKind.not then elna_tac_make_variable(operand, symbol_table, parser_node^.type_decoration); instruction := elna_tac_instruction_create(ElnaTacOperator.complement); elna_tac_instruction_set_op(instruction, 1u, @base); elna_tac_instruction_set_op(instruction, 2u, operand); elna_list_append(instructions, cast(instruction: ^ElnaListNode)) elsif operand_result.kind = ElnaTacOperandType.dereferenced_pointer then elna_tac_make_variable(operand, symbol_table, parser_node^.type_decoration); instruction := elna_tac_instruction_create(ElnaTacOperator.load); elna_tac_instruction_set_op(instruction, 1u, @base); elna_tac_instruction_set_op(instruction, 2u, operand); elna_list_append(instructions, cast(instruction: ^ElnaListNode)) elsif operand_result.kind = ElnaTacOperandType.sub_object then elna_tac_make_variable(operand, symbol_table, parser_node^.type_decoration); instruction := elna_tac_instruction_create(ElnaTacOperator.copy_from_offset); elna_tac_instruction_set_op(instruction, 1u, @base); elna_tac_instruction_set_const(instruction, 2u, operand_result.offset, 4u); elna_tac_instruction_set_op(instruction, 3u, operand); elna_list_append(instructions, cast(instruction: ^ElnaListNode)) else operand^ := base end return proc elna_tac_add_pointer(parser_node: ^ElnaTreeExpression; lhs, rhs: ^ElnaTacOperand; symbol_table: ^ElnaSymbolTable; operand: ^ElnaTacOperand) -> ^ElnaTacInstruction var instruction: ^ElnaTacInstruction pointer_type_node: ^ElnaTypePointer elem_size: Word := 1u begin if parser_node^.type_decoration <> nil & parser_node^.type_decoration^.kind = ElnaTypeKind.pointer then pointer_type_node := cast(parser_node^.type_decoration: ^ElnaTypePointer); if pointer_type_node^.base <> nil then elem_size := pointer_type_node^.base^.size end end; instruction := elna_tac_instruction_create(ElnaTacOperator.add_ptr); elna_tac_make_variable(operand, symbol_table, word_type); elna_tac_instruction_set_op(instruction, 1u, lhs); elna_tac_instruction_set_op(instruction, 2u, rhs); elna_tac_instruction_set_const(instruction, 3u, elem_size, 4u); elna_tac_instruction_set_op(instruction, 4u, operand) return instruction proc elna_tac_binary_create(instructions: ^ElnaList; operator: ElnaTacOperator; lhs, rhs: ^ElnaTacOperand; symbol_table: ^ElnaSymbolTable; operand: ^ElnaTacOperand) var instruction: ^ElnaTacInstruction lhs_symbol: ^ElnaSymbolInfo temp_info: ^ElnaSymbolTemporaryInfo variable_type: ^ElnaType := word_type begin if lhs^.kind = ElnaTacKind.variable then lhs_symbol := elna_symbol_table_lookup(symbol_table, lhs^.name); if lhs_symbol <> nil & lhs_symbol^.kind = ElnaSymbolInfoKind.temporary_info then temp_info := cast(lhs_symbol: ^ElnaSymbolTemporaryInfo); variable_type := temp_info^.variable_type end end; elna_tac_make_variable(operand, symbol_table, variable_type); instruction := elna_tac_instruction_create(operator); elna_tac_instruction_set_op(instruction, 1u, lhs); elna_tac_instruction_set_op(instruction, 2u, rhs); elna_tac_instruction_set_op(instruction, 3u, operand); elna_list_append(instructions, cast(instruction: ^ElnaListNode)) return proc elna_tac_subtract_create(instructions: ^ElnaList; parser_node: ^ElnaTreeBinaryExpression; lhs, rhs: ^ElnaTacOperand; symbol_table: ^ElnaSymbolTable; operand: ^ElnaTacOperand) var instruction: ^ElnaTacInstruction lhs_is_pointer: Bool := false rhs_is_not_pointer: Bool := true begin if parser_node^.lhs^.type_decoration <> nil & parser_node^.lhs^.type_decoration^.kind = ElnaTypeKind.pointer then lhs_is_pointer := true end; if parser_node^.rhs^.type_decoration <> nil & parser_node^.rhs^.type_decoration^.kind = ElnaTypeKind.pointer then rhs_is_not_pointer := false end; if lhs_is_pointer & rhs_is_not_pointer then instruction := elna_tac_instruction_create(ElnaTacOperator.negate); elna_tac_instruction_set_op(instruction, 1u, rhs); elna_tac_instruction_set_op(instruction, 2u, operand); elna_list_append(instructions, cast(instruction: ^ElnaListNode)); instruction := elna_tac_add_pointer(parser_node^.lhs, lhs, operand, symbol_table, operand); elna_list_append(instructions, cast(instruction: ^ElnaListNode)) else elna_tac_binary_create(instructions, ElnaTacOperator.subtract, lhs, rhs, symbol_table, operand) end return proc elna_tac_binary_expression(instructions: ^ElnaList; parser_node: ^ElnaTreeExpression; symbol_table: ^ElnaSymbolTable; operand: ^ElnaTacOperand) var binary_node: ^ElnaTreeBinaryExpression instruction: ^ElnaTacInstruction lhs: ElnaTacOperand rhs: ElnaTacOperand begin if parser_node^.kind <> ElnaTreeKind.binary_expression then elna_tac_unary_expression(instructions, parser_node, symbol_table, operand) else binary_node := cast(parser_node: ^ElnaTreeBinaryExpression); elna_tac_unary_expression(instructions, binary_node^.lhs, symbol_table, @lhs); elna_tac_unary_expression(instructions, binary_node^.rhs, symbol_table, @rhs); case binary_node^.operator of ElnaLexerKind.plus: if binary_node^.lhs^.type_decoration <> nil & binary_node^.lhs^.type_decoration^.kind = ElnaTypeKind.pointer then instruction := elna_tac_add_pointer(binary_node^.lhs, @lhs, @rhs, symbol_table, operand); elna_list_append(instructions, cast(instruction: ^ElnaListNode)) elsif binary_node^.rhs^.type_decoration <> nil & binary_node^.rhs^.type_decoration^.kind = ElnaTypeKind.pointer then instruction := elna_tac_add_pointer(binary_node^.rhs, @rhs, @lhs, symbol_table, operand); elna_list_append(instructions, cast(instruction: ^ElnaListNode)) else elna_tac_binary_create(instructions, ElnaTacOperator.add, @lhs, @rhs, symbol_table, operand) end | ElnaLexerKind.minus: elna_tac_subtract_create(instructions, binary_node, @lhs, @rhs, symbol_table, operand) | ElnaLexerKind.multiplication: elna_tac_binary_create(instructions, ElnaTacOperator.multiply, @lhs, @rhs, symbol_table, operand) | ElnaLexerKind.division: elna_tac_binary_create(instructions, ElnaTacOperator.divide, @lhs, @rhs, symbol_table, operand) | ElnaLexerKind.remainder: elna_tac_binary_create(instructions, ElnaTacOperator.remainder, @lhs, @rhs, symbol_table, operand) | ElnaLexerKind.and: elna_tac_binary_create(instructions, ElnaTacOperator.and, @lhs, @rhs, symbol_table, operand) | ElnaLexerKind._or: elna_tac_binary_create(instructions, ElnaTacOperator._or, @lhs, @rhs, symbol_table, operand) | ElnaLexerKind._xor: elna_tac_binary_create(instructions, ElnaTacOperator._xor, @lhs, @rhs, symbol_table, operand) | ElnaLexerKind.equals: elna_tac_binary_create(instructions, ElnaTacOperator.equal, @lhs, @rhs, symbol_table, operand) | ElnaLexerKind.not_equal: elna_tac_binary_create(instructions, ElnaTacOperator.not_equal, @lhs, @rhs, symbol_table, operand) | ElnaLexerKind.less_than: elna_tac_binary_create(instructions, ElnaTacOperator.less_than, @lhs, @rhs, symbol_table, operand) | ElnaLexerKind.greater_than: elna_tac_binary_create(instructions, ElnaTacOperator.greater_than, @lhs, @rhs, symbol_table, operand) | ElnaLexerKind.less_equal: elna_tac_binary_create(instructions, ElnaTacOperator.less_or_equal, @lhs, @rhs, symbol_table, operand) | ElnaLexerKind.greater_equal: elna_tac_binary_create(instructions, ElnaTacOperator.greater_or_equal, @lhs, @rhs, symbol_table, operand) else end end return proc elna_tac_call(instructions: ^ElnaList; parsed_call: ^ElnaTreeCall; symbol_table: ^ElnaSymbolTable; operand: ^ElnaTacOperand) var parsed_expression: ^ElnaTreeVariableExpression arguments_operand: ^ElnaTacOperand call_instruction: ^ElnaTacInstruction argument_entry: ^ElnaTreeExpressionList symbol_info: ^ElnaSymbolInfo procedure_info: ^ElnaSymbolProcedureInfo i: Word := 0u begin parsed_expression := parsed_call^.callee; arguments_operand := malloc#[ElnaTacOperand](parsed_call^.count * #size(ElnaTacOperand)); symbol_info := elna_symbol_table_lookup(symbol_table, parsed_expression^.name); call_instruction := elna_tac_instruction_create(ElnaTacOperator.proc_call); elna_tac_instruction_set_label(call_instruction, 1u, parsed_expression^.name); call_instruction^.operands[2].kind := ElnaTacKind.list; call_instruction^.operands[2].list_operands := arguments_operand; call_instruction^.operands[2].length := parsed_call^.count; if symbol_info <> nil & symbol_info^.kind = ElnaSymbolInfoKind.procedure_info then procedure_info := cast(symbol_info: ^ElnaSymbolProcedureInfo); if procedure_info^.procedure_type^.return_type = nil then call_instruction^.operands[3].kind := ElnaTacKind.ignore else elna_tac_make_variable(operand, symbol_table, procedure_info^.procedure_type^.return_type); elna_tac_instruction_set_op(call_instruction, 3u, operand) end else call_instruction^.operands[3].kind := ElnaTacKind.ignore end; argument_entry := parsed_call^.arguments; while argument_entry <> nil & i < parsed_call^.count do elna_tac_binary_expression(instructions, argument_entry^.expression, symbol_table, arguments_operand + i); argument_entry := argument_entry^.next; i := i + 1u end; elna_list_append(instructions, cast(call_instruction: ^ElnaListNode)) return proc elna_tac_goto_statement(instructions: ^ElnaList; parser_node: ^ElnaTreeGotoStatement) var instruction: ^ElnaTacInstruction begin instruction := elna_tac_instruction_create(ElnaTacOperator.jump); elna_tac_instruction_set_label(instruction, 1u, parser_node^.label); elna_list_append(instructions, cast(instruction: ^ElnaListNode)) return proc elna_tac_label_declaration(instructions: ^ElnaList; parser_node: ^ElnaTreeLabelDeclaration) var result: ^ElnaTacInstruction begin result := elna_tac_instruction_create(ElnaTacOperator.label); elna_tac_instruction_set_label(result, 1u, parser_node^.label); elna_list_append(instructions, cast(result: ^ElnaListNode)) return proc elna_tac_enumeration_value(field_access_expression: ^ElnaTreeFieldAccessExpression; operand: ^ElnaTacOperand) var enumeration_type: ^ElnaTypeEnumeration members: ^[]const Word8 members_length: Word symbol_info: ^ElnaSymbolInfo type_info: ^ElnaSymbolTypeInfo counter: Word := 1u found: Bool := false aggregate_tree: ^ElnaTreeVariableExpression begin aggregate_tree := cast(field_access_expression^.aggregate: ^ElnaTreeVariableExpression); symbol_info := elna_symbol_table_lookup(symbol_table_global, aggregate_tree^.name); if symbol_info <> nil & symbol_info^.kind = ElnaSymbolInfoKind.type_info then type_info := cast(symbol_info: ^ElnaSymbolTypeInfo); if type_info^._type^.kind = ElnaTypeKind.enumeration then enumeration_type := cast(type_info^._type: ^ElnaTypeEnumeration); members := enumeration_type^.members; members_length := enumeration_type^.length; while members_length > 0u & ~found do if members^ = field_access_expression^.field then found := true; operand^.kind := ElnaTacKind.constant; operand^.constant := counter; operand^.length := 4u; operand^.name := ""; operand^.list_operands := nil else members_length := members_length - 1u; members := members + 1u; counter := counter + 1u end end end end return proc elna_tac_dereference_expression(instructions: ^ElnaList; dereference_expression: ^ElnaTreeDereferenceExpression; symbol_table: ^ElnaSymbolTable; operand: ^ElnaTacOperand) var load_instruction: ^ElnaTacInstruction operand_result: ElnaTacOperandResult intermediate: ElnaTacOperand begin elna_tac_designator(instructions, dereference_expression^.pointer, symbol_table, @operand_result, operand); if operand_result.kind = ElnaTacOperandType.dereferenced_pointer then intermediate := operand^; elna_tac_make_variable(operand, symbol_table, word_type); load_instruction := elna_tac_instruction_create(ElnaTacOperator.load); elna_tac_instruction_set_op(load_instruction, 1u, @intermediate); elna_tac_instruction_set_op(load_instruction, 2u, operand); elna_list_append(instructions, cast(load_instruction: ^ElnaListNode)) elsif operand_result.kind = ElnaTacOperandType.sub_object then intermediate := operand^; elna_tac_make_variable(operand, symbol_table, word_type); load_instruction := elna_tac_instruction_create(ElnaTacOperator.copy_from_offset); elna_tac_instruction_set_op(load_instruction, 1u, @intermediate); elna_tac_instruction_set_const(load_instruction, 2u, operand_result.offset, 4u); elna_tac_instruction_set_op(load_instruction, 3u, operand); elna_list_append(instructions, cast(load_instruction: ^ElnaListNode)) end return proc elna_tac_field_access_expression(instructions: ^ElnaList; field_access_expression: ^ElnaTreeFieldAccessExpression; symbol_table: ^ElnaSymbolTable; operand_result: ^ElnaTacOperandResult; operand: ^ElnaTacOperand) var field_type: ^ElnaType instruction: ^ElnaTacInstruction designator_base: ^ElnaTreeExpression aggregate_type: ^ElnaTypeRecord field_count: Word current_field: ^ElnaTypeField field_offset: Word := 0u base: ElnaTacOperand found: Bool := false begin designator_base := field_access_expression^.aggregate; aggregate_type := cast(designator_base^.type_decoration: ^ElnaTypeRecord); elna_tac_designator(instructions, designator_base, symbol_table, operand_result, @base); if aggregate_type <> nil & aggregate_type^.kind = ElnaTypeKind._record then field_count := aggregate_type^.length; current_field := aggregate_type^.members; while field_count > 0u & ~found do if current_field^.name = field_access_expression^.field then found := true else field_type := current_field^.field_type; field_count := field_count - 1u; field_offset := field_offset + field_type^.size; current_field := current_field + 1u end end end; if operand_result^.kind = ElnaTacOperandType.dereferenced_pointer then elna_tac_make_variable(operand, symbol_table, word_type); instruction := elna_tac_instruction_create(ElnaTacOperator.add); elna_tac_instruction_set_op(instruction, 1u, @base); elna_tac_instruction_set_const(instruction, 2u, field_offset, 4u); elna_tac_instruction_set_op(instruction, 3u, operand); elna_list_append(instructions, cast(instruction: ^ElnaListNode)) elsif operand_result^.kind = ElnaTacOperandType.sub_object then operand^ := base; operand_result^.offset := operand_result^.offset + field_offset else operand^ := base; operand_result^.kind := ElnaTacOperandType.sub_object; operand_result^.offset := field_offset end return proc elna_tac_array_access_expression(instructions: ^ElnaList; array_access_expression: ^ElnaTreeArrayAccessExpression; symbol_table: ^ElnaSymbolTable; operand: ^ElnaTacOperand) var instruction: ^ElnaTacInstruction operand_result: ElnaTacOperandResult inter_operand: ElnaTacOperand index_operand: ElnaTacOperand aggregate_type: ^ElnaTypeArray designator_base: ^ElnaTreeExpression element_type: ^ElnaType elem_size: Word := 1u begin designator_base := array_access_expression^.array; if designator_base^.type_decoration <> nil & designator_base^.type_decoration^.kind = ElnaTypeKind.array then aggregate_type := cast(designator_base^.type_decoration: ^ElnaTypeArray); element_type := aggregate_type^.base; if element_type <> nil then elem_size := element_type^.size end end; elna_tac_binary_expression(instructions, array_access_expression^.index, symbol_table, @inter_operand); elna_tac_make_variable(@index_operand, symbol_table, word_type); (* In Elna arrays are 1-based, subtract 1 for 0-based index *) instruction := elna_tac_instruction_create(ElnaTacOperator.subtract); elna_tac_instruction_set_op(instruction, 1u, @inter_operand); elna_tac_instruction_set_const(instruction, 2u, 1u, 4u); elna_tac_instruction_set_op(instruction, 3u, @index_operand); elna_list_append(instructions, cast(instruction: ^ElnaListNode)); elna_tac_designator(instructions, array_access_expression^.array, symbol_table, @operand_result, @inter_operand); elna_tac_make_variable(operand, symbol_table, word_type); elna_tac_copy_address(instructions, @operand_result, @inter_operand, operand); instruction := elna_tac_instruction_create(ElnaTacOperator.add_ptr); elna_tac_instruction_set_op(instruction, 1u, operand); elna_tac_instruction_set_op(instruction, 2u, @index_operand); elna_tac_instruction_set_const(instruction, 3u, elem_size, 4u); elna_tac_instruction_set_op(instruction, 4u, operand); elna_list_append(instructions, cast(instruction: ^ElnaListNode)) return proc elna_tac_designator(instructions: ^ElnaList; parser_node: ^ElnaTreeExpression; symbol_table: ^ElnaSymbolTable; operand_result: ^ElnaTacOperandResult; operand: ^ElnaTacOperand) var field_access_expression: ^ElnaTreeFieldAccessExpression designator_base: ^ElnaTreeExpression begin operand_result^.kind := ElnaTacOperandType.plain_object; operand_result^.offset := 0u; case parser_node^.kind of ElnaTreeKind.dereference_expression: elna_tac_dereference_expression(instructions, cast(parser_node: ^ElnaTreeDereferenceExpression), symbol_table, operand); operand_result^.kind := ElnaTacOperandType.dereferenced_pointer | ElnaTreeKind.field_access_expression: field_access_expression := cast(parser_node: ^ElnaTreeFieldAccessExpression); designator_base := field_access_expression^.aggregate; if designator_base^.type_decoration = nil then elna_tac_enumeration_value(field_access_expression, operand) else elna_tac_field_access_expression(instructions, field_access_expression, symbol_table, operand_result, operand) end | ElnaTreeKind.array_access_expression: elna_tac_array_access_expression(instructions, cast(parser_node: ^ElnaTreeArrayAccessExpression), symbol_table, operand); operand_result^.kind := ElnaTacOperandType.dereferenced_pointer | ElnaTreeKind.call: elna_tac_call(instructions, cast(parser_node: ^ElnaTreeCall), symbol_table, operand) else elna_tac_simple_expression(instructions, parser_node, symbol_table, operand) end return proc elna_tac_assign_statement(instructions: ^ElnaList; parser_tree: ^ElnaTreeAssignStatement; symbol_table: ^ElnaSymbolTable) var operand_result: ElnaTacOperandResult instruction: ^ElnaTacInstruction assignment_operand: ElnaTacOperand assignee: ElnaTacOperand begin elna_tac_designator(instructions, parser_tree^.assignee, symbol_table, @operand_result, @assignee); elna_tac_binary_expression(instructions, parser_tree^.assignment, symbol_table, @assignment_operand); if operand_result.kind = ElnaTacOperandType.dereferenced_pointer then instruction := elna_tac_instruction_create(ElnaTacOperator.store); elna_tac_instruction_set_op(instruction, 1u, @assignment_operand); elna_tac_instruction_set_op(instruction, 2u, @assignee) elsif operand_result.kind = ElnaTacOperandType.sub_object then instruction := elna_tac_instruction_create(ElnaTacOperator.copy_to_offset); elna_tac_instruction_set_op(instruction, 1u, @assignment_operand); elna_tac_instruction_set_op(instruction, 2u, @assignee); elna_tac_instruction_set_const(instruction, 3u, operand_result.offset, 4u) else instruction := elna_tac_instruction_create(ElnaTacOperator.copy); elna_tac_instruction_set_op(instruction, 1u, @assignment_operand); elna_tac_instruction_set_op(instruction, 2u, @assignee) end; elna_list_append(instructions, cast(instruction: ^ElnaListNode)) return proc elna_tac_return_statement(instructions: ^ElnaList; parser_node: ^ElnaTreeReturnStatement; symbol_table: ^ElnaSymbolTable) var instruction: ^ElnaTacInstruction operand: ElnaTacOperand begin instruction := elna_tac_instruction_create(ElnaTacOperator._return); if parser_node^.returned <> nil then elna_tac_binary_expression(instructions, parser_node^.returned, symbol_table, @operand); elna_tac_instruction_set_op(instruction, 1u, @operand) else instruction^.operands[1].kind := ElnaTacKind.ignore end; elna_list_append(instructions, cast(instruction: ^ElnaListNode)) return proc elna_tac_conditional_statements(instructions: ^ElnaList; parser_node: ^ElnaTreeConditionalStatements; after_end_label: []const Word8; symbol_table: ^ElnaSymbolTable) var condition_label: []const Word8 instruction: ^ElnaTacInstruction operand: ElnaTacOperand begin elna_tac_binary_expression(instructions, parser_node^.condition, symbol_table, @operand); condition_label := elna_tac_label(); instruction := elna_tac_instruction_create(ElnaTacOperator.jump_if_zero); elna_tac_instruction_set_op(instruction, 1u, @operand); elna_tac_instruction_set_label(instruction, 2u, condition_label); elna_list_append(instructions, cast(instruction: ^ElnaListNode)); elna_tac_statements(instructions, parser_node^.statements, symbol_table); instruction := elna_tac_instruction_create(ElnaTacOperator.jump); elna_tac_instruction_set_label(instruction, 1u, after_end_label); elna_list_append(instructions, cast(instruction: ^ElnaListNode)); instruction := elna_tac_instruction_create(ElnaTacOperator.label); elna_tac_instruction_set_label(instruction, 1u, condition_label); elna_list_append(instructions, cast(instruction: ^ElnaListNode)) return proc elna_tac_if_statement(instructions: ^ElnaList; parser_node: ^ElnaTreeIfStatement; symbol_table: ^ElnaSymbolTable) var current_node: ^ElnaTreeConditionalStatements after_end_label: []const Word8 instruction: ^ElnaTacInstruction begin after_end_label := elna_tac_label(); current_node := parser_node^.conditionals; while current_node <> nil do elna_tac_conditional_statements(instructions, current_node, after_end_label, symbol_table); current_node := current_node^.next end; if parser_node^._else <> nil then elna_tac_statements(instructions, parser_node^._else, symbol_table) end; instruction := elna_tac_instruction_create(ElnaTacOperator.label); elna_tac_instruction_set_label(instruction, 1u, after_end_label); elna_list_append(instructions, cast(instruction: ^ElnaListNode)) return proc elna_tac_statement(instructions: ^ElnaList; parser_node: ^ElnaTreeStatement; symbol_table: ^ElnaSymbolTable) var operand: ElnaTacOperand call_statement: ^ElnaTreeCallStatement begin case parser_node^.kind of ElnaTreeKind.goto_statement: elna_tac_goto_statement(instructions, cast(parser_node: ^ElnaTreeGotoStatement)) | ElnaTreeKind.if_statement: elna_tac_if_statement(instructions, cast(parser_node: ^ElnaTreeIfStatement), symbol_table) | ElnaTreeKind.return_statement: elna_tac_return_statement(instructions, cast(parser_node: ^ElnaTreeReturnStatement), symbol_table) | ElnaTreeKind.label_declaration: elna_tac_label_declaration(instructions, cast(parser_node: ^ElnaTreeLabelDeclaration)) | ElnaTreeKind.call_statement: call_statement := cast(parser_node: ^ElnaTreeCallStatement); elna_tac_call(instructions, call_statement^.call, symbol_table, @operand) | ElnaTreeKind.assign_statement: elna_tac_assign_statement(instructions, cast(parser_node: ^ElnaTreeAssignStatement), symbol_table) else end return proc elna_tac_statements(instructions: ^ElnaList; current_statement: ^ElnaTreeStatement; symbol_table: ^ElnaSymbolTable) var current: ^ElnaTreeStatement begin current := current_statement; while current <> nil do elna_tac_statement(instructions, current, symbol_table); current := current^.next end return proc elna_tac_parameters(ast_list: ^ElnaTreeDeclaration; parameter_count: ^Word) -> ^[]const Word8 var ast_parameter: ^ElnaTreeDeclaration parameter_list: ^[]const Word8 count: Word := 0u i: Word := 0u begin ast_parameter := ast_list; while ast_parameter <> nil do count := count + 1u; ast_parameter := ast_parameter^.next end; parameter_count^ := count; parameter_list := malloc#[[]const Word8](count * #size([]const Word8)); ast_parameter := ast_list; while i < count do (parameter_list + i)^ := ast_parameter^.name; i := i + 1u; ast_parameter := ast_parameter^.next end return parameter_list proc elna_tac_procedure_declaration(parser_node: ^ElnaTreeProcedureDeclaration) -> ^ElnaTacProcedure var symbol_info: ^ElnaSymbolInfo procedure_info: ^ElnaSymbolProcedureInfo result: ^ElnaTacProcedure parameter_count: Word := 0u begin result := malloc#[ElnaTacProcedure](#size(ElnaTacProcedure)); result^.next := nil; elna_list_initialize(@result^.body); result^.name := parser_node^.name; symbol_info := elna_symbol_table_lookup(symbol_table_global, parser_node^.name); if symbol_info <> nil & symbol_info^.kind = ElnaSymbolInfoKind.procedure_info then procedure_info := cast(symbol_info: ^ElnaSymbolProcedureInfo); result^.symbol_table := procedure_info^.symbol_table else result^.symbol_table := symbol_table_global end; result^.parameters := elna_tac_parameters(parser_node^.parameters, @parameter_count); result^.count := parameter_count; pseudo_counter := 0u; elna_tac_statements(@result^.body, parser_node^.body, result^.symbol_table) return result proc elna_tac_procedures(parser_node: ^ElnaTreeProcedureDeclaration) -> ^ElnaTacProcedure var result: ^ElnaTacProcedure current_procedure: ^ElnaTacProcedure := nil first_procedure: ^ElnaTacProcedure := nil current_node: ^ElnaTreeProcedureDeclaration begin current_node := parser_node; while current_node <> nil do if ~current_node^.is_extern then result := elna_tac_procedure_declaration(current_node); if first_procedure = nil then first_procedure := result else current_procedure^.next := result end; current_procedure := result end; current_node := cast(current_node^.next: ^ElnaTreeProcedureDeclaration) end return first_procedure proc elna_tac_variable_declaration(parser_tree: ^ElnaTreeVariableDeclaration) -> ^ElnaTacStaticVariable var result: ^ElnaTacStaticVariable symbol_info: ^ElnaSymbolInfo temp_info: ^ElnaSymbolTemporaryInfo begin result := malloc#[ElnaTacStaticVariable](#size(ElnaTacStaticVariable)); result^.next := nil; result^.name := parser_tree^.name; result^.size := 4u; symbol_info := elna_symbol_table_lookup(symbol_table_global, parser_tree^.name); if symbol_info <> nil & symbol_info^.kind = ElnaSymbolInfoKind.temporary_info then temp_info := cast(symbol_info: ^ElnaSymbolTemporaryInfo); if temp_info^.variable_type <> nil then result^.size := temp_info^.variable_type^.size end end return result proc elna_tac_var_part(parser_node: ^ElnaTreeDeclaration) -> ^ElnaTacStaticVariable var node: ^ElnaTacStaticVariable current_variable: ^ElnaTacStaticVariable := nil first_variable: ^ElnaTacStaticVariable := nil current_node: ^ElnaTreeDeclaration begin current_node := parser_node; while current_node <> nil do node := elna_tac_variable_declaration(cast(current_node: ^ElnaTreeVariableDeclaration)); if first_variable = nil then first_variable := node else current_variable^.next := node end; current_variable := node; current_node := current_node^.next end return first_variable proc elna_tac_program_body(parser_node: ^ElnaTreeStatement) -> ^ElnaTacProcedure var result: ^ElnaTacProcedure symbol_info: ^ElnaSymbolInfo procedure_info: ^ElnaSymbolProcedureInfo begin result := malloc#[ElnaTacProcedure](#size(ElnaTacProcedure)); result^.next := nil; elna_list_initialize(@result^.body); result^.name := "main"; symbol_info := elna_symbol_table_lookup(symbol_table_global, result^.name); if symbol_info <> nil & symbol_info^.kind = ElnaSymbolInfoKind.procedure_info then procedure_info := cast(symbol_info: ^ElnaSymbolProcedureInfo); result^.symbol_table := procedure_info^.symbol_table else result^.symbol_table := symbol_table_global end; result^.parameters := nil; result^.count := 0u; pseudo_counter := 0u; elna_tac_statements(@result^.body, parser_node, result^.symbol_table) return result proc elna_tac_module_declaration*(parser_node: ^ElnaTreeModuleDeclaration) -> ^ElnaInstructionModule var result: ^ElnaInstructionModule last_proc: ^ElnaTacProcedure body_proc: ^ElnaTacProcedure begin result := malloc#[ElnaInstructionModule](#size(ElnaInstructionModule)); result^.strings := module_strings_first; result^.data := elna_tac_var_part(parser_node^.globals); result^.code := elna_tac_procedures(parser_node^.procedures); if parser_node^.body <> nil then body_proc := elna_tac_program_body(parser_node^.body); if result^.code = nil then result^.code := body_proc else last_proc := result^.code; while last_proc^.next <> nil do last_proc := last_proc^.next end; last_proc^.next := body_proc end end; result^.strings := module_strings_first return result end.