(* * 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 16 compiler. *) type (** * List of intermediate representation items. *) ElnaInstructionList = record next: Word end; ElnaInstructionDeclaration = record next: Word; name: Word; length: Word; body: Word; stack: Word end; _node = record kind: Word end; _integer_literal_node = record kind: Word; value: Word; length: Word end; _character_literal_node = record kind: Word; value: Word; length: Word end; _nil_node = record kind: Word end; _variable_expression = record kind: Word; name: Word; length: Word end; _string_literal_node = record kind: Word; value: Word; length: Word end; _dereference_expression = record kind: Word; pointer: Word end; _binary_expression = record kind: Word; lhs: Word; rhs: Word; operator: Word end; _unary_expression = record kind: Word; operand: Word; operator: Word end; _if_statement = record kind: Word; next: Word; conditionals: Word; _else: Word end; (** * All statements are chained into a list. Next contains a pointer to the next * statement in the statement list. *) _statement = record kind: Word; next: Word end; _goto_statement = record kind: Word; next: Word; label: Word; length: Word end; _label_declaration = record kind: Word; next: Word; label: Word; length: Word end; _field_access_expression = record kind: Word; aggregate: Word; field: Word; length: Word end; ElnaInstructionModule = record data: Word; code: Word end; _module_declaration = record kind: Word; types: Word; globals: Word; procedures: Word end; _assign_statement = record kind: Word; next: Word; assignee: Word; assignment: Word end; _return_statement = record kind: Word; next: Word; returned: Word end; _type = record kind: Word; size: Word end; _enumeration_type = record kind: Word; size: Word; members: Word; length: Word end; _enumeration_type_expression = record kind: Word; members: Word; length: Word end; _record_type = record kind: Word; size: Word; members: Word; length: Word end; _record_type_expression = record kind: Word; members: Word; length: Word end; _named_type_expression = record kind: Word; name: Word; length: Word end; _info = record kind: Word end; _type_info = record kind: Word; _type: Word end; _parameter_info = record kind: Word; offset: Word end; _temporary_info = record kind: Word; offset: Word end; _procedure_info = record kind: Word; symbol_table: Word end; (** * Conditional statements is a list of pairs: condition and statements. * Used for example to represent if and elsif blocks with beloning statements. *) _conditional_statements = record condition: Word; statements: Word; next: Word end; _declaration = record kind: Word; next: Word; name: Word; length: Word end; _procedure_declaration = record kind: Word; next: Word; name: Word; length: Word; body: Word; temporaries: Word; parameters: Word end; _type_declaration = record kind: Word; next: Word; name: Word; length: Word; _type: Word end; _variable_declaration = record kind: Word; next: Word; name: Word; length: Word; _type: Word end; ElnaLexerAction = (none, accumulate, skip, single, eof, finalize, composite, key_id, integer, delimited); (** * Classification table assigns each possible character to a group (class). All * characters of the same group a handled equivalently. * * Transition = record * action: TransitionAction; * next_state: TransitionState * end; *) 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 ); ElnaLexerState = ( start, colon, identifier, decimal, leading_zero, greater, minus, left_paren, less, dot, comment, closing_comment, character, character_escape, string, string_escape, finish ); ElnaLexerKind = ( identifier, _const, _var, _proc, _type, _begin, _end, _if, _then, _else, _elsif, _while, _do, _extern, _record, _true, _false, 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 ); NodeKind = ( integer_literal, string_literal, character_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, null ); InfoKind = (type_info, parameter_info, temporary_info, procedure_info); TypeKind = (primitive, enumeration, _record); ElnaTacOperator = ( get_address, add, load, store, proc_call, copy, 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 ); ElnaRtlOperator = ( li, la, add, addi, lw, sw, jal, move, sub, div, rem, mul, _xor, _or, and, seqz, snez, slt, xori, neg, not, j, beqz, bnez, label, allocate_stack, ret ); ElnaTacOperand = (temporary, immediate, symbol, stack); ElnaRtlOperand = (register, immediate, symbol, offset); 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 ); var symbol_table_global: Array; compiler_strings: Array; classification: Array; source_code: Word; compiler_strings_position: Word; compiler_strings_length: Word; label_counter: Word; symbol_table_store: Word; temporary_variable_counter: Word; (** * Calculates and returns the string token length between quotes, including the * escaping slash characters. * * Parameters: * string - String token pointer. * * Returns the length in a0. *) proc _string_length(string: Word); var counter: Word; current_byte: Word; begin (* Reset the counter. *) counter := 0; .string_length_loop; string := string + 1; current_byte := _load_byte(string); if current_byte <> '"' then counter := counter + 1; goto string_length_loop end; return counter end; (** * Adds a string to the global, read-only string storage. * * Parameters: * string - String token. * * Returns the offset from the beginning of the storage to the new string in a0. *) proc _add_string(string: Word); var contents: Word; result: Word; current_byte: Word; begin contents := string + 1; result := compiler_strings_length; .add_string_loop; current_byte := _load_byte(contents); if current_byte <> '"' then _store_byte(current_byte, compiler_strings_position); compiler_strings_position := compiler_strings_position + 1; contents := contents + 1; if current_byte <> '\\' then compiler_strings_length := compiler_strings_length + 1 end; goto add_string_loop end; return result end; (** * Reads standard input into a buffer. * * Parameters: * buffer - Buffer pointer. * size - Buffer size. * * Returns the amount of bytes written in a0. *) proc _read_file(buffer: Word, size: Word); return _syscall(0, buffer, size, 0, 0, 0, 63) end; (** * Writes to the standard output. * * Parameters: * buffer - Buffer. * size - Buffer length. *) proc _write_s(buffer: Word, size: Word); begin _syscall(1, buffer, size, 0, 0, 0, 64) end; (** * Writes a number to a string buffer. * * Parameters: * number - Whole number. * output_buffer - Buffer pointer. * * Sets a0 to the length of the written number. *) proc _print_i(number: Word, output_buffer: Word); var local_buffer: Word; is_negative: Word; current_character: Word; result: Word; begin local_buffer := @result + 11; if number >= 0 then is_negative := 0 else number = -number; is_negative := 1 end; .print_i_digit10; current_character := number % 10; _store_byte(current_character + '0', local_buffer); number := number / 10; local_buffer := local_buffer - 1; if number <> 0 then goto print_i_digit10 end; if is_negative = 1 then _store_byte('-', local_buffer); local_buffer := local_buffer - 1 end; result := @result + 11; result := result - local_buffer; memcpy(output_buffer, local_buffer + 1, result); return result end; (** * Writes a number to the standard output. * * Parameters: * number - Whole number. *) proc _write_i(number: Word); var local_buffer: Word; length: Word; begin length := _print_i(number, @local_buffer); _write_s(@local_buffer, length) end; (** * Writes a character from a0 into the standard output. * * Parameters: * character - Character to write. *) proc _write_c(character: Word); begin _write_s(@character, 1) end; (** * Write null terminated string. * * Parameters: * string - String. *) proc _write_z(string: Word); var next_byte: Word; begin (* Check for 0 character. *) next_byte := _load_byte(string); if next_byte <> 0 then (* Print a character. *) _write_c(next_byte); (* Advance the input string by one byte. *) _write_z(string + 1) end end; (** * Detects if the passed character is a 7-bit alpha character or an underscore. * * Paramters: * character - Tested character. * * Sets a0 to 1 if the character is an alpha character or underscore, sets it to 0 otherwise. *) proc _is_alpha(character: Word); var is_underscore: Word; begin is_underscore := character = '_'; return isalpha(character) or is_underscore end; proc _is_alnum(character: Word); return _is_alpha(character) or isdigit(character) end; proc _elna_tac_instruction_size(); return 44 end; proc _elna_tac_instruction_get_kind(this: Word); begin this := this + 4; return this^ end; proc _elna_tac_instruction_set_kind(this: Word, value: Word); begin this := this + 4; this^ := value end; proc elna_instruction_list_concatenate(this: Word, value: Word); var start: Word; begin if this = 0 then start := value; goto elna_instruction_list_concatenate_end else start := this end; .elna_instruction_list_concatenate_loop; if value <> 0 then if this^ <> 0 then this := this^; goto elna_instruction_list_concatenate_loop end end; this^ := value; .elna_instruction_list_concatenate_end; return start end; proc _elna_tac_instruction_get_operand_type(this: Word, n: Word); begin n := n - 1; n := n * 12; this := this + 8; this := this + n; return this^ end; proc _elna_tac_instruction_get_operand_value(this: Word, n: Word); begin n := n - 1; n := n * 12; this := this + 8; this := this + n; this := this + 4; return this^ end; proc _elna_tac_instruction_get_operand_length(this: Word, n: Word); begin n := n - 1; n := n * 12; this := this + 8; this := this + n; this := this + 8; return this^ end; proc _elna_tac_instruction_set_operand(this: Word, n: Word, operand_type: Word, operand_value: Word, operand_length: Word); begin n := n - 1; n := n * 12; this := this + 8; this := this + n; this^ := operand_type; this := this + 4; this^ := operand_value; this := this + 4; this^ := operand_length end; proc _elna_tac_instruction_create(kind: Word); var result: Word; begin result := malloc(_elna_tac_instruction_size()); _elna_tac_instruction_set_kind(result, kind); elna_instruction_list_concatenate(result, 0); return result end; proc elna_rtl_instruction_create(kind: Word); var result: Word; begin result := malloc(elna_rtl_instruction_size()); elna_rtl_instruction_set_kind(result, kind); ElnaInstructionList_set_next(result, 0); return result end; proc elna_rtl_instruction_size(); return 44 end; proc elna_rtl_instruction_get_kind(this: Word); begin this := this + 4; return this^ end; proc elna_rtl_instruction_set_kind(this: Word, value: Word); begin this := this + 4; this^ := value end; proc elna_rtl_instruction_get_operand_type(this: Word, n: Word); begin n := n - 1; n := n * 12; this := this + 8; this := this + n; return this^ end; proc elna_rtl_instruction_get_operand_value(this: Word, n: Word); begin n := n - 1; n := n * 12; this := this + 8; this := this + n; this := this + 4; return this^ end; proc elna_rtl_instruction_get_operand_length(this: Word, n: Word); begin n := n - 1; n := n * 12; this := this + 8; this := this + n; this := this + 8; return this^ end; proc elna_rtl_instruction_set_operand(this: Word, n: Word, operand_type: Word, operand_value: Word, operand_length: Word); begin n := n - 1; n := n * 12; this := this + 8; this := this + n; this^ := operand_type; this := this + 4; this^ := operand_value; this := this + 4; this^ := operand_length end; proc elna_rtl_load_operand_value(tac_instruction: Word, operand_number: Word, into: Word); var result: Word; operand_value: Word; operand_length: Word; operand_type: Word; next_instruction: Word; begin operand_value := _elna_tac_instruction_get_operand_value(tac_instruction, operand_number); operand_length := _elna_tac_instruction_get_operand_length(tac_instruction, operand_number); operand_type := _elna_tac_instruction_get_operand_type(tac_instruction, operand_number); if operand_type = ElnaTacOperand.immediate then result := elna_rtl_instruction_create(ElnaRtlOperator.li); elna_rtl_instruction_set_operand(result, 1, ElnaRtlOperand.register, into, 0); elna_rtl_instruction_set_operand(result, 2, ElnaRtlOperand.immediate, operand_value, operand_length) elsif operand_type = ElnaTacOperand.stack then result := elna_rtl_instruction_create(ElnaRtlOperator.lw); elna_rtl_instruction_set_operand(result, 1, ElnaRtlOperand.register, into, 0); elna_rtl_instruction_set_operand(result, 2, ElnaRtlOperand.offset, ElnaRtlRegister.sp, operand_value) elsif operand_type = ElnaTacOperand.symbol then result := elna_rtl_instruction_create(ElnaRtlOperator.la); elna_rtl_instruction_set_operand(result, 1, ElnaRtlOperand.register, into, 0); elna_rtl_instruction_set_operand(result, 2, ElnaRtlOperand.symbol, operand_value, operand_length); next_instruction := elna_rtl_instruction_create(ElnaRtlOperator.lw); elna_rtl_instruction_set_operand(next_instruction, 1, ElnaRtlOperand.register, into, 0); elna_rtl_instruction_set_operand(next_instruction, 2, ElnaRtlOperand.offset, into, 0); ElnaInstructionList_set_next(result, next_instruction) elsif operand_type = ElnaTacOperand.temporary then result := elna_rtl_instruction_create(ElnaRtlOperator.move); elna_rtl_instruction_set_operand(result, 1, ElnaRtlOperand.register, into, 0); elna_rtl_instruction_set_operand(result, 2, ElnaRtlOperand.register, operand_value, 0) end; return result end; proc elna_rtl_load_operand_address(tac_instruction: Word, operand_number: Word, into: Word); var result: Word; operand_value: Word; operand_length: Word; operand_type: Word; begin operand_value := _elna_tac_instruction_get_operand_value(tac_instruction, operand_number); operand_length := _elna_tac_instruction_get_operand_length(tac_instruction, operand_number); operand_type := _elna_tac_instruction_get_operand_type(tac_instruction, operand_number); if operand_type = ElnaTacOperand.stack then result := elna_rtl_instruction_create(ElnaRtlOperator.addi); elna_rtl_instruction_set_operand(result, 1, ElnaRtlOperand.register, into, 0); elna_rtl_instruction_set_operand(result, 2, ElnaRtlOperand.offset, ElnaRtlRegister.sp, operand_value) elsif operand_type = ElnaTacOperand.symbol then result := elna_rtl_instruction_create(ElnaRtlOperator.la); elna_rtl_instruction_set_operand(result, 1, ElnaRtlOperand.register, into, 0); elna_rtl_instruction_set_operand(result, 2, ElnaRtlOperand.symbol, operand_value, operand_length) end; return result end; proc elna_rtl_binary_operands(tac_instruction: Word, next_instruction: Word); var lhs: Word; rhs: Word; begin lhs := elna_rtl_load_operand_value(tac_instruction, 2, ElnaRtlRegister.t2); rhs := elna_rtl_load_operand_value(tac_instruction, 3, ElnaRtlRegister.t3); next_instruction^ := ElnaInstructionList_get_next(lhs); if next_instruction^ = 0 then ElnaInstructionList_set_next(lhs, rhs) else ElnaInstructionList_set_next(next_instruction^, rhs) end; next_instruction^ := ElnaInstructionList_get_next(rhs); if next_instruction^ = 0 then next_instruction^ := rhs end; return lhs end; proc elna_rtl_binary_arithmetic(tac_instruction: Word, next_instruction: Word, operation: Word); var lhs: Word; binary_result: Word; begin lhs := elna_rtl_binary_operands(tac_instruction, next_instruction); binary_result := elna_rtl_instruction_create(operation); elna_rtl_copy_operand(tac_instruction, 1, binary_result); elna_rtl_instruction_set_operand(binary_result, 2, ElnaRtlOperand.register, ElnaRtlRegister.t2, 0); elna_rtl_instruction_set_operand(binary_result, 3, ElnaRtlOperand.register, ElnaRtlRegister.t3, 0); elna_rtl_instruction_set_kind(binary_result, operation); ElnaInstructionList_set_next(next_instruction^, binary_result); next_instruction^ := binary_result; return lhs end; proc elna_rtl_binary_equality(tac_instruction: Word, instruction_kind: Word, next_instruction: Word); var operands: Word; intermediate_instruction: Word; binary_result: Word; begin operands := elna_rtl_binary_operands(tac_instruction, next_instruction); intermediate_instruction := elna_rtl_instruction_create(ElnaRtlOperator._xor); elna_rtl_instruction_set_operand(intermediate_instruction, 1, ElnaRtlOperand.register, ElnaRtlRegister.t2, 0); elna_rtl_instruction_set_operand(intermediate_instruction, 2, ElnaRtlOperand.register, ElnaRtlRegister.t2, 0); elna_rtl_instruction_set_operand(intermediate_instruction, 3, ElnaRtlOperand.register, ElnaRtlRegister.t3, 0); ElnaInstructionList_set_next(next_instruction^, intermediate_instruction); next_instruction^ := intermediate_instruction; binary_result := elna_rtl_instruction_create(instruction_kind); elna_rtl_copy_operand(tac_instruction, 1, binary_result); elna_rtl_instruction_set_operand(binary_result, 2, ElnaRtlOperand.register, ElnaRtlRegister.t2, 0); ElnaInstructionList_set_next(next_instruction^, binary_result); next_instruction^ := binary_result; return operands end; proc elna_rtl_copy_operand(tac_instruction: Word, number: Word, rtl_instruction: Word); var operand_length: Word; operand_type: Word; operand_value: Word; begin operand_type := _elna_tac_instruction_get_operand_type(tac_instruction, number); operand_value := _elna_tac_instruction_get_operand_value(tac_instruction, number); operand_length := _elna_tac_instruction_get_operand_length(tac_instruction, number); elna_rtl_instruction_set_operand(rtl_instruction, number, operand_type, operand_value, operand_length) end; proc elna_rtl_conditional_jump(tac_instruction: Word, condition: Word); var result: Word; begin result := elna_rtl_instruction_create(condition); elna_rtl_copy_operand(tac_instruction, 1, result); elna_rtl_copy_operand(tac_instruction, 2, result); return result end; proc elna_rtl_unary(tac_instruction: Word, rtl_operator: Word, next_instruction: Word); var result: Word; begin result := elna_rtl_load_operand_value(tac_instruction, 2, ElnaRtlRegister.t0); next_instruction^ := elna_rtl_instruction_create(rtl_operator); elna_rtl_copy_operand(tac_instruction, 1, result); elna_rtl_instruction_set_operand(result, 2, ElnaRtlOperand.register, ElnaRtlRegister.t0); result := ElnaInstructionList_set_next(result, next_instruction^); return result end; proc elna_rtl_instruction(tac_instruction: Word, next_instruction: Word); var result: Word; instruction_size: Word; instruction_kind: Word; operand_type: Word; operand_value: Word; operands: Word; intermediate_instruction: Word; begin instruction_size := elna_rtl_instruction_size(); result := malloc(instruction_size); instruction_kind := _elna_tac_instruction_get_kind(tac_instruction); next_instruction^ := nil; if instruction_kind = ElnaTacOperator.get_address then operand_type := _elna_tac_instruction_get_operand_type(tac_instruction, 2); if operand_type = ElnaTacOperand.stack then operand_value := _elna_tac_instruction_get_operand_value(tac_instruction, 2); result := elna_rtl_instruction_create(ElnaRtlOperator.add); elna_rtl_copy_operand(tac_instruction, 1, result); elna_rtl_instruction_set_operand(result, 2, ElnaRtlOperand.register, ElnaRtlRegister.sp, 0); elna_rtl_instruction_set_operand(result, 3, ElnaRtlOperand.immediate, operand_value, 0) elsif operand_type = ElnaTacOperand.symbol then result := elna_rtl_instruction_create(ElnaRtlOperator.la); elna_rtl_copy_operand(tac_instruction, 1, result); elna_rtl_copy_operand(tac_instruction, 2, result) elsif operand_type = ElnaTacOperand.temporary then result := elna_rtl_instruction_create(ElnaRtlOperator.move); elna_rtl_copy_operand(tac_instruction, 1, result); elna_rtl_copy_operand(tac_instruction, 2, result) end elsif instruction_kind = ElnaTacOperator.add then result := elna_rtl_binary_arithmetic(tac_instruction, next_instruction, ElnaRtlOperator.add) elsif instruction_kind = ElnaTacOperator.load then operand_type := _elna_tac_instruction_get_operand_type(tac_instruction, 2); if operand_type = ElnaTacOperand.stack then result := elna_rtl_instruction_create(ElnaRtlOperator.lw); operand_value := _elna_tac_instruction_get_operand_value(tac_instruction, 1); next_instruction^ := elna_rtl_instruction_create(ElnaRtlOperator.lw); elna_rtl_instruction_set_operand(next_instruction^, 1, ElnaRtlOperand.register, operand_value, 0); elna_rtl_instruction_set_operand(next_instruction^, 2, ElnaRtlOperand.offset, operand_value, 0); operand_value := _elna_tac_instruction_get_operand_value(tac_instruction, 2); elna_rtl_instruction_set_operand(result, 2, ElnaRtlOperand.offset, ElnaRtlRegister.sp, operand_value); ElnaInstructionList_set_next(result, next_instruction^) elsif operand_type = ElnaTacOperand.temporary then result := elna_rtl_instruction_create(ElnaRtlOperator.lw); elna_rtl_copy_operand(tac_instruction, 1, result); operand_value := _elna_tac_instruction_get_operand_value(tac_instruction, 2); elna_rtl_instruction_set_operand(result, 2, ElnaRtlOperand.offset, operand_value, 0) end elsif instruction_kind = ElnaTacOperator.store then operands := elna_rtl_load_operand_value(tac_instruction, 1, ElnaRtlRegister.t0); next_instruction^ := elna_rtl_instruction_create(ElnaRtlOperator.sw); result := operands; operands := ElnaInstructionList_get_next(result); elna_rtl_instruction_set_operand(next_instruction^, 1, ElnaRtlOperand.register, ElnaRtlRegister.t0, 0); operand_type := _elna_tac_instruction_get_operand_type(tac_instruction, 2); if operand_type = ElnaTacOperand.stack then operand_value := _elna_tac_instruction_get_operand_value(tac_instruction, 2); elna_rtl_instruction_set_operand(next_instruction^, 2, ElnaRtlOperand.offset, ElnaRtlRegister.sp, operand_value) elsif operand_type = ElnaTacOperand.temporary then operand_value := _elna_tac_instruction_get_operand_value(tac_instruction, 2); elna_rtl_instruction_set_operand(next_instruction^, 2, ElnaRtlOperand.offset, operand_value, 0) end; if operands = 0 then ElnaInstructionList_set_next(result, next_instruction^) else ElnaInstructionList_set_next(operands, next_instruction^) end elsif instruction_kind = ElnaTacOperator.proc_call then result := elna_rtl_instruction_create(ElnaRtlOperator.jal); elna_rtl_copy_operand(tac_instruction, 1, result) elsif instruction_kind = ElnaTacOperator.subtract then result := elna_rtl_binary_arithmetic(tac_instruction, next_instruction, ElnaRtlOperator.sub) elsif instruction_kind = ElnaTacOperator.multiply then result := elna_rtl_binary_arithmetic(tac_instruction, next_instruction, ElnaRtlOperator.mul) elsif instruction_kind = ElnaTacOperator.divide then result := elna_rtl_binary_arithmetic(tac_instruction, next_instruction, ElnaRtlOperator.div) elsif instruction_kind = ElnaTacOperator.remainder then result := elna_rtl_binary_arithmetic(tac_instruction, next_instruction, ElnaRtlOperator.rem) elsif instruction_kind = ElnaTacOperator._xor then result := elna_rtl_binary_arithmetic(tac_instruction, next_instruction, ElnaRtlOperator._xor) elsif instruction_kind = ElnaTacOperator._or then result := elna_rtl_binary_arithmetic(tac_instruction, next_instruction, ElnaRtlOperator._or) elsif instruction_kind = ElnaTacOperator.and then result := elna_rtl_binary_arithmetic(tac_instruction, next_instruction, ElnaRtlOperator.and) elsif instruction_kind = ElnaTacOperator.less_than then result := elna_rtl_binary_arithmetic(tac_instruction, next_instruction, ElnaRtlOperator.slt) elsif instruction_kind = ElnaTacOperator.greater_than then operands := elna_rtl_binary_operands(tac_instruction, next_instruction); result := elna_rtl_instruction_create(ElnaRtlOperator.slt); elna_rtl_copy_operand(tac_instruction, 1, result); elna_rtl_instruction_set_operand(result, 2, ElnaRtlOperand.register, ElnaRtlRegister.t3, 0); elna_rtl_instruction_set_operand(result, 3, ElnaRtlOperand.register, ElnaRtlRegister.t2, 0); ElnaInstructionList_set_next(next_instruction^, result); next_instruction^ := result; result := operands; elna_rtl_instruction_set_kind(next_instruction^, ElnaRtlOperator.slt) elsif instruction_kind = ElnaTacOperator.less_or_equal then operands := elna_rtl_binary_operands(tac_instruction, next_instruction); intermediate_instruction := elna_rtl_instruction_create(ElnaRtlOperator.slt); elna_rtl_instruction_set_operand(intermediate_instruction, 1, ElnaRtlOperand.register, ElnaRtlRegister.t2, 0); elna_rtl_instruction_set_operand(intermediate_instruction, 2, ElnaRtlOperand.register, ElnaRtlRegister.t3, 0); elna_rtl_instruction_set_operand(intermediate_instruction, 3, ElnaRtlOperand.register, ElnaRtlRegister.t2, 0); ElnaInstructionList_set_next(next_instruction^, intermediate_instruction); next_instruction^ := intermediate_instruction; result := elna_rtl_instruction_create(ElnaRtlOperator.xori); elna_rtl_copy_operand(tac_instruction, 1, result); elna_rtl_instruction_set_operand(result, 2, ElnaRtlOperand.register, ElnaRtlRegister.t2, 0); elna_rtl_instruction_set_operand(result, 3, ElnaRtlOperand.immediate, 1, 0); ElnaInstructionList_set_next(next_instruction^, result); next_instruction^ := result; result := operands; elna_rtl_instruction_set_kind(next_instruction^, ElnaRtlOperator.xori) elsif instruction_kind = ElnaTacOperator.greater_or_equal then operands := elna_rtl_binary_operands(tac_instruction, next_instruction); intermediate_instruction := elna_rtl_instruction_create(ElnaRtlOperator.slt); elna_rtl_instruction_set_operand(intermediate_instruction, 1, ElnaRtlOperand.register, ElnaRtlRegister.t2, 0); elna_rtl_instruction_set_operand(intermediate_instruction, 2, ElnaRtlOperand.register, ElnaRtlRegister.t2, 0); elna_rtl_instruction_set_operand(intermediate_instruction, 3, ElnaRtlOperand.register, ElnaRtlRegister.t3, 0); ElnaInstructionList_set_next(next_instruction^, intermediate_instruction); next_instruction^ := intermediate_instruction; result := elna_rtl_instruction_create(ElnaRtlOperator.xori); elna_rtl_copy_operand(tac_instruction, 1, result); elna_rtl_instruction_set_operand(result, 2, ElnaRtlOperand.register, ElnaRtlRegister.t2, 0); elna_rtl_instruction_set_operand(result, 3, ElnaRtlOperand.immediate, 1, 0); ElnaInstructionList_set_next(next_instruction^, result); next_instruction^ := result; result := operands; elna_rtl_instruction_set_kind(next_instruction^, ElnaRtlOperator.xori) elsif instruction_kind = ElnaTacOperator.equal then result := elna_rtl_binary_equality(tac_instruction, ElnaRtlOperator.seqz, next_instruction) elsif instruction_kind = ElnaTacOperator.not_equal then result := elna_rtl_binary_equality(tac_instruction, ElnaRtlOperator.snez, next_instruction) elsif instruction_kind = ElnaTacOperator.negate then result := elna_rtl_unary(tac_instruction, ElnaRtlOperator.neg, next_instruction) elsif instruction_kind = ElnaTacOperator.complement then result := elna_rtl_unary(tac_instruction, ElnaRtlOperator.not, next_instruction) elsif instruction_kind = ElnaTacOperator.jump then result := elna_rtl_instruction_create(ElnaRtlOperator.j); elna_rtl_copy_operand(tac_instruction, 1, result) elsif instruction_kind = ElnaTacOperator.jump_if_zero then result := elna_rtl_conditional_jump(tac_instruction, ElnaRtlOperator.beqz) elsif instruction_kind = ElnaTacOperator.jump_if_not_zero then result := elna_rtl_conditional_jump(tac_instruction, ElnaRtlOperator.bnez) elsif instruction_kind = ElnaTacOperator._return then result := elna_rtl_load_operand_value(tac_instruction, 1, ElnaRtlRegister.a0) elsif instruction_kind = ElnaTacOperator.label then result := elna_rtl_instruction_create(ElnaRtlOperator.label); elna_rtl_copy_operand(tac_instruction, 1, result) elsif instruction_kind = ElnaTacOperator.copy then operand_type := _elna_tac_instruction_get_operand_type(tac_instruction, 1); operand_value := _elna_tac_instruction_get_operand_value(tac_instruction, 1); if operand_type = ElnaTacOperand.temporary then result := elna_rtl_load_operand_value(tac_instruction, 2, operand_value); next_instruction^ := ElnaInstructionList_get_next(result) elsif operand_type = ElnaTacOperand.stack then operands := elna_rtl_load_operand_value(tac_instruction, 2, ElnaRtlRegister.t4); result := operands; operands := ElnaInstructionList_get_next(result); next_instruction^ := elna_rtl_instruction_create(ElnaRtlOperator.sw); _elna_tac_instruction_set_operand(next_instruction^, 1, ElnaRtlOperand.register, ElnaRtlRegister.t4, 0); _elna_tac_instruction_set_operand(next_instruction^, 2, ElnaRtlOperand.offset, ElnaRtlRegister.sp, operand_value); if operands = 0 then ElnaInstructionList_set_next(result, next_instruction^) else ElnaInstructionList_set_next(operands, next_instruction^) end (* Debug. Error stream output. _syscall(2, name_pointer, name_length, 0, 0, 0, 64); printf("# here\n\0"); fflush(0); *) end end; if next_instruction^ = 0 then next_instruction^ := result; end; return result end; proc elna_instruction_module_create(data: Word, code: Word); var result: Word; begin result := malloc(ElnaInstructionModule_size()); ElnaInstructionModule_set_data(result, data); ElnaInstructionModule_set_code(result, code); return result end; proc _elna_tac_label(counter: Word, length: Word); var result: Word; begin result := _elna_tac_instruction_create(ElnaTacOperator.label); _elna_tac_instruction_set_operand(result, 1, ElnaTacOperand.symbol, counter, length); return result end; proc _elna_writer_instruction_name(instruction_kind: Word); var argument_count: Word; begin if instruction_kind = ElnaRtlOperator.li then argument_count := 2; _write_s("\tli", 3) elsif instruction_kind = ElnaRtlOperator.la then argument_count := 2; _write_s("\tla", 3) elsif instruction_kind = ElnaRtlOperator.add then argument_count := 3; _write_s("\tadd", 4) elsif instruction_kind = ElnaRtlOperator.addi then argument_count := 3; _write_s("\taddi", 5) elsif instruction_kind = ElnaRtlOperator.lw then argument_count := 2; _write_s("\tlw", 3) elsif instruction_kind = ElnaRtlOperator.sw then argument_count := 2; _write_s("\tsw", 3) elsif instruction_kind = ElnaRtlOperator.jal then argument_count := 1; _write_s("\tcall", 5) elsif instruction_kind = ElnaRtlOperator.move then argument_count := 2; _write_s("\tmv", 3) elsif instruction_kind = ElnaRtlOperator.sub then argument_count := 3; _write_s("\tsub", 4) elsif instruction_kind = ElnaRtlOperator.mul then argument_count := 3; _write_s("\tmul", 4) elsif instruction_kind = ElnaRtlOperator.div then argument_count := 3; _write_s("\tdiv", 4) elsif instruction_kind = ElnaRtlOperator.rem then argument_count := 3; _write_s("\trem", 4) elsif instruction_kind = ElnaRtlOperator._xor then argument_count := 3; _write_s("\txor", 4) elsif instruction_kind = ElnaRtlOperator.xori then argument_count := 3; _write_s("\txori", 5) elsif instruction_kind = ElnaRtlOperator._or then argument_count := 3; _write_s("\tor", 3) elsif instruction_kind = ElnaRtlOperator.and then argument_count := 3; _write_s("\tand", 4) elsif instruction_kind = ElnaRtlOperator.seqz then argument_count := 2; _write_s("\tseqz", 5) elsif instruction_kind = ElnaRtlOperator.snez then argument_count := 2; _write_s("\tsnez", 5) elsif instruction_kind = ElnaRtlOperator.slt then argument_count := 3; _write_s("\tslt", 4) elsif instruction_kind = ElnaRtlOperator.neg then argument_count := 2; _write_s("\tneg", 4) elsif instruction_kind = ElnaRtlOperator.not then argument_count := 2; _write_s("\tnot", 4) elsif instruction_kind = ElnaRtlOperator.j then argument_count := 1; _write_s("\tj", 2) elsif instruction_kind = ElnaRtlOperator.beqz then argument_count := 2; _write_s("\tbeqz", 5) elsif instruction_kind = ElnaRtlOperator.bnez then argument_count := 2; _write_s("\tbnez", 5) else (* ElnaRtlOperator.allocate_stack or ElnaRtlOperator.ret *) argument_count := 0 end; return argument_count end; proc _elna_writer_register(register: Word); begin _write_c('x'); _write_i(register - 1) end; proc _elna_writer_operand(instruction: Word, n: Word); var operand_value: Word; operand_length: Word; operand_type: Word; begin operand_type := elna_rtl_instruction_get_operand_type(instruction, n); operand_value := elna_rtl_instruction_get_operand_value(instruction, n); operand_length := elna_rtl_instruction_get_operand_length(instruction, n); _write_c(' '); if operand_type = ElnaRtlOperand.register then _elna_writer_register(operand_value) elsif operand_type = ElnaRtlOperand.offset then _write_i(operand_length); _write_c('('); _elna_writer_register(operand_value); _write_c(')') elsif operand_type = ElnaRtlOperand.symbol then if operand_length = 0 then _write_label(operand_value, 0) else _write_s(operand_value, operand_length) end elsif operand_length = 0 then (* ElnaRtlOperand.immediate *) _write_i(operand_value) else _write_s(operand_value, operand_length) end end; proc _elna_writer_instruction(instruction: Word); var instruction_kind: Word; argument_count: Word; current_argument: Word; operand_value: Word; operand_length: Word; begin instruction_kind := elna_rtl_instruction_get_kind(instruction); if instruction_kind = ElnaRtlOperator.label then argument_count := 0; operand_value := elna_rtl_instruction_get_operand_value(instruction, 1); operand_length := elna_rtl_instruction_get_operand_length(instruction, 1); _write_label(operand_value, operand_length); _write_c(':') else argument_count := _elna_writer_instruction_name(instruction_kind) end; current_argument := 1; .elna_writer_instruction_loop; if current_argument <= argument_count then _elna_writer_operand(instruction, current_argument); current_argument := current_argument + 1 end; if current_argument <= argument_count then _write_c(','); goto elna_writer_instruction_loop end; _write_c('\n') end; proc elna_rtl_instructions(instruction: Word); var current_copy: Word; next_copy: Word; first_copy: Word; last_copy: Word; begin if instruction <> 0 then first_copy := elna_rtl_instruction(instruction, @current_copy); instruction := ElnaInstructionList_get_next(instruction) else first_copy := 0; current_copy := 0 end; .elna_rtl_instructions_start; if instruction <> 0 then next_copy := elna_rtl_instruction(instruction, @last_copy); instruction := ElnaInstructionList_get_next(instruction); ElnaInstructionList_set_next(current_copy, next_copy); current_copy := last_copy; goto elna_rtl_instructions_start end; return first_copy end; proc _elna_writer_instructions(instruction: Word); begin .elna_writer_instructions_start; if instruction <> 0 then _elna_writer_instruction(instruction); instruction := ElnaInstructionList_get_next(instruction); goto elna_writer_instructions_start end end; proc _elna_writer_procedure(procedure: Word); var name_pointer: Word; name_length: Word; body_statements: Word; has_stack: Word; begin .elna_writer_procedure_loop; name_pointer := ElnaInstructionDeclaration_get_name(procedure); name_length := ElnaInstructionDeclaration_get_length(procedure); body_statements := ElnaInstructionDeclaration_get_body(procedure); has_stack := ElnaInstructionDeclaration_get_stack(procedure); (* Write .type _procedure_name, @function. *) _write_z(".type \0"); _write_s(name_pointer, name_length); _write_z(", @function\n\0"); (* Write procedure label, _procedure_name: *) _write_s(name_pointer, name_length); _write_z(":\n\0"); (* Write the prologue. *) if has_stack then _write_z("\taddi sp, sp, -144\n\tsw ra, 140(sp)\n\tsw s0, 136(sp)\n\taddi s0, sp, 144\n\0") end; _elna_writer_instructions(body_statements); (* Write the epilogue. *) if has_stack then _write_z("\tlw ra, 140(sp)\n\tlw s0, 136(sp)\n\taddi sp, sp, 144\n\0") end; _write_z("\tret\n\0"); procedure := ElnaInstructionList_get_next(procedure); if procedure <> 0 then goto elna_writer_procedure_loop end end; proc _elna_writer_variable(variable: Word); var name: Word; name_length: Word; size: Word; begin .elna_writer_variable_loop; if variable <> 0 then name := ElnaInstructionDeclaration_get_name(variable); name_length := ElnaInstructionDeclaration_get_length(variable); size := ElnaInstructionDeclaration_get_body(variable); _write_z(".type \0"); _write_s(name, name_length); _write_z(", @object\n\0"); _write_s(name, name_length); _write_c(':'); _write_z(" .zero \0"); _write_i(size); _write_c('\n'); variable := ElnaInstructionList_get_next(variable); goto elna_writer_variable_loop end end; proc elna_rtl_module_declaration(tac_module: Word); var code_part: Word; data_part: Word; current_part: Word; begin current_part := ElnaInstructionModule_get_data(tac_module); data_part := elna_rtl_globals(current_part); current_part := ElnaInstructionModule_get_code(tac_module); code_part := elna_rtl_procedures(current_part); return elna_instruction_module_create(data_part, code_part) end; proc _elna_writer_module(pair: Word); var compiler_strings_copy: Word; compiler_strings_end: Word; current_byte: Word; current_part: Word; begin _write_z(".globl main\n\n\0"); _write_z(".section .data\n\0"); current_part := ElnaInstructionModule_get_data(pair); _elna_writer_variable(current_part); _write_z(".section .text\n\n\0"); _write_z(".type _syscall, @function\n_syscall:\n\tmv a7, a6\n\tecall\n\tret\n\n\0"); _write_z(".type _load_byte, @function\n_load_byte:\n\tlb a0, (a0)\nret\n\n\0"); _write_z(".type _store_byte, @function\n_store_byte:\n\tsb a0, (a1)\nret\n\n\0"); current_part := ElnaInstructionModule_get_code(pair); _elna_writer_procedure(current_part); _write_z(".section .rodata\n.type strings, @object\nstrings: .ascii \0"); _write_c('"'); compiler_strings_copy := @compiler_strings; compiler_strings_end := compiler_strings_position; .elna_writer_module_loop; if compiler_strings_copy < compiler_strings_end then current_byte := _load_byte(compiler_strings_copy); compiler_strings_copy := compiler_strings_copy + 1; _write_c(current_byte); goto elna_writer_module_loop end; _write_c('"'); _write_c('\n'); end; proc _elna_parser_integer_literal(); var integer_token: Word; integer_length: Word; result: Word; begin result := malloc(_integer_literal_node_size()); integer_token := _elna_lexer_global_get_start(); integer_length := _elna_lexer_global_get_end(); integer_length := integer_length - integer_token; _elna_lexer_skip_token(); _node_set_kind(result, NodeKind.integer_literal); _integer_literal_node_set_value(result, integer_token); _integer_literal_node_set_length(result, integer_length); return result end; proc _elna_parser_nil(); var result: Word; begin _elna_lexer_skip_token(); result := malloc(_nil_node_size()); _nil_node_set_kind(result, NodeKind.null); return result end; proc _elna_tac_integer_literal(integer_literal_node: Word, operand_type: Word, operand_value: Word, operand_length: Word); begin operand_type^ := ElnaTacOperand.immediate; operand_value^ := _integer_literal_node_get_value(integer_literal_node); operand_length^ := _integer_literal_node_get_length(integer_literal_node); return 0 end; proc _elna_tac_nil(nil_node: Word, operand_type: Word, operand_value: Word, operand_length: Word); begin operand_type^ := ElnaTacOperand.immediate; operand_value^ := 0; operand_length^ := 0; return 0 end; proc _elna_parser_character_literal(); var character: Word; character_length: Word; result: Word; begin result := malloc(_character_literal_node_size()); character := _elna_lexer_global_get_start(); character_length := _elna_lexer_global_get_end(); character_length := character_length - character; _elna_lexer_skip_token(); _node_set_kind(result, NodeKind.character_literal); _integer_literal_node_set_value(result, character); _integer_literal_node_set_length(result, character_length); return result end; proc _elna_tac_character_literal(character_literal_node: Word, operand_type: Word, operand_value: Word, operand_length: Word); begin operand_type^ := ElnaTacOperand.immediate; operand_value^ :=_character_literal_node_get_value(character_literal_node); operand_length^ := _character_literal_node_get_length(character_literal_node); return 0 end; proc _elna_parser_variable_expression(); var name: Word; name_token: Word; result: Word; begin name := _elna_lexer_global_get_start(); name_token := _elna_lexer_global_get_end(); name_token := name_token - name; _elna_lexer_skip_token(); result := malloc(_variable_expression_size()); _node_set_kind(result, NodeKind.variable_expression); _variable_expression_set_name(result, name); _variable_expression_set_length(result, name_token); return result end; proc _elna_tac_variable_expression(variable_expression: Word, symbol_table: Word, operand_type: Word, operand_value: Word, operand_length: Word); var name_pointer: Word; name_length: Word; lookup_result: Word; begin name_pointer := _variable_expression_get_name(variable_expression); name_length := _variable_expression_get_length(variable_expression); lookup_result := _symbol_table_lookup(symbol_table, name_pointer, name_length); if lookup_result <> 0 then operand_type^ := ElnaTacOperand.stack; operand_value^ := _parameter_info_get_offset(lookup_result); operand_length^ := 0 else operand_type^ := ElnaTacOperand.symbol; operand_value^ := name_pointer; operand_length^ := name_length end; return 0 end; proc _elna_parser_string_literal(); var length: Word; token_start: Word; result: Word; begin result := malloc(_string_literal_node_size()); token_start := _elna_lexer_global_get_start(); length := _string_length(token_start); _elna_lexer_skip_token(); _node_set_kind(result, NodeKind.string_literal); _string_literal_node_set_value(result, token_start); _string_literal_node_set_length(result, length); return result end; proc _elna_tac_string_literal(string_literal_node: Word, operand_type: Word, operand_value: Word, operand_length: Word); var token_start: Word; offset: Word; first_instruction: Word; next_instruction: Word; begin token_start := _string_literal_node_get_value(string_literal_node); offset := _add_string(token_start); first_instruction := _elna_tac_instruction_create(ElnaTacOperator.get_address); _elna_tac_instruction_set_operand(first_instruction, 1, ElnaTacOperand.temporary, 6, 0); _elna_tac_instruction_set_operand(first_instruction, 2, ElnaTacOperand.symbol, "strings", 7); (* Add offset to the string block pointer. *) next_instruction := _elna_tac_instruction_create(ElnaTacOperator.add); _elna_tac_instruction_set_operand(next_instruction, 1, ElnaTacOperand.temporary, 6, 0); _elna_tac_instruction_set_operand(next_instruction, 2, ElnaTacOperand.temporary, 6, 0); _elna_tac_instruction_set_operand(next_instruction, 3, ElnaTacOperand.immediate, offset, 0); operand_type^ := ElnaTacOperand.temporary; operand_value^ := 6; operand_length^ := 0; return elna_instruction_list_concatenate(first_instruction, next_instruction) end; proc _elna_parser_simple_expression(); var current_character: Word; parser_node: Word; token_kind: Word; begin parser_node := 0; _elna_lexer_read_token(@token_kind); if token_kind = ElnaLexerKind.character then parser_node := _elna_parser_character_literal() elsif token_kind = ElnaLexerKind.integer then parser_node := _elna_parser_integer_literal() elsif token_kind = ElnaLexerKind.string then parser_node := _elna_parser_string_literal() elsif token_kind = ElnaLexerKind.null then parser_node := _elna_parser_nil() elsif token_kind = ElnaLexerKind.identifier then parser_node := _elna_parser_variable_expression() end; return parser_node end; proc _elna_parser_dereference_expression(simple_expression: Word); var result: Word; begin result := malloc(_dereference_expression_size()); _node_set_kind(result, NodeKind.dereference_expression); _dereference_expression_set_pointer(result, simple_expression); _elna_lexer_skip_token(); return result end; proc _elna_parser_designator(); var simple_expression: Word; token_kind: Word; begin simple_expression := _elna_parser_simple_expression(); _elna_lexer_read_token(@token_kind); if token_kind = ElnaLexerKind.hat then simple_expression := _elna_parser_dereference_expression(simple_expression) elsif token_kind = ElnaLexerKind.dot then simple_expression := _elna_parser_field_access_expression(simple_expression) elsif token_kind = ElnaLexerKind.left_paren then simple_expression := _elna_parser_call(simple_expression) end; return simple_expression end; proc _elna_tac_simple_expression(parser_node: Word, symbol_table: Word, operand_type: Word, operand_value: Word, operand_length: Word); var node_kind: Word; instruction: Word; begin node_kind := _node_get_kind(parser_node); if node_kind = NodeKind.character_literal then instruction := _elna_tac_character_literal(parser_node, operand_type, operand_value, operand_length) elsif node_kind = NodeKind.string_literal then instruction := _elna_tac_string_literal(parser_node, operand_type, operand_value, operand_length) elsif node_kind = NodeKind.integer_literal then instruction := _elna_tac_integer_literal(parser_node, operand_type, operand_value, operand_length) elsif node_kind = NodeKind.null then instruction := _elna_tac_nil(parser_node, operand_type, operand_value, operand_length) else instruction := _elna_tac_variable_expression(parser_node, symbol_table, operand_type, operand_value, operand_length) end; return instruction end; proc _elna_parser_unary_expression(); var token_kind: Word; result: Word; operand: Word; operator: Word; begin _elna_lexer_read_token(@token_kind); 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_skip_token() end; result := _elna_parser_designator(); if operator <> 0 then operand := result; result := malloc(_unary_expression_size()); _node_set_kind(result, NodeKind.unary_expression); _unary_expression_set_operand(result, operand); _unary_expression_set_operator(result, operator) end; return result end; proc _elna_tac_unary_expression(parser_node: Word, symbol_table: Word, operand_type: Word, operand_value: Word, operand_length: Word); var current_character: Word; token_kind: Word; expression_kind: Word; operator: Word; operand: Word; is_address: Word; first_instruction: Word; instruction: Word; temp: Word; begin operator := 0; operand := 0; expression_kind := _node_get_kind(parser_node); if expression_kind = NodeKind.unary_expression then operator := _unary_expression_get_operator(parser_node); operand := _unary_expression_get_operand(parser_node) else operand := parser_node end; if operator = '@' then first_instruction := _elna_tac_designator(operand, symbol_table, @is_address, operand_type, operand_value, operand_length); instruction := _elna_tac_instruction_create(ElnaTacOperator.get_address); _elna_tac_instruction_set_operand(instruction, 1, ElnaTacOperand.temporary, 6, 0); _elna_tac_instruction_set_operand(instruction, 2, operand_type^, operand_value^, operand_length^); operand_type^ := ElnaTacOperand.temporary; operand_value^ := 6; operand_length^ := 0; first_instruction := elna_instruction_list_concatenate(first_instruction, instruction) else current_character := operand_type^; first_instruction := _elna_tac_designator(operand, symbol_table, @is_address, operand_type, operand_value, operand_length); operand := operand_type^; if is_address then instruction := _elna_tac_instruction_create(ElnaTacOperator.load); _elna_tac_instruction_set_operand(instruction, 1, ElnaTacOperand.temporary, 6, 0); _elna_tac_instruction_set_operand(instruction, 2, operand_type^, operand_value^, operand_length^); operand_type^ := ElnaTacOperand.temporary; operand_value^ := 6; operand_length^ := 0; elna_instruction_list_concatenate(first_instruction, instruction) elsif operator = 0 then instruction := _elna_tac_instruction_create(ElnaTacOperator.copy); _elna_tac_instruction_set_operand(instruction, 1, ElnaTacOperand.temporary, 6, 0); _elna_tac_instruction_set_operand(instruction, 2, operand_type^, operand_value^, operand_length^); operand_type^ := ElnaTacOperand.temporary; operand_value^ := 6; operand_length^ := 0; first_instruction := elna_instruction_list_concatenate(first_instruction, instruction) end end; if operator = '-' then instruction := _elna_tac_instruction_create(ElnaTacOperator.negate); _elna_tac_instruction_set_operand(instruction, 1, ElnaTacOperand.temporary, 6, 0); _elna_tac_instruction_set_operand(instruction, 2, operand_type^, operand_value^, operand_length^); elna_instruction_list_concatenate(first_instruction, instruction) elsif operator = '~' then instruction := _elna_tac_instruction_create(ElnaTacOperator.complement); _elna_tac_instruction_set_operand(instruction, 1, ElnaTacOperand.temporary, 6, 0); _elna_tac_instruction_set_operand(instruction, 2, operand_type^, operand_value^, operand_length^); elna_instruction_list_concatenate(first_instruction, instruction) end; return first_instruction end; proc _elna_parser_binary_expression(); var lhs_node: Word; rhs_node: Word; token_kind: Word; result: Word; begin lhs_node := _elna_parser_unary_expression(); rhs_node := 0; _elna_lexer_read_token(@token_kind); if token_kind = ElnaLexerKind.plus then _elna_lexer_skip_token(); rhs_node := _elna_parser_unary_expression() elsif token_kind = ElnaLexerKind.minus then _elna_lexer_skip_token(); rhs_node := _elna_parser_unary_expression() elsif token_kind = ElnaLexerKind.multiplication then _elna_lexer_skip_token(); rhs_node := _elna_parser_unary_expression() elsif token_kind = ElnaLexerKind.and then _elna_lexer_skip_token(); rhs_node := _elna_parser_unary_expression() elsif token_kind = ElnaLexerKind._or then _elna_lexer_skip_token(); rhs_node := _elna_parser_unary_expression() elsif token_kind = ElnaLexerKind._xor then _elna_lexer_skip_token(); rhs_node := _elna_parser_unary_expression() elsif token_kind = ElnaLexerKind.equals then _elna_lexer_skip_token(); rhs_node := _elna_parser_unary_expression() elsif token_kind = ElnaLexerKind.remainder then _elna_lexer_skip_token(); rhs_node := _elna_parser_unary_expression() elsif token_kind = ElnaLexerKind.division then _elna_lexer_skip_token(); rhs_node := _elna_parser_unary_expression() elsif token_kind = ElnaLexerKind.less_than then _elna_lexer_skip_token(); rhs_node := _elna_parser_unary_expression() elsif token_kind = ElnaLexerKind.greater_than then _elna_lexer_skip_token(); rhs_node := _elna_parser_unary_expression() elsif token_kind = ElnaLexerKind.less_equal then _elna_lexer_skip_token(); rhs_node := _elna_parser_unary_expression() elsif token_kind = ElnaLexerKind.not_equal then _elna_lexer_skip_token(); rhs_node := _elna_parser_unary_expression() elsif token_kind = ElnaLexerKind.greater_equal then _elna_lexer_skip_token(); rhs_node := _elna_parser_unary_expression() end; if rhs_node <> 0 then result := malloc(_binary_expression_size()); _node_set_kind(result, NodeKind.binary_expression); _binary_expression_set_lhs(result, lhs_node); _binary_expression_set_rhs(result, rhs_node); _binary_expression_set_operator(result, token_kind) else result := lhs_node end; return result end; proc _elna_tac_binary_expression(parser_node: Word, symbol_table: Word, operand_type: Word, operand_value: Word, operand_length: Word); var token_kind: Word; expression_kind: Word; operand_node: Word; first_instruction: Word; instruction: Word; current_instruction: Word; lhs_type: Word; lhs_value: Word; lhs_length: Word; begin expression_kind := _node_get_kind(parser_node); if expression_kind <> NodeKind.binary_expression then first_instruction := _elna_tac_unary_expression(parser_node, symbol_table, operand_type, operand_value, operand_length) else token_kind := _binary_expression_get_operator(parser_node); operand_node := _binary_expression_get_lhs(parser_node); lhs_type := 0; lhs_value := 0; lhs_length := 0; first_instruction := _elna_tac_unary_expression(operand_node, symbol_table, @lhs_type, @lhs_value, @lhs_length); (* Save the value of the left expression on the stack. *) instruction := _elna_tac_instruction_create(ElnaTacOperator.copy); _elna_tac_instruction_set_operand(instruction, 1, ElnaTacOperand.stack, 72, 0); _elna_tac_instruction_set_operand(instruction, 2, lhs_type, lhs_value, lhs_length); if first_instruction = 0 then first_instruction := instruction else elna_instruction_list_concatenate(first_instruction, instruction) end; current_instruction := instruction; operand_node := _binary_expression_get_rhs(parser_node); lhs_type := 0; lhs_value := 0; lhs_length := 0; instruction := _elna_tac_unary_expression(operand_node, symbol_table, @lhs_type, @lhs_value, @lhs_length); current_instruction := elna_instruction_list_concatenate(current_instruction, instruction); (* Load the left expression from the stack; *) instruction := _elna_tac_instruction_create(ElnaTacOperator.copy); _elna_tac_instruction_set_operand(instruction, 1, ElnaTacOperand.temporary, 7, 0); _elna_tac_instruction_set_operand(instruction, 2, ElnaTacOperand.stack, 72, 0); elna_instruction_list_concatenate(current_instruction, instruction); current_instruction := instruction; if token_kind = ElnaLexerKind.plus then instruction := _elna_tac_instruction_create(ElnaTacOperator.add) elsif token_kind = ElnaLexerKind.minus then instruction := _elna_tac_instruction_create(ElnaTacOperator.subtract) elsif token_kind = ElnaLexerKind.multiplication then instruction := _elna_tac_instruction_create(ElnaTacOperator.multiply) elsif token_kind = ElnaLexerKind.and then instruction := _elna_tac_instruction_create(ElnaTacOperator.and) elsif token_kind = ElnaLexerKind._or then instruction := _elna_tac_instruction_create(ElnaTacOperator._or) elsif token_kind = ElnaLexerKind._xor then instruction := _elna_tac_instruction_create(ElnaTacOperator._xor) elsif token_kind = ElnaLexerKind.equals then instruction := _elna_tac_instruction_create(ElnaTacOperator.equal) elsif token_kind = ElnaLexerKind.remainder then instruction := _elna_tac_instruction_create(ElnaTacOperator.remainder) elsif token_kind = ElnaLexerKind.division then instruction := _elna_tac_instruction_create(ElnaTacOperator.divide) elsif token_kind = ElnaLexerKind.less_than then instruction := _elna_tac_instruction_create(ElnaTacOperator.less_than) elsif token_kind = ElnaLexerKind.greater_than then instruction := _elna_tac_instruction_create(ElnaTacOperator.greater_than) elsif token_kind = ElnaLexerKind.less_equal then instruction := _elna_tac_instruction_create(ElnaTacOperator.less_or_equal) elsif token_kind = ElnaLexerKind.greater_equal then instruction := _elna_tac_instruction_create(ElnaTacOperator.greater_or_equal) elsif token_kind = ElnaLexerKind.not_equal then instruction := _elna_tac_instruction_create(ElnaTacOperator.not_equal) end; _elna_tac_instruction_set_operand(instruction, 1, ElnaTacOperand.temporary, 6, 0); _elna_tac_instruction_set_operand(instruction, 2, ElnaTacOperand.stack, 72, 0); _elna_tac_instruction_set_operand(instruction, 3, lhs_type, lhs_value, lhs_length); elna_instruction_list_concatenate(current_instruction, instruction); operand_type^ := ElnaTacOperand.temporary; operand_value^ := 6; operand_length^ := 0 end; return first_instruction end; (* 4 bytes node kind + 4 byte pointer to variable expression + 4 * 7 for arguments. *) proc _call_size(); return 44 end; proc _call_get_name(this: Word); begin this := this + 8; return this^ end; proc _call_set_name(this: Word, value: Word); begin this := this + 8; this^ := value end; proc _call_get_argument(this: Word, n: Word); begin n := n * 4; this := this + 8; this := this + n; return this^ end; proc _call_set_argument(this: Word, n: Word, value: Word); begin n := n * 4; this := this + 8; this := this + n; this^ := value end; proc _elna_parser_call(callee: Word); var parsed_expression: Word; result: Word; argument_number: Word; token_kind: Word; begin result := malloc(_call_size()); _node_set_kind(result, NodeKind.call); _statement_set_next(result, 0); argument_number := 1; _call_set_name(result, callee); _elna_lexer_read_token(@token_kind); _elna_lexer_skip_token(); _elna_lexer_read_token(@token_kind); if token_kind = ElnaLexerKind.right_paren then _elna_lexer_skip_token(); goto elna_parser_call_end end; .elna_parser_call_loop; parsed_expression := _elna_parser_binary_expression(); _call_set_argument(result, argument_number, parsed_expression); argument_number := argument_number + 1; _elna_lexer_read_token(@token_kind); _elna_lexer_skip_token(); if token_kind = ElnaLexerKind.comma then goto elna_parser_call_loop end; .elna_parser_call_end; (* Set the trailing argument to nil. *) _call_set_argument(result, argument_number, 0); return result end; proc _elna_tac_call(parsed_call: Word, symbol_table: Word); var name_length: Word; name: Word; argument_count: Word; stack_offset: Word; parsed_expression: Word; instruction: Word; first_instruction: Word; current_instruction: Word; operand_type: Word; operand_value: Word; operand_length: Word; begin parsed_expression := _call_get_name(parsed_call); name := _variable_expression_get_name(parsed_expression); name_length := _variable_expression_get_length(parsed_expression); argument_count := 0; first_instruction := 0; .elna_tac_call_loop; parsed_expression := _call_get_argument(parsed_call, argument_count + 1); if parsed_expression = 0 then goto elna_tac_call_finalize else operand_type := 0; operand_value := 0; operand_length := 0; instruction := _elna_tac_binary_expression(parsed_expression, symbol_table, @operand_type, @operand_value, @operand_length); if first_instruction = 0 then first_instruction := instruction else elna_instruction_list_concatenate(current_instruction, instruction) end; if instruction <> 0 then current_instruction := instruction end; (* Save the argument on the stack. *) stack_offset := argument_count * 4; instruction := _elna_tac_instruction_create(ElnaTacOperator.store); _elna_tac_instruction_set_operand(instruction, 1, operand_type, operand_value, operand_length); _elna_tac_instruction_set_operand(instruction, 2, ElnaTacOperand.stack, 132 - stack_offset, 0); if first_instruction = 0 then first_instruction := instruction else elna_instruction_list_concatenate(current_instruction, instruction) end; current_instruction := instruction; argument_count := argument_count + 1; goto elna_tac_call_loop end; .elna_tac_call_finalize; (* Load the argument from the stack. *) if argument_count <> 0 then (* Decrement the argument counter. *) argument_count := argument_count - 1; stack_offset := argument_count * 4; (* Calculate the stack offset: 132 - (4 * argument_counter) *) instruction := _elna_tac_instruction_create(ElnaTacOperator.copy); _elna_tac_instruction_set_operand(instruction, 1, ElnaTacOperand.temporary, 11 + argument_count, 0); _elna_tac_instruction_set_operand(instruction, 2, ElnaTacOperand.stack, 132 - stack_offset, 0); elna_instruction_list_concatenate(current_instruction, instruction); current_instruction := instruction; goto elna_tac_call_finalize end; instruction := _elna_tac_instruction_create(ElnaTacOperator.proc_call); _elna_tac_instruction_set_operand(instruction, 1, ElnaTacOperand.symbol, name, name_length); if first_instruction = 0 then first_instruction := instruction else elna_instruction_list_concatenate(current_instruction, instruction) end; return first_instruction end; proc _elna_parser_goto_statement(); var token_kind: Word; label_name: Word; label_length: Word; result: Word; begin _elna_lexer_skip_token(); _elna_lexer_read_token(@token_kind); label_name := _elna_lexer_global_get_start(); label_length := _elna_lexer_global_get_end() - label_name; _elna_lexer_skip_token(); result := malloc(_goto_statement_size()); _node_set_kind(result, NodeKind.goto_statement); _statement_set_next(result, 0); _goto_statement_set_label(result, label_name); _goto_statement_set_length(result, label_length); return result end; proc _elna_tac_goto_statement(parser_node: Word); var label_name: Word; label_length: Word; label_with_dot: Word; instruction: Word; begin label_name := _goto_statement_get_label(parser_node); label_length := _goto_statement_get_length(parser_node); label_with_dot := malloc(label_length + 1); _store_byte('.', label_with_dot); memcpy(label_with_dot + 1, label_name, label_length); instruction := _elna_tac_instruction_create(ElnaTacOperator.jump); _elna_tac_instruction_set_operand(instruction, 1, ElnaTacOperand.symbol, label_with_dot, label_length + 1); return instruction end; proc _elna_parser_label_declaration(); var token_kind: Word; label_name: Word; label_length: Word; result: Word; begin _elna_lexer_skip_token(); _elna_lexer_read_token(@token_kind); label_name := _elna_lexer_global_get_start(); label_length := _elna_lexer_global_get_end() - label_name; _elna_lexer_skip_token(); result := malloc(_label_declaration_size()); _node_set_kind(result, NodeKind.label_declaration); _statement_set_next(result, 0); _goto_statement_set_label(result, label_name); _goto_statement_set_length(result, label_length); return result end; proc _elna_tac_label_declaration(parser_node: Word); var label_name: Word; label_length: Word; begin label_name := _label_declaration_get_label(parser_node); label_length := _label_declaration_get_length(parser_node); return _elna_tac_label(label_name, label_length) end; proc _elna_tac_enumeration_value(field_access_expression: Word, operand_type: Word, operand_value: Word, operand_length: Word); var enumeration_type: Word; members: Word; members_length: Word; token_type: Word; value_name: Word; name_length: Word; member_name: Word; member_length: Word; counter: Word; symbol: Word; begin symbol := _field_access_expression_get_aggregate(field_access_expression); value_name := _variable_expression_get_name(symbol); name_length := _variable_expression_get_length(symbol); symbol := _symbol_table_lookup(@symbol_table_global, value_name, name_length); enumeration_type := _type_info_get__type(symbol); members := _enumeration_type_get_members(enumeration_type); members_length := _enumeration_type_get_length(enumeration_type); _elna_lexer_read_token(@token_type); value_name := _field_access_expression_get_field(field_access_expression); name_length := _field_access_expression_get_length(field_access_expression); 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(value_name, name_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_type^ := ElnaTacOperand.immediate; operand_value^ := counter; operand_length^ := 0 end; return 0 end; proc _elna_parser_field_access_expression(aggregate: Word); var token_kind: Word; name: Word; name_token: Word; result: Word; begin (* Skip dot. Read the enumeration value. *) _elna_lexer_skip_token(); _elna_lexer_read_token(@token_kind); name := _elna_lexer_global_get_start(); name_token := _elna_lexer_global_get_end(); name_token := name_token - name; _elna_lexer_skip_token(); result := malloc(_field_access_expression_size()); _node_set_kind(result, NodeKind.field_access_expression); _field_access_expression_set_aggregate(result, aggregate); _field_access_expression_set_field(result, name); _field_access_expression_set_length(result, name_token); return result end; proc _elna_tac_designator(parser_node: Word, symbol_table: Word, is_address: Word, operand_type: Word, operand_value: Word, operand_length: Word); var parser_node: Word; node_kind: Word; first_instruction: Word; last_instruction: Word; begin node_kind := _node_get_kind(parser_node); if node_kind = NodeKind.dereference_expression then parser_node := _dereference_expression_get_pointer(parser_node); first_instruction := _elna_tac_simple_expression(parser_node, symbol_table, operand_type, operand_value, operand_length); last_instruction := _elna_tac_instruction_create(ElnaTacOperator.copy); _elna_tac_instruction_set_operand(last_instruction, 1, ElnaTacOperand.temporary, 6, 0); _elna_tac_instruction_set_operand(last_instruction, 2, operand_type^, operand_value^, operand_length^); operand_type^ := ElnaTacOperand.temporary; operand_value^ := 6; operand_length^ := 0; is_address^ := 2; first_instruction := elna_instruction_list_concatenate(first_instruction, last_instruction) elsif node_kind = NodeKind.field_access_expression then first_instruction := _elna_tac_enumeration_value(parser_node, operand_type, operand_value, operand_length); is_address^ := 0 elsif node_kind = NodeKind.call then first_instruction := _elna_tac_call(parser_node, symbol_table); last_instruction := _elna_tac_instruction_create(ElnaTacOperator.copy); _elna_tac_instruction_set_operand(last_instruction, 1, ElnaTacOperand.temporary, 6, 0); _elna_tac_instruction_set_operand(last_instruction, 2, ElnaTacOperand.temporary, 11, 0); operand_type^ := ElnaTacOperand.temporary; operand_value^ := 6; operand_length^ := 0; elna_instruction_list_concatenate(first_instruction, last_instruction); is_address^ := 0 else first_instruction := _elna_tac_simple_expression(parser_node, symbol_table, operand_type, operand_value, operand_length); is_address^ := 0 end; return first_instruction end; proc _elna_parser_assign_statement(assignee: Word); var result: Word; token_kind: Word; assignment_node: Word; begin result := malloc(_assign_statement_size()); _node_set_kind(result, NodeKind.assign_statement); _statement_set_next(result, 0); _assign_statement_set_assignee(result, assignee); (* Skip the assignment sign (:=) with surrounding whitespaces. *) _elna_lexer_read_token(@token_kind); _elna_lexer_skip_token(); assignment_node := _elna_parser_binary_expression(); _assign_statement_set_assignment(result, assignment_node); return result end; proc _elna_tac_assign_statement(parser_tree: Word, symbol_table: Word); var current_expression: Word; is_address: Word; first_instruction: Word; instruction: Word; current_instruction: Word; operand_type: Word; operand_value: Word; operand_length: Word; begin current_expression := _assign_statement_get_assignee(parser_tree); operand_type := 0; operand_value := 0; operand_length := 0; first_instruction := _elna_tac_designator(current_expression, symbol_table, @is_address, @operand_type, @operand_value, @operand_length); current_instruction := _elna_tac_instruction_create(ElnaTacOperator.get_address); _elna_tac_instruction_set_operand(current_instruction, 1, ElnaTacOperand.temporary, 6, 0); _elna_tac_instruction_set_operand(current_instruction, 2, operand_type, operand_value, operand_length); first_instruction := elna_instruction_list_concatenate(first_instruction, current_instruction); (* Save the assignee address on the stack. *) current_instruction := _elna_tac_instruction_create(ElnaTacOperator.store); _elna_tac_instruction_set_operand(current_instruction, 1, ElnaTacOperand.temporary, 6, 0); _elna_tac_instruction_set_operand(current_instruction, 2, ElnaTacOperand.stack, 76, 0); elna_instruction_list_concatenate(first_instruction, current_instruction); (* Compile the assignment. *) current_expression := _assign_statement_get_assignment(parser_tree); operand_type := 0; operand_value := 0; operand_length := 0; instruction := _elna_tac_binary_expression(current_expression, symbol_table, @operand_type, @operand_value, @operand_length); if instruction <> 0 then elna_instruction_list_concatenate(current_instruction, instruction); current_instruction := instruction end; instruction := _elna_tac_instruction_create(ElnaTacOperator.copy); _elna_tac_instruction_set_operand(instruction, 1, ElnaTacOperand.temporary, 7, 0); _elna_tac_instruction_set_operand(instruction, 2, ElnaTacOperand.stack, 76, 0); elna_instruction_list_concatenate(current_instruction, instruction); current_instruction := instruction; instruction := _elna_tac_instruction_create(ElnaTacOperator.store); _elna_tac_instruction_set_operand(instruction, 1, operand_type, operand_value, operand_length); _elna_tac_instruction_set_operand(instruction, 2, ElnaTacOperand.temporary, 7, 0); elna_instruction_list_concatenate(current_instruction, instruction); return first_instruction end; proc _elna_parser_return_statement(); var token_kind: Word; returned: Word; label_length: Word; result: Word; begin (* Skip "return" keyword and whitespace after it. *) _elna_lexer_skip_token(); _elna_lexer_read_token(@token_kind); returned := _elna_parser_binary_expression(); result := malloc(_return_statement_size()); _node_set_kind(result, NodeKind.return_statement); _statement_set_next(result, 0); _return_statement_set_returned(result, returned); return result end; proc _elna_tac_return_statement(parser_node: Word, symbol_table: Word); var return_expression: Word; first_instruction: Word; instruction: Word; operand_type: Word; operand_value: Word; operand_length: Word; begin operand_type := 0; operand_value := 0; operand_length := 0; return_expression := _return_statement_get_returned(parser_node); first_instruction := _elna_tac_binary_expression(return_expression, symbol_table, @operand_type, @operand_value, @operand_length); instruction := _elna_tac_instruction_create(ElnaTacOperator._return); _elna_tac_instruction_set_operand(instruction, 1, operand_type, operand_value, operand_length); return elna_instruction_list_concatenate(first_instruction, 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 _write_s(".L", 2); _write_i(counter) else first_byte := _load_byte(counter); if first_byte <> '.' then _write_c('.') end; _write_s(counter, length) end end; proc _elna_parser_conditional_statements(); var token_kind: Word; current_node: Word; result: Word; begin result := malloc(_conditional_statements_size()); (* Skip "if", "while" or "elsif". *) _elna_lexer_skip_token(); current_node := _elna_parser_binary_expression(); _conditional_statements_set_condition(result, current_node); (* Skip "then" or "do". *) _elna_lexer_read_token(@token_kind); _elna_lexer_skip_token(); current_node := _elna_parser_statements(); _conditional_statements_set_statements(result, current_node); _conditional_statements_set_next(result, 0); return result end; proc _elna_tac_conditional_statements(parser_node: Word, after_end_label: Word, symbol_table: Word); var condition_label: Word; current_node: Word; instruction: Word; current_instruction: Word; first_instruction: Word; operand_type: Word; operand_value: Word; operand_length: Word; begin (* Compile condition. *) operand_type := 0; operand_value := 0; operand_length := 0; current_node := _conditional_statements_get_condition(parser_node); first_instruction := _elna_tac_binary_expression(current_node, symbol_table, @operand_type, @operand_value, @operand_length); (* condition_label is the label in front of the next elsif condition or end. *) condition_label := label_counter; label_counter := label_counter + 1; current_instruction := _elna_tac_instruction_create(ElnaTacOperator.jump_if_zero); _elna_tac_instruction_set_operand(current_instruction, 1, operand_type, operand_value, operand_length); _elna_tac_instruction_set_operand(current_instruction, 2, ElnaTacOperand.symbol, condition_label, 0); elna_instruction_list_concatenate(first_instruction, current_instruction); current_node := _conditional_statements_get_statements(parser_node); instruction := _elna_tac_statements(current_node, symbol_table); if instruction <> 0 then elna_instruction_list_concatenate(current_instruction, instruction); current_instruction := instruction end; instruction := _elna_tac_instruction_create(ElnaTacOperator.jump); _elna_tac_instruction_set_operand(instruction, 1, ElnaTacOperand.symbol, after_end_label, 0); elna_instruction_list_concatenate(current_instruction, instruction); current_instruction := _elna_tac_label(condition_label, 0); elna_instruction_list_concatenate(instruction, current_instruction); return first_instruction end; proc _elna_parser_if_statement(); var current_node: Word; result: Word; token_kind: Word; previous_conditional: Word; next_conditional: Word; begin result := malloc(_if_statement_size()); _node_set_kind(result, NodeKind.if_statement); _statement_set_next(result, 0); previous_conditional := _elna_parser_conditional_statements(); _if_statement_set_conditionals(result, previous_conditional); .elna_parser_if_statement_loop; _elna_lexer_read_token(@token_kind); if token_kind = ElnaLexerKind._elsif then next_conditional := _elna_parser_conditional_statements(); _conditional_statements_set_next(previous_conditional, next_conditional); previous_conditional = next_conditional; goto elna_parser_if_statement_loop elsif token_kind = ElnaLexerKind._else then _elna_lexer_skip_token(); current_node := _elna_parser_statements(); _if_statement_set__else(result, current_node) else _if_statement_set__else(result, 0) end; _elna_lexer_skip_token(); return result end; proc _elna_parser_statement(); var token_kind: Word; result : Word; temporary: Word; begin result := 0; _elna_lexer_read_token(@token_kind); if token_kind = ElnaLexerKind._goto then result := _elna_parser_goto_statement() elsif token_kind = ElnaLexerKind._if then result := _elna_parser_if_statement() elsif token_kind = ElnaLexerKind._return then result := _elna_parser_return_statement() elsif token_kind = ElnaLexerKind.dot then result := _elna_parser_label_declaration() elsif token_kind = ElnaLexerKind.identifier then result := _elna_parser_designator(); temporary := _node_get_kind(result); if temporary <> NodeKind.call then result := _elna_parser_assign_statement(result) end end; return result end; proc _elna_parser_statements(); var token_kind: Word; previous_statement: Word; next_statement: Word; first_statement: Word; begin _skip_empty_lines(); first_statement := _elna_parser_statement(); previous_statement := first_statement; if previous_statement = 0 then goto elna_parser_statements_end end; .elna_parser_statement_loop; _elna_lexer_read_token(@token_kind); if token_kind = ElnaLexerKind.semicolon then _elna_lexer_skip_token(); _skip_empty_lines(); next_statement := _elna_parser_statement(); _statement_set_next(previous_statement, next_statement); previous_statement := next_statement; if previous_statement <> 0 then goto elna_parser_statement_loop end end; .elna_parser_statements_end; _skip_empty_lines(); return first_statement end; proc _elna_tac_statements(parser_node: Word, symbol_table: Word); var current_statement: Word; instruction: Word; first_instruction: Word; current_instruction: Word; begin current_statement := parser_node; first_instruction := 0; .elna_tac_statements_loop; if current_statement <> 0 then instruction := _elna_tac_statement(current_statement, symbol_table); current_statement := _statement_get_next(current_statement); if instruction = 0 then goto elna_tac_statements_loop end; if first_instruction = 0 then first_instruction := instruction else elna_instruction_list_concatenate(current_instruction, instruction) end; current_instruction := instruction; goto elna_tac_statements_loop end; return first_instruction end; proc _elna_tac_if_statement(parser_node: Word, symbol_table: Word); var current_node: Word; after_end_label: Word; condition_label: Word; first_instruction: Word; instruction: Word; current_instruction: Word; begin after_end_label := label_counter; label_counter := label_counter + 1; current_node := _if_statement_get_conditionals(parser_node); first_instruction := _elna_tac_conditional_statements(current_node, after_end_label, symbol_table); current_instruction := first_instruction; .elna_tac_if_statement_loop; current_node := _conditional_statements_get_next(current_node); if current_node <> 0 then instruction := _elna_tac_conditional_statements(current_node, after_end_label, symbol_table); elna_instruction_list_concatenate(current_instruction, instruction); current_instruction := instruction; goto elna_tac_if_statement_loop end; current_node := _if_statement_get__else(parser_node); if current_node <> 0 then instruction := _elna_tac_statements(current_node, symbol_table); if instruction <> 0 then elna_instruction_list_concatenate(current_instruction, instruction); current_instruction := instruction end end; instruction := _elna_tac_label(after_end_label, 0); elna_instruction_list_concatenate(current_instruction, instruction); return first_instruction end; proc _elna_tac_statement(parser_node: Word, symbol_table: Word); var statement_kind: Word; instruction: Word; begin statement_kind := _node_get_kind(parser_node); if statement_kind = NodeKind.goto_statement then instruction := _elna_tac_goto_statement(parser_node) elsif statement_kind = NodeKind.if_statement then instruction := _elna_tac_if_statement(parser_node, symbol_table) elsif statement_kind = NodeKind.return_statement then instruction := _elna_tac_return_statement(parser_node, symbol_table) elsif statement_kind = NodeKind.label_declaration then instruction := _elna_tac_label_declaration(parser_node) elsif statement_kind = NodeKind.call then instruction := _elna_tac_call(parser_node, symbol_table) elsif statement_kind = NodeKind.assign_statement then instruction := _elna_tac_assign_statement(parser_node, symbol_table) else instruction := 0 end; return instruction 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(); var entry: Word; member_count: Word; memory_start: Word; field_name: Word; field_length: Word; field_type: Word; token_kind: Word; result: Word; previous_entry: Word; begin _elna_lexer_skip_token(); member_count := 0; memory_start := 0; _elna_lexer_read_token(@token_kind); if token_kind = ElnaLexerKind._end then goto elna_parser_record_type_expression_end end; .elna_parser_record_type_expression_loop; entry := malloc(16); member_count := member_count + 1; field_name := _elna_lexer_global_get_start(); field_length := _elna_lexer_global_get_end() - field_name; entry^ := field_name; entry := entry + 4; entry^ := field_length; entry := entry + 4; (* Skip the identifier. *) _elna_lexer_skip_token(); _elna_lexer_read_token(@token_kind); _elna_lexer_skip_token(); field_type := _elna_parser_type_expression(); entry^ := field_type; entry := entry + 4; entry^ := 0; if memory_start = 0 then memory_start := entry - 12 else previous_entry^ := entry - 12 end; previous_entry := entry; _elna_lexer_read_token(@token_kind); if token_kind = ElnaLexerKind.semicolon then _elna_lexer_skip_token(); _elna_lexer_read_token(@token_kind); goto elna_parser_record_type_expression_loop end; .elna_parser_record_type_expression_end; _elna_lexer_skip_token(); result := malloc(_enumeration_type_expression_size()); _node_set_kind(result, NodeKind.record_type_expression); _record_type_expression_set_members(result, memory_start); _record_type_expression_set_length(result, member_count); return result end; proc _elna_parser_enumeration_type_expression(); var token_kind: Word; enumeration_name: Word; name_length: Word; memory_start: Word; member_count: Word; result: Word; entry: Word; previous_entry: Word; begin _elna_lexer_skip_token(); memory_start := 0; member_count := 0; _elna_lexer_read_token(@token_kind); if token_kind = ElnaLexerKind.right_paren then goto elna_parser_enumeration_type_expression_end end; .elna_parser_enumeration_type_expression_loop; entry := malloc(12); member_count := member_count + 1; enumeration_name := _elna_lexer_global_get_start(); name_length := _elna_lexer_global_get_end() - enumeration_name; entry^ := enumeration_name; entry := entry + 4; entry^ := name_length; entry := entry + 4; entry^ := 0; if memory_start = 0 then memory_start := entry - 8 else previous_entry^ := entry - 8 end; previous_entry := entry; (* Skip the identifier. *) _elna_lexer_skip_token(); _elna_lexer_read_token(@token_kind); if token_kind = ElnaLexerKind.comma then _elna_lexer_skip_token(); _elna_lexer_read_token(@token_kind); goto elna_parser_enumeration_type_expression_loop end; .elna_parser_enumeration_type_expression_end; _elna_lexer_skip_token(); result := malloc(_enumeration_type_expression_size()); _node_set_kind(result, NodeKind.enumeration_type_expression); _enumeration_type_expression_set_members(result, memory_start); _enumeration_type_expression_set_length(result, member_count); 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_type_enumeration(parser_node: Word); var result: Word; memory_start: Word; member_count: Word; member_array_start: Word; member_array_current: Word; begin result := malloc(_enumeration_type_size()); memory_start := _enumeration_type_expression_get_members(parser_node); member_count := _enumeration_type_expression_get_length(parser_node); (* 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 := _enumeration_type_expression_get_length(parser_node); _type_set_kind(result, TypeKind.enumeration); _type_set_size(result, 4); _enumeration_type_set_members(result, member_array_start); _enumeration_type_set_length(result, member_count); return _type_info_create(result) end; proc _elna_name_type_record(parser_node: Word); var result: Word; memory_start: Word; member_count: Word; member_array_start: Word; member_array_current: Word; begin result := malloc(_record_type_size()); memory_start := _record_type_expression_get_members(parser_node); member_count := _record_type_expression_get_length(parser_node); member_array_start := malloc(member_count * 12); member_array_current := member_array_start; .elna_name_type_record_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; member_array_current^ := _elna_name_type_expression(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_record_loop end; member_count := _record_type_expression_get_length(parser_node); _type_set_kind(result, TypeKind._record); _type_set_size(result, member_count * 4); _record_type_set_members(result, member_array_start); _record_type_set_length(result, member_count); return _type_info_create(result) end; proc _elna_parser_named_type_expression(); var result: Word; type_name: Word; name_length: Word; begin result := malloc(_named_type_expression_size()); _node_set_kind(result, NodeKind.named_type_expression); type_name := _elna_lexer_global_get_start(); name_length := _elna_lexer_global_get_end() - type_name; _named_type_expression_set_name(result, type_name); _named_type_expression_set_length(result, name_length); _elna_lexer_skip_token(); return result end; proc _elna_parser_type_expression(); var token_kind: Word; result: Word; begin result := 0; _elna_lexer_read_token(@token_kind); if token_kind = ElnaLexerKind.identifier then result := _elna_parser_named_type_expression() elsif token_kind = ElnaLexerKind.left_paren then result := _elna_parser_enumeration_type_expression() elsif token_kind = ElnaLexerKind._record then result := _elna_parser_record_type_expression() end; return result end; proc _elna_name_type_expression(parser_node: Word); var token_kind: Word; type_name: Word; name_length: Word; result: Word; begin token_kind := _node_get_kind(parser_node); if token_kind = NodeKind.named_type_expression then type_name := _named_type_expression_get_name(parser_node); name_length := _named_type_expression_get_length(parser_node); result := _symbol_table_lookup(@symbol_table_global, type_name, name_length); result := _type_info_get__type(result) elsif token_kind = NodeKind.enumeration_type_expression then result := _elna_name_type_enumeration(parser_node) elsif token_kind = NodeKind.record_type_expression then result := _elna_name_type_record(parser_node) end; return result end; (** * Parameters: * parameter_index - Parameter index. *) proc _parameter_info_create(parameter_index: Word); var offset: Word; result: Word; begin result := malloc(_parameter_info_size()); _info_set_kind(result, InfoKind.parameter_info); (* Calculate the stack offset: 104 - (4 * parameter_counter) *) offset := parameter_index * 4; _parameter_info_set_offset(result, 104 - offset); return result end; proc _type_info_create(type_representation: Word); var result: Word; begin result := malloc(_type_info_size()); _info_set_kind(result, InfoKind.type_info); _type_info_set__type(result, type_representation); return result end; (** * Parameters: * temporary_index - Parameter index. *) proc _temporary_info_create(temporary_index: Word); var result: Word; begin result := malloc(_temporary_info_size()); _info_set_kind(result, InfoKind.temporary_info); (* Calculate the stack offset: 4 * variable_counter. *) _temporary_info_set_offset(result, temporary_index * 4); return result end; (** * Parameters: * symbol_table - Local symbol table. *) proc _procedure_info_create(symbol_table: Word); var result: Word; begin result := malloc(_procedure_info_size()); _info_set_kind(result, InfoKind.procedure_info); _procedure_info_set_symbol_table(result, symbol_table); return result end; (** * Parameters: * parameter_index - Parameter index. *) proc _elna_name_procedure_parameter(parser_node: Word, parameter_index: Word, symbol_table: Word); var name_length: Word; info: Word; name_position: Word; begin name_position := _declaration_get_name(parser_node); name_length := _declaration_get_length(parser_node); info := _parameter_info_create(parameter_index); _symbol_table_enter(symbol_table, name_position, name_length, info) end; (** * Parameters: * variable_index - Variable index. *) proc _elna_name_procedure_temporary(parser_node: Word, variable_index: Word, symbol_table: Word); var name_length: Word; info: Word; name_position: Word; begin name_position := _declaration_get_name(parser_node); name_length := _declaration_get_length(parser_node); info := _temporary_info_create(variable_index); _symbol_table_enter(symbol_table, name_position, name_length, info) end; proc _elna_name_procedure_temporaries(parser_node: Word, symbol_table: Word); var temporary_counter: Word; begin temporary_counter := 0; .elna_name_procedure_temporaries_loop; if parser_node <> 0 then _elna_name_procedure_temporary(parser_node, temporary_counter, symbol_table); temporary_counter := temporary_counter + 1; parser_node := _declaration_get_next(parser_node); goto elna_name_procedure_temporaries_loop end end; proc _elna_parser_procedure_declaration(); var name_pointer: Word; name_length: Word; token_kind: Word; result: Word; parameter_head: Word; begin result := malloc(_procedure_declaration_size()); _node_set_kind(result, NodeKind.procedure_declaration); _declaration_set_next(result, 0); (* Skip "proc ". *) _elna_lexer_skip_token(); _elna_lexer_read_token(@token_kind); name_pointer := _elna_lexer_global_get_start(); name_length := _elna_lexer_global_get_end() - name_pointer; _declaration_set_name(result, name_pointer); _declaration_set_length(result, name_length); (* Skip procedure name. *) _elna_lexer_skip_token(); (* Skip open paren. *) _elna_lexer_read_token(@token_kind); _elna_lexer_skip_token(); parameter_head := 0; .elna_parser_procedure_declaration_parameter; _elna_lexer_read_token(@token_kind); if token_kind <> ElnaLexerKind.right_paren then name_pointer := _elna_parser_variable_declaration(); if parameter_head = 0 then parameter_head := name_pointer else _declaration_set_next(name_length, name_pointer) end; name_length := name_pointer; _elna_lexer_read_token(@token_kind); if token_kind = ElnaLexerKind.comma then _elna_lexer_skip_token(); goto elna_parser_procedure_declaration_parameter end end; (* Skip close paren. *) _elna_lexer_skip_token(); _procedure_declaration_set_parameters(result, parameter_head); (* Skip semicolon and newline. *) _elna_lexer_read_token(@token_kind); _elna_lexer_skip_token(); parameter_head := _elna_parser_var_part(); _procedure_declaration_set_temporaries(result, parameter_head); (* Skip semicolon, "begin" and newline. *) _elna_lexer_read_token(@token_kind); if token_kind = ElnaLexerKind._begin then _elna_lexer_skip_token(); parameter_head := _elna_parser_statements() elsif token_kind = ElnaLexerKind._return then parameter_head := _elna_parser_return_statement() end; _procedure_declaration_set_body(result, parameter_head); (* Skip the "end" keyword. *) _elna_lexer_read_token(@token_kind); _elna_lexer_skip_token(); return result end; proc _elna_tac_parameters(current_parameter: Word, new_symbol_table: Word); var name_pointer: Word; name_length: Word; parameter_counter: Word; instruction: Word; first_instruction: Word; current_instruction: Word; symbol_info: Word; begin first_instruction := 0; parameter_counter := 0; .elna_tac_parameters_loop; if current_parameter <> 0 then name_pointer := _declaration_get_name(current_parameter); name_length := _declaration_get_length(current_parameter); symbol_info := _symbol_table_lookup(new_symbol_table, name_pointer, name_length); symbol_info := _parameter_info_get_offset(symbol_info); instruction := _elna_tac_instruction_create(ElnaTacOperator.store); _elna_tac_instruction_set_operand(instruction, 1, ElnaTacOperand.temporary, 11 + parameter_counter, 0); _elna_tac_instruction_set_operand(instruction, 2, ElnaTacOperand.stack, symbol_info, 0); if first_instruction = 0 then first_instruction := instruction else elna_instruction_list_concatenate(current_instruction, instruction) end; current_instruction := instruction; parameter_counter := parameter_counter + 1; current_parameter := _declaration_get_next(current_parameter); goto elna_tac_parameters_loop end; return first_instruction end; proc elna_rtl_global_declaration(tac_declaration: Word); var name: Word; length: Word; body: Word; result: Word; begin result := malloc(ElnaInstructionDeclaration_size()); name := ElnaInstructionDeclaration_get_name(tac_declaration); length := ElnaInstructionDeclaration_get_length(tac_declaration); body := ElnaInstructionDeclaration_get_body(tac_declaration); ElnaInstructionList_set_next(result, 0); ElnaInstructionDeclaration_set_name(result, name); ElnaInstructionDeclaration_set_length(result, length); ElnaInstructionDeclaration_set_body(result, body); return result end; proc elna_rtl_procedure_declaration(tac_declaration: Word); var name: Word; length: Word; body: Word; result: Word; return_instruction: Word; has_stack: Word; begin result := malloc(ElnaInstructionDeclaration_size()); name := ElnaInstructionDeclaration_get_name(tac_declaration); length := ElnaInstructionDeclaration_get_length(tac_declaration); has_stack := ElnaInstructionDeclaration_get_stack(tac_declaration); body := ElnaInstructionDeclaration_get_body(tac_declaration); body := elna_rtl_instructions(body); return_instruction := elna_rtl_instruction_create(ElnaRtlOperator.allocate_stack); ElnaInstructionList_set_next(return_instruction, body); body := return_instruction; return_instruction := elna_rtl_instruction_create(ElnaRtlOperator.ret); elna_instruction_list_concatenate(body, return_instruction); ElnaInstructionList_set_next(result, 0); ElnaInstructionDeclaration_set_name(result, name); ElnaInstructionDeclaration_set_length(result, length); ElnaInstructionDeclaration_set_stack(result, has_stack); ElnaInstructionDeclaration_set_body(result, body); return result end; proc _elna_tac_procedure_declaration(parser_node: Word); var name_pointer: Word; name_length: Word; current_parameter: Word; body: Word; new_symbol_table: Word; symbol_info: Word; instruction: Word; first_instruction: Word; result: Word; result_size: Word; begin result := malloc(ElnaInstructionDeclaration_size()); ElnaInstructionList_set_next(result, 0); ElnaInstructionDeclaration_set_stack(result, 1); name_pointer := _declaration_get_name(parser_node); name_length := _declaration_get_length(parser_node); ElnaInstructionDeclaration_set_name(result, name_pointer); ElnaInstructionDeclaration_set_length(result, name_length); symbol_info := _symbol_table_lookup(@symbol_table_global, name_pointer, name_length); new_symbol_table := _procedure_info_get_symbol_table(symbol_info); current_parameter := _procedure_declaration_get_parameters(parser_node); first_instruction := _elna_tac_parameters(current_parameter, new_symbol_table); body := _procedure_declaration_get_body(parser_node); instruction := _elna_tac_statements(body, new_symbol_table); first_instruction := elna_instruction_list_concatenate(first_instruction, instruction); ElnaInstructionDeclaration_set_body(result, first_instruction); return result end; proc _elna_parser_procedures(); var parser_node: Word; result: Word; current_declaration: Word; token_kind: Word; begin result := 0; .elna_parser_procedures_loop; _skip_empty_lines(); _elna_lexer_read_token(@token_kind); if token_kind = ElnaLexerKind._proc then parser_node := _elna_parser_procedure_declaration(); if result = 0 then result := parser_node else _declaration_set_next(current_declaration, parser_node) end; current_declaration := parser_node; (* Skip semicolon. *) _elna_lexer_read_token(@token_kind); _elna_lexer_skip_token(); goto elna_parser_procedures_loop end; return result end; proc elna_rtl_globals(tac_procedure: Word); var current_copy: Word; next_copy: Word; first_copy: Word; begin if tac_procedure <> 0 then first_copy := elna_rtl_global_declaration(tac_procedure); tac_procedure := ElnaInstructionList_get_next(tac_procedure) else first_copy := 0; end; current_copy := first_copy; .elna_rtl_globals_start; if tac_procedure <> 0 then next_copy := elna_rtl_global_declaration(tac_procedure); tac_procedure := ElnaInstructionList_get_next(tac_procedure); ElnaInstructionList_set_next(current_copy, next_copy); current_copy := next_copy; goto elna_rtl_globals_start end; return first_copy end; proc elna_rtl_procedures(tac_procedure: Word); var current_copy: Word; next_copy: Word; first_copy: Word; begin if tac_procedure <> 0 then first_copy := elna_rtl_procedure_declaration(tac_procedure); tac_procedure := ElnaInstructionList_get_next(tac_procedure) else first_copy := 0; end; current_copy := first_copy; .elna_rtl_procedures_start; if tac_procedure <> 0 then next_copy := elna_rtl_procedure_declaration(tac_procedure); tac_procedure := ElnaInstructionList_get_next(tac_procedure); ElnaInstructionList_set_next(current_copy, next_copy); current_copy := next_copy; goto elna_rtl_procedures_start end; return first_copy end; proc _elna_tac_procedures(parser_node: Word); var result: Word; current_procedure: Word; first_procedure: Word; begin first_procedure := 0; .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 = 0 then first_procedure := result else ElnaInstructionList_set_next(current_procedure, result) end; current_procedure := result; parser_node := _declaration_get_next(parser_node); goto elna_tac_procedures_loop; .elna_tac_procedures_end; return first_procedure end; (** * Skips comments. *) proc _skip_empty_lines(); var token_kind: Word; begin .skip_empty_lines_rerun; _elna_lexer_read_token(@token_kind); if token_kind = ElnaLexerKind.comment then _elna_lexer_skip_token(); goto skip_empty_lines_rerun end end; proc _elna_parser_type_declaration(); var token_kind: Word; type_name: Word; name_length: Word; parser_node: Word; result: Word; begin _elna_lexer_read_token(@token_kind); type_name := _elna_lexer_global_get_start(); name_length := _elna_lexer_global_get_end() - type_name; _elna_lexer_skip_token(); _elna_lexer_read_token(@token_kind); _elna_lexer_skip_token(); parser_node := _elna_parser_type_expression(); result := malloc(_type_declaration_size()); _node_set_kind(result, NodeKind.type_declaration); _declaration_set_next(result, 0); _declaration_set_name(result, type_name); _declaration_set_length(result, name_length); _type_declaration_set__type(result, parser_node); _elna_lexer_read_token(@token_kind); _elna_lexer_skip_token(); return result end; proc _elna_name_type_declaration(parser_node: Word); var type_name: Word; name_length: Word; type_info: Word; begin type_name := _declaration_get_name(parser_node); name_length := _declaration_get_length(parser_node); parser_node := _type_declaration_get__type(parser_node); type_info := _elna_name_type_expression(parser_node); _symbol_table_enter(@symbol_table_global, type_name, name_length, type_info) end; proc _elna_type_type_declaration(parser_node: Word); begin end; proc _elna_parser_type_part(); var token_kind: Word; parser_node: Word; result: Word; current_declaration: Word; begin result := 0; _skip_empty_lines(); _elna_lexer_read_token(@token_kind); if token_kind <> ElnaLexerKind._type then goto elna_parser_type_part_end end; _elna_lexer_skip_token(); .elna_parser_type_part_loop; _skip_empty_lines(); _elna_lexer_read_token(@token_kind); if token_kind = ElnaLexerKind.identifier then parser_node := _elna_parser_type_declaration(); if result = 0 then result := parser_node else _declaration_set_next(current_declaration, 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(); var token_kind: Word; name: Word; name_length: Word; variable_type: Word; result: Word; begin _elna_lexer_read_token(@token_kind); name := _elna_lexer_global_get_start(); name_length := _elna_lexer_global_get_end() - name; (* Skip the variable name and colon with the type. *) _elna_lexer_skip_token(); _elna_lexer_read_token(@token_kind); _elna_lexer_skip_token(); variable_type := _elna_parser_type_expression(); result := malloc(_variable_declaration_size()); _node_set_kind(result, NodeKind.variable_declaration); _declaration_set_next(result, 0); _declaration_set_name(result, name); _declaration_set_length(result, name_length); _variable_declaration_set__type(result, variable_type); return result end; proc _elna_tac_variable_declaration(parser_tree: Word); var name: Word; name_length: Word; variable_type: Word; result: Word; begin result := malloc(ElnaInstructionDeclaration_size()); ElnaInstructionList_set_next(result, 0); name := _declaration_get_name(parser_tree); name_length := _declaration_get_length(parser_tree); variable_type := _variable_declaration_get__type(parser_tree); ElnaInstructionDeclaration_set_name(result, name); ElnaInstructionDeclaration_set_length(result, name_length); name := _named_type_expression_get_name(variable_type); name_length := _named_type_expression_get_length(variable_type); if string_compare("Array", 5, name, name_length) then (* Else we assume this is a zeroed 4096 bytes big array. *) ElnaInstructionDeclaration_set_body(result, 4096) else ElnaInstructionDeclaration_set_body(result, 4) end; return result end; proc _elna_tac_accessor(name_pointer: Word, name_length: Word, field_pointer: Word, field_offset: Word, result: Word, method: Word); var instruction: Word; new_name: Word; name_target: Word; new_length: Word; field_length: Word; begin field_length := field_pointer + 4; field_length := field_length^; new_length := field_length + name_length; new_length := new_length + 5; result^ := malloc(ElnaInstructionDeclaration_size()); ElnaInstructionList_set_next(result^, 0); ElnaInstructionDeclaration_set_stack(result^, 0); new_name := malloc(new_length); name_target := new_name; memcpy(name_target, name_pointer, name_length); name_target := name_target + name_length; memcpy(name_target, method, 5); name_target := name_target + 5; memcpy(name_target, field_pointer^, field_length); ElnaInstructionDeclaration_set_name(result^, new_name); ElnaInstructionDeclaration_set_length(result^, new_length); instruction := _elna_tac_instruction_create(ElnaTacOperator.add); _elna_tac_instruction_set_operand(instruction, 1, ElnaTacOperand.temporary, 11, 0); _elna_tac_instruction_set_operand(instruction, 2, ElnaTacOperand.temporary, 11, 0); _elna_tac_instruction_set_operand(instruction, 3, ElnaTacOperand.immediate, field_offset, 0); return instruction end; proc _elna_tac_type_field(name_pointer: Word, name_length: Word, field_pointer: Word, field_offset: Word); var first_result: Word; second_result: Word; instruction: Word; next_instruction: Word; begin instruction := _elna_tac_accessor(name_pointer, name_length, field_pointer, field_offset, @first_result, "_get_"); next_instruction := _elna_tac_instruction_create(ElnaTacOperator.load); _elna_tac_instruction_set_operand(next_instruction, 1, ElnaTacOperand.temporary, 11, 0); _elna_tac_instruction_set_operand(next_instruction, 2, ElnaTacOperand.temporary, 11, 0); elna_instruction_list_concatenate(instruction, next_instruction); ElnaInstructionDeclaration_set_body(first_result, instruction); instruction := _elna_tac_accessor(name_pointer, name_length, field_pointer, field_offset, @second_result, "_set_"); next_instruction := _elna_tac_instruction_create(ElnaTacOperator.store); _elna_tac_instruction_set_operand(next_instruction, 1, ElnaTacOperand.temporary, 12, 0); _elna_tac_instruction_set_operand(next_instruction, 2, ElnaTacOperand.temporary, 11, 0); elna_instruction_list_concatenate(instruction, next_instruction); ElnaInstructionDeclaration_set_body(second_result, instruction); ElnaInstructionList_set_next(first_result, second_result); return first_result end; proc _elna_tac_type_record(name_pointer: Word, name_length: Word, type_representation: Word, current_result: Word); var first_result: Word; result: Word; type_size: Word; new_name: Word; new_length: Word; instruction: Word; field_count: Word; field_offset: Word; field_pointer: Word; begin first_result := malloc(ElnaInstructionDeclaration_size()); result := 0; type_size := _type_get_size(type_representation); new_length := name_length + 5; new_name := malloc(new_length); memcpy(new_name, name_pointer, name_length); memcpy(new_name + name_length, "_size", 5); ElnaInstructionDeclaration_set_name(first_result, new_name); ElnaInstructionDeclaration_set_length(first_result, new_length); instruction := _elna_tac_instruction_create(ElnaTacOperator.copy); _elna_tac_instruction_set_operand(instruction, 1, ElnaTacOperand.temporary, 11, 0); _elna_tac_instruction_set_operand(instruction, 2, ElnaTacOperand.immediate, type_size, 0); ElnaInstructionDeclaration_set_body(first_result, instruction); field_count := _record_type_get_length(type_representation); field_pointer := _record_type_get_members(type_representation); field_offset := 0; current_result^ := first_result; .elna_tac_type_record_fields; if field_count > 0 then result := _elna_tac_type_field(name_pointer, name_length, field_pointer, field_offset); ElnaInstructionList_set_next(current_result^, result); current_result^ := ElnaInstructionList_get_next(result); field_offset := field_offset + 4; field_count := field_count - 1; field_pointer := field_pointer + 12; goto elna_tac_type_record_fields end; return first_result end; proc _elna_tac_type_part(parser_node: Word); var name_pointer: Word; name_length: Word; result: Word; first_result: Word; symbol: Word; info_type: Word; type_kind: Word; current_result: Word; out_result: Word; begin first_result := 0; .elna_tac_type_part_loop; if parser_node = 0 then goto elna_tac_type_part_end end; name_pointer := _declaration_get_name(parser_node); name_length := _declaration_get_length(parser_node); symbol := _symbol_table_lookup(@symbol_table_global, name_pointer, name_length); info_type := _type_info_get__type(symbol); type_kind := _type_get_kind(info_type); if type_kind = TypeKind._record then result := _elna_tac_type_record(name_pointer, name_length, info_type, @out_result) else result := 0; out_result := 0 end; if first_result = 0 then first_result := result; current_result := out_result elsif result <> 0 then ElnaInstructionList_set_next(current_result, result); current_result := out_result end; parser_node := _declaration_get_next(parser_node); goto elna_tac_type_part_loop; .elna_tac_type_part_end; return first_result end; proc _elna_parser_var_part(); var result: Word; token_kind: Word; variable_node: Word; current_declaration: Word; begin result := 0; _elna_lexer_read_token(@token_kind); if token_kind <> ElnaLexerKind._var then goto elna_parser_var_part_end end; (* Skip "var". *) _elna_lexer_skip_token(); .elna_parser_var_part_loop; _skip_empty_lines(); _elna_lexer_read_token(@token_kind); if token_kind = ElnaLexerKind.identifier then variable_node := _elna_parser_variable_declaration(); (* Skip semicolon. *) _elna_lexer_read_token(@token_kind); _elna_lexer_skip_token(); if result = 0 then result := variable_node else _declaration_set_next(current_declaration, 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: Word); var node: Word; current_variable: Word; first_variable: Word; begin first_variable := 0; if parser_node = 0 then goto elna_tac_var_part_end end; .elna_tac_var_part_loop; node := _elna_tac_variable_declaration(parser_node); if first_variable = 0 then first_variable := node else ElnaInstructionList_set_next(current_variable, node) end; current_variable := node; parser_node := _declaration_get_next(parser_node); if parser_node <> 0 then goto elna_tac_var_part_loop end; .elna_tac_var_part_end; return first_variable end; proc _elna_parser_module_declaration(); var parser_node: Word; result: Word; token_kind: Word; begin result := malloc(_module_declaration_size()); _node_set_kind(result, NodeKind.module_declaration); (* Skip "program;". *) _skip_empty_lines(); _elna_lexer_read_token(@token_kind); _elna_lexer_skip_token(); _elna_lexer_read_token(@token_kind); _elna_lexer_skip_token(); parser_node := _elna_parser_type_part(); _module_declaration_set_types(result, parser_node); parser_node := _elna_parser_var_part(); _module_declaration_set_globals(result, parser_node); parser_node := _elna_parser_procedures(); _module_declaration_set_procedures(result, parser_node); return result end; (** * Process the source code and print the generated code. *) proc _elna_tac_module_declaration(parser_node: Word); var data_part: Word; code_part: Word; type_part: Word; current_declaration: Word; next_declaration: Word; begin type_part := _module_declaration_get_types(parser_node); type_part := _elna_tac_type_part(type_part); data_part := _module_declaration_get_globals(parser_node); data_part := _elna_tac_var_part(data_part); code_part := _module_declaration_get_procedures(parser_node); code_part := _elna_tac_procedures(code_part); current_declaration := code_part; .elna_tac_module_declaration_types; next_declaration := ElnaInstructionList_get_next(current_declaration); if next_declaration <> 0 then current_declaration := next_declaration; goto elna_tac_module_declaration_types end; ElnaInstructionList_set_next(current_declaration, type_part); return elna_instruction_module_create(data_part, code_part) end; proc _elna_name_procedure_declaration(parser_node: Word); var name_pointer: Word; name_length: Word; new_symbol_table: Word; parameter_counter: Word; symbol_info: Word; current_parameter: Word; begin new_symbol_table := _symbol_table_create(); symbol_info := _procedure_info_create(new_symbol_table); name_pointer := _declaration_get_name(parser_node); name_length := _declaration_get_length(parser_node); current_parameter := _procedure_declaration_get_parameters(parser_node); parameter_counter := 0; .elna_name_procedure_declaration_parameter; if current_parameter <> 0 then _elna_name_procedure_parameter(current_parameter, parameter_counter, new_symbol_table); parameter_counter := parameter_counter + 1; current_parameter := _declaration_get_next(current_parameter); goto elna_name_procedure_declaration_parameter end; current_parameter := _procedure_declaration_get_temporaries(parser_node); _elna_name_procedure_temporaries(current_parameter, new_symbol_table); _symbol_table_enter(@symbol_table_global, name_pointer, name_length, symbol_info) end; proc _elna_type_procedure_declaration(parser_node: Word); begin end; proc _elna_name_module_declaration(parser_node: Word); var current_part: Word; result: Word; begin current_part := _module_declaration_get_types(parser_node); .elna_name_module_declaration_type; if current_part <> 0 then _elna_name_type_declaration(current_part); current_part := _declaration_get_next(current_part); goto elna_name_module_declaration_type end; current_part := _module_declaration_get_procedures(parser_node); .elna_name_module_declaration_procedure; if current_part <> 0 then _elna_name_procedure_declaration(current_part); current_part := _declaration_get_next(current_part); goto elna_name_module_declaration_procedure end end; proc _elna_type_module_declaration(parser_node: Word); var current_part: Word; begin current_part := _module_declaration_get_types(parser_node); .elna_type_module_declaration_type; if current_part <> 0 then _elna_type_type_declaration(current_part); current_part := _declaration_get_next(current_part); goto elna_type_module_declaration_type end; current_part := _module_declaration_get_procedures(parser_node); .elna_type_module_declaration_procedure; if current_part <> 0 then _elna_type_procedure_declaration(current_part); current_part := _declaration_get_next(current_part); goto elna_type_module_declaration_procedure end end; proc _compile(); var parser_node: Word; tac: Word; rtl: Word; begin parser_node := _elna_parser_module_declaration(); _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_writer_module(rtl) end; (** * Terminates the program. a0 contains the return code. * * Parameters: * a0 - Status code. *) proc _exit(status: Word); begin _syscall(status, 0, 0, 0, 0, 0, 93) 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 0 in a0. *) proc _symbol_table_lookup(symbol_table: Word, symbol_name: Word, name_length: Word); var result: Word; symbol_table_length: Word; current_name: Word; current_length: Word; begin result := 0; (* The first word in the symbol table is its length, get it. *) symbol_table_length := symbol_table^; (* Go to the first symbol position. *) symbol_table := symbol_table + 4; .symbol_table_lookup_loop; if symbol_table_length = 0 then goto symbol_table_lookup_end end; (* Symbol name pointer and length. *) current_name := symbol_table^; current_length := symbol_table + 4; current_length := current_length^; (* If lengths don't match, exit and return nil. *) if name_length <> current_length then goto symbol_table_lookup_repeat end; (* If names don't match, exit and return nil. *) if memcmp(symbol_name, current_name, name_length) then goto symbol_table_lookup_repeat end; (* Otherwise, the symbol is found. *) result := symbol_table + 8; result := result^; goto symbol_table_lookup_end; .symbol_table_lookup_repeat; symbol_table := symbol_table + 12; 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. *) proc _symbol_table_create(); var new_symbol_table: Word; table_length: Word; current_table: Word; begin new_symbol_table := symbol_table_store; .symbol_table_create_loop; table_length := new_symbol_table^; if table_length <> 0 then table_length := table_length * 12; table_length := table_length + 4; new_symbol_table := new_symbol_table + table_length; goto symbol_table_create_loop end; 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 _symbol_table_enter(symbol_table: Word, symbol_name: Word, name_length: Word, symbol: Word); var table_length: Word; symbol_pointer: Word; begin (* The first word in the symbol table is its length, get it. *) table_length := symbol_table^; (* Calculate the offset for the new symbol. *) symbol_pointer := table_length * 12; symbol_pointer := symbol_pointer + 4; symbol_pointer := symbol_table + symbol_pointer; symbol_pointer^ := symbol_name; symbol_pointer := symbol_pointer + 4; symbol_pointer^ := name_length; symbol_pointer := symbol_pointer + 4; symbol_pointer^ := symbol; (* Increment the symbol table length. *) table_length := table_length + 1; symbol_table^ := table_length end; proc _symbol_table_build(); var current_info: Word; current_type: Word; begin (* Set the table length to 0. *) symbol_table_global := 0; current_type := malloc(_type_size()); _type_set_kind(current_type, TypeKind.primitive); _type_set_size(current_type, 4); (* Enter built-in symbols. *) current_info := _type_info_create(current_type); _symbol_table_enter(@symbol_table_global, "Word", 4, current_info); current_info := _type_info_create(current_type); _symbol_table_enter(@symbol_table_global, "Array", 5, current_info) end; (** * Assigns some value to at array index. * * Parameters: * array - Array pointer. * index - Index (word offset into the array). * data - Data to assign. *) proc _assign_at(array: Word, index: Word, data: Word); var target: Word; begin target := index - 1; target := target * 4; target := array + target; target^ := data end; proc _get_at(array: Word, index: Word); var target: Word; begin target := index - 1; target := target * 4; target := array + target; return target^ end; (** * Initializes the array with character classes. *) proc _elna_lexer_classifications(); var code: Word; begin _assign_at(@classification, 1, ElnaLexerClass.eof); _assign_at(@classification, 2, ElnaLexerClass.invalid); _assign_at(@classification, 3, ElnaLexerClass.invalid); _assign_at(@classification, 4, ElnaLexerClass.invalid); _assign_at(@classification, 5, ElnaLexerClass.invalid); _assign_at(@classification, 6, ElnaLexerClass.invalid); _assign_at(@classification, 7, ElnaLexerClass.invalid); _assign_at(@classification, 8, ElnaLexerClass.invalid); _assign_at(@classification, 9, ElnaLexerClass.invalid); _assign_at(@classification, 10, ElnaLexerClass.space); _assign_at(@classification, 11, ElnaLexerClass.space); _assign_at(@classification, 12, ElnaLexerClass.invalid); _assign_at(@classification, 13, ElnaLexerClass.invalid); _assign_at(@classification, 14, ElnaLexerClass.space); _assign_at(@classification, 15, ElnaLexerClass.invalid); _assign_at(@classification, 16, ElnaLexerClass.invalid); _assign_at(@classification, 17, ElnaLexerClass.invalid); _assign_at(@classification, 18, ElnaLexerClass.invalid); _assign_at(@classification, 19, ElnaLexerClass.invalid); _assign_at(@classification, 20, ElnaLexerClass.invalid); _assign_at(@classification, 21, ElnaLexerClass.invalid); _assign_at(@classification, 22, ElnaLexerClass.invalid); _assign_at(@classification, 23, ElnaLexerClass.invalid); _assign_at(@classification, 24, ElnaLexerClass.invalid); _assign_at(@classification, 25, ElnaLexerClass.invalid); _assign_at(@classification, 26, ElnaLexerClass.invalid); _assign_at(@classification, 27, ElnaLexerClass.invalid); _assign_at(@classification, 28, ElnaLexerClass.invalid); _assign_at(@classification, 29, ElnaLexerClass.invalid); _assign_at(@classification, 30, ElnaLexerClass.invalid); _assign_at(@classification, 31, ElnaLexerClass.invalid); _assign_at(@classification, 32, ElnaLexerClass.invalid); _assign_at(@classification, 33, ElnaLexerClass.space); _assign_at(@classification, 34, ElnaLexerClass.single); _assign_at(@classification, 35, ElnaLexerClass.double_quote); _assign_at(@classification, 36, ElnaLexerClass.other); _assign_at(@classification, 37, ElnaLexerClass.other); _assign_at(@classification, 38, ElnaLexerClass.single); _assign_at(@classification, 39, ElnaLexerClass.single); _assign_at(@classification, 40, ElnaLexerClass.single_quote); _assign_at(@classification, 41, ElnaLexerClass.left_paren); _assign_at(@classification, 42, ElnaLexerClass.right_paren); _assign_at(@classification, 43, ElnaLexerClass.asterisk); _assign_at(@classification, 44, ElnaLexerClass.single); _assign_at(@classification, 45, ElnaLexerClass.single); _assign_at(@classification, 46, ElnaLexerClass.minus); _assign_at(@classification, 47, ElnaLexerClass.dot); _assign_at(@classification, 48, ElnaLexerClass.single); _assign_at(@classification, 49, ElnaLexerClass.zero); _assign_at(@classification, 50, ElnaLexerClass.digit); _assign_at(@classification, 51, ElnaLexerClass.digit); _assign_at(@classification, 52, ElnaLexerClass.digit); _assign_at(@classification, 53, ElnaLexerClass.digit); _assign_at(@classification, 54, ElnaLexerClass.digit); _assign_at(@classification, 55, ElnaLexerClass.digit); _assign_at(@classification, 56, ElnaLexerClass.digit); _assign_at(@classification, 57, ElnaLexerClass.digit); _assign_at(@classification, 58, ElnaLexerClass.digit); _assign_at(@classification, 59, ElnaLexerClass.colon); _assign_at(@classification, 60, ElnaLexerClass.single); _assign_at(@classification, 61, ElnaLexerClass.less); _assign_at(@classification, 62, ElnaLexerClass.equals); _assign_at(@classification, 63, ElnaLexerClass.greater); _assign_at(@classification, 64, ElnaLexerClass.other); _assign_at(@classification, 65, ElnaLexerClass.single); _assign_at(@classification, 66, ElnaLexerClass.alpha); _assign_at(@classification, 67, ElnaLexerClass.alpha); _assign_at(@classification, 68, ElnaLexerClass.alpha); _assign_at(@classification, 69, ElnaLexerClass.alpha); _assign_at(@classification, 70, ElnaLexerClass.alpha); _assign_at(@classification, 71, ElnaLexerClass.alpha); _assign_at(@classification, 72, ElnaLexerClass.alpha); _assign_at(@classification, 73, ElnaLexerClass.alpha); _assign_at(@classification, 74, ElnaLexerClass.alpha); _assign_at(@classification, 75, ElnaLexerClass.alpha); _assign_at(@classification, 76, ElnaLexerClass.alpha); _assign_at(@classification, 77, ElnaLexerClass.alpha); _assign_at(@classification, 78, ElnaLexerClass.alpha); _assign_at(@classification, 79, ElnaLexerClass.alpha); _assign_at(@classification, 80, ElnaLexerClass.alpha); _assign_at(@classification, 81, ElnaLexerClass.alpha); _assign_at(@classification, 82, ElnaLexerClass.alpha); _assign_at(@classification, 83, ElnaLexerClass.alpha); _assign_at(@classification, 84, ElnaLexerClass.alpha); _assign_at(@classification, 85, ElnaLexerClass.alpha); _assign_at(@classification, 86, ElnaLexerClass.alpha); _assign_at(@classification, 87, ElnaLexerClass.alpha); _assign_at(@classification, 88, ElnaLexerClass.alpha); _assign_at(@classification, 89, ElnaLexerClass.alpha); _assign_at(@classification, 90, ElnaLexerClass.alpha); _assign_at(@classification, 91, ElnaLexerClass.alpha); _assign_at(@classification, 92, ElnaLexerClass.single); _assign_at(@classification, 93, ElnaLexerClass.backslash); _assign_at(@classification, 94, ElnaLexerClass.single); _assign_at(@classification, 95, ElnaLexerClass.single); _assign_at(@classification, 96, ElnaLexerClass.alpha); _assign_at(@classification, 97, ElnaLexerClass.other); _assign_at(@classification, 98, ElnaLexerClass.hex); _assign_at(@classification, 99, ElnaLexerClass.hex); _assign_at(@classification, 100, ElnaLexerClass.hex); _assign_at(@classification, 101, ElnaLexerClass.hex); _assign_at(@classification, 102, ElnaLexerClass.hex); _assign_at(@classification, 103, ElnaLexerClass.hex); _assign_at(@classification, 104, ElnaLexerClass.alpha); _assign_at(@classification, 105, ElnaLexerClass.alpha); _assign_at(@classification, 106, ElnaLexerClass.alpha); _assign_at(@classification, 107, ElnaLexerClass.alpha); _assign_at(@classification, 108, ElnaLexerClass.alpha); _assign_at(@classification, 109, ElnaLexerClass.alpha); _assign_at(@classification, 110, ElnaLexerClass.alpha); _assign_at(@classification, 111, ElnaLexerClass.alpha); _assign_at(@classification, 112, ElnaLexerClass.alpha); _assign_at(@classification, 113, ElnaLexerClass.alpha); _assign_at(@classification, 114, ElnaLexerClass.alpha); _assign_at(@classification, 115, ElnaLexerClass.alpha); _assign_at(@classification, 116, ElnaLexerClass.alpha); _assign_at(@classification, 117, ElnaLexerClass.alpha); _assign_at(@classification, 118, ElnaLexerClass.alpha); _assign_at(@classification, 119, ElnaLexerClass.alpha); _assign_at(@classification, 120, ElnaLexerClass.alpha); _assign_at(@classification, 121, ElnaLexerClass.x); _assign_at(@classification, 122, ElnaLexerClass.alpha); _assign_at(@classification, 123, ElnaLexerClass.alpha); _assign_at(@classification, 124, ElnaLexerClass.other); _assign_at(@classification, 125, ElnaLexerClass.single); _assign_at(@classification, 126, ElnaLexerClass.other); _assign_at(@classification, 127, ElnaLexerClass.single); _assign_at(@classification, 128, ElnaLexerClass.invalid); code := 129; (* Set the remaining 129 - 256 bytes to transitionClassOther. *) .create_classification_loop; _assign_at(@classification, code, ElnaLexerClass.other); code := code + 1; if code < 257 then goto create_classification_loop end end; proc _elna_lexer_get_transition(current_state: Word, character_class: Word); var transition_table: Word; row_position: Word; column_position: Word; target: Word; begin (* Each state is 8 bytes long (2 words: action and next state). There are 22 character classes, so a transition row 8 * 22 = 176 bytes long. *) row_position := current_state - 1; row_position := row_position * 176; column_position := character_class - 1; column_position := column_position * 8; target := _elna_lexer_get_transition_table(); target := target + row_position; 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: Word); var transition: Word; begin transition := _elna_lexer_get_transition(current_state, character_class); _elna_lexer_transition_set_action(transition, action); _elna_lexer_transition_set_state(transition, 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: Word); 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) 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); (* 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) end; (** * Transition table is saved after character classification table. * Each character entry is 1 word long and there are 256 characters. * 1024 = 256 * 4 *) proc _elna_lexer_get_transition_table(); return @classification + 1024 end; (** * Lexer state is saved after the transition tables. * Each transition table entry is 8 bytes long. The table has 16 rows (transition states) * and 22 columns (character classes), so 2992 = 8 * 17 * 22. *) proc _elna_lexer_global_state(); var result: Word; begin result := _elna_lexer_get_transition_table(); return result + 2992 end; (** * Gets pointer to the token start. *) proc _elna_lexer_global_get_start(); var target: Word; begin target := _elna_lexer_global_state() + 4; return target^ end; (** * Sets pointer to the token start. *) proc _elna_lexer_global_set_start(new_start: Word); var target: Word; begin target := _elna_lexer_global_state() + 4; target^ := new_start end; (** * Gets pointer to the token end. *) proc _elna_lexer_global_get_end(); var target: Word; begin target := _elna_lexer_global_state() + 8; return target^ end; (** * Sets pointer to the token end. *) proc _elna_lexer_global_set_end(new_start: Word); var target: Word; begin target := _elna_lexer_global_state() + 8; target^ := new_start end; proc _elna_lexer_transition_get_action(this: Word); return this^ end; proc _elna_lexer_transition_set_action(this: Word, value: Word); begin this^ := value end; proc _elna_lexer_transition_get_state(this: Word); begin this := this + 4; return this^ end; proc _elna_lexer_transition_set_state(this: Word, value: Word); begin this := this + 4; this^ := value end; (** * Resets the lexer state for reading the next token. *) proc _elna_lexer_reset(); var state: Word; begin (* Transition start state is 1. *) state := _elna_lexer_global_state(); state^ := ElnaLexerState.start; state := _elna_lexer_global_get_start(); _elna_lexer_global_set_end(state) end; (** * One time lexer initialization. *) proc _elna_lexer_initialize(code_pointer: Word); begin _elna_lexer_classifications(); _elna_lexer_transitions(); _elna_lexer_global_set_start(code_pointer); _elna_lexer_global_set_end(code_pointer) end; proc _elna_lexer_next_transition(); var current_character: Word; character_class: Word; current_state: Word; begin current_character := _elna_lexer_global_get_end(); current_character := _load_byte(current_character); character_class := _get_at(@classification, current_character + 1); current_state := _elna_lexer_global_state(); current_state := current_state^; return _elna_lexer_get_transition(current_state, character_class) end; proc string_compare(lhs_pointer: Word, lhs_length: Word, rhs_pointer: Word, rhs_length: Word); var result: Word; begin result := 0; if lhs_length = rhs_length then result := memcmp(lhs_pointer, rhs_pointer, lhs_length); result := result = 0 end; return result end; proc _elna_lexer_classify_keyword(position_start: Word, position_end: Word); var result: Word; token_length: Word; begin result := ElnaLexerKind.identifier; token_length := position_end - position_start; if string_compare(position_start, token_length, "const", 5) then result := ElnaLexerKind._const elsif string_compare(position_start, token_length, "var", 3) then result := ElnaLexerKind._var elsif string_compare(position_start, token_length, "proc", 4) then result := ElnaLexerKind._proc elsif string_compare(position_start, token_length, "type", 4) then result := ElnaLexerKind._type elsif string_compare(position_start, token_length, "begin", 5) then result := ElnaLexerKind._begin elsif string_compare(position_start, token_length, "end", 3) then result := ElnaLexerKind._end elsif string_compare(position_start, token_length, "return", 6) then result := ElnaLexerKind._return elsif string_compare(position_start, token_length, "goto", 4) then result := ElnaLexerKind._goto elsif string_compare(position_start, token_length, "if", 2) then result := ElnaLexerKind._if elsif string_compare(position_start, token_length, "while", 5) then result := ElnaLexerKind._while elsif string_compare(position_start, token_length, "then", 4) then result := ElnaLexerKind._then elsif string_compare(position_start, token_length, "else", 4) then result := ElnaLexerKind._else elsif string_compare(position_start, token_length, "elsif", 5) then result := ElnaLexerKind._elsif elsif string_compare(position_start, token_length, "record", 6) then result := ElnaLexerKind._record elsif string_compare(position_start, token_length, "or", 2) then result := ElnaLexerKind._or elsif string_compare(position_start, token_length, "xor", 3) then result := ElnaLexerKind._xor elsif string_compare(position_start, token_length, "program", 7) then result := ElnaLexerKind._program elsif string_compare(position_start, token_length, "module", 6) then result := ElnaLexerKind._module elsif string_compare(position_start, token_length, "nil", 3) then result := ElnaLexerKind.null end; return result end; proc _elna_lexer_classify_finalize(start_position: Word); var character: Word; result: Word; begin result := 0; character := _load_byte(start_position); if character = ':' then result := ElnaLexerKind.colon elsif character = '.' then result := ElnaLexerKind.dot elsif character = '(' then result := ElnaLexerKind.left_paren elsif character = '-' then result := ElnaLexerKind.minus elsif character = '<' then result := ElnaLexerKind.less_than elsif character = '>' then result := ElnaLexerKind.greater_than end; return result end; proc _elna_lexer_classify_single(start_position: Word); var character: Word; result: Word; begin result := 0; character := _load_byte(start_position); if character = ';' then result := ElnaLexerKind.semicolon elsif character = ',' then result := ElnaLexerKind.comma elsif character = ')' then result := ElnaLexerKind.right_paren elsif character = '@' then result := ElnaLexerKind.at elsif character = '~' then result := ElnaLexerKind.not elsif character = '&' then result := ElnaLexerKind.and elsif character = '+' then result := ElnaLexerKind.plus elsif character = '*' then result := ElnaLexerKind.multiplication elsif character = '=' then result := ElnaLexerKind.equals elsif character = '%' then result := ElnaLexerKind.remainder elsif character = '/' then result := ElnaLexerKind.division elsif character = '.' then result := ElnaLexerKind.dot elsif character = '^' then result := ElnaLexerKind.hat end; return result end; proc _elna_lexer_classify_composite(start_position: Word, one_before_last: Word); var first_character: Word; last_character: Word; result: Word; begin first_character := _load_byte(start_position); last_character := _load_byte(one_before_last); if first_character = ':' then result := ElnaLexerKind.assignment elsif first_character = '<' then if last_character = '=' then result := ElnaLexerKind.less_equal elsif last_character = '>' then result := ElnaLexerKind.not_equal end elsif first_character = '>' then if last_character = '=' then result := ElnaLexerKind.greater_equal end end; return result end; proc _elna_lexer_classify_delimited(start_position: Word, end_position: Word); var token_length: Word; delimiter: Word; result: Word; begin token_length := end_position - start_position; delimiter := _load_byte(start_position); if delimiter = '(' then result := ElnaLexerKind.comment elsif delimiter = '\'' then result := ElnaLexerKind.character elsif delimiter = '"' then result := ElnaLexerKind.string end; return result end; proc _elna_lexer_classify_integer(start_position: Word, end_position: Word); return ElnaLexerKind.integer end; proc _elna_lexer_execute_action(action_to_perform: Word, kind: Word); var position_start: Word; position_end: Word; intermediate: Word; begin position_start := _elna_lexer_global_get_start(); position_end := _elna_lexer_global_get_end(); if action_to_perform = ElnaLexerAction.none then elsif action_to_perform = ElnaLexerAction.accumulate then _elna_lexer_global_set_end(position_end + 1) elsif action_to_perform = ElnaLexerAction.skip then _elna_lexer_global_set_start(position_start + 1); _elna_lexer_global_set_end(position_end + 1) elsif action_to_perform = ElnaLexerAction.single then _elna_lexer_global_set_end(position_end + 1); intermediate := _elna_lexer_classify_single(position_start); kind^ := intermediate elsif action_to_perform = ElnaLexerAction.eof then intermediate := ElnaLexerKind.eof; kind^ := intermediate elsif action_to_perform = ElnaLexerAction.finalize then intermediate := _elna_lexer_classify_finalize(position_start); kind^ := intermediate elsif action_to_perform = ElnaLexerAction.composite then _elna_lexer_global_set_end(position_end + 1); intermediate := _elna_lexer_classify_composite(position_start, position_end); kind^ := intermediate elsif action_to_perform = ElnaLexerAction.key_id then intermediate := _elna_lexer_classify_keyword(position_start, position_end); kind^ := intermediate elsif action_to_perform = ElnaLexerAction.integer then intermediate := _elna_lexer_classify_integer(position_start, position_end); kind^ := intermediate elsif action_to_perform = ElnaLexerAction.delimited then _elna_lexer_global_set_end(position_end + 1); intermediate := _elna_lexer_classify_delimited(position_start, position_end + 1); kind^ := intermediate end end; proc _elna_lexer_execute_transition(kind: Word); var next_transition: Word; next_state: Word; global_state: Word; action_to_perform: Word; begin next_transition := _elna_lexer_next_transition(); next_state := _elna_lexer_transition_get_state(next_transition); action_to_perform := _elna_lexer_transition_get_action(next_transition); global_state := _elna_lexer_global_state(); global_state^ := next_state; _elna_lexer_execute_action(action_to_perform, kind); return next_state end; proc _elna_lexer_advance_token(kind: Word); var result_state: Word; begin result_state := _elna_lexer_execute_transition(kind); if result_state <> ElnaLexerState.finish then _elna_lexer_advance_token(kind) end end; (** * Reads the next token and writes its type into the address in the kind parameter. *) proc _elna_lexer_read_token(kind: Word); begin _elna_lexer_reset(); _elna_lexer_advance_token(kind) end; (** * Advances the token stream past the last read token. *) proc _elna_lexer_skip_token(); var old_end: Word; begin old_end := _elna_lexer_global_get_end(); _elna_lexer_global_set_start(old_end) end; proc _initialize_global_state(); begin compiler_strings_position := @compiler_strings; source_code := malloc(495616); symbol_table_store := malloc(4194304); temporary_variable_counter := 0 end; (* * Entry point. *) proc main(); var last_read: Word; offset: Word; begin _initialize_global_state(); _elna_lexer_initialize(source_code); _symbol_table_build(); (* 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_file(offset, 409600); if last_read > 0 then offset := offset + last_read; goto start_read end; _compile(); _exit(0) end;