(* * 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/. *) program; (* Stage 22 compiler. *) (* - Support 2 word procedure arguments. *) (* - Characters are parsed to a character ASCII code. *) (* - Strings are parsed and their length doesn't count for escaping backslash. *) type ElnaListNode = record next: Word end ElnaList = record first: ^ElnaListNode; last: ^ElnaListNode end ElnaLocation = record line: Word; column: Word end ElnaPosition = record start_location: ElnaLocation; end_location: ElnaLocation end (** * List of intermediate representation items. *) ElnaInstructionModule = record data: Word; code: Word end (* Type representation. *) ElnaTypeKind = (primitive, enumeration, _record, pointer, array) ElnaType = record kind: ElnaTypeKind; size: Word; alignment: Word end ElnaTypeEnumeration = record kind: ElnaTypeKind; size: Word; alignment: Word; members: Word; length: Word end ElnaTypeField = record name: ElnaTypeKind; length: Word; field_type: ^ElnaType end ElnaTypeRecord = record kind: ElnaTypeKind; size: Word; alignment: Word; members: ^ElnaTypeField; length: Word end ElnaTypePointer = record kind: ElnaTypeKind; size: Word; alignment: Word; base: ^ElnaType end ElnaTypeArray = record kind: ElnaTypeKind; size: Word; alignment: Word; base: ^ElnaType; length: Word end (* Abstract syntax tree nodes. *) ElnaTreeKind = ( integer_literal, string_literal, character_literal, boolean_literal, variable_expression, field_access_expression, dereference_expression, unary_expression, binary_expression, call, goto_statement, label_declaration, return_statement, assign_statement, if_statement, procedure_declaration, variable_declaration, enumeration_type_expression, named_type_expression, type_declaration, module_declaration, record_type_expression, pointer_type_expression, array_type_expression, null, trait_expression, array_access_expression, _cast ) ElnaTreeNode = record kind: ElnaTreeKind end ElnaTreeTypeExpression = record kind: ElnaTreeKind end ElnaTreeExpression = record kind: ElnaTreeKind; type_decoration: ^ElnaType end ElnaTreeIntegerLiteral = record kind: ElnaTreeKind; type_decoration: ^ElnaType; value: Word end ElnaTreeCharacterLiteral = record kind: ElnaTreeKind; type_decoration: ^ElnaType; value: ^Char; length: Word end ElnaTreeNilLiteral = record kind: ElnaTreeKind; type_decoration: ^ElnaType end ElnaTreeBooleanLiteral = record kind: ElnaTreeKind; type_decoration: ^ElnaType; value: Word end ElnaTreeStringLiteral = record kind: ElnaTreeKind; type_decoration: ^ElnaType; value: ^Char; length: Word end ElnaTreeVariableExpression = record kind: ElnaTreeKind; type_decoration: ^ElnaType; name: Word; length: Word end ElnaTreeCastExpression = record kind: ElnaTreeKind; type_decoration: ^ElnaType; expression: ^ElnaTreeExpression; type_expression: ^ElnaTreeTypeExpression end ElnaTreeDereferenceExpression = record kind: ElnaTreeKind; type_decoration: ^ElnaType; pointer: Word end ElnaTreeBinaryExpression = record kind: ElnaTreeKind; type_decoration: ^ElnaType; lhs: ^ElnaTreeExpression; rhs: ^ElnaTreeExpression; operator: Word end ElnaTreeUnaryExpression = record kind: ElnaTreeKind; type_decoration: ^ElnaType; operand: ^ElnaTreeExpression; operator: Word end (** * All statements are chained into a list. Next contains a pointer to the next * statement in the statement list. *) ElnaTreeStatement = record kind: ElnaTreeKind; next: Word end ElnaTreeIfStatement = record kind: ElnaTreeKind; next: Word; conditionals: Word; _else: ^ElnaTreeStatement end ElnaTreeGotoStatement = record kind: ElnaTreeKind; next: Word; label: Word; length: Word end ElnaTreeAssignStatement = record kind: ElnaTreeKind; next: Word; assignee: Word; assignment: ^ElnaTreeExpression end ElnaTreeReturnStatement = record kind: ElnaTreeKind; next: Word; returned: ^ElnaTreeExpression end ElnaTreeLabelDeclaration = record kind: ElnaTreeKind; next: Word; label: Word; length: Word end ElnaTreeFieldAccessExpression = record kind: ElnaTreeKind; type_decoration: ^ElnaType; aggregate: Word; field: Word; length: Word end ElnaTreeArrayAccessExpression = record kind: ElnaTreeKind; type_decoration: ^ElnaType; array: Word; index: ^ElnaTreeExpression end ElnaTreeEnumeration = record name: Word; length: Word; next: Word end ElnaTreeEnumerationTypeExpression = record kind: ElnaTreeKind; members: Word; length: Word end ElnaTreeField = record name: Word; length: Word; type_expression: ^ElnaTreeTypeExpression; next: Word end ElnaTreeRecordTypeExpression = record kind: ElnaTreeKind; members: ^ElnaTreeField; length: Word end ElnaTreeNamedTypeExpression = record kind: ElnaTreeKind; name: Word; length: Word end ElnaTreePointerTypeExpression = record kind: ElnaTreeKind; base: ^ElnaTreeTypeExpression end ElnaTreeArrayTypeExpression = record kind: ElnaTreeKind; base: ^ElnaTreeTypeExpression; length: Word end ElnaTreeTraitExpression = record kind: ElnaTreeKind; name: Word; length: Word; argument: Word end (** * Conditional statements is a list of pairs: condition and statements. * Used for example to represent if and elsif blocks with beloning statements. *) ElnaTreeConditionalStatements = record condition: ^ElnaTreeExpression; statements: ^ElnaTreeStatement; next: Word end ElnaTreeDeclaration = record kind: ElnaTreeKind; next: Word; name: Word; length: Word end ElnaTreeProcedureDeclaration = record kind: ElnaTreeKind; next: Word; name: Word; length: Word; body: ^ElnaTreeStatement; temporaries: Word; parameters: Word; return_type: ^ElnaTreeTypeExpression end ElnaTreeModuleDeclaration = record kind: ElnaTreeKind; types: Word; globals: Word; procedures: ^ElnaTreeProcedureDeclaration; body: ^ElnaTreeStatement end ElnaTreeTypeDeclaration = record kind: ElnaTreeKind; next: Word; name: Word; length: Word; type_expression: ^ElnaTreeTypeExpression end ElnaTreeVariableDeclaration = record kind: ElnaTreeKind; next: Word; name: Word; length: Word; type_expression: Word end ElnaTreeExpressionList = record expression: ^ElnaTreeBinaryExpression; next: Word end ElnaTreeCall = record kind: ElnaTreeKind; next: Word; callee: ^ElnaTreeVariableExpression; arguments: ^ElnaTreeExpressionList; count: Word end (* Symbol table information. *) ElnaSymbolEntry = record name: Word; length: Word; symbol_info: Word end ElnaSymbolTable = record count: Word; parent: Word; symbols: ^ElnaSymbolEntry end ElnaSymbolInfoKind = (type_info, temporary_info, procedure_info) ElnaSymbolInfo = record kind: ElnaSymbolInfoKind end ElnaSymbolTypeInfo = record kind: ElnaSymbolInfoKind; _type: ^ElnaType end ElnaSymbolTemporaryInfo = record kind: ElnaSymbolInfoKind; attr: Word; variable_type: ^ElnaType end ElnaSymbolProcedureInfo = record kind: ElnaSymbolInfoKind; symbol_table: ^ElnaSymbolTable end ElnaError = record next: Word end ElnaLexerAction = (none, accumulate, skip, single, eof, finalize, composite, key_id, integer, delimited) ElnaLexerState = ( start, colon, identifier, decimal, leading_zero, greater, minus, left_paren, less, dot, comment, closing_comment, character, character_escape, string, string_escape, number_sign, trait, finish ) ElnaLexerTransition = record action: ElnaLexerAction; next_state: ElnaLexerState end (** * Classification table assigns each possible character to a group (class). All * characters of the same group a handled equivalently. *) ElnaLexerClass = ( invalid, digit, alpha, space, colon, equals, left_paren, right_paren, asterisk, backslash, single, hex, zero, x, eof, dot, minus, single_quote, double_quote, greater, less, other, number_sign ) ElnaLexerKind = ( identifier, _const, _var, _proc, _type, _begin, _end, _if, _then, _else, _elsif, _while, _do, _extern, _record, boolean, null, and, _or, _xor, pipe, not, _return, _module, _program, _import, _cast, _defer, _case, _of, trait, left_paren, right_paren, left_square, right_square, shift_left, shift_right, greater_equal, less_equal, greater_than, less_than, not_equal, equals, semicolon, dot, comma, plus, arrow, minus, multiplication, division, remainder, assignment, colon, hat, at, comment, string, character, integer, word, _goto, eof ) ElnaLexerToken = record kind: ElnaLexerKind; start: Word; length: Word; position: ElnaPosition end ElnaLexerCursor = record state: ElnaLexerState; start: ^Char; finish: ^Char; token: ^ElnaLexerToken; position: ElnaPosition end 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) (* - If value is a variable or label, length is its name length. - If value is a constant, length is its size. - If value is a list, length is the list length. *) ElnaTacOperand = record kind: ElnaTacKind; value: Word; length: Word end ElnaTacInstruction = record next: Word; operator: ElnaTacOperator; operands: [4]ElnaTacOperand end ElnaTacProcedure = record next: Word; name: Word; length: Word; body: ElnaList; parameters: Word; count: Word; symbol_table: ^ElnaSymbolTable end ElnaTacStaticVariable = record next: Word; name: Word; length: Word; body: Word end ElnaTacOperandType = (plain_object, dereferenced_pointer, sub_object) ElnaTacOperandResult = record kind: ElnaTacOperandType; offset: Word end ElnaRtlOperator = ( li, la, add, addi, lw, sw, jal, mv, sub, div, rem, mul, _xor, _or, and, seqz, snez, slt, xori, neg, not, j, beqz, bnez, label, allocate_stack, ret, nop ) ElnaRtlKind = (register, immediate, data, pseudo, memory, pseudo_mem) ElnaRtlOperand = record kind: ElnaRtlKind; value: Word; length: Word; offset: Word end ElnaRtlStaticVariable = record next: Word; name: Word; length: Word; body: Word end ElnaRtlProcedure = record next: Word; name: Word; length: Word; body: ElnaList; variable_map: ^ElnaSymbolTable end ElnaRtlTypeKind = (byte, long_word, byte_array) ElnaRtlType = record kind: ElnaRtlTypeKind end ElnaRtlTypeWord = record kind: ElnaRtlTypeKind end ElnaRtlTypeByteArray = record kind: ElnaRtlTypeKind; size: Word; alignment: Word end ElnaRtlInstruction = record next: Word; operator: ElnaRtlOperator; operands: [3]ElnaRtlOperand; types: [2]^ElnaRtlType end ElnaRtlRegister = ( zero, ra, sp, gp, tp, t0, t1, t2, s0, s1, a0, a1, a2, a3, a4, a5, a6, a7, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, t3, t4, t5, t6 ) ElnaRtlInfoKind = (object_info, static_info) ElnaRtlInfo = record kind: ElnaRtlInfoKind end ElnaRtlStaticInfo = record kind: ElnaRtlInfoKind; rtl_type: ^ElnaRtlType end ElnaRtlObjectInfo = record kind: ElnaRtlInfoKind; rtl_type: ^ElnaRtlType; counter: Word; allocated: Bool end var symbol_table_global: ^ElnaSymbolTable variable_map_global: ^ElnaSymbolTable compiler_strings: [4096]Char classification: [256]Word transition_table: [19][23]ElnaLexerTransition word_type: ^ElnaType char_type: ^ElnaType bool_type: ^ElnaType string_type: ^ElnaTypeRecord source_code: Word compiler_strings_position: ^Char compiler_strings_length: Word label_counter: Word temporary_variable_counter: Word pseudo_counter: Word (** * Adds a string to the global, read-only string storage. * * Parameters: * string - String token. * string_length - String length. * * Returns the offset from the beginning of the storage to the new string. *) proc elna_tac_add_string(string: ^Char, string_length: Word) -> Word var result: Word current_byte: Char current_target: ^Char begin result := compiler_strings_length; (* Global character pointer is appearently dereferenced wrongly. *) current_target := compiler_strings_position; .add_string_loop; current_byte := string^; if current_byte = 92 then current_target^ := '\\'; current_target := current_target + 1; current_target^ := '\\' elsif current_byte = 10 then current_target^ := '\\'; current_target := current_target + 1; current_target^ := 'n' elsif current_byte = 9 then current_target^ := '\\'; current_target := current_target + 1; current_target^ := 't' elsif current_byte = 12 then current_target^ := '\\'; current_target := current_target + 1; current_target^ := 'f' elsif current_byte = 13 then current_target^ := '\\'; current_target := current_target + 1; current_target^ := 'r' elsif current_byte = 11 then current_target^ := '\\'; current_target := current_target + 1; current_target^ := 'v' elsif current_byte = 34 then current_target^ := '\\'; current_target := current_target + 1; current_target^ := '"' elsif current_byte = 39 then current_target^ := '\\'; current_target := current_target + 1; current_target^ := '\'' elsif current_byte = 0 then current_target^ := '\\'; current_target := current_target + 1; current_target^ := '0' else current_target^ := current_byte end; current_target := current_target + 1; string := string + 1; string_length := string_length - 1; compiler_strings_length := compiler_strings_length + 1; if string_length > 0 then goto add_string_loop end; compiler_strings_position := current_target; return result end (** * Writes to the standard output. * * Parameters: * buffer - Buffer. * size - Buffer length. *) proc _write_s(buffer: Word, size: Word) begin write(1, buffer, size) end (** * Writes a number to the standard output. * * Parameters: * number - Whole number. *) proc _write_i(number: Word) begin printf("%i\0".ptr, number); fflush(nil) end (** * Writes a character from a0 into the standard output. * * Parameters: * character - Character to write. *) proc _write_c(character: Word) begin write(1, @character, 1) end proc elna_list_initialize(list: ^ElnaList) begin list^.first := nil; list^.last := nil end (** * Appends a new element to the end of the list. *) proc elna_list_append(list: ^ElnaList, element: ^ElnaListNode) -> ^ElnaListNode begin if element <> nil then if list^.first = nil then list^.first := element else list^.last^.next := element end; list^.last := element end; return element end (** * Prepends a new element to the beginning of the given list. *) proc elna_list_prepend(list: ^ElnaList, element: ^ElnaListNode) begin element^.next := list^.first; list^.first := element; if list^.last = nil then list^.last := element end end (** * Inserts a new element after an existing one into the given list. *) proc elna_list_insert(list: ^ElnaList, existing: ^ElnaListNode, new: ^ElnaListNode) begin if existing^.next = nil then list^.last := new else new^.next := existing^.next end; existing^.next := new end proc elna_tac_make_variable(operand: ^ElnaTacOperand, symbol_table: ^ElnaSymbolTable, variable_type: ^ElnaType) var buffer: Word temporary_info: ^ElnaSymbolTemporaryInfo begin pseudo_counter := pseudo_counter + 1; buffer := malloc(7); sprintf(buffer, "$a%i\0".ptr, pseudo_counter); operand^.kind := ElnaTacKind.variable; operand^.value := buffer; operand^.length := strlen(buffer); temporary_info := temporary_info_create(1, variable_type); elna_symbol_table_enter(symbol_table, buffer, operand^.length, temporary_info) end proc elna_tac_instruction_set_operand(this: ^ElnaTacInstruction, n: Word, operand_type: Word, operand_value: Word, operand_length: Word) begin this^.operands[n].kind := operand_type; this^.operands[n].value := operand_value; this^.operands[n].length := operand_length end proc elna_tac_instruction_create(kind: Word) -> ^ElnaTacInstruction var result: ^ElnaTacInstruction begin result := malloc(#size(ElnaTacInstruction)); result^.operator := kind; result^.next := nil; return result end proc elna_rtl_instruction_create(operator: ElnaRtlOperator) -> ^ElnaRtlInstruction var result: ^ElnaRtlInstruction begin result := malloc(#size(ElnaRtlInstruction)); result^.operator := operator; result^.next := nil; result^.types[1] := nil; result^.types[2] := nil; return result end proc elna_rtl_instruction_set_operand(this: ^ElnaRtlInstruction, n: Word, operand_type: Word, operand_value: Word, operand_length: Word, operand_offset: Word) begin this^.operands[n].kind := operand_type; this^.operands[n].value := operand_value; this^.operands[n].length := operand_length; this^.operands[n].offset := operand_offset end (** * Loads or moves the value from a TAC operand into the given register. *) proc elna_rtl_hardware_value(instructions: ^ElnaList, operand: ^ElnaTacOperand, into: ElnaRtlRegister, variable_map: ^ElnaSymbolTable) -> Word var instruction: ^ElnaRtlInstruction pseudo_symbol: ^ElnaRtlObjectInfo target_operand: ElnaRtlOperand long_word_type: ^ElnaRtlType registers_used: Word begin registers_used := 1; if operand^.kind = ElnaTacKind.constant then instruction := elna_rtl_instruction_create(ElnaRtlOperator.li); elna_rtl_instruction_set_operand(instruction, 1, ElnaRtlKind.register, into, 0, 0); elna_rtl_instruction_set_operand(instruction, 2, ElnaRtlKind.immediate, operand^.value, 0, 0); elna_list_append(instructions, instruction) elsif operand^.kind = ElnaTacKind.variable then pseudo_symbol := elna_symbol_table_lookup(variable_map, operand^.value, operand^.length); long_word_type := elna_rtl_constant_type(4); if pseudo_symbol^.rtl_type^.kind = ElnaRtlTypeKind.byte_array then target_operand.kind := ElnaRtlKind.register; target_operand.value := ElnaRtlRegister.t0; target_operand.length := 0; target_operand.offset := 0; instruction := elna_rtl_variable_address(variable_map, pseudo_symbol, operand, @target_operand); elna_list_append(instructions, instruction); instruction := elna_rtl_instruction_create(ElnaRtlOperator.lw); elna_rtl_instruction_set_operand(instruction, 1, ElnaRtlKind.register, into, 0, 0); elna_rtl_instruction_set_operand(instruction, 2, ElnaRtlKind.memory, target_operand.value, target_operand.length, 0); instruction^.types[1] := long_word_type; elna_list_append(instructions, instruction); instruction := elna_rtl_instruction_create(ElnaRtlOperator.lw); elna_rtl_instruction_set_operand(instruction, 1, ElnaRtlKind.register, into + 1, 0, 0); elna_rtl_instruction_set_operand(instruction, 2, ElnaRtlKind.memory, target_operand.value, target_operand.length, 4); instruction^.types[1] := long_word_type; elna_list_append(instructions, instruction); registers_used := 2 else instruction := elna_rtl_instruction_create(ElnaRtlOperator.mv); elna_rtl_instruction_set_operand(instruction, 1, ElnaRtlKind.register, into, 0, 0); elna_rtl_instruction_set_operand(instruction, 2, ElnaRtlKind.pseudo, operand^.value, operand^.length, 0); instruction^.types[1] := long_word_type; elna_list_append(instructions, instruction) end end; return registers_used end proc elna_rtl_operand_value(instructions: ^ElnaList, tac_operand: ^ElnaTacOperand, variable_map: ^ElnaSymbolTable, rtl_operand: ^ElnaRtlOperand) -> ^ElnaRtlInfo var instruction: ^ElnaRtlInstruction pseudo_symbol: ^ElnaRtlObjectInfo rtl_type: ^ElnaRtlType begin pseudo_symbol := nil; if tac_operand^.kind = ElnaTacKind.constant then rtl_type := elna_rtl_constant_type(tac_operand^.length); pseudo_symbol := elna_rtl_generate_pseudo(rtl_operand, variable_map, rtl_type); instruction := elna_rtl_instruction_create(ElnaRtlOperator.li); elna_rtl_instruction_set_operand(instruction, 1, rtl_operand^.kind, rtl_operand^.value, rtl_operand^.length, 0); elna_rtl_instruction_set_operand(instruction, 2, ElnaRtlKind.immediate, tac_operand^.value, 0, 0); instruction^.types[1] := rtl_type; elna_list_append(instructions, instruction) elsif tac_operand^.kind = ElnaTacKind.variable then pseudo_symbol := elna_symbol_table_lookup(variable_map, tac_operand^.value, tac_operand^.length); if pseudo_symbol^.rtl_type^.kind = ElnaRtlTypeKind.byte_array then rtl_operand^.kind := ElnaRtlKind.pseudo_mem; rtl_operand^.value := tac_operand^.value; rtl_operand^.length := tac_operand^.length; rtl_operand^.offset := 0 else rtl_operand^.kind := ElnaRtlKind.pseudo; rtl_operand^.value := tac_operand^.value; rtl_operand^.length := tac_operand^.length; rtl_operand^.offset := 0 end end; return pseudo_symbol end proc elna_rtl_binary_arithmetic(instructions: ^ElnaList, tac_instruction: ^ElnaTacInstruction, operation: ElnaRtlOperator, variable_map: ^ElnaSymbolTable) -> ^ElnaRtlInstruction var result: ^ElnaRtlInstruction rtl_operand: ElnaRtlOperand begin result := elna_rtl_instruction_create(operation); elna_rtl_instruction_set_operand(result, 1, ElnaRtlKind.pseudo, tac_instruction^.operands[3].value, tac_instruction^.operands[3].length, 0); elna_rtl_operand_value(instructions, @tac_instruction^.operands[1], variable_map, @rtl_operand); elna_rtl_instruction_set_operand(result, 2, rtl_operand.kind, rtl_operand.value, rtl_operand.length, 0); elna_rtl_operand_value(instructions, @tac_instruction^.operands[2], variable_map, @rtl_operand); elna_rtl_instruction_set_operand(result, 3, rtl_operand.kind, rtl_operand.value, rtl_operand.length, 0); elna_list_append(instructions, result); return result end proc elna_rtl_add_ptr(instructions: ^ElnaList, tac_instruction: ^ElnaTacInstruction, variable_map: ^ElnaSymbolTable) var result: ^ElnaRtlInstruction rtl_operand: ElnaRtlOperand intermediate: ElnaRtlOperand rtl_type: ^ElnaRtlType begin rtl_type := malloc(#size(ElnaRtlTypeWord)); rtl_type^.kind := ElnaRtlTypeKind.long_word; elna_rtl_generate_pseudo(@intermediate, variable_map, rtl_type); result := elna_rtl_instruction_create(ElnaRtlOperator.li); elna_rtl_instruction_set_operand(result, 1, intermediate.kind, intermediate.value, intermediate.length, 0); elna_rtl_instruction_set_operand(result, 2, ElnaRtlKind.immediate, tac_instruction^.operands[3].value, 0, 0); result^.types[1] := rtl_type; elna_list_append(instructions, result); result := elna_rtl_instruction_create(ElnaRtlOperator.mul); elna_rtl_instruction_set_operand(result, 1, intermediate.kind, intermediate.value, intermediate.length, 0); elna_rtl_instruction_set_operand(result, 2, intermediate.kind, intermediate.value, intermediate.length, 0); elna_rtl_operand_value(instructions, @tac_instruction^.operands[2], variable_map, @rtl_operand); elna_rtl_instruction_set_operand(result, 3, rtl_operand.kind, rtl_operand.value, rtl_operand.length, 0); elna_list_append(instructions, result); result := elna_rtl_instruction_create(ElnaRtlOperator.add); elna_rtl_instruction_set_operand(result, 1, ElnaRtlKind.pseudo, tac_instruction^.operands[4].value, tac_instruction^.operands[4].length, 0); elna_rtl_operand_value(instructions, @tac_instruction^.operands[1], variable_map, @rtl_operand); elna_rtl_instruction_set_operand(result, 2, rtl_operand.kind, rtl_operand.value, rtl_operand.length, 0); elna_rtl_instruction_set_operand(result, 3, intermediate.kind, intermediate.value, intermediate.length, 0); elna_list_append(instructions, result) end proc elna_rtl_binary_equality(instructions: ^ElnaList, tac_instruction: ^ElnaTacInstruction, instruction_kind: ElnaRtlOperator, variable_map: ^ElnaSymbolTable) var instruction: ^ElnaRtlInstruction target_operand: ^ElnaRtlOperand begin instruction := elna_rtl_binary_arithmetic(instructions, tac_instruction, ElnaRtlOperator._xor, variable_map); target_operand := @instruction^.operands[1]; instruction := elna_rtl_instruction_create(instruction_kind); elna_rtl_instruction_set_operand(instruction, 1, target_operand^.kind, target_operand^.value, target_operand^.length, 0); elna_rtl_instruction_set_operand(instruction, 2, target_operand^.kind, target_operand^.value, target_operand^.length, 0); elna_list_append(instructions, instruction) end proc elna_rtl_set_less_than(instructions: ^ElnaList, tac_instruction: ^ElnaTacInstruction, lhs: Word, rhs: Word, variable_map: ^ElnaSymbolTable) -> ^ElnaRtlInstruction var slt_instruction: ^ElnaRtlInstruction rtl_operand: ElnaRtlOperand begin slt_instruction := elna_rtl_instruction_create(ElnaRtlOperator.slt); elna_rtl_instruction_set_operand(slt_instruction, 1, ElnaRtlKind.pseudo, tac_instruction^.operands[3].value, tac_instruction^.operands[3].length, 0); elna_rtl_operand_value(instructions, @tac_instruction^.operands[1], variable_map, @rtl_operand); elna_rtl_instruction_set_operand(slt_instruction, lhs, rtl_operand.kind, rtl_operand.value, rtl_operand.length, 0); elna_rtl_operand_value(instructions, @tac_instruction^.operands[2], variable_map, @rtl_operand); elna_rtl_instruction_set_operand(slt_instruction, rhs, rtl_operand.kind, rtl_operand.value, rtl_operand.length, 0); elna_list_append(instructions, slt_instruction); return slt_instruction end proc elna_rtl_binary_comparison(instructions: ^ElnaList, tac_instruction: ^ElnaTacInstruction, lhs: Word, rhs: Word, variable_map: ^ElnaSymbolTable) var slt_instruction: ^ElnaRtlInstruction xor_instruction: ^ElnaRtlInstruction rtl_operand: ElnaRtlOperand begin slt_instruction := elna_rtl_set_less_than(instructions, tac_instruction, lhs, rhs, variable_map); xor_instruction := elna_rtl_instruction_create(ElnaRtlOperator.xori); elna_rtl_instruction_set_operand(xor_instruction, 1, slt_instruction^.operands[1].kind, slt_instruction^.operands[1].value, slt_instruction^.operands[1].length, 0); elna_rtl_instruction_set_operand(xor_instruction, 2, slt_instruction^.operands[1].kind, slt_instruction^.operands[1].value, slt_instruction^.operands[1].length, 0); elna_rtl_instruction_set_operand(xor_instruction, 3, ElnaRtlKind.immediate, 1, 0, 0); elna_list_append(instructions, xor_instruction) end proc elna_rtl_variable_address(variable_map: ^ElnaSymbolTable, pseudo_symbol: ^ElnaRtlObjectInfo, addressable: ^ElnaTacOperand, target_operand: ^ElnaRtlOperand) -> ^ElnaRtlInstruction var instruction: ^ElnaRtlInstruction begin instruction := elna_rtl_instruction_create(ElnaRtlOperator.la); elna_rtl_instruction_set_operand(instruction, 1, target_operand^.kind, target_operand^.value, target_operand^.length, 0); (* "strings" variable with static strings is in the end object and should be addressable, but not in the symbol table. *) if pseudo_symbol = nil then elna_rtl_instruction_set_operand(instruction, 2, ElnaRtlKind.data, addressable^.value, addressable^.length, 0) elsif pseudo_symbol^.kind = ElnaRtlInfoKind.static_info then elna_rtl_instruction_set_operand(instruction, 2, ElnaRtlKind.data, addressable^.value, addressable^.length, 0) elsif pseudo_symbol^.rtl_type^.kind = ElnaRtlTypeKind.byte_array then elna_rtl_instruction_set_operand(instruction, 2, ElnaRtlKind.pseudo_mem, addressable^.value, addressable^.length, 0) else elna_rtl_instruction_set_operand(instruction, 2, ElnaRtlKind.pseudo, addressable^.value, addressable^.length, 0) end; return instruction end proc elna_rtl_operand_address(variable_map: ^ElnaSymbolTable, addressable: ^ElnaTacOperand, target_operand: ^ElnaRtlOperand) -> ^ElnaRtlInstruction var pseudo_symbol: ^ElnaRtlObjectInfo instruction: ^ElnaRtlInstruction begin pseudo_symbol := elna_symbol_table_lookup(variable_map, addressable^.value, addressable^.length); return elna_rtl_variable_address(variable_map, pseudo_symbol, addressable, target_operand) end proc elna_rtl_get_address(instructions: ^ElnaList, tac_instruction: ^ElnaTacInstruction, variable_map: ^ElnaSymbolTable) var instruction: ^ElnaRtlInstruction rtl_operand: ElnaRtlOperand begin rtl_operand.kind := ElnaRtlKind.pseudo; rtl_operand.value := tac_instruction^.operands[2].value; rtl_operand.length := tac_instruction^.operands[2].length; instruction := elna_rtl_operand_address(variable_map, @tac_instruction^.operands[1], @rtl_operand); elna_list_append(instructions, instruction) end proc elna_rtl_conditional_jump(instructions: ^ElnaList, tac_instruction: ^ElnaTacInstruction, condition: ElnaRtlOperator, variable_map: ^ElnaSymbolTable) var instruction: ^ElnaRtlInstruction rtl_operand: ElnaRtlOperand begin instruction := elna_rtl_instruction_create(condition); elna_rtl_operand_value(instructions, @tac_instruction^.operands[1], variable_map, @rtl_operand); elna_rtl_instruction_set_operand(instruction, 1, rtl_operand.kind, rtl_operand.value, rtl_operand.length, 0); elna_rtl_instruction_set_operand(instruction, 2, ElnaRtlKind.data, tac_instruction^.operands[2].value, tac_instruction^.operands[2].length, 0); elna_list_append(instructions, instruction) end proc elna_rtl_unary(instructions: ^ElnaList, tac_instruction: ^ElnaTacInstruction, rtl_operator: ElnaRtlOperator, variable_map: ^ElnaSymbolTable) var instruction: ^ElnaRtlInstruction rtl_operand: ElnaRtlOperand begin elna_rtl_operand_value(instructions, @tac_instruction^.operands[1], variable_map, @rtl_operand); instruction := elna_rtl_instruction_create(rtl_operator); elna_rtl_instruction_set_operand(instruction, 1, ElnaRtlKind.pseudo, tac_instruction^.operands[2].value, tac_instruction^.operands[2].length, 0); elna_rtl_instruction_set_operand(instruction, 2, rtl_operand.kind, rtl_operand.value, rtl_operand.length, 0); elna_list_append(instructions, instruction) end proc elna_rtl_call(instructions: ^ElnaList, tac_instruction: ^ElnaTacInstruction, variable_map: ^ElnaSymbolTable) var argument_count: Word current_argument: ^ElnaTacOperand instruction: ^ElnaRtlInstruction current_register: Word registers_used: Word begin current_register := 0; current_argument := tac_instruction^.operands[2].value; argument_count := tac_instruction^.operands[2].length; .elna_rtl_call_loop; if argument_count > 0 then argument_count := argument_count - 1; registers_used := elna_rtl_hardware_value(instructions, current_argument, ElnaRtlRegister.a0 + current_register, variable_map); current_argument := current_argument + 1; current_register := current_register + registers_used; goto elna_rtl_call_loop end; instruction := elna_rtl_instruction_create(ElnaRtlOperator.jal); elna_rtl_instruction_set_operand(instruction, 1, ElnaRtlKind.data, tac_instruction^.operands[1].value, tac_instruction^.operands[1].length, 0); elna_list_append(instructions, instruction); instruction := elna_rtl_instruction_create(ElnaRtlOperator.mv); elna_rtl_instruction_set_operand(instruction, 1, tac_instruction^.operands[3].kind, tac_instruction^.operands[3].value, tac_instruction^.operands[3].length, 0); elna_rtl_instruction_set_operand(instruction, 2, ElnaRtlKind.register, ElnaRtlRegister.a0, 0, 0); elna_list_append(instructions, instruction) end proc elna_rtl_store(instructions: ^ElnaList, tac_instruction: ^ElnaTacInstruction, variable_map: ^ElnaSymbolTable) var instruction: ^ElnaRtlInstruction rtl_operand: ElnaRtlOperand pseudo_symbol: ^ElnaRtlObjectInfo rtl_type: ^ElnaRtlType begin if tac_instruction^.operands[1].kind = ElnaTacKind.variable then pseudo_symbol := elna_symbol_table_lookup(variable_map, tac_instruction^.operands[1].value, tac_instruction^.operands[1].length); rtl_type := pseudo_symbol^.rtl_type else rtl_type := elna_rtl_constant_type(tac_instruction^.operands[1].length) end; elna_rtl_operand_value(instructions, @tac_instruction^.operands[1], variable_map, @rtl_operand); if tac_instruction^.operands[1].kind = ElnaTacKind.variable then if rtl_type^.kind = ElnaRtlTypeKind.byte_array then instruction := elna_rtl_instruction_create(ElnaRtlOperator.mv); elna_rtl_instruction_set_operand(instruction, 1, ElnaRtlKind.register, ElnaRtlRegister.a0, 0, 0); elna_rtl_instruction_set_operand(instruction, 2, ElnaRtlKind.pseudo, tac_instruction^.operands[2].value, tac_instruction^.operands[2].length, 0); elna_list_append(instructions, instruction); instruction := elna_rtl_instruction_create(ElnaRtlOperator.la); elna_rtl_instruction_set_operand(instruction, 1, ElnaRtlKind.register, ElnaRtlRegister.a1, 0, 0); elna_rtl_instruction_set_operand(instruction, 2, rtl_operand.kind, rtl_operand.value, rtl_operand.length, 0); elna_list_append(instructions, instruction); elna_rtl_memcpy(instructions, pseudo_symbol^.rtl_type); rtl_type := nil end end; if rtl_type <> nil then instruction := elna_rtl_instruction_create(ElnaRtlOperator.sw); elna_rtl_instruction_set_operand(instruction, 1, rtl_operand.kind, rtl_operand.value, rtl_operand.length, 0); elna_rtl_instruction_set_operand(instruction, 2, ElnaRtlKind.pseudo, tac_instruction^.operands[2].value, tac_instruction^.operands[2].length, 0); instruction^.types[1] := rtl_type; elna_list_append(instructions, instruction) end; end proc elna_rtl_copy_to_offset(instructions: ^ElnaList, tac_instruction: ^ElnaTacInstruction, variable_map: ^ElnaSymbolTable) var instruction: ^ElnaRtlInstruction target_operand: ElnaRtlOperand source_operand: ElnaRtlOperand pseudo_symbol: ^ElnaRtlObjectInfo rtl_type: ^ElnaRtlType address_type: ^ElnaRtlType begin if tac_instruction^.operands[1].kind = ElnaTacKind.variable then pseudo_symbol := elna_symbol_table_lookup(variable_map, tac_instruction^.operands[1].value, tac_instruction^.operands[1].length); rtl_type := pseudo_symbol^.rtl_type else rtl_type := elna_rtl_constant_type(tac_instruction^.operands[1].length) end; elna_rtl_operand_value(instructions, @tac_instruction^.operands[1], variable_map, @source_operand); if tac_instruction^.operands[1].kind = ElnaTacKind.variable then if rtl_type^.kind = ElnaRtlTypeKind.byte_array then instruction := elna_rtl_instruction_create(ElnaRtlOperator.la); elna_rtl_instruction_set_operand(instruction, 1, ElnaRtlKind.register, ElnaRtlRegister.a0, 0, 0); elna_rtl_instruction_set_operand(instruction, 2, ElnaRtlKind.pseudo_mem, tac_instruction^.operands[2].value, tac_instruction^.operands[2].length, 0); elna_list_append(instructions, instruction); instruction := elna_rtl_instruction_create(ElnaRtlOperator.addi); elna_rtl_instruction_set_operand(instruction, 1, ElnaRtlKind.register, ElnaRtlRegister.a0, 0, 0); elna_rtl_instruction_set_operand(instruction, 2, ElnaRtlKind.register, ElnaRtlRegister.a0, 0, 0); elna_rtl_instruction_set_operand(instruction, 3, ElnaRtlKind.immediate, tac_instruction^.operands[3].value, 0, 0); elna_list_append(instructions, instruction); instruction := elna_rtl_instruction_create(ElnaRtlOperator.la); elna_rtl_instruction_set_operand(instruction, 1, ElnaRtlKind.register, ElnaRtlRegister.a1, 0, 0); elna_rtl_instruction_set_operand(instruction, 2, source_operand.kind, source_operand.value, source_operand.length, 0); elna_list_append(instructions, instruction); elna_rtl_memcpy(instructions, pseudo_symbol^.rtl_type); rtl_type := nil end end; if rtl_type <> nil then address_type := elna_rtl_constant_type(4); elna_rtl_generate_pseudo(@target_operand, variable_map, address_type); instruction := elna_rtl_operand_address(variable_map, @tac_instruction^.operands[2], @target_operand); elna_list_append(instructions, instruction); instruction := elna_rtl_instruction_create(ElnaRtlOperator.sw); elna_rtl_instruction_set_operand(instruction, 1, source_operand.kind, source_operand.value, source_operand.length, 0); elna_rtl_instruction_set_operand(instruction, 2, target_operand.kind, target_operand.value, target_operand.length, tac_instruction^.operands[3].value); instruction^.types[1] := rtl_type; elna_list_append(instructions, instruction) end end proc elna_rtl_load(instructions: ^ElnaList, tac_instruction: ^ElnaTacInstruction, variable_map: ^ElnaSymbolTable) var instruction: ^ElnaRtlInstruction rtl_operand: ElnaRtlOperand pseudo_symbol: ^ElnaRtlObjectInfo begin elna_rtl_operand_value(instructions, @tac_instruction^.operands[1], variable_map, @rtl_operand); pseudo_symbol := elna_symbol_table_lookup(variable_map, tac_instruction^.operands[2].value, tac_instruction^.operands[2].length); if pseudo_symbol^.rtl_type^.kind = ElnaRtlTypeKind.byte_array then instruction := elna_rtl_instruction_create(ElnaRtlOperator.la); elna_rtl_instruction_set_operand(instruction, 1, ElnaRtlKind.register, ElnaRtlRegister.a0, 0, 0); elna_rtl_instruction_set_operand(instruction, 2, ElnaRtlKind.pseudo, tac_instruction^.operands[2].value, tac_instruction^.operands[2].length, 0); elna_list_append(instructions, instruction); instruction := elna_rtl_instruction_create(ElnaRtlOperator.mv); elna_rtl_instruction_set_operand(instruction, 1, ElnaRtlKind.register, ElnaRtlRegister.a1, 0, 0); elna_rtl_instruction_set_operand(instruction, 2, rtl_operand.kind, rtl_operand.value, rtl_operand.length, 0); elna_list_append(instructions, instruction); elna_rtl_memcpy(instructions, pseudo_symbol^.rtl_type) else instruction := elna_rtl_instruction_create(ElnaRtlOperator.lw); elna_rtl_instruction_set_operand(instruction, 1, ElnaRtlKind.pseudo, tac_instruction^.operands[2].value, tac_instruction^.operands[2].length, 0); elna_rtl_instruction_set_operand(instruction, 2, rtl_operand.kind, rtl_operand.value, rtl_operand.length, 0); instruction^.types[1] := pseudo_symbol^.rtl_type; elna_list_append(instructions, instruction) end end proc elna_rtl_copy_from_offset(instructions: ^ElnaList, tac_instruction: ^ElnaTacInstruction, variable_map: ^ElnaSymbolTable) var instruction: ^ElnaRtlInstruction rtl_operand: ElnaRtlOperand pseudo_symbol: ^ElnaRtlObjectInfo begin pseudo_symbol := elna_symbol_table_lookup(variable_map, tac_instruction^.operands[3].value, tac_instruction^.operands[3].length); if pseudo_symbol^.rtl_type^.kind = ElnaRtlTypeKind.byte_array then instruction := elna_rtl_instruction_create(ElnaRtlOperator.la); elna_rtl_instruction_set_operand(instruction, 1, ElnaRtlKind.register, ElnaRtlRegister.a0, 0, 0); elna_rtl_instruction_set_operand(instruction, 2, ElnaRtlKind.pseudo_mem, tac_instruction^.operands[3].value, tac_instruction^.operands[3].length, 0); elna_list_append(instructions, instruction); instruction := elna_rtl_instruction_create(ElnaRtlOperator.la); elna_rtl_instruction_set_operand(instruction, 1, ElnaRtlKind.register, ElnaRtlRegister.a1, 0, 0); elna_rtl_instruction_set_operand(instruction, 2, ElnaRtlKind.pseudo_mem, tac_instruction^.operands[1].value, tac_instruction^.operands[1].length, 0); elna_list_append(instructions, instruction); instruction := elna_rtl_instruction_create(ElnaRtlOperator.addi); elna_rtl_instruction_set_operand(instruction, 1, ElnaRtlKind.register, ElnaRtlRegister.a1, 0, 0); elna_rtl_instruction_set_operand(instruction, 2, ElnaRtlKind.register, ElnaRtlRegister.a1, 0, 0); elna_rtl_instruction_set_operand(instruction, 3, ElnaRtlKind.immediate, tac_instruction^.operands[2].value, 0, 0); elna_list_append(instructions, instruction); elna_rtl_memcpy(instructions, pseudo_symbol^.rtl_type) else rtl_operand.kind := ElnaRtlKind.pseudo; rtl_operand.value := tac_instruction^.operands[3].value; rtl_operand.length := tac_instruction^.operands[3].length; instruction := elna_rtl_operand_address(variable_map, @tac_instruction^.operands[1], @rtl_operand); elna_list_append(instructions, instruction); instruction := elna_rtl_instruction_create(ElnaRtlOperator.lw); elna_rtl_instruction_set_operand(instruction, 1, ElnaRtlKind.pseudo, tac_instruction^.operands[3].value, tac_instruction^.operands[3].length, 0); elna_rtl_instruction_set_operand(instruction, 2, ElnaRtlKind.pseudo, tac_instruction^.operands[3].value, tac_instruction^.operands[3].length, tac_instruction^.operands[2].value); elna_list_append(instructions, instruction); instruction^.types[1] := pseudo_symbol^.rtl_type end end proc elna_rtl_memcpy(instructions: ^ElnaList, byte_array: ^ElnaRtlTypeByteArray) var instruction: ^ElnaRtlInstruction begin instruction := elna_rtl_instruction_create(ElnaRtlOperator.li); elna_rtl_instruction_set_operand(instruction, 1, ElnaRtlKind.register, ElnaRtlRegister.a2, 0, 0); elna_rtl_instruction_set_operand(instruction, 2, ElnaRtlKind.immediate, byte_array^.size, 0, 0); elna_list_append(instructions, instruction); instruction := elna_rtl_instruction_create(ElnaRtlOperator.jal); elna_rtl_instruction_set_operand(instruction, 1, ElnaRtlKind.data, "memcpy".ptr, 6, 0); elna_list_append(instructions, instruction) end proc elna_rtl_copy(instructions: ^ElnaList, tac_instruction: ^ElnaTacInstruction, variable_map: ^ElnaSymbolTable) var instruction: ^ElnaRtlInstruction source_operand: ElnaRtlOperand pseudo_symbol: ^ElnaRtlObjectInfo begin pseudo_symbol := elna_symbol_table_lookup(variable_map, tac_instruction^.operands[2].value, tac_instruction^.operands[2].length); elna_rtl_operand_value(instructions, @tac_instruction^.operands[1], variable_map, @source_operand); if source_operand.kind = ElnaRtlKind.pseudo then instruction := elna_rtl_instruction_create(ElnaRtlOperator.mv); elna_rtl_instruction_set_operand(instruction, 1, ElnaRtlKind.pseudo, tac_instruction^.operands[2].value, tac_instruction^.operands[2].length, 0); elna_rtl_instruction_set_operand(instruction, 2, source_operand.kind, source_operand.value, source_operand.length, 0); instruction^.types[1] := pseudo_symbol^.rtl_type; elna_list_append(instructions, instruction); else instruction := elna_rtl_instruction_create(ElnaRtlOperator.la); elna_rtl_instruction_set_operand(instruction, 1, ElnaRtlKind.register, ElnaRtlRegister.a0, 0, 0); elna_rtl_instruction_set_operand(instruction, 2, ElnaRtlKind.pseudo_mem, tac_instruction^.operands[2].value, tac_instruction^.operands[2].length, 0); elna_list_append(instructions, instruction); instruction := elna_rtl_instruction_create(ElnaRtlOperator.la); elna_rtl_instruction_set_operand(instruction, 1, ElnaRtlKind.register, ElnaRtlRegister.a1, 0, 0); elna_rtl_instruction_set_operand(instruction, 2, source_operand.kind, source_operand.value, source_operand.length, 0); elna_list_append(instructions, instruction); elna_rtl_memcpy(instructions, pseudo_symbol^.rtl_type) end end proc elna_rtl_instruction(instructions: ^ElnaList, tac_instruction: ^ElnaTacInstruction, variable_map: ^ElnaSymbolTable) var instruction: ^ElnaRtlInstruction begin if tac_instruction^.operator = ElnaTacOperator.get_address then elna_rtl_get_address(instructions, tac_instruction, variable_map) elsif tac_instruction^.operator = ElnaTacOperator.add then elna_rtl_binary_arithmetic(instructions, tac_instruction, ElnaRtlOperator.add, variable_map) elsif tac_instruction^.operator = ElnaTacOperator.add_ptr then elna_rtl_add_ptr(instructions, tac_instruction, variable_map) elsif tac_instruction^.operator = ElnaTacOperator.load then elna_rtl_load(instructions, tac_instruction, variable_map) elsif tac_instruction^.operator = ElnaTacOperator.copy_from_offset then elna_rtl_copy_from_offset(instructions, tac_instruction, variable_map) elsif tac_instruction^.operator = ElnaTacOperator.store then elna_rtl_store(instructions, tac_instruction, variable_map) elsif tac_instruction^.operator = ElnaTacOperator.copy_to_offset then elna_rtl_copy_to_offset(instructions, tac_instruction, variable_map) elsif tac_instruction^.operator = ElnaTacOperator.proc_call then elna_rtl_call(instructions, tac_instruction, variable_map) elsif tac_instruction^.operator = ElnaTacOperator.subtract then elna_rtl_binary_arithmetic(instructions, tac_instruction, ElnaRtlOperator.sub, variable_map) elsif tac_instruction^.operator = ElnaTacOperator.multiply then elna_rtl_binary_arithmetic(instructions, tac_instruction, ElnaRtlOperator.mul, variable_map) elsif tac_instruction^.operator = ElnaTacOperator.divide then elna_rtl_binary_arithmetic(instructions, tac_instruction, ElnaRtlOperator.div, variable_map) elsif tac_instruction^.operator = ElnaTacOperator.remainder then elna_rtl_binary_arithmetic(instructions, tac_instruction, ElnaRtlOperator.rem, variable_map) elsif tac_instruction^.operator = ElnaTacOperator._xor then elna_rtl_binary_arithmetic(instructions, tac_instruction, ElnaRtlOperator._xor, variable_map) elsif tac_instruction^.operator = ElnaTacOperator._or then elna_rtl_binary_arithmetic(instructions, tac_instruction, ElnaRtlOperator._or, variable_map) elsif tac_instruction^.operator = ElnaTacOperator.and then elna_rtl_binary_arithmetic(instructions, tac_instruction, ElnaRtlOperator.and, variable_map) elsif tac_instruction^.operator = ElnaTacOperator.less_than then elna_rtl_set_less_than(instructions, tac_instruction, 2, 3, variable_map) elsif tac_instruction^.operator = ElnaTacOperator.greater_than then elna_rtl_set_less_than(instructions, tac_instruction, 3, 2, variable_map) elsif tac_instruction^.operator = ElnaTacOperator.less_or_equal then elna_rtl_binary_comparison(instructions, tac_instruction, 3, 2, variable_map) elsif tac_instruction^.operator = ElnaTacOperator.greater_or_equal then elna_rtl_binary_comparison(instructions, tac_instruction, 2, 3, variable_map) elsif tac_instruction^.operator = ElnaTacOperator.equal then elna_rtl_binary_equality(instructions, tac_instruction, ElnaRtlOperator.seqz, variable_map) elsif tac_instruction^.operator = ElnaTacOperator.not_equal then elna_rtl_binary_equality(instructions, tac_instruction, ElnaRtlOperator.snez, variable_map) elsif tac_instruction^.operator = ElnaTacOperator.negate then elna_rtl_unary(instructions, tac_instruction, ElnaRtlOperator.neg, variable_map) elsif tac_instruction^.operator = ElnaTacOperator.complement then elna_rtl_unary(instructions, tac_instruction, ElnaRtlOperator.not, variable_map) elsif tac_instruction^.operator = ElnaTacOperator.jump then instruction := elna_rtl_instruction_create(ElnaRtlOperator.j); elna_rtl_instruction_set_operand(instruction, 1, ElnaRtlKind.data, tac_instruction^.operands[1].value, tac_instruction^.operands[1].length, 0); elna_list_append(instructions, instruction) elsif tac_instruction^.operator = ElnaTacOperator.jump_if_zero then elna_rtl_conditional_jump(instructions, tac_instruction, ElnaRtlOperator.beqz, variable_map) elsif tac_instruction^.operator = ElnaTacOperator.jump_if_not_zero then elna_rtl_conditional_jump(instructions, tac_instruction, ElnaRtlOperator.bnez, variable_map) elsif tac_instruction^.operator = ElnaTacOperator._return then elna_rtl_hardware_value(instructions, @tac_instruction^.operands[1], ElnaRtlRegister.a0, variable_map) elsif tac_instruction^.operator = ElnaTacOperator.label then instruction := elna_rtl_instruction_create(ElnaRtlOperator.label); elna_rtl_instruction_set_operand(instruction, 1, ElnaRtlKind.data, tac_instruction^.operands[1].value, tac_instruction^.operands[1].length, 0); elna_list_append(instructions, instruction) elsif tac_instruction^.operator = ElnaTacOperator.copy then elna_rtl_copy(instructions, tac_instruction, variable_map) elsif tac_instruction^.operator = ElnaTacOperator.nop then instruction := elna_rtl_instruction_create(ElnaRtlOperator.nop); elna_rtl_instruction_set_operand(instruction, 1, tac_instruction^.operands[1].kind, tac_instruction^.operands[1].value, tac_instruction^.operands[1].length, 0); elna_rtl_instruction_set_operand(instruction, 2, tac_instruction^.operands[2].kind, tac_instruction^.operands[2].value, tac_instruction^.operands[2].length, 0); elna_rtl_instruction_set_operand(instruction, 3, tac_instruction^.operands[3].kind, tac_instruction^.operands[3].value, tac_instruction^.operands[3].length, 0); elna_list_append(instructions, instruction) end end proc elna_tac_label(instructions: ^ElnaList, counter: Word, length: Word) var result: ^ElnaTacInstruction begin result := elna_tac_instruction_create(ElnaTacOperator.label); elna_tac_instruction_set_operand(result, 1, ElnaTacKind.label, counter, length); elna_list_append(instructions, result) end proc elna_riscv_instruction_name(instruction_kind: ElnaRtlOperator, source_type: ^ElnaRtlType, destination_type: ^ElnaRtlType) -> Word var argument_count: Word begin if instruction_kind = ElnaRtlOperator.li then argument_count := 2; _write_s("\tli".ptr, 3) elsif instruction_kind = ElnaRtlOperator.la then argument_count := 2; _write_s("\tla".ptr, 3) elsif instruction_kind = ElnaRtlOperator.add then argument_count := 3; _write_s("\tadd".ptr, 4) elsif instruction_kind = ElnaRtlOperator.addi then argument_count := 3; _write_s("\taddi".ptr, 5) elsif instruction_kind = ElnaRtlOperator.lw then argument_count := 2; if source_type^.kind = ElnaRtlTypeKind.byte then _write_s("\tlb".ptr, 3) else _write_s("\tlw".ptr, 3) end elsif instruction_kind = ElnaRtlOperator.sw then argument_count := 2; if source_type^.kind = ElnaRtlTypeKind.byte then _write_s("\tsb".ptr, 3) else _write_s("\tsw".ptr, 3) end elsif instruction_kind = ElnaRtlOperator.jal then argument_count := 1; _write_s("\tcall".ptr, 5) elsif instruction_kind = ElnaRtlOperator.mv then argument_count := 2; _write_s("\tmv".ptr, 3) elsif instruction_kind = ElnaRtlOperator.sub then argument_count := 3; _write_s("\tsub".ptr, 4) elsif instruction_kind = ElnaRtlOperator.mul then argument_count := 3; _write_s("\tmul".ptr, 4) elsif instruction_kind = ElnaRtlOperator.div then argument_count := 3; _write_s("\tdiv".ptr, 4) elsif instruction_kind = ElnaRtlOperator.rem then argument_count := 3; _write_s("\trem".ptr, 4) elsif instruction_kind = ElnaRtlOperator._xor then argument_count := 3; _write_s("\txor".ptr, 4) elsif instruction_kind = ElnaRtlOperator.xori then argument_count := 3; _write_s("\txori".ptr, 5) elsif instruction_kind = ElnaRtlOperator._or then argument_count := 3; _write_s("\tor".ptr, 3) elsif instruction_kind = ElnaRtlOperator.and then argument_count := 3; _write_s("\tand".ptr, 4) elsif instruction_kind = ElnaRtlOperator.seqz then argument_count := 2; _write_s("\tseqz".ptr, 5) elsif instruction_kind = ElnaRtlOperator.snez then argument_count := 2; _write_s("\tsnez".ptr, 5) elsif instruction_kind = ElnaRtlOperator.slt then argument_count := 3; _write_s("\tslt".ptr, 4) elsif instruction_kind = ElnaRtlOperator.neg then argument_count := 2; _write_s("\tneg".ptr, 4) elsif instruction_kind = ElnaRtlOperator.not then argument_count := 2; _write_s("\tnot".ptr, 4) elsif instruction_kind = ElnaRtlOperator.j then argument_count := 1; _write_s("\tj".ptr, 2) elsif instruction_kind = ElnaRtlOperator.beqz then argument_count := 2; _write_s("\tbeqz".ptr, 5) elsif instruction_kind = ElnaRtlOperator.bnez then argument_count := 2; _write_s("\tbnez".ptr, 5) end; return argument_count end proc elna_riscv_register(register: Word) begin printf("x%i\0".ptr, register - 1); fflush(nil) end proc elna_riscv_operand(instruction: ^ElnaRtlInstruction, n: Word) var operand_type: Word begin operand_type := instruction^.operands[n].kind; _write_c(' '); if operand_type = ElnaRtlKind.register then elna_riscv_register(instruction^.operands[n].value) elsif operand_type = ElnaRtlKind.memory then _write_i(instruction^.operands[n].offset); _write_c('('); elna_riscv_register(instruction^.operands[n].value); _write_c(')') elsif operand_type = ElnaRtlKind.data then if instruction^.operands[n].length = 0 then _write_label(instruction^.operands[n].value, 0) else _write_s(instruction^.operands[n].value, instruction^.operands[n].length) end elsif instruction^.operands[n].length = 0 then (* ElnaRtlKind.immediate *) _write_i(instruction^.operands[n].value) else _write_s(instruction^.operands[n].value, instruction^.operands[n].length) end end proc elna_alloc_variable(operand_value: Word, operand_length: Word, variable_map: ^ElnaSymbolTable) -> ^ElnaRtlObjectInfo var pseudo_symbol: ^ElnaRtlObjectInfo pseudo_type: ^ElnaRtlTypeByteArray type_size: Word begin pseudo_symbol := elna_symbol_table_lookup(variable_map, operand_value, operand_length); if pseudo_symbol = nil then goto elna_alloc_variable_end end; if pseudo_symbol^.allocated = false then if pseudo_symbol^.rtl_type^.kind = ElnaRtlTypeKind.byte_array then pseudo_type := pseudo_symbol^.rtl_type; pseudo_symbol^.counter := _align_at(temporary_variable_counter, pseudo_type^.alignment); type_size := pseudo_type^.size elsif pseudo_symbol^.rtl_type^.kind = ElnaRtlTypeKind.byte then pseudo_symbol^.counter := temporary_variable_counter; type_size := 1 else pseudo_symbol^.counter := _align_at(temporary_variable_counter, 4); type_size := 4 end; temporary_variable_counter := pseudo_symbol^.counter + type_size; pseudo_symbol^.allocated := true end; .elna_alloc_variable_end; return pseudo_symbol end (** * If the first operand of an instruction is an operation target and expected * to be a register, but is not a register, then this procedure rewrites it * to a temporary register and preserves its value in the following instruction. *) proc elna_alloc_operation_target(instructions: ^ElnaList, instruction: ^ElnaRtlInstruction, variable_map: ^ElnaSymbolTable) -> ^ElnaRtlInstruction var pseudo_symbol: ^ElnaRtlObjectInfo store_instruction: ^ElnaRtlInstruction begin if instruction^.operands[1].kind = ElnaRtlKind.pseudo then store_instruction := elna_rtl_instruction_create(ElnaRtlOperator.sw); pseudo_symbol := elna_alloc_variable(instruction^.operands[1].value, instruction^.operands[1].length, variable_map); elna_rtl_instruction_set_operand(store_instruction, 1, ElnaRtlKind.register, ElnaRtlRegister.t0, 0, 0); elna_rtl_instruction_set_operand(store_instruction, 2, ElnaRtlKind.memory, ElnaRtlRegister.sp, 0, pseudo_symbol^.counter); store_instruction^.types[1] := pseudo_symbol^.rtl_type; elna_rtl_instruction_set_operand(instruction, 1, ElnaRtlKind.register, ElnaRtlRegister.t0, 0, 0); elna_list_insert(instructions, instruction, store_instruction); instruction := store_instruction end; return instruction end proc elna_alloc_load_address(instructions: ^ElnaList, instruction: ^ElnaRtlInstruction, variable_map: ^ElnaSymbolTable) var pseudo_symbol: ^ElnaRtlObjectInfo begin (* pseudo or pseudo_mem. *) if instruction^.operands[2].kind <> ElnaRtlKind.data then pseudo_symbol := elna_symbol_table_lookup(variable_map, instruction^.operands[2].value, instruction^.operands[2].length); if pseudo_symbol^.kind = ElnaRtlInfoKind.object_info then pseudo_symbol := elna_alloc_variable(instruction^.operands[2].value, instruction^.operands[2].length, variable_map); instruction^.operator := ElnaRtlOperator.addi; elna_rtl_instruction_set_operand(instruction, 2, ElnaRtlKind.register, ElnaRtlRegister.sp, 0, 0); elna_rtl_instruction_set_operand(instruction, 3, ElnaRtlKind.immediate, pseudo_symbol^.counter, 0, 0) end end; if instruction^.operands[1].kind = ElnaRtlKind.pseudo then elna_alloc_operation_target(instructions, instruction, variable_map) end end proc elna_alloc_store(instructions: ^ElnaList, instruction: ^ElnaRtlInstruction, variable_map: ^ElnaSymbolTable) -> ^ElnaRtlInstruction var store_instruction: ^ElnaRtlInstruction pseudo_symbol: ^ElnaRtlObjectInfo begin instruction := elna_alloc_operand(instructions, instruction, 1, ElnaRtlRegister.t0, variable_map); if instruction^.operands[2].kind = ElnaRtlKind.pseudo then store_instruction := elna_rtl_instruction_create(ElnaRtlOperator.sw); elna_rtl_instruction_set_operand(store_instruction, 1, ElnaRtlKind.register, ElnaRtlRegister.t0, 0, 0); elna_rtl_instruction_set_operand(store_instruction, 2, ElnaRtlKind.memory, ElnaRtlRegister.t1, 0, instruction^.operands[2].offset); store_instruction^.types[1] := instruction^.types[1]; pseudo_symbol := elna_alloc_variable(instruction^.operands[2].value, instruction^.operands[2].length, variable_map); instruction^.operator := ElnaRtlOperator.lw; (* Because it is an address. *) instruction^.types[1] := elna_rtl_constant_type(4); elna_rtl_instruction_set_operand(instruction, 1, ElnaRtlKind.register, ElnaRtlRegister.t1, 0, 0); elna_rtl_instruction_set_operand(instruction, 2, ElnaRtlKind.memory, ElnaRtlRegister.sp, 0, pseudo_symbol^.counter); elna_list_insert(instructions, instruction, store_instruction); instruction := store_instruction end; return instruction end proc elna_alloc_load(instructions: ^ElnaList, instruction: ^ElnaRtlInstruction, variable_map: ^ElnaSymbolTable) -> ^ElnaRtlInstruction var new_instruction: ^ElnaRtlInstruction pseudo_symbol: ^ElnaRtlObjectInfo begin if instruction^.operands[2].kind = ElnaRtlKind.pseudo then pseudo_symbol := elna_alloc_variable(instruction^.operands[2].value, instruction^.operands[2].length, variable_map); new_instruction := malloc(#size(ElnaRtlInstruction)); new_instruction^ := instruction^; new_instruction^.next := nil; elna_rtl_instruction_set_operand(new_instruction, 2, ElnaRtlKind.memory, ElnaRtlRegister.t0, 0, instruction^.operands[2].offset); instruction^.operator := ElnaRtlOperator.lw; (* Because it is an address. *) instruction^.types[1] := elna_rtl_constant_type(4); elna_rtl_instruction_set_operand(instruction, 1, ElnaRtlKind.register, ElnaRtlRegister.t0, 0, 0); elna_rtl_instruction_set_operand(instruction, 2, ElnaRtlKind.memory, ElnaRtlRegister.sp, 0, pseudo_symbol^.counter); elna_list_insert(instructions, instruction, new_instruction); instruction := new_instruction end; return elna_alloc_operation_target(instructions, instruction, variable_map) end proc elna_alloc_move(instructions: ^ElnaList, instruction: ^ElnaRtlInstruction, variable_map: ^ElnaSymbolTable) -> ^ElnaRtlInstruction var load_instruction: ^ElnaRtlInstruction source_operand: ElnaRtlOperand target_operand: ElnaRtlOperand pseudo_symbol: ^ElnaRtlObjectInfo destination_pseudo: Word source_pseudo: Word begin target_operand := instruction^.operands[1]; source_operand := instruction^.operands[2]; destination_pseudo := target_operand.kind = ElnaRtlKind.pseudo; source_pseudo := source_operand.kind = ElnaRtlKind.pseudo; if destination_pseudo & source_pseudo then pseudo_symbol := elna_symbol_table_lookup(variable_map, target_operand.value, target_operand.length); instruction := elna_alloc_operand(instructions, instruction, 2, ElnaRtlRegister.t0, variable_map); if pseudo_symbol^.kind = ElnaRtlInfoKind.static_info then load_instruction := malloc(#size(ElnaRtlInstruction)); load_instruction^ := instruction^; load_instruction^.next := nil; instruction^.operator = ElnaRtlOperator.la; elna_rtl_instruction_set_operand(instruction, 1, ElnaRtlKind.register, ElnaRtlRegister.t1, 0, 0); elna_rtl_instruction_set_operand(instruction, 2, ElnaRtlKind.data, target_operand.value, target_operand.length, 0); elna_list_insert(instructions, instruction, load_instruction); load_instruction^.operator = ElnaRtlOperator.sw; elna_rtl_instruction_set_operand(load_instruction, 1, ElnaRtlKind.register, ElnaRtlRegister.t0, 0, 0); elna_rtl_instruction_set_operand(load_instruction, 2, ElnaRtlKind.memory, ElnaRtlRegister.t1, 0, 0) else pseudo_symbol := elna_alloc_variable(target_operand.value, target_operand.length, variable_map); instruction^.operator = ElnaRtlOperator.sw; elna_rtl_instruction_set_operand(instruction, 1, ElnaRtlKind.register, ElnaRtlRegister.t0, 0, 0); elna_rtl_instruction_set_operand(instruction, 2, ElnaRtlKind.memory, ElnaRtlRegister.sp, 0, pseudo_symbol^.counter); instruction^.types[1] := pseudo_symbol^.rtl_type end elsif destination_pseudo then pseudo_symbol := elna_symbol_table_lookup(variable_map, target_operand.value, target_operand.length); if pseudo_symbol^.kind = ElnaRtlInfoKind.static_info then load_instruction := malloc(#size(ElnaRtlInstruction)); load_instruction^ := instruction^; load_instruction^.next := nil; instruction^.operator = ElnaRtlOperator.la; elna_rtl_instruction_set_operand(instruction, 1, ElnaRtlKind.register, ElnaRtlRegister.t1, 0, 0); elna_list_insert(instructions, instruction, load_instruction); load_instruction^.operator = ElnaRtlOperator.sw; elna_rtl_instruction_set_operand(load_instruction, 2, ElnaRtlKind.memory, ElnaRtlRegister.t1, 0, 0) else pseudo_symbol := elna_alloc_variable(target_operand.value, target_operand.length, variable_map); instruction^.operator = ElnaRtlOperator.sw; elna_rtl_instruction_set_operand(instruction, 1, source_operand.kind, source_operand.value, source_operand.length, 0); elna_rtl_instruction_set_operand(instruction, 2, ElnaRtlKind.memory, ElnaRtlRegister.sp, 0, pseudo_symbol^.counter) end; instruction^.types[1] := pseudo_symbol^.rtl_type elsif source_pseudo then pseudo_symbol := elna_alloc_variable(source_operand.value, source_operand.length, variable_map); if pseudo_symbol^.kind = ElnaRtlInfoKind.static_info then load_instruction := malloc(#size(ElnaRtlInstruction)); load_instruction^ := instruction^; load_instruction^.next := nil; instruction^.operator := ElnaRtlOperator.la; elna_list_insert(instructions, instruction, load_instruction); load_instruction^.operator = ElnaRtlOperator.lw; elna_rtl_instruction_set_operand(load_instruction, 2, ElnaRtlKind.memory, target_operand.value, target_operand.length, 0); load_instruction^.types[1] := pseudo_symbol^.rtl_type else instruction^.operator = ElnaRtlOperator.lw; elna_rtl_instruction_set_operand(instruction, 2, ElnaRtlKind.memory, ElnaRtlRegister.sp, 0, pseudo_symbol^.counter) end; instruction^.types[1] := pseudo_symbol^.rtl_type end; return instruction end (** * Move the operand value from the pseudo register into a hardware register if needed. * If copying requires an additional instruction inserts the instruction before the * current instruction and returns the new current instruction. *) proc elna_alloc_operand(instructions: ^ElnaList, instruction: ^ElnaRtlInstruction, number: Word, target: ElnaRtlRegister, variable_map: ^ElnaSymbolTable) -> ^ElnaRtlInstruction var main_instruction: ^ElnaRtlInstruction load_instruction: ^ElnaRtlInstruction pseudo_symbol: ^ElnaRtlObjectInfo begin if instruction^.operands[number].kind = ElnaRtlKind.pseudo then pseudo_symbol := elna_alloc_variable(instruction^.operands[number].value, instruction^.operands[number].length, variable_map); main_instruction := malloc(#size(ElnaRtlInstruction)); main_instruction^ := instruction^; main_instruction^.next := nil; elna_rtl_instruction_set_operand(main_instruction, number, ElnaRtlKind.register, target, 0, 0); if pseudo_symbol^.kind = ElnaRtlInfoKind.static_info then instruction^.operator := ElnaRtlOperator.la; elna_rtl_instruction_set_operand(instruction, 2, ElnaRtlKind.data, instruction^.operands[number].value, instruction^.operands[number].length, 0); elna_rtl_instruction_set_operand(instruction, 1, ElnaRtlKind.register, target, 0, 0); elna_list_insert(instructions, instruction, main_instruction); load_instruction := elna_rtl_instruction_create(ElnaRtlOperator.lw); elna_rtl_instruction_set_operand(load_instruction, 1, ElnaRtlKind.register, target, 0, 0); elna_rtl_instruction_set_operand(load_instruction, 2, ElnaRtlKind.memory, target, 0, 0); load_instruction^.types[1] := pseudo_symbol^.rtl_type; elna_list_insert(instructions, instruction, load_instruction) else instruction^.operator := ElnaRtlOperator.lw; elna_rtl_instruction_set_operand(instruction, 1, ElnaRtlKind.register, target, 0, 0); elna_rtl_instruction_set_operand(instruction, 2, ElnaRtlKind.memory, ElnaRtlRegister.sp, 0, pseudo_symbol^.counter); instruction^.types[1] := pseudo_symbol^.rtl_type; elna_list_insert(instructions, instruction, main_instruction); end; instruction := main_instruction end; return instruction end proc elna_alloc_binary(instructions: ^ElnaList, instruction: ^ElnaRtlInstruction, variable_map: ^ElnaSymbolTable) -> ^ElnaRtlInstruction begin instruction := elna_alloc_operand(instructions, instruction, 2, ElnaRtlRegister.t0, variable_map); instruction := elna_alloc_operand(instructions, instruction, 3, ElnaRtlRegister.t1, variable_map); return elna_alloc_operation_target(instructions, instruction, variable_map) end proc elna_alloc_unary(instructions: ^ElnaList, instruction: ^ElnaRtlInstruction, variable_map: ^ElnaSymbolTable) -> ^ElnaRtlInstruction begin instruction := elna_alloc_operand(instructions, instruction, 2, ElnaRtlRegister.t0, variable_map); return elna_alloc_operation_target(instructions, instruction, variable_map) end proc elna_alloc_instruction(instructions: ^ElnaList, instruction: ^ElnaRtlInstruction, variable_map: ^ElnaSymbolTable) -> ^ElnaRtlInstruction var pseudo_symbol: ^ElnaRtlObjectInfo new_instruction: ^ElnaRtlInstruction begin if instruction^.operator = ElnaRtlOperator.mv then instruction := elna_alloc_move(instructions, instruction, variable_map) elsif instruction^.operator = ElnaRtlOperator.la then elna_alloc_load_address(instructions, instruction, variable_map) elsif instruction^.operator = ElnaRtlOperator.lw then instruction := elna_alloc_load(instructions, instruction, variable_map) elsif instruction^.operator = ElnaRtlOperator.sw then instruction := elna_alloc_store(instructions, instruction, variable_map) elsif instruction^.operator = ElnaRtlOperator._or then instruction := elna_alloc_binary(instructions, instruction, variable_map) elsif instruction^.operator = ElnaRtlOperator.and then instruction := elna_alloc_binary(instructions, instruction, variable_map) elsif instruction^.operator = ElnaRtlOperator.mul then instruction := elna_alloc_binary(instructions, instruction, variable_map) elsif instruction^.operator = ElnaRtlOperator.sub then instruction := elna_alloc_binary(instructions, instruction, variable_map) elsif instruction^.operator = ElnaRtlOperator.add then instruction := elna_alloc_binary(instructions, instruction, variable_map) elsif instruction^.operator = ElnaRtlOperator._xor then instruction := elna_alloc_binary(instructions, instruction, variable_map) elsif instruction^.operator = ElnaRtlOperator.div then instruction := elna_alloc_binary(instructions, instruction, variable_map) elsif instruction^.operator = ElnaRtlOperator.rem then instruction := elna_alloc_binary(instructions, instruction, variable_map) elsif instruction^.operator = ElnaRtlOperator.slt then instruction := elna_alloc_binary(instructions, instruction, variable_map) elsif instruction^.operator = ElnaRtlOperator.beqz then if instruction^.operands[1].kind = ElnaRtlKind.pseudo then new_instruction := elna_rtl_instruction_create(ElnaRtlOperator.beqz); elna_rtl_instruction_set_operand(new_instruction, 1, ElnaRtlKind.register, ElnaRtlRegister.t0, 0, 0); elna_rtl_instruction_set_operand(new_instruction, 2, instruction^.operands[2].kind, instruction^.operands[2].value, instruction^.operands[2].length, 0); pseudo_symbol := elna_alloc_variable(instruction^.operands[1].value, instruction^.operands[1].length, variable_map); instruction^.operator := ElnaRtlOperator.lw; elna_rtl_instruction_set_operand(instruction, 1, ElnaRtlKind.register, ElnaRtlRegister.t0, 0, 0); elna_rtl_instruction_set_operand(instruction, 2, ElnaRtlKind.memory, ElnaRtlRegister.sp, 0, pseudo_symbol^.counter); instruction^.types[1] := pseudo_symbol^.rtl_type; elna_list_insert(instructions, instruction, new_instruction) end elsif instruction^.operator = ElnaRtlOperator.seqz then instruction := elna_alloc_unary(instructions, instruction, variable_map) elsif instruction^.operator = ElnaRtlOperator.snez then instruction := elna_alloc_unary(instructions, instruction, variable_map) elsif instruction^.operator = ElnaRtlOperator.xori then instruction := elna_alloc_binary(instructions, instruction, variable_map) elsif instruction^.operator = ElnaRtlOperator.addi then instruction := elna_alloc_binary(instructions, instruction, variable_map) elsif instruction^.operator = ElnaRtlOperator.li then elna_alloc_operation_target(instructions, instruction, variable_map) end; return instruction^.next end proc elna_riscv_instruction(instruction: ^ElnaRtlInstruction) var argument_count: Word current_argument: Word operand_value: Word begin argument_count := 0; if instruction^.operator = ElnaRtlOperator.label then _write_label(instruction^.operands[1].value, instruction^.operands[1].length); (* _write_c(':') *) _write_c(58) elsif instruction^.operator = ElnaRtlOperator.allocate_stack then operand_value := instruction^.operands[1].value; (* Write the prologue. *) printf("\taddi sp, sp, -%i\n\tsw ra, %i(sp)\n\tsw s0, %i(sp)\n\taddi s0, sp, %i\n\0".ptr, operand_value, operand_value - 4, operand_value - 8, operand_value); fflush(nil) elsif instruction^.operator = ElnaRtlOperator.ret then operand_value := instruction^.operands[1].value; (* Write the epilogue. *) printf("\tlw ra, %i(sp)\n\tlw s0, %i(sp)\n\taddi sp, sp, %i\n\0".ptr, operand_value - 4, operand_value - 8, operand_value); fflush(nil) elsif instruction^.operator = ElnaRtlOperator.nop then operand_value := instruction^.operands[1].kind; if operand_value = ElnaTacKind.constant then printf("\n# copy %i (%i %i) (%i %.*s)\n\0".ptr, instruction^.operands[3].kind, instruction^.operands[1].kind, instruction^.operands[1].value, instruction^.operands[2].kind, instruction^.operands[2].length, instruction^.operands[2].value) else printf("\n# copy (%i %.*s) (%i %.*s)\n\0".ptr, instruction^.operands[1].kind, instruction^.operands[1].length, instruction^.operands[1].value, instruction^.operands[2].kind, instruction^.operands[2].length, instruction^.operands[2].value) end; fflush(nil) else argument_count := elna_riscv_instruction_name(instruction^.operator, instruction^.types[1], instruction^.types[2]) end; current_argument := 1; .elna_riscv_instruction_loop; if current_argument <= argument_count then elna_riscv_operand(instruction, current_argument); current_argument := current_argument + 1 end; if current_argument <= argument_count then (* _write_c(','); *) _write_c(44); goto elna_riscv_instruction_loop end; _write_c(10) end proc elna_rtl_instructions(instructions: ^ElnaList, instruction: ^ElnaTacInstruction, variable_map: ^ElnaSymbolTable) begin .elna_rtl_instructions_start; if instruction <> nil then elna_rtl_instruction(instructions, instruction, variable_map); instruction := instruction^.next; goto elna_rtl_instructions_start end end proc elna_alloc_instructions(instructions: ^ElnaList, variable_map: ^ElnaSymbolTable) var instruction: ^ElnaRtlInstruction begin instruction := instructions^.first; .elna_alloc_instructions_start; if instruction <> nil then instruction := elna_alloc_instruction(instructions, instruction, variable_map); goto elna_alloc_instructions_start end end proc elna_riscv_instructions(instruction: ^ElnaRtlInstruction) begin .elna_riscv_instructions_start; if instruction <> nil then elna_riscv_instruction(instruction); instruction := instruction^.next; goto elna_riscv_instructions_start end end proc elna_alloc_procedure(rtl_declaration: ^ElnaRtlProcedure) var stack_instruction: ^ElnaRtlInstruction begin .elna_alloc_procedure_loop; temporary_variable_counter := 0; elna_alloc_instructions(@rtl_declaration^.body, rtl_declaration^.variable_map); stack_instruction := elna_rtl_instruction_create(ElnaRtlOperator.allocate_stack); elna_rtl_instruction_set_operand(stack_instruction, 1, ElnaRtlKind.immediate, temporary_variable_counter, 0, 0); elna_list_prepend(@rtl_declaration^.body, stack_instruction); stack_instruction := elna_rtl_instruction_create(ElnaRtlOperator.ret); elna_rtl_instruction_set_operand(stack_instruction, 1, ElnaRtlKind.immediate, temporary_variable_counter, 0, 0); elna_list_append(@rtl_declaration^.body, stack_instruction); rtl_declaration := rtl_declaration^.next; if rtl_declaration <> nil then goto elna_alloc_procedure_loop end end proc elna_riscv_procedure(procedure: ^ElnaRtlProcedure) begin .elna_riscv_procedure_loop; (* Write .type _procedure_name, @function. *) printf(".type %.*s, @function\n\0".ptr, procedure^.length, procedure^.name); (* Write procedure label, _procedure_name: *) printf("%.*s:\n\0".ptr, procedure^.length, procedure^.name); elna_riscv_instructions(procedure^.body.first); printf("\tret\n\0".ptr); fflush(nil); procedure := procedure^.next; if procedure <> nil then goto elna_riscv_procedure_loop end end proc elna_riscv_variable(variable: ^ElnaRtlStaticVariable) begin .elna_riscv_variable_loop; if variable <> 0 then printf(".type %.*s, @object\n\0".ptr, variable^.length, variable^.name); printf("%.*s: .zero %i\n\0".ptr, variable^.length, variable^.name, variable^.body); variable := variable^.next; goto elna_riscv_variable_loop end end proc elna_rtl_module_declaration(tac_module: ^ElnaInstructionModule) -> ^ElnaInstructionModule var result: ^ElnaInstructionModule count: Word current_entry: ^ElnaSymbolEntry pseudo_symbol: ^ElnaRtlStaticInfo variable_info: ^ElnaSymbolTemporaryInfo info: ^ElnaSymbolInfo begin result := malloc(#size(ElnaInstructionModule)); result^.data := elna_rtl_globals(tac_module^.data); (* Rewrite global symbol table into global backend symbol table. *) variable_map_global := elna_symbol_table_create(nil); count := symbol_table_global^.count; current_entry := symbol_table_global^.symbols; .elna_rtl_module_declaration_loop; if count = 0 then goto elna_rtl_module_declaration_end end; info := current_entry^.symbol_info; if info^.kind = ElnaSymbolInfoKind.temporary_info then variable_info := current_entry^.symbol_info; pseudo_symbol := malloc(#size(ElnaRtlStaticInfo)); pseudo_symbol^.kind := ElnaRtlInfoKind.static_info; pseudo_symbol^.rtl_type := elna_rtl_symbol_type(variable_info^.variable_type); elna_symbol_table_enter(variable_map_global, current_entry^.name, current_entry^.length, pseudo_symbol) end; count := count - 1; current_entry := current_entry + 1; goto elna_rtl_module_declaration_loop; .elna_rtl_module_declaration_end; result^.code := elna_rtl_procedures(tac_module^.code); return result end proc elna_alloc_module(pair: ^ElnaInstructionModule) begin elna_alloc_procedure(pair^.code) end proc _align_at(address: Word, alignment: Word) -> Word begin if address % alignment then address := address + alignment end; address := address / alignment; address := address * alignment; return address end proc elna_fixup_instructions(instructions: ^ElnaList) var instruction: ^ElnaRtlInstruction stack_size: Word begin instruction := instructions^.first; (* Align the stack. *) stack_size := _align_at(instruction^.operands[1].value + 8, 16); instruction^.operands[1].value := stack_size; instruction := instructions^.last; instruction^.operands[1].value := stack_size end proc elna_fixup_procedure(rtl_declaration: ^ElnaRtlProcedure) begin .elna_fixup_procedure_loop; elna_fixup_instructions(@rtl_declaration^.body); rtl_declaration := rtl_declaration^.next; if rtl_declaration <> nil then goto elna_fixup_procedure_loop end end proc elna_fixup_module(pair: ^ElnaInstructionModule) begin elna_fixup_procedure(pair^.code) end proc elna_riscv_module(pair: ^ElnaInstructionModule) var compiler_strings_copy: ^Char compiler_strings_end: ^Char current_byte: Char begin printf(".globl main\n\n\0".ptr); printf(".section .data\n\0".ptr); elna_riscv_variable(pair^.data); printf(".section .text\n\n\0".ptr); elna_riscv_procedure(pair^.code); printf(".section .rodata\n.type strings, @object\nstrings: .ascii \0".ptr); fflush(nil); _write_c('"'); compiler_strings_copy := @compiler_strings; compiler_strings_end := compiler_strings_position; .elna_riscv_module_loop; if compiler_strings_copy < compiler_strings_end then current_byte := compiler_strings_copy^; compiler_strings_copy := compiler_strings_copy + 1; _write_c(current_byte); goto elna_riscv_module_loop end; _write_c('"'); _write_c('\n') end proc elna_parser_integer_literal(cursor: ^ElnaLexerCursor) -> ^ElnaTreeIntegerLiteral var token: ^ElnaLexerToken result: ^ElnaTreeIntegerLiteral buffer: Pointer begin result := malloc(#size(ElnaTreeIntegerLiteral)); token := elna_lexer_read(cursor); buffer := malloc(token^.length + 1); bzero(buffer, token^.length + 1); memcpy(buffer, token^.start, token^.length); result^.kind := ElnaTreeKind.integer_literal; result^.value := atoi(buffer); result^.type_decoration := nil; return result end proc elna_parser_boolean_literal(cursor: ^ElnaLexerCursor) -> ^ElnaTreeBooleanLiteral var result: ^ElnaTreeBooleanLiteral begin result := malloc(#size(ElnaTreeBooleanLiteral)); result^.kind := ElnaTreeKind.boolean_literal; result^.value := string_compare(cursor^.start, 4, "true".ptr, 4); result^.type_decoration := nil; elna_lexer_read(cursor); return result end proc elna_parser_nil_literal(cursor: ^ElnaLexerCursor) -> ^ElnaTreeNilLiteral var result: ^ElnaTreeNilLiteral begin elna_lexer_read(cursor); result := malloc(#size(ElnaTreeNilLiteral)); result^.kind := ElnaTreeKind.null; result^.type_decoration := nil; return result end proc elna_tac_integer_literal(integer_literal_node: ^ElnaTreeIntegerLiteral, operand: ^ElnaTacOperand) begin operand^.kind := ElnaTacKind.constant; operand^.value := integer_literal_node^.value; operand^.length := 4 end proc elna_tac_boolean_literal(boolean_literal_node: ^ElnaTreeBooleanLiteral, operand: ^ElnaTacOperand) begin operand^.kind := ElnaTacKind.constant; operand^.value := boolean_literal_node^.value; operand^.length := 1 end proc elna_tac_nil_literal(nil_node: Word, operand: ^ElnaTacOperand) begin operand^.kind := ElnaTacKind.constant; operand^.value := 0; operand^.length := 4 end (* Escape a single character if needed. *) proc elna_parser_escape(current_position: ^Char, result: ^Char) -> Word var parsed_length: Word next_character: Char begin next_character := current_position^; if next_character = '\\' then parsed_length := 2; current_position := current_position + 1; next_character := current_position^; if next_character = '\\' then (* \\ Backslash. *) result^ := 92 elsif next_character = 'n' then (* \n Newline. *) result^ := 10 elsif next_character = 't' then (* \t Tab. *) result^ := 9 elsif next_character = 'f' then (* \f Formfeed. *) result^ := 12 elsif next_character = 'r' then (* \r Carriage return. *) result^ := 13 elsif next_character = 'v' then (* \v Vertical tab. *) result^ := 11 elsif next_character = '"' then (* \" Double quote. *) result^ := 34 elsif next_character = '\'' then (* \' Single quote. *) result^ := 39 elsif next_character = '0' then (* \0 Null character. *) result^ := 0 end else parsed_length := 1; result^ := next_character end; return parsed_length end proc elna_parser_character_literal(cursor: ^ElnaLexerCursor) -> ^ElnaTreeCharacterLiteral var result: ^ElnaTreeCharacterLiteral token: ^ElnaLexerToken begin result := malloc(#size(ElnaTreeCharacterLiteral)); token := elna_lexer_read(cursor); result^.kind := ElnaTreeKind.character_literal; elna_parser_escape(token^.start + 1, @result^.value); result^.length := 0; result^.type_decoration := nil; return result end proc elna_tac_character_literal(character_literal_node: ^ElnaTreeCharacterLiteral, operand: ^ElnaTacOperand) var next_character: Word current_position: ^Char begin operand^.kind := ElnaTacKind.constant; operand^.value := character_literal_node^.value; operand^.length := 1 end proc elna_parser_variable_expression(cursor: ^ElnaLexerCursor) -> ^ElnaTreeVariableExpression var result: ^ElnaTreeVariableExpression token: ^ElnaLexerToken begin result := malloc(#size(ElnaTreeVariableExpression)); token := elna_lexer_read(cursor); result^.kind := ElnaTreeKind.variable_expression; result^.name := token^.start; result^.length := token^.length; result^.type_decoration := nil; return result end proc elna_tac_variable_expression(variable_expression: ^ElnaTreeVariableExpression, operand: ^ElnaTacOperand) begin operand^.kind := ElnaTacKind.variable; operand^.value := variable_expression^.name; operand^.length := variable_expression^.length end proc elna_parser_string_literal(cursor: ^ElnaLexerCursor) -> ^ElnaTreeStringLiteral var result: ^ElnaTreeStringLiteral token: ^ElnaLexerToken current_position: ^Char parsed_length: Word parsed_total: Word target_position: ^Char begin result := malloc(#size(ElnaTreeStringLiteral)); token := elna_lexer_read(cursor); target_position := malloc(token^.length - 2); current_position := token^.start + 1; parsed_total := 2; result^.kind := ElnaTreeKind.string_literal; result^.value := target_position; result^.length := 0; .elna_parser_escape_loop; parsed_length := elna_parser_escape(current_position, target_position); current_position := current_position + parsed_length; parsed_total := parsed_total + parsed_length; target_position := target_position + 1; if parsed_total < token^.length then goto elna_parser_escape_loop end; result^.length := target_position - result^.value; result^.type_decoration := nil; return result end proc elna_tac_string_literal(instructions: ^ElnaList, string_literal_node: ^ElnaTreeStringLiteral, symbol_table: ^ElnaSymbolTable, operand: ^ElnaTacOperand) var offset: Word instruction: ^ElnaTacInstruction begin offset := elna_tac_add_string(string_literal_node^.value, string_literal_node^.length); elna_tac_make_variable(operand, symbol_table, word_type); instruction := elna_tac_instruction_create(ElnaTacOperator.get_address); elna_tac_instruction_set_operand(instruction, 1, ElnaTacKind.variable, "strings".ptr, 7); elna_tac_instruction_set_operand(instruction, 2, operand^.kind, operand^.value, operand^.length); elna_list_append(instructions, instruction); (* Add offset to the string block pointer. *) instruction := elna_tac_instruction_create(ElnaTacOperator.add); elna_tac_instruction_set_operand(instruction, 1, operand^.kind, operand^.value, operand^.length); elna_tac_instruction_set_operand(instruction, 2, ElnaTacKind.constant, offset, 4); elna_tac_instruction_set_operand(instruction, 3, operand^.kind, operand^.value, operand^.length); elna_list_append(instructions, instruction) end proc elna_parser_trait_expression(cursor: ^ElnaLexerCursor) -> ^ElnaTreeTraitExpression var result: ^ElnaTreeTraitExpression token: ^ElnaLexerToken begin result := malloc(#size(ElnaTreeTraitExpression)); result^.kind := ElnaTreeKind.trait_expression; token := elna_lexer_read(cursor); result^.name := token^.start; result^.length := token^.length; elna_lexer_read(cursor); result^.argument := elna_parser_type_expression(cursor); elna_lexer_read(cursor); return result end proc elna_parser_cast_expression(cursor: ^ElnaLexerCursor) -> ^ElnaTreeCastExpression var result: ^ElnaTreeCastExpression token: ^ElnaLexerToken begin result := malloc(#size(ElnaTreeCastExpression)); result^.kind := ElnaTreeKind._cast; result^.type_decoration := nil; (* Skip the cast keyword and opening paren. *) elna_lexer_read(cursor); elna_lexer_read(cursor); result^.expression := elna_parser_binary_expression(cursor); (* Skip the colon. *) elna_lexer_read(cursor); result^.type_expression := elna_parser_type_expression(cursor); (* Skip the closing paren. *) elna_lexer_read(cursor); return result end proc elna_parser_simple_expression(cursor: ^ElnaLexerCursor) -> ^ElnaTreeExpression var current_character: Word parser_node: ^ElnaTreeExpression token: ^ElnaLexerToken begin parser_node := nil; token := elna_lexer_peek(cursor); if token^.kind = ElnaLexerKind.character then parser_node := elna_parser_character_literal(cursor) elsif token^.kind = ElnaLexerKind.integer then parser_node := elna_parser_integer_literal(cursor) elsif token^.kind = ElnaLexerKind.string then parser_node := elna_parser_string_literal(cursor) elsif token^.kind = ElnaLexerKind.boolean then parser_node := elna_parser_boolean_literal(cursor) elsif token^.kind = ElnaLexerKind.null then parser_node := elna_parser_nil_literal(cursor) elsif token^.kind = ElnaLexerKind._cast then parser_node := elna_parser_cast_expression(cursor) elsif token^.kind = ElnaLexerKind.trait then parser_node := elna_parser_trait_expression(cursor) elsif token^.kind = ElnaLexerKind.identifier then parser_node := elna_parser_variable_expression(cursor) end; return parser_node end proc elna_parser_dereference_expression(cursor: ^ElnaLexerCursor, simple_expression: ^ElnaTreeExpression) -> ^ElnaTreeDereferenceExpression var result: ^ElnaTreeDereferenceExpression begin result := malloc(#size(ElnaTreeDereferenceExpression)); result^.kind := ElnaTreeKind.dereference_expression; result^.pointer := simple_expression; result^.type_decoration := nil; elna_lexer_read(cursor); return result end proc elna_parser_designator(cursor: ^ElnaLexerCursor) -> ^ElnaTreeExpression var simple_expression: ^ElnaTreeExpression token: ^ElnaLexerToken begin simple_expression := elna_parser_simple_expression(cursor); .elna_parser_designator_loop; token := elna_lexer_peek(cursor); if token^.kind = ElnaLexerKind.hat then simple_expression := elna_parser_dereference_expression(cursor, simple_expression); goto elna_parser_designator_loop elsif token^.kind = ElnaLexerKind.dot then simple_expression := elna_parser_field_access_expression(cursor, simple_expression); goto elna_parser_designator_loop elsif token^.kind = ElnaLexerKind.left_square then simple_expression := elna_parser_array_access_expression(cursor, simple_expression); goto elna_parser_designator_loop elsif token^.kind = ElnaLexerKind.left_paren then simple_expression := elna_parser_call(cursor, simple_expression); goto elna_parser_designator_loop end; return simple_expression end proc elna_tac_trait_expression(trait_node: ^ElnaTreeTraitExpression, operand: ^ElnaTacOperand) var symbol: ^ElnaSymbolTypeInfo info_type: ^ElnaType parser_node: ^ElnaTreeNamedTypeExpression begin parser_node := trait_node^.argument; symbol := elna_symbol_table_lookup(symbol_table_global, parser_node^.name, parser_node^.length); info_type := symbol^._type; operand^.kind := ElnaTacKind.constant; operand^.value := info_type^.size; operand^.length := 4 end proc elna_tac_cast_expression(instructions: ^ElnaList, parser_node: ^ElnaTreeCastExpression, symbol_table: ^ElnaSymbolTable, operand: ^ElnaTacOperand) begin elna_tac_binary_expression(instructions, parser_node^.expression, symbol_table, operand) end proc elna_tac_simple_expression(instructions: ^ElnaList, parser_node: ^ElnaTreeNode, symbol_table: ^ElnaSymbolTable, operand: ^ElnaTacOperand) begin if parser_node^.kind = ElnaTreeKind.character_literal then elna_tac_character_literal(parser_node, operand) elsif parser_node^.kind = ElnaTreeKind.string_literal then elna_tac_string_literal(instructions, parser_node, symbol_table, operand) elsif parser_node^.kind = ElnaTreeKind.integer_literal then elna_tac_integer_literal(parser_node, operand) elsif parser_node^.kind = ElnaTreeKind.boolean_literal then elna_tac_boolean_literal(parser_node, operand) elsif parser_node^.kind = ElnaTreeKind.null then elna_tac_nil_literal(parser_node, operand) elsif parser_node^.kind = ElnaTreeKind._cast then elna_tac_cast_expression(instructions, parser_node, symbol_table, operand) elsif parser_node^.kind = ElnaTreeKind.trait_expression then elna_tac_trait_expression(parser_node, operand) else elna_tac_variable_expression(parser_node, operand) end end proc elna_parser_unary_expression(cursor: ^ElnaLexerCursor) -> ^ElnaTreeExpression var result: ^ElnaTreeUnaryExpression operand: Word operator: Word token: ^ElnaLexerToken begin token := elna_lexer_peek(cursor); operator := 0; if token^.kind = ElnaLexerKind.at then operator := '@' elsif token^.kind = ElnaLexerKind.minus then operator := '-' elsif token^.kind = ElnaLexerKind.not then operator := '~' end; if operator <> 0 then elna_lexer_read(cursor) end; result := elna_parser_designator(cursor); if operator <> 0 then operand := result; result := malloc(#size(ElnaTreeUnaryExpression)); result^.kind := ElnaTreeKind.unary_expression; result^.operand := operand; result^.operator := operator; result^.type_decoration := nil end; return result end proc elna_tac_copy_address(instructions: ^ElnaList, operand_result: ^ElnaTacOperandResult, from: ^ElnaTacOperand, to: ^ElnaTacOperand) var instruction: ^ElnaTacInstruction begin if operand_result^.kind = ElnaTacOperandType.dereferenced_pointer then to^ := from^ elsif operand_result^.kind = ElnaTacOperandType.sub_object then instruction := elna_tac_instruction_create(ElnaTacOperator.get_address); elna_tac_instruction_set_operand(instruction, 1, from^.kind, from^.value, from^.length); elna_tac_instruction_set_operand(instruction, 2, to^.kind, to^.value, to^.length); elna_list_append(instructions, instruction); instruction := elna_tac_instruction_create(ElnaTacOperator.add); elna_tac_instruction_set_operand(instruction, 1, ElnaTacKind.constant, operand_result^.offset, 4); elna_tac_instruction_set_operand(instruction, 2, to^.kind, to^.value, to^.length); elna_tac_instruction_set_operand(instruction, 3, to^.kind, to^.value, to^.length); elna_list_append(instructions, instruction) else (* ElnaTacOperandType.plain_object *) instruction := elna_tac_instruction_create(ElnaTacOperator.get_address); elna_tac_instruction_set_operand(instruction, 1, from^.kind, from^.value, from^.length); elna_tac_instruction_set_operand(instruction, 2, to^.kind, to^.value, to^.length); elna_list_append(instructions, instruction) end end proc elna_tac_unary_expression(instructions: ^ElnaList, parser_node: ^ElnaTreeUnaryExpression, symbol_table: ^ElnaSymbolTable, operand: ^ElnaTacOperand) var token_kind: Word operator: Word unary_operand: ^ElnaTreeExpression operand_result: ElnaTacOperandResult instruction: ^ElnaTacInstruction base: ElnaTacOperand temporary_info: ^ElnaSymbolTemporaryInfo begin if parser_node^.kind = ElnaTreeKind.unary_expression then operator := parser_node^.operator; unary_operand := parser_node^.operand else operator := 0; unary_operand := parser_node end; elna_tac_designator(instructions, unary_operand, symbol_table, @operand_result, @base); if operator = '@' then elna_tac_make_variable(operand, symbol_table, word_type); elna_tac_copy_address(instructions, @operand_result, @base, operand) elsif operator = '-' then elna_tac_make_variable(operand, symbol_table, parser_node^.type_decoration); instruction := elna_tac_instruction_create(ElnaTacOperator.negate); elna_tac_instruction_set_operand(instruction, 1, base.kind, base.value, base.length); elna_tac_instruction_set_operand(instruction, 2, operand^.kind, operand^.value, operand^.length); elna_list_append(instructions, instruction) elsif operator = '~' then elna_tac_make_variable(operand, symbol_table, parser_node^.type_decoration); instruction := elna_tac_instruction_create(ElnaTacOperator.complement); elna_tac_instruction_set_operand(instruction, 1, base.kind, base.value, base.length); elna_tac_instruction_set_operand(instruction, 2, operand^.kind, operand^.value, operand^.length); elna_list_append(instructions, instruction) 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_operand(instruction, 1, base.kind, base.value, base.length); elna_tac_instruction_set_operand(instruction, 2, operand^.kind, operand^.value, operand^.length); elna_list_append(instructions, instruction) 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_operand(instruction, 1, base.kind, base.value, base.length); elna_tac_instruction_set_operand(instruction, 2, ElnaTacKind.constant, operand_result.offset, 4); elna_tac_instruction_set_operand(instruction, 3, operand^.kind, operand^.value, operand^.length); elna_list_append(instructions, instruction) else operand^.kind := base.kind; operand^.value := base.value; operand^.length := base.length end end proc elna_parser_binary_expression(cursor: ^ElnaLexerCursor) -> ^ElnaTreeExpression var lhs_node: ^ElnaTreeExpression rhs_node: ^ElnaTreeExpression result: ^ElnaTreeBinaryExpression token: ^ElnaLexerToken begin lhs_node := elna_parser_unary_expression(cursor); rhs_node := nil; token := elna_lexer_peek(cursor); if token^.kind = ElnaLexerKind.plus then elna_lexer_read(cursor); rhs_node := elna_parser_unary_expression(cursor) elsif token^.kind = ElnaLexerKind.minus then elna_lexer_read(cursor); rhs_node := elna_parser_unary_expression(cursor) elsif token^.kind = ElnaLexerKind.multiplication then elna_lexer_read(cursor); rhs_node := elna_parser_unary_expression(cursor) elsif token^.kind = ElnaLexerKind.and then elna_lexer_read(cursor); rhs_node := elna_parser_unary_expression(cursor) elsif token^.kind = ElnaLexerKind._or then elna_lexer_read(cursor); rhs_node := elna_parser_unary_expression(cursor) elsif token^.kind = ElnaLexerKind._xor then elna_lexer_read(cursor); rhs_node := elna_parser_unary_expression(cursor) elsif token^.kind = ElnaLexerKind.equals then elna_lexer_read(cursor); rhs_node := elna_parser_unary_expression(cursor) elsif token^.kind = ElnaLexerKind.remainder then elna_lexer_read(cursor); rhs_node := elna_parser_unary_expression(cursor) elsif token^.kind = ElnaLexerKind.division then elna_lexer_read(cursor); rhs_node := elna_parser_unary_expression(cursor) elsif token^.kind = ElnaLexerKind.less_than then elna_lexer_read(cursor); rhs_node := elna_parser_unary_expression(cursor) elsif token^.kind = ElnaLexerKind.greater_than then elna_lexer_read(cursor); rhs_node := elna_parser_unary_expression(cursor) elsif token^.kind = ElnaLexerKind.less_equal then elna_lexer_read(cursor); rhs_node := elna_parser_unary_expression(cursor) elsif token^.kind = ElnaLexerKind.not_equal then elna_lexer_read(cursor); rhs_node := elna_parser_unary_expression(cursor) elsif token^.kind = ElnaLexerKind.greater_equal then elna_lexer_read(cursor); rhs_node := elna_parser_unary_expression(cursor) end; if rhs_node <> nil then result := malloc(#size(ElnaTreeBinaryExpression)); result^.kind := ElnaTreeKind.binary_expression; result^.lhs := lhs_node; result^.rhs := rhs_node; result^.operator := token^.kind; result^.type_decoration := nil else result := lhs_node end; return result end proc elna_tac_add_pointer(parser_node: ^ElnaTreeExpression, lhs: ^ElnaTacOperand, rhs: ^ElnaTacOperand, symbol_table: ^ElnaSymbolTable, operand: ^ElnaTacOperand) -> ^ElnaTacInstruction var instruction: ^ElnaTacInstruction pointer_type: ^ElnaTypePointer begin pointer_type := parser_node^.type_decoration; instruction := elna_tac_instruction_create(ElnaTacOperator.add_ptr); elna_tac_make_variable(operand, symbol_table, word_type); elna_tac_instruction_set_operand(instruction, 1, lhs^.kind, lhs^.value, lhs^.length); elna_tac_instruction_set_operand(instruction, 2, rhs^.kind, rhs^.value, rhs^.length); elna_tac_instruction_set_operand(instruction, 3, ElnaTacKind.constant, pointer_type^.base^.size, 4); elna_tac_instruction_set_operand(instruction, 4, operand^.kind, operand^.value, operand^.length); return instruction end proc elna_tac_subtract_create(instructions: ^ElnaList, parser_node: ^ElnaTreeBinaryExpression, lhs: ^ElnaTacOperand, rhs: ^ElnaTacOperand, symbol_table: ^ElnaSymbolTable, operand: ^ElnaTacOperand) var instruction: ^ElnaTacInstruction lhs_is_pointer: Word rhs_is_not_pointer: Word begin lhs_is_pointer := parser_node^.lhs^.type_decoration^.kind = ElnaTypeKind.pointer; rhs_is_not_pointer := parser_node^.rhs^.type_decoration^.kind <> ElnaTypeKind.pointer; if lhs_is_pointer & rhs_is_not_pointer then instruction := elna_tac_instruction_create(ElnaTacOperator.negate); elna_tac_instruction_set_operand(instruction, 1, rhs^.kind, rhs^.value, rhs^.length); elna_tac_instruction_set_operand(instruction, 2, operand^.kind, operand^.value, operand^.length); elna_list_append(instructions, instruction); instruction := elna_tac_add_pointer(parser_node^.lhs, lhs, operand, symbol_table, operand); elna_list_append(instructions, instruction) else elna_tac_binary_create(instructions, ElnaTacOperator.subtract, lhs, rhs, symbol_table, operand) end end proc elna_tac_binary_create(instructions: ^ElnaList, operator: ElnaTacOperator, lhs: ^ElnaTacOperand, rhs: ^ElnaTacOperand, symbol_table: ^ElnaSymbolTable, operand: ^ElnaTacOperand) var instruction: ^ElnaTacInstruction lhs_symbol: ^ElnaSymbolTemporaryInfo variable_type: ^ElnaType begin if lhs^.kind = ElnaTacKind.variable then lhs_symbol := elna_symbol_table_lookup(symbol_table, lhs^.value, lhs^.length); variable_type := lhs_symbol^.variable_type else variable_type := word_type end; elna_tac_make_variable(operand, symbol_table, variable_type); instruction := elna_tac_instruction_create(operator); elna_tac_instruction_set_operand(instruction, 1, lhs^.kind, lhs^.value, lhs^.length); elna_tac_instruction_set_operand(instruction, 2, rhs^.kind, rhs^.value, rhs^.length); elna_tac_instruction_set_operand(instruction, 3, operand^.kind, operand^.value, operand^.length); elna_list_append(instructions, instruction) end proc elna_tac_binary_expression(instructions: ^ElnaList, parser_node: ^ElnaTreeBinaryExpression, symbol_table: ^ElnaSymbolTable, operand: ^ElnaTacOperand) var 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 elna_tac_unary_expression(instructions, parser_node^.lhs, symbol_table, @lhs); elna_tac_unary_expression(instructions, parser_node^.rhs, symbol_table, @rhs); if parser_node^.operator = ElnaLexerKind.plus then if parser_node^.lhs^.type_decoration^.kind = ElnaTypeKind.pointer then instruction := elna_tac_add_pointer(parser_node^.lhs, @lhs, @rhs, symbol_table, operand); elna_list_append(instructions, instruction) elsif parser_node^.rhs^.type_decoration^.kind = ElnaTypeKind.pointer then instruction := elna_tac_add_pointer(parser_node^.rhs, @rhs, @lhs, symbol_table, operand); elna_list_append(instructions, instruction) else elna_tac_binary_create(instructions, ElnaTacOperator.add, @lhs, @rhs, symbol_table, operand) end elsif parser_node^.operator = ElnaLexerKind.minus then elna_tac_subtract_create(instructions, parser_node, @lhs, @rhs, symbol_table, operand) elsif parser_node^.operator = ElnaLexerKind.multiplication then elna_tac_binary_create(instructions, ElnaTacOperator.multiply, @lhs, @rhs, symbol_table, operand) elsif parser_node^.operator = ElnaLexerKind.and then elna_tac_binary_create(instructions, ElnaTacOperator.and, @lhs, @rhs, symbol_table, operand) elsif parser_node^.operator = ElnaLexerKind._or then elna_tac_binary_create(instructions, ElnaTacOperator._or, @lhs, @rhs, symbol_table, operand) elsif parser_node^.operator = ElnaLexerKind._xor then elna_tac_binary_create(instructions, ElnaTacOperator._xor, @lhs, @rhs, symbol_table, operand) elsif parser_node^.operator = ElnaLexerKind.equals then elna_tac_binary_create(instructions, ElnaTacOperator.equal, @lhs, @rhs, symbol_table, operand) elsif parser_node^.operator = ElnaLexerKind.remainder then elna_tac_binary_create(instructions, ElnaTacOperator.remainder, @lhs, @rhs, symbol_table, operand) elsif parser_node^.operator = ElnaLexerKind.division then elna_tac_binary_create(instructions, ElnaTacOperator.divide, @lhs, @rhs, symbol_table, operand) elsif parser_node^.operator = ElnaLexerKind.less_than then elna_tac_binary_create(instructions, ElnaTacOperator.less_than, @lhs, @rhs, symbol_table, operand) elsif parser_node^.operator = ElnaLexerKind.greater_than then elna_tac_binary_create(instructions, ElnaTacOperator.greater_than, @lhs, @rhs, symbol_table, operand) elsif parser_node^.operator = ElnaLexerKind.less_equal then elna_tac_binary_create(instructions, ElnaTacOperator.less_or_equal, @lhs, @rhs, symbol_table, operand) elsif parser_node^.operator = ElnaLexerKind.greater_equal then elna_tac_binary_create(instructions, ElnaTacOperator.greater_or_equal, @lhs, @rhs, symbol_table, operand) elsif parser_node^.operator = ElnaLexerKind.not_equal then elna_tac_binary_create(instructions, ElnaTacOperator.not_equal, @lhs, @rhs, symbol_table, operand) end end end proc elna_parser_call(cursor: ^ElnaLexerCursor, callee: ^ElnaTreeExpression) -> ^ElnaTreeCall var result: ^ElnaTreeCall argument_number: Word argument_entry: ^ElnaTreeExpressionList token: ^ElnaLexerToken begin result := malloc(#size(ElnaTreeCall)); result^.kind := ElnaTreeKind.call; result^.next := nil; result^.arguments := nil; argument_number := 1; result^.callee := callee; elna_lexer_read(cursor); token := elna_lexer_peek(cursor); if token^.kind = ElnaLexerKind.right_paren then elna_lexer_read(cursor); goto elna_parser_call_end end; .elna_parser_call_loop; if result^.arguments = nil then result^.arguments := malloc(#size(ElnaTreeExpressionList)); argument_entry := result^.arguments else argument_entry^.next := malloc(#size(ElnaTreeExpressionList)); argument_entry := argument_entry^.next end; argument_entry^.next := nil; argument_entry^.expression := elna_parser_binary_expression(cursor); argument_number := argument_number + 1; token := elna_lexer_read(cursor); if token^.kind = ElnaLexerKind.comma then goto elna_parser_call_loop end; .elna_parser_call_end; (* Save the number of arguments. *) result^.count := argument_number - 1; return result end 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 begin parsed_expression := parsed_call^.callee; arguments_operand := malloc(parsed_call^.count * #size(ElnaTacOperand)); elna_tac_make_variable(operand, symbol_table, word_type); call_instruction := elna_tac_instruction_create(ElnaTacOperator.proc_call); elna_tac_instruction_set_operand(call_instruction, 1, ElnaTacKind.label, parsed_expression^.name, parsed_expression^.length); elna_tac_instruction_set_operand(call_instruction, 2, ElnaTacKind.list, arguments_operand, parsed_call^.count); elna_tac_instruction_set_operand(call_instruction, 3, operand^.kind, operand^.value, operand^.length); argument_entry := parsed_call^.arguments; .elna_tac_call_loop; if argument_entry <> nil then elna_tac_binary_expression(instructions, argument_entry^.expression, symbol_table, arguments_operand); arguments_operand := arguments_operand + 1; argument_entry := argument_entry^.next; goto elna_tac_call_loop end; elna_list_append(instructions, call_instruction) end proc elna_parser_goto_statement(cursor: ^ElnaLexerCursor) -> ^ElnaTreeGotoStatement var result: ^ElnaTreeGotoStatement token: ^ElnaLexerToken begin elna_lexer_read(cursor); token := elna_lexer_read(cursor); result := malloc(#size(ElnaTreeGotoStatement)); result^.kind := ElnaTreeKind.goto_statement; result^.next := nil; result^.label := token^.start; result^.length := token^.length; return result end proc elna_tac_goto_statement(instructions: ^ElnaList, parser_node: ^ElnaTreeGotoStatement) var label_length: Word label_with_dot: ^Char instruction: ^ElnaTacInstruction begin label_length := parser_node^.length + 1; label_with_dot := malloc(label_length); label_with_dot^ := '.'; memcpy(label_with_dot + 1, parser_node^.label, parser_node^.length); instruction := elna_tac_instruction_create(ElnaTacOperator.jump); elna_tac_instruction_set_operand(instruction, 1, ElnaTacKind.label, label_with_dot, label_length); elna_list_append(instructions, instruction) end proc elna_parser_label_declaration(cursor: ^ElnaLexerCursor) -> ^ElnaTreeLabelDeclaration var result: ^ElnaTreeLabelDeclaration token: ^ElnaLexerToken begin elna_lexer_read(cursor); token := elna_lexer_read(cursor); result := malloc(#size(ElnaTreeLabelDeclaration)); result^.kind := ElnaTreeKind.label_declaration; result^.next := nil; result^.label := token^.start; result^.length := token^.length; return result end proc elna_tac_label_declaration(instructions: ^ElnaList, parser_node: ^ElnaTreeLabelDeclaration) begin elna_tac_label(instructions, parser_node^.label, parser_node^.length) end proc elna_tac_enumeration_value(field_access_expression: ^ElnaTreeFieldAccessExpression, operand: ^ElnaTacOperand) var enumeration_type: ^ElnaTypeEnumeration members: Word members_length: Word token_type: Word symbol_info: ^ElnaSymbolTypeInfo member_name: Word member_length: Word counter: Word enumeration_type_name: ^ElnaTreeVariableExpression begin enumeration_type_name := field_access_expression^.aggregate; symbol_info := elna_symbol_table_lookup(symbol_table_global, enumeration_type_name^.name, enumeration_type_name^.length); enumeration_type := symbol_info^._type; members := enumeration_type^.members; members_length := enumeration_type^.length; counter := 1; .elna_tac_enumeration_value_members; if members_length > 0 then member_name := members^; member_length := members + 4; member_length := member_length^; if string_compare(field_access_expression^.field, field_access_expression^.length, member_name, member_length) = 0 then members_length := members_length - 1; members := members + 8; counter := counter + 1; goto elna_tac_enumeration_value_members end; (* Found. *) operand^.kind := ElnaTacKind.constant; operand^.value := counter; operand^.length := 4 end end proc elna_parser_field_access_expression(cursor: ^ElnaLexerCursor, aggregate: ^ElnaTreeExpression) -> ^ElnaTreeFieldAccessExpression var result: ^ElnaTreeFieldAccessExpression token: ^ElnaLexerToken begin (* Skip dot. Read the enumeration value. *) elna_lexer_read(cursor); token := elna_lexer_read(cursor); result := malloc(#size(ElnaTreeFieldAccessExpression)); result^.kind := ElnaTreeKind.field_access_expression; result^.type_decoration := nil; result^.aggregate := aggregate; result^.field := token^.start; result^.length := token^.length; return result end proc elna_parser_array_access_expression(cursor: ^ElnaLexerCursor, array: ^ElnaTreeExpression) -> ^ElnaTreeArrayAccessExpression var result: ^ElnaTreeArrayAccessExpression begin elna_lexer_read(cursor); result := malloc(#size(ElnaTreeArrayAccessExpression)); result^.kind := ElnaTreeKind.array_access_expression; result^.type_decoration := nil; result^.array := array; result^.index := elna_parser_binary_expression(cursor); elna_lexer_read(cursor); return result end 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_operand(load_instruction, 1, intermediate.kind, intermediate.value, intermediate.length); elna_tac_instruction_set_operand(load_instruction, 2, operand^.kind, operand^.value, operand^.length); elna_list_append(instructions, load_instruction) 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_operand(load_instruction, 1, intermediate.kind, intermediate.value, intermediate.length); elna_tac_instruction_set_operand(load_instruction, 2, ElnaTacKind.constant, operand_result.offset, 4); elna_tac_instruction_set_operand(load_instruction, 3, operand^.kind, operand^.value, operand^.length); elna_list_append(instructions, load_instruction) end end 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 := 0; if parser_node^.kind = ElnaTreeKind.dereference_expression then elna_tac_dereference_expression(instructions, parser_node, symbol_table, operand); operand_result^.kind := ElnaTacOperandType.dereferenced_pointer 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 elna_tac_enumeration_value(field_access_expression, operand) else elna_tac_field_access_expression(instructions, field_access_expression, symbol_table, operand_result, operand) end elsif parser_node^.kind = ElnaTreeKind.array_access_expression then elna_tac_array_access_expression(instructions, parser_node, symbol_table, operand); operand_result^.kind := ElnaTacOperandType.dereferenced_pointer elsif parser_node^.kind = ElnaTreeKind.call then elna_tac_call(instructions, parser_node, symbol_table, operand) else elna_tac_simple_expression(instructions, parser_node, symbol_table, operand) end end 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 base: ElnaTacOperand begin designator_base := field_access_expression^.aggregate; aggregate_type := designator_base^.type_decoration; elna_tac_designator(instructions, designator_base, symbol_table, operand_result, @base); field_count := aggregate_type^.length; current_field := aggregate_type^.members; field_offset := 0; .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 + 1; field_offset := field_offset + field_type^.size; goto elna_tac_field_access_expression_field 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_operand(instruction, 1, base.kind, base.value, base.length); elna_tac_instruction_set_operand(instruction, 2, ElnaTacKind.constant, field_offset, 4); elna_tac_instruction_set_operand(instruction, 3, operand^.kind, operand^.value, operand^.length); elna_list_append(instructions, instruction) elsif operand_result^.kind = ElnaTacOperandType.sub_object then operand^ := base; operand_result^.offset := operand_result^.offset + field_offset else (* ElnaTacOperandType.plain_object *) operand^ := base; operand_result^.kind := ElnaTacOperandType.sub_object; operand_result^.offset := field_offset end end 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 begin designator_base := array_access_expression^.array; aggregate_type := designator_base^.type_decoration; element_type := aggregate_type^.base; elna_tac_binary_expression(instructions, array_access_expression^.index, symbol_table, @inter_operand); elna_tac_make_variable(@index_operand, symbol_table, word_type); instruction := elna_tac_instruction_create(ElnaTacOperator.subtract); elna_tac_instruction_set_operand(instruction, 1, inter_operand.kind, inter_operand.value, inter_operand.length); elna_tac_instruction_set_operand(instruction, 2, ElnaTacKind.constant, 1, 4); elna_tac_instruction_set_operand(instruction, 3, index_operand.kind, index_operand.value, index_operand.length); elna_list_append(instructions, instruction); 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_operand(instruction, 1, operand^.kind, operand^.value, operand^.length); elna_tac_instruction_set_operand(instruction, 2, index_operand.kind, index_operand.value, index_operand.length); elna_tac_instruction_set_operand(instruction, 3, ElnaTacKind.constant, element_type^.size, 4); elna_tac_instruction_set_operand(instruction, 4, operand^.kind, operand^.value, operand^.length); elna_list_append(instructions, instruction) end proc elna_parser_assign_statement(cursor: ^ElnaLexerCursor, assignee: ^ElnaTreeNode) -> ^ElnaTreeAssignStatement var result: ^ElnaTreeAssignStatement begin result := malloc(#size(ElnaTreeAssignStatement)); result^.kind := ElnaTreeKind.assign_statement; result^.next := nil; result^.assignee := assignee; (* Skip the assignment sign (:=) with surrounding whitespaces. *) elna_lexer_read(cursor); result^.assignment := elna_parser_binary_expression(cursor); return result end proc elna_tac_assign_statement(instructions: ^ElnaList, parser_tree: ^ElnaTreeAssignStatement, symbol_table: ^ElnaSymbolTable) var operand_result: ElnaTacOperandResult instruction: ^ElnaTacInstruction assignment_operand: ElnaTacOperand assignee: ElnaTacOperand symbol_info: ^ElnaSymbolInfo begin elna_tac_designator(instructions, parser_tree^.assignee, symbol_table, @operand_result, @assignee); (* Compile the assignment. *) 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_operand(instruction, 1, assignment_operand.kind, assignment_operand.value, assignment_operand.length); elna_tac_instruction_set_operand(instruction, 2, assignee.kind, assignee.value, assignee.length) elsif operand_result.kind = ElnaTacOperandType.sub_object then instruction := elna_tac_instruction_create(ElnaTacOperator.copy_to_offset); elna_tac_instruction_set_operand(instruction, 1, assignment_operand.kind, assignment_operand.value, assignment_operand.length); elna_tac_instruction_set_operand(instruction, 2, assignee.kind, assignee.value, assignee.length); elna_tac_instruction_set_operand(instruction, 3, ElnaTacKind.constant, operand_result.offset, 4) else instruction := elna_tac_instruction_create(ElnaTacOperator.copy); elna_tac_instruction_set_operand(instruction, 1, assignment_operand.kind, assignment_operand.value, assignment_operand.length); elna_tac_instruction_set_operand(instruction, 2, assignee.kind, assignee.value, assignee.length) end; elna_list_append(instructions, instruction) end proc elna_parser_return_statement(cursor: ^ElnaLexerCursor) -> ^ElnaTreeReturnStatement var returned: ^ElnaTreeExpression label_length: Word result: ^ElnaTreeReturnStatement begin (* Skip "return" keyword and whitespace after it. *) elna_lexer_read(cursor); returned := elna_parser_binary_expression(cursor); result := malloc(#size(ElnaTreeReturnStatement)); result^.kind := ElnaTreeKind.return_statement; result^.next := nil; result^.returned := returned; return result end proc elna_tac_return_statement(instructions: ^ElnaList, parser_node: ^ElnaTreeReturnStatement, symbol_table: ^ElnaSymbolTable) var instruction: ^ElnaTacInstruction operand: ElnaTacOperand begin elna_tac_binary_expression(instructions, parser_node^.returned, symbol_table, @operand); instruction := elna_tac_instruction_create(ElnaTacOperator._return); elna_tac_instruction_set_operand(instruction, 1, operand.kind, operand.value, operand.length); elna_list_append(instructions, instruction) end (** * Writes a label, .Ln, where n is a unique number. * * Parameters: * counter - Label counter. *) proc _write_label(counter: Word, length: Word) var first_byte: Word begin if length = 0 then printf(".L%i\0".ptr, counter) else printf(".%.*s\0".ptr, length, counter); end; fflush(nil) end proc elna_parser_conditional_statements(cursor: ^ElnaLexerCursor) -> ^ElnaTreeConditionalStatements var result: ^ElnaTreeConditionalStatements begin result := malloc(#size(ElnaTreeConditionalStatements)); (* Skip "if", "while" or "elsif". *) elna_lexer_read(cursor); result^.condition := elna_parser_binary_expression(cursor); (* Skip "then" or "do". *) elna_lexer_read(cursor); result^.statements := elna_parser_statements(cursor); result^.next := nil; return result end proc elna_tac_conditional_statements(instructions: ^ElnaList, parser_node: ^ElnaTreeConditionalStatements, after_end_label: Word, symbol_table: ^ElnaSymbolTable) var condition_label: Word instruction: ^ElnaTacInstruction operand: ElnaTacOperand begin (* Compile condition. *) elna_tac_binary_expression(instructions, parser_node^.condition, symbol_table, @operand); (* condition_label is the label in front of the next elsif condition or end. *) condition_label := label_counter; label_counter := label_counter + 1; instruction := elna_tac_instruction_create(ElnaTacOperator.jump_if_zero); elna_tac_instruction_set_operand(instruction, 1, operand.kind, operand.value, operand.length); elna_tac_instruction_set_operand(instruction, 2, ElnaTacKind.label, condition_label, 0); elna_list_append(instructions, instruction); elna_tac_statements(instructions, parser_node^.statements, symbol_table); instruction := elna_tac_instruction_create(ElnaTacOperator.jump); elna_tac_instruction_set_operand(instruction, 1, ElnaTacKind.label, after_end_label, 0); elna_list_append(instructions, instruction); elna_tac_label(instructions, condition_label, 0) end proc elna_parser_if_statement(cursor: ^ElnaLexerCursor) -> ^ElnaTreeIfStatement var result: ^ElnaTreeIfStatement previous_conditional: ^ElnaTreeConditionalStatements next_conditional: ^ElnaTreeConditionalStatements token: ^ElnaLexerToken begin result := malloc(#size(ElnaTreeIfStatement)); result^.kind := ElnaTreeKind.if_statement; result^.next := nil; previous_conditional := elna_parser_conditional_statements(cursor); result^.conditionals := previous_conditional; .elna_parser_if_statement_loop; token := elna_lexer_peek(cursor); if token^.kind = ElnaLexerKind._elsif then next_conditional := elna_parser_conditional_statements(cursor); previous_conditional^.next := next_conditional; previous_conditional = next_conditional; goto elna_parser_if_statement_loop elsif token^.kind = ElnaLexerKind._else then elna_lexer_read(cursor); result^._else := elna_parser_statements(cursor) else result^._else := nil end; elna_lexer_read(cursor); return result end proc elna_parser_statement(cursor: ^ElnaLexerCursor) -> ^ElnaTreeStatement var result: ^ElnaTreeStatement designator: ^ElnaTreeNode token: ^ElnaLexerToken begin result := nil; token := elna_lexer_peek(cursor); if token^.kind = ElnaLexerKind._goto then result := elna_parser_goto_statement(cursor) elsif token^.kind = ElnaLexerKind._if then result := elna_parser_if_statement(cursor) elsif token^.kind = ElnaLexerKind._return then result := elna_parser_return_statement(cursor) elsif token^.kind = ElnaLexerKind.dot then result := elna_parser_label_declaration(cursor) elsif token^.kind = ElnaLexerKind.identifier then designator := elna_parser_designator(cursor); if designator^.kind <> ElnaTreeKind.call then result := elna_parser_assign_statement(cursor, designator) else result := designator end end; return result end proc elna_parser_statements(cursor: ^ElnaLexerCursor) -> ^ElnaTreeStatement var previous_statement: ^ElnaTreeStatement next_statement: ^ElnaTreeStatement first_statement: ^ElnaTreeStatement token: ^ElnaLexerToken begin elna_lexer_skip_empty_lines(cursor); first_statement := elna_parser_statement(cursor); previous_statement := first_statement; if previous_statement = 0 then goto elna_parser_statements_end end; .elna_parser_statement_loop; token := elna_lexer_peek(cursor); if token^.kind = ElnaLexerKind.semicolon then elna_lexer_read(cursor); elna_lexer_skip_empty_lines(cursor); next_statement := elna_parser_statement(cursor); previous_statement^.next := next_statement; previous_statement := next_statement; if previous_statement <> 0 then goto elna_parser_statement_loop end end; .elna_parser_statements_end; elna_lexer_skip_empty_lines(cursor); return first_statement end proc elna_tac_statements(instructions: ^ElnaList, current_statement: ^ElnaTreeStatement, symbol_table: ^ElnaSymbolTable) begin .elna_tac_statements_loop; if current_statement <> nil then elna_tac_statement(instructions, current_statement, symbol_table); current_statement := current_statement^.next; goto elna_tac_statements_loop end end proc elna_tac_if_statement(instructions: ^ElnaList, parser_node: ^ElnaTreeIfStatement, symbol_table: ^ElnaSymbolTable) var current_node: ^ElnaTreeConditionalStatements after_end_label: Word condition_label: Word begin after_end_label := label_counter; label_counter := label_counter + 1; current_node := parser_node^.conditionals; elna_tac_conditional_statements(instructions, current_node, after_end_label, symbol_table); .elna_tac_if_statement_loop; current_node := current_node^.next; if current_node <> nil then elna_tac_conditional_statements(instructions, current_node, after_end_label, symbol_table); goto elna_tac_if_statement_loop end; current_node := parser_node^._else; if parser_node^._else <> nil then elna_tac_statements(instructions, parser_node^._else, symbol_table) end; elna_tac_label(instructions, after_end_label, 0) end proc elna_tac_statement(instructions: ^ElnaList, parser_node: ^ElnaTreeNode, symbol_table: ^ElnaSymbolTable) var operand: ElnaTacOperand begin if parser_node^.kind = ElnaTreeKind.goto_statement then elna_tac_goto_statement(instructions, parser_node) elsif parser_node^.kind = ElnaTreeKind.if_statement then elna_tac_if_statement(instructions, parser_node, symbol_table) elsif parser_node^.kind = ElnaTreeKind.return_statement then elna_tac_return_statement(instructions, parser_node, symbol_table) elsif parser_node^.kind = ElnaTreeKind.label_declaration then elna_tac_label_declaration(instructions, parser_node) elsif parser_node^.kind = ElnaTreeKind.call then elna_tac_call(instructions, parser_node, symbol_table, @operand) elsif parser_node^.kind = ElnaTreeKind.assign_statement then elna_tac_assign_statement(instructions, parser_node, symbol_table) end end (** * Writes a regster name to the standard output. * * Parameters: * register_character - Register character. * register_number - Register number. *) proc _write_register(register_character: Word, register_number: Word) begin _write_c(register_character); _write_c(register_number + '0') end proc elna_parser_record_type_expression(cursor: ^ElnaLexerCursor) -> ^ElnaTreeRecordTypeExpression var entry: ^ElnaTreeField result: ^ElnaTreeRecordTypeExpression previous_entry: ^ElnaTreeField token: ^ElnaLexerToken begin elna_lexer_read(cursor); result := malloc(#size(ElnaTreeRecordTypeExpression)); result^.members := nil; result^.length := 0; token := elna_lexer_read(cursor); if token^.kind = ElnaLexerKind._end then goto elna_parser_record_type_expression_end end; .elna_parser_record_type_expression_loop; entry := malloc(#size(ElnaTreeField)); result^.length := result^.length + 1; entry^.name := token^.start; entry^.length := token^.length; (* Skip the colon. *) elna_lexer_read(cursor); entry^.type_expression := elna_parser_type_expression(cursor); entry^.next := nil; if result^.members = nil then result^.members := entry else previous_entry^.next := entry end; previous_entry := entry; token := elna_lexer_read(cursor); if token^.kind = ElnaLexerKind.semicolon then token := elna_lexer_read(cursor); goto elna_parser_record_type_expression_loop end; .elna_parser_record_type_expression_end; result^.kind := ElnaTreeKind.record_type_expression; return result end proc elna_parser_enumeration_type_expression(cursor: ^ElnaLexerCursor) -> ^ElnaTreeEnumerationTypeExpression var result: ^ElnaTreeEnumerationTypeExpression entry: ^ElnaTreeEnumeration previous_entry: ^ElnaTreeEnumeration token: ^ElnaLexerToken begin elna_lexer_read(cursor); result := malloc(#size(ElnaTreeEnumerationTypeExpression)); result^.kind := ElnaTreeKind.enumeration_type_expression; result^.members := nil; result^.length := 0; .elna_parser_enumeration_type_expression_loop; token := elna_lexer_read(cursor); entry := malloc(#size(ElnaTreeEnumeration)); result^.length := result^.length + 1; entry^.name := token^.start; entry^.length := token^.length; entry^.next := nil; if result^.members = nil then result^.members := entry else previous_entry^.next := entry end; previous_entry := entry; (* Skip the identifier. *) token := elna_lexer_read(cursor); if token^.kind = ElnaLexerKind.comma then goto elna_parser_enumeration_type_expression_loop end; return result end (** * Reads and creates enumeration type representation. * * record * type_kind: Word; * size: Word; * members: StringArray; * length: Word * end; * * Returns enumeration type description. *) proc elna_name_enumeration_type_expression(parser_node: ^ElnaTreeEnumerationTypeExpression) -> ^ElnaTypeEnumeration var result: ^ElnaTypeEnumeration memory_start: Word member_count: Word member_array_start: Word member_array_current: Word begin result := malloc(#size(ElnaTypeEnumeration)); memory_start := parser_node^.members; member_count := parser_node^.length; (* Copy the list of enumeration members into an array of strings. *) member_array_start := malloc(member_count * 8); member_array_current := member_array_start; .elna_name_type_enumeration_loop; if member_count > 0 then member_array_current^ := memory_start^; member_array_current := member_array_current + 4; memory_start := memory_start + 4; member_array_current^ := memory_start^; member_array_current := member_array_current + 4; memory_start := memory_start + 4; memory_start := memory_start^; member_count := member_count - 1; goto elna_name_type_enumeration_loop end; member_count := parser_node^.length; result^.kind := ElnaTypeKind.enumeration; result^.size := 4; result^.alignment := 4; result^.members := member_array_start; result^.length := member_count; return result end proc elna_name_pointer_type_expression(parser_node: ^ElnaTreePointerTypeExpression) -> ^ElnaTypePointer var result: ^ElnaTypePointer begin result := malloc(#size(ElnaTypePointer)); result^.kind := ElnaTypeKind.pointer; result^.size := 4; result^.alignment := 4; result^.base := elna_name_type_expression(parser_node^.base); return result end proc elna_name_array_type_expression(parser_node: ^ElnaTreeArrayTypeExpression) -> ^ElnaTypeArray var base: ^ElnaType result: ^ElnaTypeArray length: ^ElnaTreeIntegerLiteral begin result := malloc(#size(ElnaTypeArray)); base := elna_name_type_expression(parser_node^.base); length := parser_node^.length; result^.kind := ElnaTypeKind.array; (* Array size in bytes. *) result^.size := base^.size * length^.value; result^.alignment := base^.alignment; result^.base := base; result^.length := length^.value; return result end proc elna_name_record_type_expression(parser_node: ^ElnaTreeRecordTypeExpression) -> ^ElnaTypeRecord var result: ^ElnaTypeRecord tree_field: ^ElnaTreeField member_array_start: ^ElnaTypeField member_array_current: ^ElnaTypeField field_type: ^ElnaType begin result := malloc(#size(ElnaTypeRecord)); result^.kind := ElnaTypeKind._record; result^.size := 0; result^.alignment := 0; result^.length := 0; result^.members := malloc(parser_node^.length * #size(ElnaTypeField)); tree_field := parser_node^.members; member_array_current := result^.members; .elna_name_type_record_loop; if result^.length < parser_node^.length then member_array_current^.name := tree_field^.name; member_array_current^.length := tree_field^.length; field_type := elna_name_type_expression(tree_field^.type_expression); result^.size := result^.size + field_type^.size; if field_type^.alignment > result^.alignment then result^.alignment := field_type^.alignment end; member_array_current^.field_type := field_type; member_array_current := member_array_current + 1; tree_field := tree_field^.next; result^.length := result^.length + 1; goto elna_name_type_record_loop end; return result end proc elna_parser_named_type_expression(cursor: ^ElnaLexerCursor) -> ^ElnaTreeNamedTypeExpression var result: ^ElnaTreeNamedTypeExpression token: ^ElnaLexerToken begin result := malloc(#size(ElnaTreeNamedTypeExpression)); token := elna_lexer_read(cursor); result^.kind := ElnaTreeKind.named_type_expression; result^.name := token^.start; result^.length := token^.length; return result end proc elna_parser_pointer_type_expression(cursor: ^ElnaLexerCursor) -> ^ElnaTreePointerTypeExpression var result: ^ElnaTreePointerTypeExpression begin elna_lexer_read(cursor); result := malloc(#size(ElnaTreePointerTypeExpression)); result^.kind := ElnaTreeKind.pointer_type_expression; result^.base := elna_parser_type_expression(cursor); return result end proc elna_parser_array_type_expression(cursor: ^ElnaLexerCursor) -> ^ElnaTreeArrayTypeExpression var result: ^ElnaTreeArrayTypeExpression begin elna_lexer_read(cursor); result := malloc(#size(ElnaTreeArrayTypeExpression)); result^.kind := ElnaTreeKind.array_type_expression; result^.length := elna_parser_binary_expression(cursor); (* Read and skip square bracket. *) elna_lexer_read(cursor); result^.base := elna_parser_type_expression(cursor); return result end proc elna_parser_type_expression(cursor: ^ElnaLexerCursor) -> ^ElnaTreeNode var result: ^ElnaTreeNode token: ^ElnaLexerToken begin result := nil; token := elna_lexer_peek(cursor); if token^.kind = ElnaLexerKind.identifier then result := elna_parser_named_type_expression(cursor) elsif token^.kind = ElnaLexerKind.left_paren then result := elna_parser_enumeration_type_expression(cursor) elsif token^.kind = ElnaLexerKind._record then result := elna_parser_record_type_expression(cursor) elsif token^.kind = ElnaLexerKind.hat then result := elna_parser_pointer_type_expression(cursor) elsif token^.kind = ElnaLexerKind.left_square then result := elna_parser_array_type_expression(cursor) end; return result end proc elna_name_type_expression(parser_node: ^ElnaTreeNode) -> ^ElnaType var named_type_expression: ^ElnaTreeNamedTypeExpression type_symbol: ^ElnaSymbolTypeInfo result: ^ElnaType begin if parser_node^.kind = ElnaTreeKind.named_type_expression then named_type_expression := parser_node; type_symbol := elna_symbol_table_lookup(symbol_table_global, named_type_expression^.name, named_type_expression^.length); result := type_symbol^._type elsif parser_node^.kind = ElnaTreeKind.enumeration_type_expression then result := elna_name_enumeration_type_expression(parser_node) elsif parser_node^.kind = ElnaTreeKind.record_type_expression then result := elna_name_record_type_expression(parser_node) elsif parser_node^.kind = ElnaTreeKind.pointer_type_expression then result := elna_name_pointer_type_expression(parser_node) elsif parser_node^.kind = ElnaTreeKind.array_type_expression then result := elna_name_array_type_expression(parser_node) end; return result end proc type_info_create(type_representation: ^ElnaType) -> ^ElnaSymbolTypeInfo var result: ^ElnaSymbolTypeInfo begin result := malloc(#size(ElnaSymbolTypeInfo)); result^.kind := ElnaSymbolInfoKind.type_info; result^._type := type_representation; return result end (** * Parameters: * attr - Local variable attributes. * temporary_type - Local variable type. *) proc temporary_info_create(attr: Word, temporary_type: ^ElnaType) -> ^ElnaSymbolTemporaryInfo var result: ^ElnaSymbolTemporaryInfo begin result := malloc(#size(ElnaSymbolTemporaryInfo)); result^.kind := ElnaSymbolInfoKind.temporary_info; (* Calculate the stack offset: 4 * variable_counter. *) result^.variable_type := temporary_type; result^.attr := attr; return result end (** * Parameters: * symbol_table - Local symbol table. *) proc procedure_info_create(symbol_table: ^ElnaSymbolTable) -> ^ElnaSymbolProcedureInfo var result: ^ElnaSymbolProcedureInfo begin result := malloc(#size(ElnaSymbolProcedureInfo)); result^.kind := ElnaSymbolInfoKind.procedure_info; result^.symbol_table := symbol_table; return result end (** * Parameters: * variable_index - Variable index. *) proc elna_name_procedure_temporary(parser_node: ^ElnaTreeVariableDeclaration, symbol_table: ^ElnaSymbolTable) var info: Word variable_type: Word begin variable_type := elna_name_type_expression(parser_node^.type_expression); info := temporary_info_create(0, variable_type); elna_symbol_table_enter(symbol_table, parser_node^.name, parser_node^.length, info) end proc elna_name_procedure_temporaries(parser_node: ^ElnaTreeVariableDeclaration, symbol_table: ^ElnaSymbolTable) begin .elna_name_procedure_temporaries_loop; if parser_node <> nil then elna_name_procedure_temporary(parser_node, symbol_table); parser_node := parser_node^.next; goto elna_name_procedure_temporaries_loop end end proc elna_parser_procedure_declaration(cursor: ^ElnaLexerCursor) -> ^ElnaTreeProcedureDeclaration var next_declaration: ^ElnaTreeDeclaration current_declaration: ^ElnaTreeDeclaration result: ^ElnaTreeProcedureDeclaration parameter_head: ^ElnaTreeDeclaration token: ^ElnaLexerToken begin result := malloc(#size(ElnaTreeProcedureDeclaration)); result^.kind := ElnaTreeKind.procedure_declaration; result^.next := nil; (* Skip "proc ". *) elna_lexer_read(cursor); (* Skip procedure name. *) token := elna_lexer_read(cursor); result^.name := token^.start; result^.length := token^.length; (* Skip open paren. *) elna_lexer_read(cursor); parameter_head := nil; .elna_parser_procedure_declaration_parameter; token := elna_lexer_peek(cursor); if token^.kind <> ElnaLexerKind.right_paren then next_declaration := elna_parser_variable_declaration(cursor); if parameter_head = nil then parameter_head := next_declaration else current_declaration^.next := next_declaration end; current_declaration := next_declaration; token := elna_lexer_peek(cursor); if token^.kind = ElnaLexerKind.comma then elna_lexer_read(cursor); goto elna_parser_procedure_declaration_parameter end end; (* Skip close paren. *) elna_lexer_read(cursor); result^.parameters := parameter_head; (* Skip semicolon or arrow. *) token := elna_lexer_peek(cursor); if token^.kind = ElnaLexerKind.arrow then elna_lexer_read(cursor); result^.return_type := elna_parser_type_expression(cursor) else result^.return_type := nil end; parameter_head := elna_parser_var_part(cursor); result^.temporaries := parameter_head; (* Skip semicolon, "begin" and newline. *) token := elna_lexer_peek(cursor); if token^.kind = ElnaLexerKind._begin then elna_lexer_read(cursor); parameter_head := elna_parser_statements(cursor) elsif token^.kind = ElnaLexerKind._return then parameter_head := elna_parser_return_statement(cursor) end; result^.body := parameter_head; (* Skip the "end" keyword. *) elna_lexer_read(cursor); return result end proc elna_tac_parameters(ast_list: ^ElnaTreeDeclaration, parameter_count: ^Word) -> ^Word var ast_parameter: ^ElnaTreeDeclaration parameter_index: Word current_parameter: Word parameter_list: Word begin ast_parameter := ast_list; parameter_count^ := 0; .elna_tac_parameters_count; if ast_parameter <> nil then ast_parameter := ast_parameter^.next; parameter_count^ := parameter_count^ + 1; goto elna_tac_parameters_count end; (* The parameters are saved as an array of name pointer + name length. *) parameter_list := malloc(parameter_count^ * 8); current_parameter := parameter_list; ast_parameter := ast_list; parameter_index := 0; .elna_tac_parameters_loop; if parameter_index < parameter_count^ then current_parameter^ := ast_parameter^.name; current_parameter := current_parameter + 4; current_parameter^ := ast_parameter^.length; current_parameter := current_parameter + 4; parameter_index := parameter_index + 1; ast_parameter := ast_parameter^.next; goto elna_tac_parameters_loop end; return parameter_list end proc elna_rtl_global_declaration(tac_declaration: ^ElnaTacStaticVariable) -> ^ElnaRtlStaticVariable var result: ^ElnaRtlStaticVariable begin result := malloc(#size(ElnaRtlStaticVariable)); result^.next := nil; result^.name := tac_declaration^.name; result^.length := tac_declaration^.length; result^.body := tac_declaration^.body; return result end proc elna_rtl_parameters(instructions: ^ElnaList, parameters: Word, count: Word, variable_map: ^ElnaSymbolTable) -> ^ElnaRtlInstruction var result: ^ElnaRtlInstruction instruction: ^ElnaRtlInstruction parameter_index: Word pseudo_symbol: ^ElnaRtlObjectInfo current_register: Word target_operand: ElnaRtlOperand source_operand: ElnaTacOperand long_word_type: ^ElnaRtlType begin result := nil; parameter_index := 0; current_register := ElnaRtlRegister.a0; .elna_rtl_parameters_loop; if parameter_index < count then source_operand.kind := ElnaTacKind.variable; source_operand.value := parameters^; parameters := parameters + 4; source_operand.length := parameters^; parameters := parameters + 4; pseudo_symbol := elna_symbol_table_lookup(variable_map, source_operand.value, source_operand.length); long_word_type := elna_rtl_constant_type(4); if pseudo_symbol^.rtl_type^.kind = ElnaRtlTypeKind.byte_array then target_operand.kind := ElnaRtlKind.register; target_operand.value := ElnaRtlRegister.t0; target_operand.length := 0; target_operand.offset := 0; instruction := elna_rtl_variable_address(variable_map, pseudo_symbol, @source_operand, @target_operand); elna_list_append(instructions, instruction); instruction := elna_rtl_instruction_create(ElnaRtlOperator.sw); elna_rtl_instruction_set_operand(instruction, 1, ElnaRtlKind.register, current_register, 0, 0); elna_rtl_instruction_set_operand(instruction, 2, ElnaRtlKind.memory, target_operand.value, target_operand.length, 0); instruction^.types[1] := long_word_type; elna_list_append(instructions, instruction); instruction := elna_rtl_instruction_create(ElnaRtlOperator.sw); elna_rtl_instruction_set_operand(instruction, 1, ElnaRtlKind.register, current_register + 1, 0, 0); elna_rtl_instruction_set_operand(instruction, 2, ElnaRtlKind.memory, target_operand.value, target_operand.length, 4); instruction^.types[1] := long_word_type; elna_list_append(instructions, instruction); current_register := current_register + 2 else instruction := elna_rtl_instruction_create(ElnaRtlOperator.mv); elna_rtl_instruction_set_operand(instruction, 1, ElnaRtlKind.pseudo, source_operand.value, source_operand.length, 0); elna_rtl_instruction_set_operand(instruction, 2, ElnaRtlKind.register, current_register, 0, 0); instruction^.types[1] := long_word_type; elna_list_append(instructions, instruction); current_register := current_register + 1 end; parameter_index := parameter_index + 1; goto elna_rtl_parameters_loop end; return result end (* Returns whether the provided type is array or record. *) proc elna_type_is_aggregate(_type: ^ElnaType) var lhs: Word rhs: Word begin lhs := _type^.kind = ElnaTypeKind._record; rhs := _type^.kind = ElnaTypeKind.array; return lhs or rhs end proc elna_rtl_generate_pseudo(operand: ^ElnaRtlOperand, variable_map: ^ElnaSymbolTable, rtl_type: ^ElnaRtlType) -> ^ElnaRtlObjectInfo var pseudo_symbol: ^ElnaRtlObjectInfo pseudo_type: ^ElnaRtlTypeWord buffer: Word begin operand^.kind := ElnaRtlKind.pseudo; pseudo_counter := pseudo_counter + 1; pseudo_symbol := malloc(#size(ElnaRtlObjectInfo)); pseudo_symbol^.kind := ElnaRtlInfoKind.object_info; buffer := malloc(7); sprintf(buffer, "$b%i\0".ptr, pseudo_counter); pseudo_symbol^.allocated := false; operand^.value := buffer; operand^.length := strlen(buffer); pseudo_symbol^.rtl_type := rtl_type; elna_symbol_table_enter(variable_map, buffer, operand^.length, pseudo_symbol); return pseudo_symbol end proc elna_rtl_constant_type(size: Word) -> ^ElnaRtlType var rtl_type: ^ElnaRtlType begin rtl_type := malloc(#size(ElnaRtlTypeWord)); if size = 1 then rtl_type^.kind := ElnaRtlTypeKind.byte else rtl_type^.kind := ElnaRtlTypeKind.long_word end; return rtl_type end proc elna_rtl_symbol_type(variable_type: ^ElnaType) -> ^ElnaRtlType var byte_array: ^ElnaRtlTypeByteArray rtl_type: ^ElnaRtlType begin if elna_type_is_aggregate(variable_type) then byte_array := malloc(#size(ElnaRtlTypeByteArray)); byte_array^.kind := ElnaRtlTypeKind.byte_array; byte_array^.size := variable_type^.size; byte_array^.alignment := variable_type^.alignment; rtl_type := byte_array else rtl_type := elna_rtl_constant_type(variable_type^.size) end; return rtl_type end proc elna_rtl_procedure_declaration(tac_declaration: ^ElnaTacProcedure) -> ^ElnaRtlProcedure var count: Word current_entry: ^ElnaSymbolEntry pseudo_symbol: ^ElnaRtlObjectInfo variable_info: ^ElnaSymbolTemporaryInfo info: ^ElnaSymbolInfo result: ^ElnaRtlProcedure begin result := malloc(#size(ElnaRtlProcedure)); elna_list_initialize(@result^.body); result^.next := nil; result^.name := tac_declaration^.name; result^.length := tac_declaration^.length; pseudo_counter := 0; result^.variable_map := elna_symbol_table_create(variable_map_global); count := tac_declaration^.symbol_table^.count; current_entry := tac_declaration^.symbol_table^.symbols; .elna_rtl_procedure_declaration_loop; if count = 0 then goto elna_rtl_procedure_declaration_end end; info := current_entry^.symbol_info; if info^.kind = ElnaSymbolInfoKind.temporary_info then variable_info := current_entry^.symbol_info; pseudo_symbol := malloc(#size(ElnaRtlObjectInfo)); pseudo_symbol^.allocated := false; pseudo_symbol^.kind := ElnaRtlInfoKind.object_info; pseudo_symbol^.rtl_type := elna_rtl_symbol_type(variable_info^.variable_type); elna_symbol_table_enter(result^.variable_map, current_entry^.name, current_entry^.length, pseudo_symbol) end; count := count - 1; current_entry := current_entry + 1; goto elna_rtl_procedure_declaration_loop; .elna_rtl_procedure_declaration_end; elna_rtl_parameters(@result^.body, tac_declaration^.parameters, tac_declaration^.count, result^.variable_map); elna_rtl_instructions(@result^.body, tac_declaration^.body.first, result^.variable_map); return result end proc elna_tac_procedure_declaration(parser_node: ^ElnaTreeProcedureDeclaration) -> ^ElnaTacProcedure var symbol_info: ^ElnaSymbolProcedureInfo result: ^ElnaTacProcedure parameter_count: Word body: ElnaList begin result := malloc(#size(ElnaTacProcedure)); result^.next := nil; elna_list_initialize(@result^.body); result^.name := parser_node^.name; result^.length := parser_node^.length; symbol_info := elna_symbol_table_lookup(symbol_table_global, parser_node^.name, parser_node^.length); result^.symbol_table := symbol_info^.symbol_table; result^.parameters := elna_tac_parameters(parser_node^.parameters, @parameter_count); result^.count := parameter_count; pseudo_counter := 0; elna_tac_statements(@result^.body, parser_node^.body, symbol_info^.symbol_table); return result end proc elna_parser_procedures(cursor: ^ElnaLexerCursor) -> ^ElnaTreeDeclaration var parser_node: ^ElnaTreeDeclaration result: ^ElnaTreeDeclaration current_declaration: ^ElnaTreeDeclaration token: ^ElnaLexerToken begin result := nil; .elna_parser_procedures_loop; elna_lexer_skip_empty_lines(cursor); token := elna_lexer_peek(cursor); if token^.kind = ElnaLexerKind._proc then parser_node := elna_parser_procedure_declaration(cursor); if result = 0 then result := parser_node else current_declaration^.next := parser_node end; current_declaration := parser_node; goto elna_parser_procedures_loop end; return result end proc elna_rtl_globals(tac_procedure: ^ElnaTacStaticVariable) -> ^ElnaRtlStaticVariable var current_copy: ^ElnaRtlStaticVariable next_copy: ^ElnaRtlStaticVariable first_copy: ^ElnaRtlStaticVariable begin if tac_procedure <> nil then first_copy := elna_rtl_global_declaration(tac_procedure); tac_procedure := tac_procedure^.next else first_copy := nil end; current_copy := first_copy; .elna_rtl_globals_start; if tac_procedure <> nil then next_copy := elna_rtl_global_declaration(tac_procedure); tac_procedure := tac_procedure^.next; current_copy^.next := next_copy; current_copy := next_copy; goto elna_rtl_globals_start end; return first_copy end proc elna_rtl_procedures(tac_procedure: ^ElnaTacProcedure) -> ^ElnaRtlProcedure var current_copy: ^ElnaRtlProcedure next_copy: ^ElnaRtlProcedure first_copy: ^ElnaRtlProcedure begin if tac_procedure <> nil then first_copy := elna_rtl_procedure_declaration(tac_procedure); tac_procedure := tac_procedure^.next else first_copy := nil; end; current_copy := first_copy; .elna_rtl_procedures_start; if tac_procedure <> nil then next_copy := elna_rtl_procedure_declaration(tac_procedure); tac_procedure := tac_procedure^.next; current_copy^.next := next_copy; current_copy := next_copy; goto elna_rtl_procedures_start end; return first_copy end proc elna_tac_procedures(parser_node: ^ElnaTreeDeclaration) -> ^ElnaTacProcedure var result: ^ElnaTacProcedure current_procedure: ^ElnaTacProcedure first_procedure: ^ElnaTacProcedure begin first_procedure := nil; .elna_tac_procedures_loop; if parser_node = 0 then goto elna_tac_procedures_end end; result := elna_tac_procedure_declaration(parser_node); if first_procedure = nil then first_procedure := result else current_procedure^.next := result end; current_procedure := result; parser_node := parser_node^.next; goto elna_tac_procedures_loop; .elna_tac_procedures_end; return first_procedure end (** * Skips comments. *) proc elna_lexer_skip_empty_lines(cursor: ^ElnaLexerCursor) var token: ^ElnaLexerToken begin .skip_empty_lines_rerun; token := elna_lexer_peek(cursor); if token^.kind = ElnaLexerKind.comment then elna_lexer_read(cursor); goto skip_empty_lines_rerun end end proc elna_parser_type_declaration(cursor: ^ElnaLexerCursor) -> ^ElnaTreeTypeDeclaration var result: ^ElnaTreeTypeDeclaration token: ^ElnaLexerToken begin result := malloc(#size(ElnaTreeTypeDeclaration)); token := elna_lexer_read(cursor); result^.kind := ElnaTreeKind.type_declaration; result^.next := nil; result^.name := token^.start; result^.length := token^.length; elna_lexer_read(cursor); result^.type_expression := elna_parser_type_expression(cursor); return result end proc elna_name_type_declaration(parser_node: ^ElnaTreeTypeDeclaration) var symbol_type: ^ElnaType type_info: ^ElnaSymbolTypeInfo begin symbol_type := elna_name_type_expression(parser_node^.type_expression); type_info := type_info_create(symbol_type); elna_symbol_table_enter(symbol_table_global, parser_node^.name, parser_node^.length, type_info) end proc elna_type_type_declaration(parser_node: Word) begin end proc elna_parser_type_part(cursor: ^ElnaLexerCursor) -> ^ElnaTreeDeclaration var parser_node: ^ElnaTreeDeclaration result: ^ElnaTreeDeclaration current_declaration: ^ElnaTreeDeclaration token: ^ElnaLexerToken begin result := nil; elna_lexer_skip_empty_lines(cursor); token := elna_lexer_peek(cursor); if token^.kind <> ElnaLexerKind._type then goto elna_parser_type_part_end end; elna_lexer_read(cursor); .elna_parser_type_part_loop; elna_lexer_skip_empty_lines(cursor); token := elna_lexer_peek(cursor); if token^.kind = ElnaLexerKind.identifier then parser_node := elna_parser_type_declaration(cursor); if result = nil then result := parser_node else current_declaration^.next := parser_node end; current_declaration := parser_node; goto elna_parser_type_part_loop end; .elna_parser_type_part_end; return result end proc elna_parser_variable_declaration(cursor: ^ElnaLexerCursor) -> ^ElnaTreeVariableDeclaration var variable_type: Word result: ^ElnaTreeVariableDeclaration token: ^ElnaLexerToken begin token := elna_lexer_read(cursor); (* Skip the variable name and colon with the type. *) elna_lexer_read(cursor); variable_type := elna_parser_type_expression(cursor); result := malloc(#size(ElnaTreeVariableDeclaration)); result^.kind := ElnaTreeKind.variable_declaration; result^.next := nil; result^.name := token^.start; result^.length := token^.length; result^.type_expression := variable_type; return result end proc elna_tac_variable_declaration(parser_tree: ^ElnaTreeVariableDeclaration) -> ^ElnaTacStaticVariable var result: ^ElnaTacStaticVariable variable_info: ^ElnaSymbolTemporaryInfo begin result := malloc(#size(ElnaTacStaticVariable)); variable_info := elna_symbol_table_lookup(symbol_table_global, parser_tree^.name, parser_tree^.length); result^.next := nil; result^.name := parser_tree^.name; result^.length := parser_tree^.length; result^.body := variable_info^.variable_type^.size; return result end proc elna_parser_var_part(cursor: ^ElnaLexerCursor) -> ^ElnaTreeDeclaration var result: ^ElnaTreeDeclaration variable_node: ^ElnaTreeDeclaration current_declaration: ^ElnaTreeDeclaration token: ^ElnaLexerToken begin result := nil; token := elna_lexer_peek(cursor); if token^.kind <> ElnaLexerKind._var then goto elna_parser_var_part_end end; (* Skip "var". *) elna_lexer_read(cursor); .elna_parser_var_part_loop; elna_lexer_skip_empty_lines(cursor); token := elna_lexer_peek(cursor); if token^.kind = ElnaLexerKind.identifier then variable_node := elna_parser_variable_declaration(cursor); if result = nil then result := variable_node else current_declaration^.next := variable_node end; current_declaration := variable_node; goto elna_parser_var_part_loop end; .elna_parser_var_part_end; return result end proc elna_tac_var_part(parser_node: ^ElnaTreeDeclaration) -> ^ElnaTacStaticVariable var node: ^ElnaTacStaticVariable current_variable: ^ElnaTacStaticVariable first_variable: ^ElnaTacStaticVariable begin first_variable := nil; if parser_node = nil then goto elna_tac_var_part_end end; .elna_tac_var_part_loop; node := elna_tac_variable_declaration(parser_node); if first_variable = nil then first_variable := node else current_variable^.next := node end; current_variable := node; parser_node := parser_node^.next; if parser_node <> nil then goto elna_tac_var_part_loop end; .elna_tac_var_part_end; return first_variable end proc elna_parser_program_body(cursor: ^ElnaLexerCursor) -> ^ElnaTreeStatement var result: ^ElnaTreeStatement token: ^ElnaLexerToken begin result := nil; token := elna_lexer_peek(cursor); if token^.kind = ElnaLexerKind._begin then elna_lexer_read(cursor); result := elna_parser_statements(cursor); end; return result end proc elna_parser_module_declaration(cursor: ^ElnaLexerCursor, error_list: ^ElnaList) -> ^ElnaTreeModuleDeclaration var parser_node: Word result: ^ElnaTreeModuleDeclaration parser_error: ^ElnaError token: ^ElnaLexerToken begin result := malloc(#size(ElnaTreeModuleDeclaration)); result^.kind := ElnaTreeKind.module_declaration; (* Skip "program;". *) elna_lexer_skip_empty_lines(cursor); token := elna_lexer_read(cursor); if token^.kind <> ElnaLexerKind._program then parser_error := malloc(#size(ElnaError)); parser_error^.next := nil; error_list^.first := parser_error; error_list^.last := parser_error else elna_lexer_read(cursor); result^.types := elna_parser_type_part(cursor); result^.globals := elna_parser_var_part(cursor); result^.procedures := elna_parser_procedures(cursor); result^.body := elna_parser_program_body(cursor) end; return result end proc elna_tac_program_body(parser_node: ^ElnaTreeStatement) -> ^ElnaTacProcedure var result: ^ElnaTacProcedure symbol_info: ^ElnaSymbolProcedureInfo begin result := malloc(#size(ElnaTacProcedure)); result^.next := nil; elna_list_initialize(@result^.body); result^.name := "main".ptr; result^.length := 4; symbol_info := elna_symbol_table_lookup(symbol_table_global, result^.name, result^.length); result^.symbol_table := symbol_info^.symbol_table; result^.parameters := nil; result^.count := 0; elna_tac_statements(@result^.body, parser_node, result^.symbol_table); return result end proc elna_tac_module_declaration(parser_node: ^ElnaTreeModuleDeclaration) -> ^ElnaInstructionModule var result: ^ElnaInstructionModule code: ^ElnaTacProcedure begin result := malloc(#size(ElnaInstructionModule)); result^.data := elna_tac_var_part(parser_node^.globals); result^.code := elna_tac_procedures(parser_node^.procedures); if parser_node^.body <> nil then code := result^.code; if code = nil then result^.code := elna_tac_program_body(parser_node^.body); goto elna_tac_module_declaration_end end; .elna_tac_module_declaration_loop; if code^.next = nil then code^.next := elna_tac_program_body(parser_node^.body) else code := code^.next; goto elna_tac_module_declaration_loop end end; .elna_tac_module_declaration_end; return result end proc elna_name_dereference_expression(parser_node: ^ElnaTreeDereferenceExpression) begin elna_name_designator(parser_node^.pointer) end proc elna_name_field_access_expression(parser_node: ^ElnaTreeFieldAccessExpression) begin elna_name_designator(parser_node^.aggregate) end proc elna_name_array_access_expression(parser_node: ^ElnaTreeArrayAccessExpression) begin elna_name_designator(parser_node^.array); elna_name_binary_expression(parser_node^.index) end proc elna_name_cast_expression(parser_node: ^ElnaTreeCastExpression) begin elna_name_binary_expression(parser_node^.expression); parser_node^.type_decoration := elna_name_type_expression(parser_node^.type_expression) end proc elna_name_simple_expression(parser_node: ^ElnaTreeNode) begin (* Not interested in literals and variable expressions. *) if parser_node^.kind = ElnaTreeKind._cast then elna_name_cast_expression(parser_node) end end proc elna_name_call(parser_node: ^ElnaTreeCall) var argument_tree: ^ElnaTreeExpressionList begin argument_tree := parser_node^.arguments; .elna_name_call_argument; if argument_tree <> nil then elna_name_binary_expression(argument_tree^.expression); argument_tree := argument_tree^.next; goto elna_name_call_argument end end proc elna_name_designator(parser_node: ^ElnaTreeNode) begin if parser_node^.kind = ElnaTreeKind.dereference_expression then elna_name_dereference_expression(parser_node) elsif parser_node^.kind = ElnaTreeKind.field_access_expression then elna_name_field_access_expression(parser_node) elsif parser_node^.kind = ElnaTreeKind.array_access_expression then elna_name_array_access_expression(parser_node) elsif parser_node^.kind = ElnaTreeKind.call then elna_name_call(parser_node) else elna_name_simple_expression(parser_node) end end proc elna_name_unary_expression(parser_node: ^ElnaTreeUnaryExpression) begin if parser_node^.kind = ElnaTreeKind.unary_expression then elna_name_designator(parser_node^.operand) else elna_name_designator(parser_node) end end proc elna_name_binary_expression(parser_node: ^ElnaTreeBinaryExpression) begin if parser_node^.kind = ElnaTreeKind.binary_expression then elna_name_unary_expression(parser_node^.lhs); elna_name_unary_expression(parser_node^.rhs) else elna_name_unary_expression(parser_node) end end proc elna_name_conditional_statements(parser_node: ^ElnaTreeConditionalStatements) begin .elna_name_conditional_statements_loop; elna_name_binary_expression(parser_node^.condition); elna_name_statements(parser_node^.statements); parser_node := parser_node^.next; if parser_node <> nil then goto elna_name_conditional_statements_loop end end proc elna_name_if_statement(parser_node: ^ElnaTreeIfStatement) var block: ^ElnaTreeConditionalStatements begin block := parser_node^.conditionals; .elna_name_if_statement_conditionals; elna_name_conditional_statements(block); block := block^.next; if block <> nil then goto elna_name_if_statement_conditionals end; block := parser_node^._else; if block <> nil then elna_name_statements(block) end end proc elna_name_return_statement(parser_node: ^ElnaTreeReturnStatement) begin elna_name_binary_expression(parser_node^.returned) end proc elna_name_assign_statement(parser_node: ^ElnaTreeAssignStatement) begin elna_name_designator(parser_node^.assignee); elna_name_binary_expression(parser_node^.assignment) end proc elna_name_statement(parser_node: ^ElnaTreeStatement) begin (* Skipping goto and label declarations. *) if parser_node^.kind = ElnaTreeKind.if_statement then elna_name_if_statement(parser_node) elsif parser_node^.kind = ElnaTreeKind.return_statement then elna_name_return_statement(parser_node) elsif parser_node^.kind = ElnaTreeKind.call then elna_name_call(parser_node) elsif parser_node^.kind = ElnaTreeKind.assign_statement then elna_name_assign_statement(parser_node) end end proc elna_name_statements(parser_node: ^ElnaTreeStatement) begin .elna_name_statements_loop; if parser_node <> nil then elna_name_statement(parser_node); parser_node := parser_node^.next; goto elna_name_statements_loop end end proc elna_name_procedure_declaration(parser_node: ^ElnaTreeProcedureDeclaration) var new_symbol_table: ^ElnaSymbolTable symbol_info: Word begin new_symbol_table := elna_symbol_table_create(symbol_table_global); symbol_info := procedure_info_create(new_symbol_table); elna_name_procedure_temporaries(parser_node^.parameters, new_symbol_table); elna_name_procedure_temporaries(parser_node^.temporaries, new_symbol_table); elna_name_statements(parser_node^.body); elna_symbol_table_enter(symbol_table_global, parser_node^.name, parser_node^.length, symbol_info) end proc elna_name_program_body(parser_node: ^ElnaTreeStatement) var new_symbol_table: ^ElnaSymbolTable symbol_info: Word begin new_symbol_table := elna_symbol_table_create(symbol_table_global); symbol_info := procedure_info_create(new_symbol_table); elna_symbol_table_enter(symbol_table_global, "main".ptr, 4, symbol_info) end proc elna_type_conditional_statements(parser_node: ^ElnaTreeConditionalStatements, symbol_table: ^ElnaSymbolTable) begin .elna_type_conditional_statements_loop; elna_type_binary_expression(parser_node^.condition, symbol_table); elna_type_statements(parser_node^.statements, symbol_table); parser_node := parser_node^.next; if parser_node <> nil then goto elna_type_conditional_statements_loop end end proc elna_type_if_statement(parser_node: ^ElnaTreeIfStatement, symbol_table: ^ElnaSymbolTable) var block: ^ElnaTreeConditionalStatements begin block := parser_node^.conditionals; .elna_type_if_statement_conditionals; elna_type_conditional_statements(block, symbol_table); block := block^.next; if block <> nil then goto elna_type_if_statement_conditionals end; block := parser_node^._else; if block <> nil then elna_type_statements(block, symbol_table) end end proc elna_type_return_statement(parser_node: ^ElnaTreeReturnStatement, symbol_table: ^ElnaSymbolTable) begin elna_type_binary_expression(parser_node^.returned, symbol_table) end proc elna_type_call(parser_node: ^ElnaTreeCall, symbol_table: ^ElnaSymbolTable) var argument_tree: ^ElnaTreeExpressionList begin argument_tree := parser_node^.arguments; .elna_type_call_argument; if argument_tree <> nil then elna_type_binary_expression(argument_tree^.expression, symbol_table); argument_tree := argument_tree^.next; goto elna_type_call_argument end end proc elna_type_assign_statement(parser_node: ^ElnaTreeAssignStatement, symbol_table: ^ElnaSymbolTable) begin elna_type_designator(parser_node^.assignee, symbol_table); elna_type_binary_expression(parser_node^.assignment, symbol_table) end proc elna_type_statement(parser_node: ^ElnaTreeNode, symbol_table: ^ElnaSymbolTable) begin (* Skipping goto and label declarations. *) if parser_node^.kind = ElnaTreeKind.if_statement then elna_type_if_statement(parser_node, symbol_table) elsif parser_node^.kind = ElnaTreeKind.return_statement then elna_type_return_statement(parser_node, symbol_table) elsif parser_node^.kind = ElnaTreeKind.call then elna_type_call(parser_node, symbol_table) elsif parser_node^.kind = ElnaTreeKind.assign_statement then elna_type_assign_statement(parser_node, symbol_table) end end proc elna_type_statements(parser_node: ^ElnaTreeStatement, symbol_table: ^ElnaSymbolTable) begin .elna_type_statements_loop; if parser_node <> nil then elna_type_statement(parser_node, symbol_table); parser_node := parser_node^.next; goto elna_type_statements_loop end end proc elna_type_character_literal(parser_node: ^ElnaTreeCharacterLiteral) begin parser_node^.type_decoration := char_type end proc elna_type_integer_literal(parser_node: ^ElnaTreeIntegerLiteral) begin parser_node^.type_decoration := word_type end proc elna_type_string_literal(parser_node: ^ElnaTreeStringLiteral) begin parser_node^.type_decoration := string_type end proc elna_type_boolean_literal(parser_node: ^ElnaTreeBooleanLiteral) begin parser_node^.type_decoration := bool_type end proc elna_type_nil_literal(parser_node: ^ElnaTreeNilLiteral) var symbol_info: ^ElnaSymbolTypeInfo begin symbol_info := elna_symbol_table_lookup(symbol_table_global, "Pointer".ptr, 7); parser_node^.type_decoration := symbol_info^._type end proc elna_type_variable_expression(parser_node: ^ElnaTreeVariableExpression, symbol_table: ^ElnaSymbolTable) var variable_info: ^ElnaSymbolInfo temporary_info: ^ElnaSymbolTemporaryInfo begin variable_info := elna_symbol_table_lookup(symbol_table, parser_node^.name, parser_node^.length); if variable_info^.kind = ElnaSymbolInfoKind.temporary_info then temporary_info := variable_info; parser_node^.type_decoration := temporary_info^.variable_type end end proc elna_type_cast_expression(parser_node: ^ElnaTreeCastExpression, symbol_table: ^ElnaSymbolTable) begin elna_type_binary_expression(parser_node^.expression, symbol_table) end proc elna_type_simple_expression(parser_node: ^ElnaTreeNode, symbol_table: ^ElnaSymbolTable) begin if parser_node^.kind = ElnaTreeKind.integer_literal then elna_type_integer_literal(parser_node) elsif parser_node^.kind = ElnaTreeKind.character_literal then elna_type_character_literal(parser_node) elsif parser_node^.kind = ElnaTreeKind.string_literal then elna_type_string_literal(parser_node) elsif parser_node^.kind = ElnaTreeKind.boolean_literal then elna_type_boolean_literal(parser_node) elsif parser_node^.kind = ElnaTreeKind.null then elna_type_nil_literal(parser_node) elsif parser_node^.kind = ElnaTreeKind._cast then elna_type_cast_expression(parser_node) elsif parser_node^.kind = ElnaTreeKind.variable_expression then elna_type_variable_expression(parser_node, symbol_table) end end proc elna_type_dereference_expression(parser_node: ^ElnaTreeDereferenceExpression, symbol_table: ^ElnaSymbolTable) var base_type: ^ElnaType pointer_type: ^ElnaTypePointer dereferenced_expression: ^ElnaTreeExpression begin elna_type_designator(parser_node^.pointer, symbol_table); dereferenced_expression := parser_node^.pointer; base_type := dereferenced_expression^.type_decoration; (* If check for compatibility, should be removed later. *) if base_type^.kind = ElnaTypeKind.pointer then pointer_type := base_type; base_type := pointer_type^.base; end; parser_node^.type_decoration := base_type end proc elna_type_field_access_expression(parser_node: ^ElnaTreeFieldAccessExpression, symbol_table: ^ElnaSymbolTable) var variable_expression: ^ElnaTreeVariableExpression base_type: Word type_kind: ^ElnaSymbolTypeInfo symbol_info: ^ElnaSymbolInfo aggregate_type: ^ElnaTypeRecord field_count: Word current_field: ^ElnaTypeField begin base_type := nil; variable_expression := parser_node^.aggregate; (* Check whether the field access is an enumeration value. *) if variable_expression^.kind = ElnaTreeKind.variable_expression then symbol_info := elna_symbol_table_lookup(symbol_table_global, variable_expression^.name, variable_expression^.length); if symbol_info <> nil then type_kind := symbol_info; if symbol_info^.kind = ElnaSymbolInfoKind.type_info then base_type := type_kind^._type end end end; (* If the base_type is still nil this is record field access. *) if base_type = nil then elna_type_designator(parser_node^.aggregate, symbol_table); aggregate_type := variable_expression^.type_decoration; field_count := aggregate_type^.length; current_field := aggregate_type^.members; .elna_type_field_access_expression_field; if string_compare(parser_node^.field, parser_node^.length, current_field^.name, current_field^.length) = 0 then field_count := field_count - 1; current_field := current_field + 1; goto elna_type_field_access_expression_field end; base_type := current_field^.field_type end; parser_node^.type_decoration := base_type end proc elna_type_array_access_expression(parser_node: ^ElnaTreeArrayAccessExpression, symbol_table: ^ElnaSymbolTable) 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: ^ElnaSymbolTable) 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 elna_type_simple_expression(parser_node, symbol_table) end end proc elna_type_unary_expression(parser_node: ^ElnaTreeUnaryExpression, symbol_table: ^ElnaSymbolTable) begin if parser_node^.kind = ElnaTreeKind.unary_expression then elna_type_designator(parser_node^.operand, symbol_table); parser_node^.type_decoration := parser_node^.operand^.type_decoration else elna_type_designator(parser_node, symbol_table) end end proc elna_type_binary_expression(parser_node: ^ElnaTreeBinaryExpression, symbol_table: ^ElnaSymbolTable) var binary_operand: ^ElnaTreeExpression begin if parser_node^.kind = ElnaTreeKind.binary_expression then elna_type_unary_expression(parser_node^.rhs, symbol_table); binary_operand := parser_node^.lhs; elna_type_unary_expression(binary_operand, symbol_table); parser_node^.type_decoration := binary_operand^.type_decoration else elna_type_unary_expression(parser_node, symbol_table) end end proc elna_type_procedure_declaration(parser_node: ^ElnaTreeProcedureDeclaration) var procedure_info: ^ElnaSymbolProcedureInfo begin procedure_info := elna_symbol_table_lookup(symbol_table_global, parser_node^.name, parser_node^.length); elna_type_statements(parser_node^.body, procedure_info^.symbol_table) end proc elna_name_module_declaration(parser_node: ^ElnaTreeModuleDeclaration) var current_part: ^ElnaTreeDeclaration begin current_part := parser_node^.types; .elna_name_module_declaration_type; if current_part <> nil then elna_name_type_declaration(current_part); current_part := current_part^.next; goto elna_name_module_declaration_type end; current_part := parser_node^.globals; .elna_name_module_declaration_global; if current_part <> nil then elna_name_procedure_temporary(current_part, symbol_table_global); current_part := current_part^.next; goto elna_name_module_declaration_global end; current_part := parser_node^.procedures; .elna_name_module_declaration_procedure; if current_part <> nil then elna_name_procedure_declaration(current_part); current_part := current_part^.next; goto elna_name_module_declaration_procedure end; if parser_node^.body <> nil then elna_name_program_body(parser_node^.body) end end proc elna_type_module_declaration(parser_node: ^ElnaTreeModuleDeclaration) var current_part: ^ElnaTreeDeclaration begin current_part := parser_node^.types; .elna_type_module_declaration_type; if current_part <> 0 then elna_type_type_declaration(current_part); current_part := current_part^.next; goto elna_type_module_declaration_type end; current_part := parser_node^.procedures; .elna_type_module_declaration_procedure; if current_part <> 0 then elna_type_procedure_declaration(current_part); current_part := current_part^.next; goto elna_type_module_declaration_procedure end end proc compile() var parser_node: Word tac: Word rtl: Word error_list: ElnaList compiled: Word lexer_state: ElnaLexerCursor begin elna_lexer_initialize(@lexer_state, source_code); elna_list_initialize(@error_list); parser_node := elna_parser_module_declaration(@lexer_state, @error_list); compiled := error_list.first = nil; if compiled then elna_name_module_declaration(parser_node); elna_type_module_declaration(parser_node); tac := elna_tac_module_declaration(parser_node); rtl := elna_rtl_module_declaration(tac); elna_alloc_module(rtl); elna_fixup_module(rtl); elna_riscv_module(rtl); end; return compiled end (** * Looks for a symbol in the given symbol table. * * Parameters: * symbol_table - Symbol table. * symbol_name - Symbol name pointer. * name_length - Symbol name length. * * Returns the symbol pointer or nil. *) proc elna_symbol_table_lookup(symbol_table: ^ElnaSymbolTable, symbol_name: Word, name_length: Word) var result: Word symbol_table_length: Word current_entry: ^ElnaSymbolEntry begin result := nil; symbol_table_length := symbol_table^.count; (* Go to the first symbol position. *) current_entry := symbol_table^.symbols; .symbol_table_lookup_loop; if symbol_table_length = 0 then if symbol_table^.parent <> nil then result := elna_symbol_table_lookup(symbol_table^.parent, symbol_name, name_length) end; goto symbol_table_lookup_end end; (* If names don't match, try the next entry. *) if string_compare(current_entry^.name, current_entry^.length, symbol_name, name_length) = 0 then goto symbol_table_lookup_repeat end; (* Otherwise, the symbol is found. *) result := current_entry^.symbol_info; goto symbol_table_lookup_end; .symbol_table_lookup_repeat; current_entry := current_entry + 1; symbol_table_length := symbol_table_length - 1; goto symbol_table_lookup_loop; .symbol_table_lookup_end; return result end (** * Create a new local symbol table in the symbol memory region after the last * known symbol table. * * Parameters: * parent - Outer scope. * * Returns newly allocated, empty symbol table. *) proc elna_symbol_table_create(parent: ^ElnaSymbolTable) var new_symbol_table: ^ElnaSymbolTable begin new_symbol_table := malloc(#size(ElnaSymbolTable)); new_symbol_table^.symbols := malloc(16384); new_symbol_table^.count := 0; new_symbol_table^.parent := parent; return new_symbol_table end (** * Inserts a symbol into the table. * * Parameters: * symbol_table - Symbol table. * symbol_name - Symbol name pointer. * name_length - Symbol name length. * symbol - Symbol pointer. *) proc elna_symbol_table_enter(symbol_table: ^ElnaSymbolTable, symbol_name: Word, name_length: Word, symbol: Word) var symbol_pointer: ^ElnaSymbolEntry begin (* Calculate the offset for the new symbol. *) symbol_pointer := symbol_table^.symbols + symbol_table^.count; symbol_pointer^.name := symbol_name; symbol_pointer^.length := name_length; symbol_pointer^.symbol_info := symbol; (* Increment the symbol table length. *) symbol_table^.count := symbol_table^.count + 1 end proc elna_symbol_string_build() var current_field: ^ElnaTypeField char_pointer: ^ElnaTypePointer begin string_type := malloc(#size(ElnaTypeRecord)); string_type^.kind := ElnaTypeKind._record; string_type^.size := 8; string_type^.alignment := 4; string_type^.members := malloc(2 * #size(ElnaTypeField)); char_pointer := malloc(#size(ElnaTypePointer)); char_pointer^.kind := ElnaTypeKind.pointer; char_pointer^.size := 4; char_pointer^.alignment := 4; char_pointer^.base := char_type; current_field := string_type^.members; current_field^.name := "ptr".ptr; current_field^.length := 3; current_field^.field_type := char_pointer; current_field := current_field + 1; current_field^.name := "length".ptr; current_field^.length := 6; current_field^.field_type := word_type end (* Build global symbol table with predefined symbols. *) proc elna_symbol_table_build() var current_info: ^ElnaSymbolTypeInfo current_type: ^ElnaType begin symbol_table_global := elna_symbol_table_create(nil); word_type := malloc(#size(ElnaType)); word_type^.kind := ElnaTypeKind.primitive; word_type^.size := 4; word_type^.alignment := 4; current_info := type_info_create(word_type); elna_symbol_table_enter(symbol_table_global, "Word".ptr, 4, current_info); current_type := malloc(#size(ElnaType)); current_type^.kind := ElnaTypeKind.primitive; current_type^.size := 4; current_type^.alignment := 4; current_info := type_info_create(current_type); elna_symbol_table_enter(symbol_table_global, "Pointer".ptr, 7, current_info); bool_type := malloc(#size(ElnaType)); bool_type^.kind := ElnaTypeKind.primitive; bool_type^.size := 1; bool_type^.alignment := 1; current_info := type_info_create(bool_type); elna_symbol_table_enter(symbol_table_global, "Bool".ptr, 4, current_info); char_type := malloc(#size(ElnaType)); char_type^.kind := ElnaTypeKind.primitive; char_type^.size := 1; char_type^.alignment := 1; current_info := type_info_create(char_type); elna_symbol_table_enter(symbol_table_global, "Char".ptr, 4, current_info); elna_symbol_string_build(); current_info := type_info_create(string_type); elna_symbol_table_enter(symbol_table_global, "String".ptr, 6, current_info) end proc elna_lexer_classifications1() begin classification[1] := ElnaLexerClass.eof; classification[10] := ElnaLexerClass.space; classification[11] := ElnaLexerClass.space; classification[14] := ElnaLexerClass.space; classification[33] := ElnaLexerClass.space; classification[34] := ElnaLexerClass.single end proc elna_lexer_classifications2() begin classification[35] := ElnaLexerClass.double_quote; classification[36] := ElnaLexerClass.number_sign; classification[37] := ElnaLexerClass.other; classification[38] := ElnaLexerClass.single; classification[39] := ElnaLexerClass.single; classification[40] := ElnaLexerClass.single_quote; classification[41] := ElnaLexerClass.left_paren; classification[42] := ElnaLexerClass.right_paren; classification[43] := ElnaLexerClass.asterisk; classification[44] := ElnaLexerClass.single; classification[45] := ElnaLexerClass.single; classification[46] := ElnaLexerClass.minus; classification[47] := ElnaLexerClass.dot; classification[48] := ElnaLexerClass.single; classification[49] := ElnaLexerClass.zero; classification[50] := ElnaLexerClass.digit; classification[51] := ElnaLexerClass.digit; classification[52] := ElnaLexerClass.digit; classification[53] := ElnaLexerClass.digit; classification[54] := ElnaLexerClass.digit; classification[55] := ElnaLexerClass.digit; classification[56] := ElnaLexerClass.digit; classification[57] := ElnaLexerClass.digit; classification[58] := ElnaLexerClass.digit; classification[59] := ElnaLexerClass.colon; classification[60] := ElnaLexerClass.single; classification[61] := ElnaLexerClass.less; classification[62] := ElnaLexerClass.equals; classification[63] := ElnaLexerClass.greater; classification[64] := ElnaLexerClass.other end proc elna_lexer_classifications3() begin classification[65] := ElnaLexerClass.single; classification[66] := ElnaLexerClass.alpha; classification[67] := ElnaLexerClass.alpha; classification[68] := ElnaLexerClass.alpha; classification[69] := ElnaLexerClass.alpha; classification[70] := ElnaLexerClass.alpha; classification[71] := ElnaLexerClass.alpha; classification[72] := ElnaLexerClass.alpha; classification[73] := ElnaLexerClass.alpha; classification[74] := ElnaLexerClass.alpha; classification[75] := ElnaLexerClass.alpha; classification[76] := ElnaLexerClass.alpha; classification[77] := ElnaLexerClass.alpha; classification[78] := ElnaLexerClass.alpha; classification[79] := ElnaLexerClass.alpha; classification[80] := ElnaLexerClass.alpha; classification[81] := ElnaLexerClass.alpha; classification[82] := ElnaLexerClass.alpha; classification[83] := ElnaLexerClass.alpha; classification[84] := ElnaLexerClass.alpha; classification[85] := ElnaLexerClass.alpha; classification[86] := ElnaLexerClass.alpha; classification[87] := ElnaLexerClass.alpha; classification[88] := ElnaLexerClass.alpha; classification[89] := ElnaLexerClass.alpha; classification[90] := ElnaLexerClass.alpha; classification[91] := ElnaLexerClass.alpha; classification[92] := ElnaLexerClass.single; classification[93] := ElnaLexerClass.backslash; classification[94] := ElnaLexerClass.single; classification[95] := ElnaLexerClass.single; classification[96] := ElnaLexerClass.alpha; classification[97] := ElnaLexerClass.other; classification[98] := ElnaLexerClass.hex end proc elna_lexer_classifications4() begin classification[99] := ElnaLexerClass.hex; classification[100] := ElnaLexerClass.hex; classification[101] := ElnaLexerClass.hex; classification[102] := ElnaLexerClass.hex; classification[103] := ElnaLexerClass.hex; classification[104] := ElnaLexerClass.alpha; classification[105] := ElnaLexerClass.alpha; classification[106] := ElnaLexerClass.alpha; classification[107] := ElnaLexerClass.alpha; classification[108] := ElnaLexerClass.alpha; classification[109] := ElnaLexerClass.alpha; classification[110] := ElnaLexerClass.alpha; classification[111] := ElnaLexerClass.alpha; classification[112] := ElnaLexerClass.alpha; classification[113] := ElnaLexerClass.alpha; classification[114] := ElnaLexerClass.alpha; classification[115] := ElnaLexerClass.alpha; classification[116] := ElnaLexerClass.alpha; classification[117] := ElnaLexerClass.alpha; classification[118] := ElnaLexerClass.alpha; classification[119] := ElnaLexerClass.alpha; classification[120] := ElnaLexerClass.alpha; classification[121] := ElnaLexerClass.x; classification[122] := ElnaLexerClass.alpha; classification[123] := ElnaLexerClass.alpha; classification[124] := ElnaLexerClass.other; classification[125] := ElnaLexerClass.single; classification[126] := ElnaLexerClass.other; classification[127] := ElnaLexerClass.single end (** * Initializes the array with character classes. *) proc elna_lexer_classifications() var code: Word begin code := 1; (* Set everything by default to invalid. *) .elna_lexer_classifications_invalid; classification[code] := ElnaLexerClass.invalid; code := code + 1; if code < 129 then goto elna_lexer_classifications_invalid end; elna_lexer_classifications1(); elna_lexer_classifications2(); elna_lexer_classifications3(); elna_lexer_classifications4(); (* Set the remaining 129 - 256 bytes to transitionClassOther. *) .elna_lexer_classifications_other; classification[code] := ElnaLexerClass.other; code := code + 1; if code < 257 then goto elna_lexer_classifications_other end end proc elna_lexer_get_transition(current_state: Word, character_class: Word) var column_position: Word target: Word begin (* Each state is 8 bytes long (2 words: action and next state). There are 23 character classes, so a transition row 8 * 23 = 184 bytes long. *) column_position := character_class - 1; column_position := column_position * 8; target := @transition_table[current_state]; return target + column_position end (** * Parameters: * current_state - First index into transitions table. * character_class - Second index into transitions table. * action - Action to assign. * next_state - Next state to assign. *) proc elna_lexer_set_transition(current_state: Word, character_class: Word, action: Word, next_state: ElnaLexerState) var transition: ^ElnaLexerTransition begin transition := elna_lexer_get_transition(current_state, character_class); transition^.action := action; transition^.next_state := next_state end (* Sets same action and state transition for all character classes in one transition row. *) (** * Parameters: * current_state - Current state (Transition state enumeration). * default_action - Default action (Callback). * next_state - Next state (Transition state enumeration). *) proc elna_lexer_default_transition(current_state: Word, default_action: Word, next_state: ElnaLexerState) begin elna_lexer_set_transition(current_state, ElnaLexerClass.invalid, default_action, next_state); elna_lexer_set_transition(current_state, ElnaLexerClass.digit, default_action, next_state); elna_lexer_set_transition(current_state, ElnaLexerClass.alpha, default_action, next_state); elna_lexer_set_transition(current_state, ElnaLexerClass.space, default_action, next_state); elna_lexer_set_transition(current_state, ElnaLexerClass.colon, default_action, next_state); elna_lexer_set_transition(current_state, ElnaLexerClass.equals, default_action, next_state); elna_lexer_set_transition(current_state, ElnaLexerClass.left_paren, default_action, next_state); elna_lexer_set_transition(current_state, ElnaLexerClass.right_paren, default_action, next_state); elna_lexer_set_transition(current_state, ElnaLexerClass.asterisk, default_action, next_state); elna_lexer_set_transition(current_state, ElnaLexerClass.backslash, default_action, next_state); elna_lexer_set_transition(current_state, ElnaLexerClass.single, default_action, next_state); elna_lexer_set_transition(current_state, ElnaLexerClass.hex, default_action, next_state); elna_lexer_set_transition(current_state, ElnaLexerClass.zero, default_action, next_state); elna_lexer_set_transition(current_state, ElnaLexerClass.x, default_action, next_state); elna_lexer_set_transition(current_state, ElnaLexerClass.eof, default_action, next_state); elna_lexer_set_transition(current_state, ElnaLexerClass.dot, default_action, next_state); elna_lexer_set_transition(current_state, ElnaLexerClass.minus, default_action, next_state); elna_lexer_set_transition(current_state, ElnaLexerClass.single_quote, default_action, next_state); elna_lexer_set_transition(current_state, ElnaLexerClass.double_quote, default_action, next_state); elna_lexer_set_transition(current_state, ElnaLexerClass.greater, default_action, next_state); elna_lexer_set_transition(current_state, ElnaLexerClass.less, default_action, next_state); elna_lexer_set_transition(current_state, ElnaLexerClass.other, default_action, next_state); elna_lexer_set_transition(current_state, ElnaLexerClass.number_sign, default_action, next_state) end (** * The transition table describes transitions from one state to another, given * a symbol (character class). * * The table has m rows and n columns, where m is the amount of states and n is * the amount of classes. So given the current state and a classified character * the table can be used to look up the next state. *) proc elna_lexer_transitions() begin (* Start state. *) elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.invalid, ElnaLexerAction.none, ElnaLexerState.finish); elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.digit, ElnaLexerAction.accumulate, ElnaLexerState.decimal); elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.alpha, ElnaLexerAction.accumulate, ElnaLexerState.identifier); elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.space, ElnaLexerAction.skip, ElnaLexerState.start); elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.colon, ElnaLexerAction.accumulate, ElnaLexerState.colon); elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.equals, ElnaLexerAction.single, ElnaLexerState.finish); elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.left_paren, ElnaLexerAction.accumulate, ElnaLexerState.left_paren); elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.right_paren, ElnaLexerAction.single, ElnaLexerState.finish); elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.asterisk, ElnaLexerAction.single, ElnaLexerState.finish); elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.backslash, ElnaLexerAction.none, ElnaLexerState.finish); elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.single, ElnaLexerAction.single, ElnaLexerState.finish); elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.hex, ElnaLexerAction.accumulate, ElnaLexerState.identifier); elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.zero, ElnaLexerAction.accumulate, ElnaLexerState.leading_zero); elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.x, ElnaLexerAction.accumulate, ElnaLexerState.identifier); elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.eof, ElnaLexerAction.eof, ElnaLexerState.finish); elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.dot, ElnaLexerAction.single, ElnaLexerState.finish); elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.minus, ElnaLexerAction.accumulate, ElnaLexerState.minus); elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.single_quote, ElnaLexerAction.accumulate, ElnaLexerState.character); elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.double_quote, ElnaLexerAction.accumulate, ElnaLexerState.string); elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.greater, ElnaLexerAction.accumulate, ElnaLexerState.greater); elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.less, ElnaLexerAction.accumulate, ElnaLexerState.less); elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.other, ElnaLexerAction.none, ElnaLexerState.finish); elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.number_sign, ElnaLexerAction.accumulate, ElnaLexerState.number_sign); (* Colon state. *) elna_lexer_default_transition(ElnaLexerState.colon, ElnaLexerAction.finalize, ElnaLexerState.finish); elna_lexer_set_transition(ElnaLexerState.colon, ElnaLexerClass.equals, ElnaLexerAction.composite, ElnaLexerState.finish); (* Identifier state. *) elna_lexer_default_transition(ElnaLexerState.identifier, ElnaLexerAction.key_id, ElnaLexerState.finish); elna_lexer_set_transition(ElnaLexerState.identifier, ElnaLexerClass.digit, ElnaLexerAction.accumulate, ElnaLexerState.identifier); elna_lexer_set_transition(ElnaLexerState.identifier, ElnaLexerClass.alpha, ElnaLexerAction.accumulate, ElnaLexerState.identifier); elna_lexer_set_transition(ElnaLexerState.identifier, ElnaLexerClass.hex, ElnaLexerAction.accumulate, ElnaLexerState.identifier); elna_lexer_set_transition(ElnaLexerState.identifier, ElnaLexerClass.zero, ElnaLexerAction.accumulate, ElnaLexerState.identifier); elna_lexer_set_transition(ElnaLexerState.identifier, ElnaLexerClass.x, ElnaLexerAction.accumulate, ElnaLexerState.identifier); (* Decimal state. *) elna_lexer_default_transition(ElnaLexerState.decimal, ElnaLexerAction.integer, ElnaLexerState.finish); elna_lexer_set_transition(ElnaLexerState.decimal, ElnaLexerClass.digit, ElnaLexerAction.accumulate, ElnaLexerState.decimal); elna_lexer_set_transition(ElnaLexerState.decimal, ElnaLexerClass.alpha, ElnaLexerAction.none, ElnaLexerState.finish); elna_lexer_set_transition(ElnaLexerState.decimal, ElnaLexerClass.hex, ElnaLexerAction.none, ElnaLexerState.finish); elna_lexer_set_transition(ElnaLexerState.decimal, ElnaLexerClass.zero, ElnaLexerAction.accumulate, ElnaLexerState.decimal); elna_lexer_set_transition(ElnaLexerState.decimal, ElnaLexerClass.x, ElnaLexerAction.none, ElnaLexerState.finish); (* Leading zero. *) elna_lexer_default_transition(ElnaLexerState.leading_zero, ElnaLexerAction.integer, ElnaLexerState.finish); elna_lexer_set_transition(ElnaLexerState.leading_zero, ElnaLexerClass.digit, ElnaLexerAction.none, ElnaLexerState.finish); elna_lexer_set_transition(ElnaLexerState.leading_zero, ElnaLexerClass.alpha, ElnaLexerAction.none, ElnaLexerState.finish); elna_lexer_set_transition(ElnaLexerState.leading_zero, ElnaLexerClass.hex, ElnaLexerAction.none, ElnaLexerState.finish); elna_lexer_set_transition(ElnaLexerState.leading_zero, ElnaLexerClass.zero, ElnaLexerAction.none, ElnaLexerState.finish); elna_lexer_set_transition(ElnaLexerState.leading_zero, ElnaLexerClass.x, ElnaLexerAction.none, ElnaLexerState.dot); (* Greater state. *) elna_lexer_default_transition(ElnaLexerState.greater, ElnaLexerAction.finalize, ElnaLexerState.finish); elna_lexer_set_transition(ElnaLexerState.greater, ElnaLexerClass.equals, ElnaLexerAction.composite, ElnaLexerState.finish); (* Minus state. *) elna_lexer_default_transition(ElnaLexerState.minus, ElnaLexerAction.finalize, ElnaLexerState.finish); elna_lexer_set_transition(ElnaLexerState.minus, ElnaLexerClass.greater, ElnaLexerAction.composite, ElnaLexerState.finish); (* Left paren state. *) elna_lexer_default_transition(ElnaLexerState.left_paren, ElnaLexerAction.finalize, ElnaLexerState.finish); elna_lexer_set_transition(ElnaLexerState.left_paren, ElnaLexerClass.asterisk, ElnaLexerAction.accumulate, ElnaLexerState.comment); (* Less state. *) elna_lexer_default_transition(ElnaLexerState.less, ElnaLexerAction.finalize, ElnaLexerState.finish); elna_lexer_set_transition(ElnaLexerState.less, ElnaLexerClass.equals, ElnaLexerAction.composite, ElnaLexerState.finish); elna_lexer_set_transition(ElnaLexerState.less, ElnaLexerClass.greater, ElnaLexerAction.composite, ElnaLexerState.finish); (* Hexadecimal after 0x. *) elna_lexer_default_transition(ElnaLexerState.dot, ElnaLexerAction.finalize, ElnaLexerState.finish); elna_lexer_set_transition(ElnaLexerState.dot, ElnaLexerClass.dot, ElnaLexerAction.composite, ElnaLexerState.finish); (* Comment. *) elna_lexer_default_transition(ElnaLexerState.comment, ElnaLexerAction.accumulate, ElnaLexerState.comment); elna_lexer_set_transition(ElnaLexerState.comment, ElnaLexerClass.asterisk, ElnaLexerAction.accumulate, ElnaLexerState.closing_comment); elna_lexer_set_transition(ElnaLexerState.comment, ElnaLexerClass.eof, ElnaLexerAction.none, ElnaLexerState.finish); (* Closing comment. *) elna_lexer_default_transition(ElnaLexerState.closing_comment, ElnaLexerAction.accumulate, ElnaLexerState.comment); elna_lexer_set_transition(ElnaLexerState.closing_comment, ElnaLexerClass.invalid, ElnaLexerAction.none, ElnaLexerState.finish); elna_lexer_set_transition(ElnaLexerState.closing_comment, ElnaLexerClass.right_paren, ElnaLexerAction.delimited, ElnaLexerState.finish); elna_lexer_set_transition(ElnaLexerState.closing_comment, ElnaLexerClass.asterisk, ElnaLexerAction.accumulate, ElnaLexerState.closing_comment); elna_lexer_set_transition(ElnaLexerState.closing_comment, ElnaLexerClass.eof, ElnaLexerAction.none, ElnaLexerState.finish); (* Character. *) elna_lexer_default_transition(ElnaLexerState.character, ElnaLexerAction.accumulate, ElnaLexerState.character); elna_lexer_set_transition(ElnaLexerState.character, ElnaLexerClass.invalid, ElnaLexerAction.none, ElnaLexerState.finish); elna_lexer_set_transition(ElnaLexerState.character, ElnaLexerClass.eof, ElnaLexerAction.none, ElnaLexerState.finish); elna_lexer_set_transition(ElnaLexerState.character, ElnaLexerClass.single_quote, ElnaLexerAction.delimited, ElnaLexerState.finish); elna_lexer_set_transition(ElnaLexerState.character, ElnaLexerClass.backslash, ElnaLexerAction.accumulate, ElnaLexerState.character_escape); (* Escape sequence in a character. *) elna_lexer_default_transition(ElnaLexerState.character_escape, ElnaLexerAction.accumulate, ElnaLexerState.character); elna_lexer_set_transition(ElnaLexerState.character_escape, ElnaLexerClass.invalid, ElnaLexerAction.none, ElnaLexerState.finish); elna_lexer_set_transition(ElnaLexerState.character_escape, ElnaLexerClass.eof, ElnaLexerAction.none, ElnaLexerState.finish); (* String. *) elna_lexer_default_transition(ElnaLexerState.string, ElnaLexerAction.accumulate, ElnaLexerState.string); elna_lexer_set_transition(ElnaLexerState.string, ElnaLexerClass.invalid, ElnaLexerAction.none, ElnaLexerState.finish); elna_lexer_set_transition(ElnaLexerState.string, ElnaLexerClass.eof, ElnaLexerAction.none, ElnaLexerState.finish); elna_lexer_set_transition(ElnaLexerState.string, ElnaLexerClass.double_quote, ElnaLexerAction.delimited, ElnaLexerState.finish); elna_lexer_set_transition(ElnaLexerState.string, ElnaLexerClass.backslash, ElnaLexerAction.accumulate, ElnaLexerState.string_escape); (* Escape sequence in a string. *) elna_lexer_default_transition(ElnaLexerState.string_escape, ElnaLexerAction.accumulate, ElnaLexerState.string); elna_lexer_set_transition(ElnaLexerState.string_escape, ElnaLexerClass.invalid, ElnaLexerAction.none, ElnaLexerState.finish); elna_lexer_set_transition(ElnaLexerState.string_escape, ElnaLexerClass.eof, ElnaLexerAction.none, ElnaLexerState.finish); (* Number sign state. *) elna_lexer_default_transition(ElnaLexerState.number_sign, ElnaLexerAction.key_id, ElnaLexerState.finish); elna_lexer_set_transition(ElnaLexerState.number_sign, ElnaLexerClass.alpha, ElnaLexerAction.accumulate, ElnaLexerState.trait); elna_lexer_set_transition(ElnaLexerState.number_sign, ElnaLexerClass.hex, ElnaLexerAction.accumulate, ElnaLexerState.trait); elna_lexer_set_transition(ElnaLexerState.number_sign, ElnaLexerClass.x, ElnaLexerAction.accumulate, ElnaLexerState.trait); (* Trait state. *) elna_lexer_default_transition(ElnaLexerState.trait, ElnaLexerAction.key_id, ElnaLexerState.finish); elna_lexer_set_transition(ElnaLexerState.trait, ElnaLexerClass.digit, ElnaLexerAction.accumulate, ElnaLexerState.trait); elna_lexer_set_transition(ElnaLexerState.trait, ElnaLexerClass.alpha, ElnaLexerAction.accumulate, ElnaLexerState.trait); elna_lexer_set_transition(ElnaLexerState.trait, ElnaLexerClass.hex, ElnaLexerAction.accumulate, ElnaLexerState.trait); elna_lexer_set_transition(ElnaLexerState.trait, ElnaLexerClass.zero, ElnaLexerAction.accumulate, ElnaLexerState.trait); elna_lexer_set_transition(ElnaLexerState.trait, ElnaLexerClass.x, ElnaLexerAction.accumulate, ElnaLexerState.trait) end (** * One time lexer initialization. *) proc elna_lexer_initialize(cursor: ^ElnaLexerCursor, code_pointer: Word) begin elna_lexer_classifications(); elna_lexer_transitions(); cursor^.start := code_pointer; cursor^.finish := code_pointer; cursor^.token := nil; cursor^.position.start_location.line := 1; cursor^.position.start_location.column := 1; cursor^.position.end_location.line := 1; cursor^.position.end_location.column := 1 end (* Returns true or false depending whether two strings are equal. *) proc string_compare(lhs_pointer: Word, lhs_length: Word, rhs_pointer: Word, rhs_length: Word) -> Bool var result: Bool begin if lhs_length = rhs_length then result := memcmp(lhs_pointer, rhs_pointer, lhs_length) = 0 else result := false end; return result end proc elna_lexer_token_create(kind: ElnaLexerKind, position: ^ElnaPosition) -> ^ElnaLexerToken var result: ^ElnaLexerToken begin result := malloc(#size(ElnaLexerToken)); result^.kind := kind; result^.position := position; return result end proc elna_lexer_classify_keyword(position_start: ^Char, position_end: Word, position: ^ElnaPosition) -> ^ElnaLexerToken var result: ^ElnaLexerToken begin result := elna_lexer_token_create(ElnaLexerKind.identifier, position); result^.start := position_start; result^.length := position_end - position_start; if position_start^ = '#' then result^.kind := ElnaLexerKind.trait elsif string_compare(position_start, result^.length, "const".ptr, 5) then result^.kind := ElnaLexerKind._const elsif string_compare(position_start, result^.length, "var".ptr, 3) then result^.kind := ElnaLexerKind._var elsif string_compare(position_start, result^.length, "proc".ptr, 4) then result^.kind := ElnaLexerKind._proc elsif string_compare(position_start, result^.length, "type".ptr, 4) then result^.kind := ElnaLexerKind._type elsif string_compare(position_start, result^.length, "begin".ptr, 5) then result^.kind := ElnaLexerKind._begin elsif string_compare(position_start, result^.length, "end".ptr, 3) then result^.kind := ElnaLexerKind._end elsif string_compare(position_start, result^.length, "return".ptr, 6) then result^.kind := ElnaLexerKind._return elsif string_compare(position_start, result^.length, "goto".ptr, 4) then result^.kind := ElnaLexerKind._goto elsif string_compare(position_start, result^.length, "if".ptr, 2) then result^.kind := ElnaLexerKind._if elsif string_compare(position_start, result^.length, "while".ptr, 5) then result^.kind := ElnaLexerKind._while elsif string_compare(position_start, result^.length, "then".ptr, 4) then result^.kind := ElnaLexerKind._then elsif string_compare(position_start, result^.length, "else".ptr, 4) then result^.kind := ElnaLexerKind._else elsif string_compare(position_start, result^.length, "elsif".ptr, 5) then result^.kind := ElnaLexerKind._elsif elsif string_compare(position_start, result^.length, "record".ptr, 6) then result^.kind := ElnaLexerKind._record elsif string_compare(position_start, result^.length, "or".ptr, 2) then result^.kind := ElnaLexerKind._or elsif string_compare(position_start, result^.length, "xor".ptr, 3) then result^.kind := ElnaLexerKind._xor elsif string_compare(position_start, result^.length, "program".ptr, 7) then result^.kind := ElnaLexerKind._program elsif string_compare(position_start, result^.length, "module".ptr, 6) then result^.kind := ElnaLexerKind._module elsif string_compare(position_start, result^.length, "nil".ptr, 3) then result^.kind := ElnaLexerKind.null elsif string_compare(position_start, result^.length, "true".ptr, 4) then result^.kind := ElnaLexerKind.boolean elsif string_compare(position_start, result^.length, "false".ptr, 5) then result^.kind := ElnaLexerKind.boolean elsif string_compare(position_start, result^.length, "cast".ptr, 4) then result^.kind := ElnaLexerKind._cast end; return result end proc elna_lexer_classify_finalize(start_position: ^Char, position: ^ElnaPosition) -> ^ElnaLexerToken var character: Char result: ^ElnaLexerToken begin character := start_position^; if character = ':' then result := elna_lexer_token_create(ElnaLexerKind.colon, position) elsif character = '.' then result := elna_lexer_token_create(ElnaLexerKind.dot, position) elsif character = '(' then result := elna_lexer_token_create(ElnaLexerKind.left_paren, position) elsif character = '-' then result := elna_lexer_token_create(ElnaLexerKind.minus, position) elsif character = '<' then result := elna_lexer_token_create(ElnaLexerKind.less_than, position) elsif character = '>' then result := elna_lexer_token_create(ElnaLexerKind.greater_than, position) end; return result end proc elna_lexer_classify_single(start_position: ^Char, position: ^ElnaPosition) -> ^ElnaLexerToken var character: Char result: ^ElnaLexerToken begin result := malloc(#size(ElnaLexerToken)); character := start_position^; if character = ';' then result := elna_lexer_token_create(ElnaLexerKind.semicolon, position) elsif character = ',' then result := elna_lexer_token_create(ElnaLexerKind.comma, position) elsif character = ')' then result := elna_lexer_token_create(ElnaLexerKind.right_paren, position) elsif character = '@' then result := elna_lexer_token_create(ElnaLexerKind.at, position) elsif character = '~' then result := elna_lexer_token_create(ElnaLexerKind.not, position) elsif character = '&' then result := elna_lexer_token_create(ElnaLexerKind.and, position) elsif character = '+' then result := elna_lexer_token_create(ElnaLexerKind.plus, position) elsif character = '*' then result := elna_lexer_token_create(ElnaLexerKind.multiplication, position) elsif character = '=' then result := elna_lexer_token_create(ElnaLexerKind.equals, position) elsif character = '%' then result := elna_lexer_token_create(ElnaLexerKind.remainder, position) elsif character = '/' then result := elna_lexer_token_create(ElnaLexerKind.division, position) elsif character = '.' then result := elna_lexer_token_create(ElnaLexerKind.dot, position) elsif character = '^' then result := elna_lexer_token_create(ElnaLexerKind.hat, position) elsif character = '[' then result := elna_lexer_token_create(ElnaLexerKind.left_square, position) elsif character = ']' then result := elna_lexer_token_create(ElnaLexerKind.right_square, position) elsif character = '|' then result := elna_lexer_token_create(ElnaLexerKind.pipe, position) end; return result end proc elna_lexer_classify_composite(start_position: ^Char, one_before_last: ^Char, position: ^ElnaPosition) -> ^ElnaLexerToken var first_character: Char last_character: Char result: ^ElnaLexerToken begin first_character := start_position^; last_character := one_before_last^; if first_character = ':' then result := elna_lexer_token_create(ElnaLexerKind.assignment, position) elsif first_character = '<' then if last_character = '=' then result := elna_lexer_token_create(ElnaLexerKind.less_equal, position) elsif last_character = '>' then result := elna_lexer_token_create(ElnaLexerKind.not_equal, position) end elsif first_character = '>' then if last_character = '=' then result := elna_lexer_token_create(ElnaLexerKind.greater_equal, position) end elsif first_character = '-' then result := elna_lexer_token_create(ElnaLexerKind.arrow, position) end; return result end proc elna_lexer_classify_delimited(start_position: ^Char, end_position: Word, position: ^ElnaPosition) -> ^ElnaLexerToken var delimiter: Char result: ^ElnaLexerToken begin delimiter := start_position^; if delimiter = '(' then result := elna_lexer_token_create(ElnaLexerKind.comment, position) elsif delimiter = '\'' then result := elna_lexer_token_create(ElnaLexerKind.character, position) elsif delimiter = '"' then result := elna_lexer_token_create(ElnaLexerKind.string, position) end; result^.start := start_position; result^.length := end_position - start_position; return result end proc elna_lexer_classify_integer(start_position: Word, end_position: Word, position: ^ElnaPosition) -> ^ElnaLexerToken var result: ^ElnaLexerToken begin result := elna_lexer_token_create(ElnaLexerKind.integer, position); result^.start := start_position; result^.length := end_position - start_position; return result end proc elna_lexer_execute_action(cursor: ^ElnaLexerCursor, action_to_perform: Word) -> ^ElnaLexerToken var token: ^ElnaLexerToken begin token := nil; if action_to_perform = ElnaLexerAction.none then elsif action_to_perform = ElnaLexerAction.accumulate then elna_lexer_advance(cursor) elsif action_to_perform = ElnaLexerAction.skip then elna_lexer_classify_space(cursor^.start, @cursor^.position.end_location); cursor^.start := cursor^.start + 1; cursor^.finish := cursor^.finish + 1 elsif action_to_perform = ElnaLexerAction.single then elna_lexer_advance(cursor); token := elna_lexer_classify_single(cursor^.start, @cursor^.position) elsif action_to_perform = ElnaLexerAction.eof then token := malloc(#size(ElnaLexerToken)); token^.kind := ElnaLexerKind.eof elsif action_to_perform = ElnaLexerAction.finalize then token := elna_lexer_classify_finalize(cursor^.start, @cursor^.position) elsif action_to_perform = ElnaLexerAction.composite then token := elna_lexer_classify_composite(cursor^.start, cursor^.finish, @cursor^.position); elna_lexer_advance(cursor) elsif action_to_perform = ElnaLexerAction.key_id then token := elna_lexer_classify_keyword(cursor^.start, cursor^.finish, @cursor^.position) elsif action_to_perform = ElnaLexerAction.integer then token := elna_lexer_classify_integer(cursor^.start, cursor^.finish, @cursor^.position) elsif action_to_perform = ElnaLexerAction.delimited then elna_lexer_advance(cursor); token := elna_lexer_classify_delimited(cursor^.start, cursor^.finish, @cursor^.position) end; return token end proc elna_lexer_execute_transition(cursor: ^ElnaLexerCursor, kind: ^ElnaLexerKind) -> ^ElnaLexerToken var next_transition: ^ElnaLexerTransition current_character: Char begin current_character := cursor^.finish^; next_transition := elna_lexer_get_transition(cursor^.state, classification[current_character + 1]); cursor^.state := next_transition^.next_state; return elna_lexer_execute_action(cursor, next_transition^.action, kind) end proc elna_lexer_classify_space(start_position: ^Char, location: ^ElnaLocation) begin if start_position^ = '\n' then location^.line := location^.line + 1; location^.column := 1 else location^.column := location^.column + 1 end end proc elna_lexer_advance(cursor: ^ElnaLexerCursor) begin cursor^.finish := cursor^.finish + 1; cursor^.position.end_location.column := cursor^.position.end_location.column + 1 end (** * Reads the next token and writes its type into the address in the kind parameter. * Resets the lexer state for reading the next token. *) proc elna_lexer_peek(cursor: ^ElnaLexerCursor) -> ^ElnaLexerToken var token: ^ElnaLexerToken begin if cursor^.token = nil then cursor^.state := ElnaLexerState.start; .elna_lexer_peek_loop; token := elna_lexer_execute_transition(cursor); if cursor^.state <> ElnaLexerState.finish then goto elna_lexer_peek_loop end; cursor^.token := token end; return cursor^.token end (** * Reads the token and advance the lexer. *) proc elna_lexer_read(cursor: ^ElnaLexerCursor) -> ^ElnaLexerToken var token: ^ElnaLexerToken begin token := elna_lexer_peek(cursor); cursor^.token := nil; cursor^.start := cursor^.finish; cursor^.position.start_location := cursor^.position.end_location; return token end proc initialize_global_state() begin compiler_strings_position := @compiler_strings; source_code := malloc(495616) end proc read_source() var last_read: Word offset: Word begin (* Read the source from the standard input. *) offset := source_code; .start_read; (* Second argument is buffer size. Modifying update the source_code definition. *) last_read := read(0, offset, 409600); if last_read > 0 then offset := offset + last_read; goto start_read end end begin initialize_global_state(); elna_symbol_table_build(); read_source(); if compile() then exit(0) else exit(4) end end.