diff --git a/Rakefile b/Rakefile index 54c3bd3..9edd271 100644 --- a/Rakefile +++ b/Rakefile @@ -40,43 +40,19 @@ end desc 'Convert previous stage language into the current stage language' task :convert do - File.open('boot/stage16.elna', 'w') do |current_stage| - skip = false - - File.readlines('boot/stage15.elna').each do |line| - if line.strip == 'type' - current_stage << <<~SNIP - type - _elna_tac_declaration = record - next: Word; - name: Word; - length: Word; - body: Word - end; - _integer_literal_node = record - kind: Word; - value: Word; - length: Word - end; - SNIP - elsif line.start_with?('proc _elna_tac_declaration_size') - skip = true - elsif line.start_with?('proc _elna_tac_declaration_get_') - skip = true - elsif line.start_with?('proc _elna_tac_declaration_set_') - skip = true - elsif line.start_with?('proc _integer_literal_node_size') - skip = true - elsif line.start_with?('proc _integer_literal_node_get_') - skip = true - elsif line.start_with?('proc _integer_literal_node_set_') - skip = true - elsif line.start_with?('end') && skip - skip = false - elsif !skip + File.open('boot/stage17/cl.elna', 'w') do |current_stage| + File.readlines('boot/stage16/cl.elna').each do |line| current_stage << line - end end + current_stage << <<~STAGE + + proc f(x: ElnaTreeExpression); + var + y: Word; + begin + y := x.kind + end; + STAGE end end diff --git a/boot/stage16/cl.elna b/boot/stage16/cl.elna index 0a6d454..438c106 100644 --- a/boot/stage16/cl.elna +++ b/boot/stage16/cl.elna @@ -24,6 +24,10 @@ type ElnaTreeNode = record kind: Word end; + ElnaTreeExpression = record + kind: Word; + type_decoration: Word + end; ElnaTreeIntegerLiteral = record kind: Word; type_decoration: Word; @@ -1581,7 +1585,7 @@ begin ElnaTreeNode_set_kind(result, ElnaTreeKind.integer_literal); ElnaTreeIntegerLiteral_set_value(result, integer_token); ElnaTreeIntegerLiteral_set_length(result, integer_length); - ElnaTreeIntegerLiteral_set_type_decoration(result, nil); + ElnaTreeExpression_set_type_decoration(result, nil); return result end; @@ -1594,7 +1598,7 @@ begin result := malloc(ElnaTreeNilLiteral_size()); ElnaTreeNode_set_kind(result, ElnaTreeKind.null); - ElnaTreeIntegerLiteral_set_type_decoration(result, nil); + ElnaTreeExpression_set_type_decoration(result, nil); return result end; @@ -1633,7 +1637,7 @@ begin ElnaTreeNode_set_kind(result, ElnaTreeKind.character_literal); ElnaTreeIntegerLiteral_set_value(result, character); ElnaTreeIntegerLiteral_set_length(result, character_length); - ElnaTreeIntegerLiteral_set_type_decoration(result, nil); + ElnaTreeExpression_set_type_decoration(result, nil); return result end; @@ -1662,7 +1666,7 @@ begin ElnaTreeNode_set_kind(result, ElnaTreeKind.variable_expression); ElnaTreeVariableExpression_set_name(result, name_pointer); ElnaTreeVariableExpression_set_length(result, name_length); - ElnaTreeVariableExpression_set_type_decoration(result, nil); + ElnaTreeExpression_set_type_decoration(result, nil); return result end; @@ -1704,7 +1708,7 @@ begin ElnaTreeNode_set_kind(result, ElnaTreeKind.string_literal); ElnaTreeStringLiteral_set_value(result, token_start); ElnaTreeStringLiteral_set_length(result, length); - ElnaTreeIntegerLiteral_set_type_decoration(result, nil); + ElnaTreeExpression_set_type_decoration(result, nil); return result end; @@ -1767,7 +1771,7 @@ begin ElnaTreeNode_set_kind(result, ElnaTreeKind.dereference_expression); ElnaTreeDereferenceExpression_set_pointer(result, simple_expression); - ElnaTreeDereferenceExpression_set_type_decoration(result, nil); + ElnaTreeExpression_set_type_decoration(result, nil); _elna_lexer_skip_token(); return result @@ -1842,7 +1846,7 @@ begin ElnaTreeNode_set_kind(result, ElnaTreeKind.unary_expression); ElnaTreeUnaryExpression_set_operand(result, operand); ElnaTreeUnaryExpression_set_operator(result, operator); - ElnaTreeUnaryExpression_set_type_decoration(result, nil) + ElnaTreeExpression_set_type_decoration(result, nil) end; return result @@ -1986,7 +1990,7 @@ begin ElnaTreeBinaryExpression_set_lhs(result, lhs_node); ElnaTreeBinaryExpression_set_rhs(result, rhs_node); ElnaTreeBinaryExpression_set_operator(result, token_kind); - ElnaTreeBinaryExpression_set_type_decoration(result, nil) + ElnaTreeExpression_set_type_decoration(result, nil) else result := lhs_node end; @@ -2368,7 +2372,7 @@ begin operand_value^ := counter; operand_length^ := 0 end; - return 0 + return nil end; proc elna_parser_field_access_expression(aggregate: Word); @@ -2392,7 +2396,7 @@ begin ElnaTreeFieldAccessExpression_set_aggregate(result, aggregate); ElnaTreeFieldAccessExpression_set_field(result, name_pointer); ElnaTreeFieldAccessExpression_set_length(result, name_length); - ElnaTreeFieldAccessExpression_set_type_decoration(result, nil); + ElnaTreeExpression_set_type_decoration(result, nil); return result end; @@ -2401,8 +2405,12 @@ proc _elna_tac_designator(parser_node: Word, symbol_table: Word, is_address: Wor var parser_node: Word; node_kind: Word; + expression_type: Word; first_instruction: Word; last_instruction: Word; + + name_pointer: Word; + name_length: Word; begin node_kind := ElnaTreeNode_get_kind(parser_node); @@ -2421,7 +2429,26 @@ begin is_address^ := 2; first_instruction := elna_instruction_list_concatenate(first_instruction, last_instruction) elsif node_kind = ElnaTreeKind.field_access_expression then - first_instruction := _elna_tac_enumeration_value(parser_node, operand_type, operand_value, operand_length); + expression_type := ElnaTreeExpression_get_type_decoration(parser_node); + name_pointer := ElnaTreeFieldAccessExpression_get_field(parser_node); + name_length := ElnaTreeFieldAccessExpression_get_length(parser_node); + + if expression_type = nil then + (* Stub for record field access. Always generate nil for field access expression until it is implemented. *) + + (* Debug. Error stream output. + _syscall(2, name_pointer, name_length, 0, 0, 0, 64); + printf("# there %.*s %i\n\0", name_length, name_pointer, expression_type); + fflush(0); *) + + operand_type^ := ElnaTacOperand.immediate; + operand_value^ := 0; + operand_length^ := 0; + + first_instruction := nil + else + first_instruction := _elna_tac_enumeration_value(parser_node, operand_type, operand_value, operand_length) + end; is_address^ := 0 elsif node_kind = ElnaTreeKind.call then first_instruction := _elna_tac_call(parser_node, symbol_table); @@ -3241,8 +3268,9 @@ end; (** * Parameters: * parameter_index - Parameter index. + * parameter_type - Parameter type. *) -proc _parameter_info_create(parameter_index: Word); +proc _parameter_info_create(parameter_index: Word, parameter_type: Word); var offset: Word; result: Word; @@ -3253,6 +3281,7 @@ begin (* Calculate the stack offset: 104 - (4 * parameter_counter) *) offset := parameter_index * 4; _parameter_info_set_offset(result, 104 - offset); + _parameter_info_set_variable_type(result, parameter_type); return result end; @@ -3270,9 +3299,10 @@ end; (** * Parameters: - * temporary_index - Parameter index. + * temporary_index - Local variable index. + * temporary_type - Local variable type. *) -proc _temporary_info_create(temporary_index: Word); +proc _temporary_info_create(temporary_index: Word, temporary_type: Word); var result: Word; begin @@ -3281,6 +3311,7 @@ begin (* Calculate the stack offset: 4 * variable_counter. *) _temporary_info_set_offset(result, temporary_index * 4); + _parameter_info_set_variable_type(result, temporary_type); return result end; @@ -3309,11 +3340,15 @@ var name_length: Word; info: Word; name_position: Word; + variable_type: Word; begin name_position := _declaration_get_name(parser_node); name_length := _declaration_get_length(parser_node); - info := _parameter_info_create(parameter_index); + variable_type := _variable_declaration_get__type(parser_node); + variable_type := elna_name_type_expression(variable_type); + + info := _parameter_info_create(parameter_index, variable_type); _symbol_table_enter(symbol_table, name_position, name_length, info) end; @@ -3326,11 +3361,15 @@ var name_length: Word; info: Word; name_position: Word; + variable_type: Word; begin name_position := _declaration_get_name(parser_node); name_length := _declaration_get_length(parser_node); - info := _temporary_info_create(variable_index); + variable_type := _variable_declaration_get__type(parser_node); + variable_type := elna_name_type_expression(variable_type); + + info := _temporary_info_create(variable_index, variable_type); _symbol_table_enter(symbol_table, name_position, name_length, info) end; @@ -4191,7 +4230,7 @@ begin goto elna_type_if_statement_conditionals end; block := _if_statement_get__else(parser_node); - if block <> 0 then + if block <> nil then elna_type_statements(block, symbol_table) end end; @@ -4230,8 +4269,8 @@ begin lhs := _assign_statement_get_assignee(parser_node); rhs := _assign_statement_get_assignment(parser_node); - elna_type_designator(parser_node, symbol_table); - elna_type_binary_expression(parser_node, symbol_table) + elna_type_designator(lhs, symbol_table); + elna_type_binary_expression(rhs, symbol_table) end; proc elna_type_statement(parser_node: Word, symbol_table: Word); @@ -4271,7 +4310,7 @@ begin symbol_info := _symbol_table_lookup(@symbol_table_global, "Word", 4); symbol_type := _type_info_get__type(symbol_info); - ElnaTreeCharacterLiteral_set_type_decoration(parser_node, symbol_type) + ElnaTreeExpression_set_type_decoration(parser_node, symbol_type) end; proc elna_type_integer_literal(parser_node: Word); @@ -4282,7 +4321,7 @@ begin symbol_info := _symbol_table_lookup(@symbol_table_global, "Word", 4); symbol_type := _type_info_get__type(symbol_info); - ElnaTreeIntegerLiteral_set_type_decoration(parser_node, symbol_type) + ElnaTreeExpression_set_type_decoration(parser_node, symbol_type) end; proc elna_type_string_literal(parser_node: Word); @@ -4293,7 +4332,7 @@ begin symbol_info := _symbol_table_lookup(@symbol_table_global, "Word", 4); symbol_type := _type_info_get__type(symbol_info); - ElnaTreeStringLiteral_set_type_decoration(parser_node, symbol_type) + ElnaTreeExpression_set_type_decoration(parser_node, symbol_type) end; proc elna_type_nil_literal(parser_node: Word); @@ -4304,7 +4343,7 @@ begin symbol_info := _symbol_table_lookup(@symbol_table_global, "Word", 4); symbol_type := _type_info_get__type(symbol_info); - ElnaTreeNilLiteral_set_type_decoration(parser_node, symbol_type) + ElnaTreeExpression_set_type_decoration(parser_node, symbol_type) end; proc elna_type_variable_expression(parser_node: Word, symbol_table: Word); @@ -4313,6 +4352,7 @@ var name_pointer: Word; name_length: Word; variable_kind: Word; + variable_type: Word; begin name_pointer := ElnaTreeVariableExpression_get_name(parser_node); name_length := ElnaTreeVariableExpression_get_length(parser_node); @@ -4322,13 +4362,15 @@ begin if variable_info = nil then variable_info := _symbol_table_lookup(@symbol_table_global, name_pointer, name_length) end; + variable_kind := _info_get_kind(variable_info); + variable_type := nil; - (* Debug. Error stream output. - _syscall(2, name_pointer, name_length, 0, 0, 0, 64); - printf("# here %p %.*s\n\0", variable_info, name_length, name_pointer); - fflush(0) - - variable_kind := _info_get_kind(variable_info) *) + if variable_kind = InfoKind.parameter_info then + variable_type := _parameter_info_get_variable_type(variable_info) + elsif variable_kind = InfoKind.temporary_info then + variable_type := _temporary_info_get_variable_type(variable_info) + end; + ElnaTreeExpression_set_type_decoration(parser_node, variable_type) end; proc elna_type_simple_expression(parser_node: Word, symbol_table: Word); @@ -4353,11 +4395,57 @@ end; proc elna_type_designator(parser_node: Word, symbol_table: Word); var expression_kind: Word; + designator_base: Word; + base_type: Word; + type_kind: Word; + name_pointer: Word; + name_length: Word; + symbol_info: Word; begin expression_kind := ElnaTreeNode_get_kind(parser_node); if expression_kind = ElnaTreeKind.dereference_expression then + designator_base := ElnaTreeDereferenceExpression_get_pointer(parser_node); + elna_type_simple_expression(designator_base, symbol_table); + + base_type := ElnaTreeExpression_get_type_decoration(designator_base); + type_kind := ElnaType_get_kind(base_type); + + (* If check for compatibility, should be removed later. *) + if type_kind = TypeKind.pointer then + base_type := ElnaTypePointer_get_base(base_type) + end; + + ElnaTreeExpression_set_type_decoration(parser_node, base_type) elsif expression_kind = ElnaTreeKind.field_access_expression then + base_type := nil; + designator_base := ElnaTreeFieldAccessExpression_get_aggregate(parser_node); + + (* Check whether the field access is an enumeration value. *) + if ElnaTreeNode_get_kind(designator_base) = ElnaTreeKind.variable_expression then + name_pointer := ElnaTreeVariableExpression_get_name(designator_base); + name_length := ElnaTreeVariableExpression_get_length(designator_base); + + symbol_info := _symbol_table_lookup(@symbol_table_global, name_pointer, name_length); + if symbol_info <> nil then + type_kind := _info_get_kind(symbol_info); + + if type_kind = InfoKind.type_info then + base_type := _type_info_get__type(symbol_info) + end + end + end; + (* If the base_type is still nil this is record field access. *) + if base_type = nil then + (* elna_type_simple_expression(designator_base, symbol_table); + base_type := ElnaTreeExpression_get_type_decoration(designator_base); *) + else + end; + + (* Change my type. *) + ElnaTreeExpression_set_type_decoration(parser_node, base_type) + elsif expression_kind = ElnaTreeKind.call then + elna_type_call(parser_node, symbol_table) else elna_type_simple_expression(parser_node, symbol_table) end @@ -4366,10 +4454,17 @@ end; proc elna_type_unary_expression(parser_node: Word, symbol_table: Word); var expression_kind: Word; + unary_operand: Word; + operand_type: Word; begin expression_kind := ElnaTreeNode_get_kind(parser_node); if expression_kind = ElnaTreeKind.unary_expression then + unary_operand := ElnaTreeUnaryExpression_get_operand(parser_node); + elna_type_designator(unary_operand, symbol_table); + + operand_type := ElnaTreeExpression_get_type_decoration(unary_operand); + ElnaTreeExpression_set_type_decoration(parser_node, operand_type) else elna_type_designator(parser_node, symbol_table) end @@ -4378,10 +4473,21 @@ end; proc elna_type_binary_expression(parser_node: Word, symbol_table: Word); var expression_kind: Word; + binary_operand: Word; + operand_type: Word; begin expression_kind := ElnaTreeNode_get_kind(parser_node); if expression_kind = ElnaTreeKind.binary_expression then + binary_operand := ElnaTreeBinaryExpression_get_rhs(parser_node); + elna_type_unary_expression(binary_operand, symbol_table); + + binary_operand := ElnaTreeBinaryExpression_get_lhs(parser_node); + elna_type_unary_expression(binary_operand, symbol_table); + + operand_type := ElnaTreeExpression_get_type_decoration(binary_operand); + + ElnaTreeExpression_set_type_decoration(parser_node, operand_type) else elna_type_unary_expression(parser_node, symbol_table) end @@ -4412,16 +4518,23 @@ var begin current_part := _module_declaration_get_types(parser_node); .elna_name_module_declaration_type; - if current_part <> 0 then + if current_part <> nil 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_globals(parser_node); + .elna_name_module_declaration_global; + if current_part <> nil then + elna_name_procedure_temporary(current_part, 0, @symbol_table_global); + current_part := _declaration_get_next(current_part); + goto elna_name_module_declaration_global + end; current_part := _module_declaration_get_procedures(parser_node); .elna_name_module_declaration_procedure; - if current_part <> 0 then + if current_part <> nil then elna_name_procedure_declaration(current_part); current_part := _declaration_get_next(current_part); diff --git a/boot/stage17/cl.elna b/boot/stage17/cl.elna new file mode 100644 index 0000000..9908b3b --- /dev/null +++ b/boot/stage17/cl.elna @@ -0,0 +1,5537 @@ +(* + * 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; + ElnaTreeNode = record + kind: Word + end; + ElnaTreeExpression = record + kind: Word; + type_decoration: Word + end; + ElnaTreeIntegerLiteral = record + kind: Word; + type_decoration: Word; + value: Word; + length: Word + end; + ElnaTreeCharacterLiteral = record + kind: Word; + type_decoration: Word; + value: Word; + length: Word + end; + ElnaTreeNilLiteral = record + kind: Word; + type_decoration: Word + end; + ElnaTreeVariableExpression = record + kind: Word; + type_decoration: Word; + name: Word; + length: Word + end; + ElnaTreeStringLiteral = record + kind: Word; + type_decoration: Word; + value: Word; + length: Word + end; + ElnaTreeDereferenceExpression = record + kind: Word; + type_decoration: Word; + pointer: Word + end; + ElnaTreeBinaryExpression = record + kind: Word; + type_decoration: Word; + lhs: Word; + rhs: Word; + operator: Word + end; + ElnaTreeUnaryExpression = record + kind: Word; + type_decoration: 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; + ElnaTreeFieldAccessExpression = record + kind: Word; + type_decoration: Word; + aggregate: Word; + field: Word; + length: Word + end; + ElnaTreeEnumerationTypeExpression = record + kind: Word; + members: 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; + ElnaTreeRecordTypeExpression = record + kind: Word; + members: Word; + length: Word + end; + ElnaTreeNamedTypeExpression = record + kind: Word; + name: Word; + length: Word + end; + ElnaTreePointerTypeExpression = record + kind: Word; + base: Word + end; + ElnaTreeArrayTypeExpression = record + kind: Word; + base: Word; + length: Word + end; + ElnaType = record + kind: Word; + size: Word + end; + ElnaTypeEnumeration = record + kind: Word; + size: Word; + members: Word; + length: Word + end; + ElnaTypeRecord = record + kind: Word; + size: Word; + members: Word; + length: Word + end; + ElnaTypePointer = record + kind: Word; + size: Word; + base: Word + end; + ElnaTypeArray = record + kind: Word; + size: Word; + base: Word; + length: Word + end; + _info = record + kind: Word + end; + _type_info = record + kind: Word; + _type: Word + end; + _parameter_info = record + kind: Word; + offset: Word; + variable_type: Word + end; + _temporary_info = record + kind: Word; + offset: Word; + variable_type: 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 + ); + ElnaTreeKind = ( + 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, + pointer_type_expression, + array_type_expression, + null + ); + InfoKind = (type_info, parameter_info, temporary_info, procedure_info); + TypeKind = (primitive, enumeration, _record, pointer, array); + 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 + 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(ElnaTreeIntegerLiteral_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(); + + ElnaTreeNode_set_kind(result, ElnaTreeKind.integer_literal); + ElnaTreeIntegerLiteral_set_value(result, integer_token); + ElnaTreeIntegerLiteral_set_length(result, integer_length); + ElnaTreeExpression_set_type_decoration(result, nil); + + return result +end; + +proc elna_parser_nil_literal(); +var + result: Word; +begin + _elna_lexer_skip_token(); + + result := malloc(ElnaTreeNilLiteral_size()); + ElnaTreeNode_set_kind(result, ElnaTreeKind.null); + ElnaTreeExpression_set_type_decoration(result, nil); + + 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^ := ElnaTreeIntegerLiteral_get_value(integer_literal_node); + operand_length^ := ElnaTreeIntegerLiteral_get_length(integer_literal_node); + + return 0 +end; + +proc _elna_tac_nil_literal(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(ElnaTreeCharacterLiteral_size()); + + character := _elna_lexer_global_get_start(); + character_length := _elna_lexer_global_get_end(); + character_length := character_length - character; + _elna_lexer_skip_token(); + + ElnaTreeNode_set_kind(result, ElnaTreeKind.character_literal); + ElnaTreeIntegerLiteral_set_value(result, character); + ElnaTreeIntegerLiteral_set_length(result, character_length); + ElnaTreeExpression_set_type_decoration(result, nil); + + 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^ :=ElnaTreeCharacterLiteral_get_value(character_literal_node); + operand_length^ := ElnaTreeCharacterLiteral_get_length(character_literal_node); + + return 0 +end; + +proc elna_parser_variable_expression(); +var + name_pointer: Word; + name_length: Word; + result: Word; +begin + name_pointer := _elna_lexer_global_get_start(); + name_length := _elna_lexer_global_get_end() - name_pointer; + _elna_lexer_skip_token(); + + result := malloc(ElnaTreeVariableExpression_size()); + + ElnaTreeNode_set_kind(result, ElnaTreeKind.variable_expression); + ElnaTreeVariableExpression_set_name(result, name_pointer); + ElnaTreeVariableExpression_set_length(result, name_length); + ElnaTreeExpression_set_type_decoration(result, nil); + + 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 := ElnaTreeVariableExpression_get_name(variable_expression); + name_length := ElnaTreeVariableExpression_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(ElnaTreeStringLiteral_size()); + + token_start := _elna_lexer_global_get_start(); + length := _string_length(token_start); + _elna_lexer_skip_token(); + + ElnaTreeNode_set_kind(result, ElnaTreeKind.string_literal); + ElnaTreeStringLiteral_set_value(result, token_start); + ElnaTreeStringLiteral_set_length(result, length); + ElnaTreeExpression_set_type_decoration(result, nil); + + 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 := ElnaTreeStringLiteral_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_literal() + 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(ElnaTreeDereferenceExpression_size()); + + ElnaTreeNode_set_kind(result, ElnaTreeKind.dereference_expression); + ElnaTreeDereferenceExpression_set_pointer(result, simple_expression); + ElnaTreeExpression_set_type_decoration(result, nil); + _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 := ElnaTreeNode_get_kind(parser_node); + + if node_kind = ElnaTreeKind.character_literal then + instruction := _elna_tac_character_literal(parser_node, operand_type, operand_value, operand_length) + elsif node_kind = ElnaTreeKind.string_literal then + instruction := _elna_tac_string_literal(parser_node, operand_type, operand_value, operand_length) + elsif node_kind = ElnaTreeKind.integer_literal then + instruction := _elna_tac_integer_literal(parser_node, operand_type, operand_value, operand_length) + elsif node_kind = ElnaTreeKind.null then + instruction := _elna_tac_nil_literal(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(ElnaTreeUnaryExpression_size()); + + ElnaTreeNode_set_kind(result, ElnaTreeKind.unary_expression); + ElnaTreeUnaryExpression_set_operand(result, operand); + ElnaTreeUnaryExpression_set_operator(result, operator); + ElnaTreeExpression_set_type_decoration(result, nil) + 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 := ElnaTreeNode_get_kind(parser_node); + + if expression_kind = ElnaTreeKind.unary_expression then + operator := ElnaTreeUnaryExpression_get_operator(parser_node); + operand := ElnaTreeUnaryExpression_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(ElnaTreeBinaryExpression_size()); + + ElnaTreeNode_set_kind(result, ElnaTreeKind.binary_expression); + ElnaTreeBinaryExpression_set_lhs(result, lhs_node); + ElnaTreeBinaryExpression_set_rhs(result, rhs_node); + ElnaTreeBinaryExpression_set_operator(result, token_kind); + ElnaTreeExpression_set_type_decoration(result, nil) + 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 := ElnaTreeNode_get_kind(parser_node); + + if expression_kind <> ElnaTreeKind.binary_expression then + first_instruction := _elna_tac_unary_expression(parser_node, symbol_table, operand_type, operand_value, operand_length) + else + token_kind := ElnaTreeBinaryExpression_get_operator(parser_node); + + operand_node := ElnaTreeBinaryExpression_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 := ElnaTreeBinaryExpression_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()); + ElnaTreeNode_set_kind(result, ElnaTreeKind.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, nil); + + 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 := ElnaTreeVariableExpression_get_name(parsed_expression); + name_length := ElnaTreeVariableExpression_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()); + + ElnaTreeNode_set_kind(result, ElnaTreeKind.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()); + + ElnaTreeNode_set_kind(result, ElnaTreeKind.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 := ElnaTreeFieldAccessExpression_get_aggregate(field_access_expression); + value_name := ElnaTreeVariableExpression_get_name(symbol); + name_length := ElnaTreeVariableExpression_get_length(symbol); + + symbol := _symbol_table_lookup(@symbol_table_global, value_name, name_length); + + enumeration_type := _type_info_get__type(symbol); + members := ElnaTypeEnumeration_get_members(enumeration_type); + members_length := ElnaTypeEnumeration_get_length(enumeration_type); + + _elna_lexer_read_token(@token_type); + + value_name := ElnaTreeFieldAccessExpression_get_field(field_access_expression); + name_length := ElnaTreeFieldAccessExpression_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 nil +end; + +proc elna_parser_field_access_expression(aggregate: Word); +var + token_kind: Word; + name_pointer: Word; + name_length: Word; + result: Word; +begin + (* Skip dot. Read the enumeration value. *) + _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; + + _elna_lexer_skip_token(); + result := malloc(ElnaTreeFieldAccessExpression_size()); + + ElnaTreeNode_set_kind(result, ElnaTreeKind.field_access_expression); + ElnaTreeFieldAccessExpression_set_aggregate(result, aggregate); + ElnaTreeFieldAccessExpression_set_field(result, name_pointer); + ElnaTreeFieldAccessExpression_set_length(result, name_length); + ElnaTreeExpression_set_type_decoration(result, nil); + + 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; + expression_type: Word; + first_instruction: Word; + last_instruction: Word; + + name_pointer: Word; + name_length: Word; +begin + node_kind := ElnaTreeNode_get_kind(parser_node); + + if node_kind = ElnaTreeKind.dereference_expression then + parser_node := ElnaTreeDereferenceExpression_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 = ElnaTreeKind.field_access_expression then + expression_type := ElnaTreeExpression_get_type_decoration(parser_node); + name_pointer := ElnaTreeFieldAccessExpression_get_field(parser_node); + name_length := ElnaTreeFieldAccessExpression_get_length(parser_node); + + if expression_type = nil then + (* Stub for record field access. Always generate nil for field access expression until it is implemented. *) + + (* Debug. Error stream output. + _syscall(2, name_pointer, name_length, 0, 0, 0, 64); + printf("# there %.*s %i\n\0", name_length, name_pointer, expression_type); + fflush(0); *) + + operand_type^ := ElnaTacOperand.immediate; + operand_value^ := 0; + operand_length^ := 0; + + first_instruction := nil + else + first_instruction := _elna_tac_enumeration_value(parser_node, operand_type, operand_value, operand_length) + end; + is_address^ := 0 + elsif node_kind = ElnaTreeKind.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()); + + ElnaTreeNode_set_kind(result, ElnaTreeKind.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()); + + ElnaTreeNode_set_kind(result, ElnaTreeKind.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()); + + ElnaTreeNode_set_kind(result, ElnaTreeKind.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 := ElnaTreeNode_get_kind(result); + + if temporary <> ElnaTreeKind.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 := ElnaTreeNode_get_kind(parser_node); + + if statement_kind = ElnaTreeKind.goto_statement then + instruction := _elna_tac_goto_statement(parser_node) + elsif statement_kind = ElnaTreeKind.if_statement then + instruction := _elna_tac_if_statement(parser_node, symbol_table) + elsif statement_kind = ElnaTreeKind.return_statement then + instruction := _elna_tac_return_statement(parser_node, symbol_table) + elsif statement_kind = ElnaTreeKind.label_declaration then + instruction := _elna_tac_label_declaration(parser_node) + elsif statement_kind = ElnaTreeKind.call then + instruction := _elna_tac_call(parser_node, symbol_table) + elsif statement_kind = ElnaTreeKind.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(ElnaTreeEnumerationTypeExpression_size()); + + ElnaTreeNode_set_kind(result, ElnaTreeKind.record_type_expression); + ElnaTreeRecordTypeExpression_set_members(result, memory_start); + ElnaTreeRecordTypeExpression_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(ElnaTreeEnumerationTypeExpression_size()); + + ElnaTreeNode_set_kind(result, ElnaTreeKind.enumeration_type_expression); + ElnaTreeEnumerationTypeExpression_set_members(result, memory_start); + ElnaTreeEnumerationTypeExpression_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_enumeration_type_expression(parser_node: Word); +var + result: Word; + memory_start: Word; + member_count: Word; + member_array_start: Word; + member_array_current: Word; +begin + result := malloc(ElnaTypeEnumeration_size()); + + memory_start := ElnaTreeEnumerationTypeExpression_get_members(parser_node); + member_count := ElnaTreeEnumerationTypeExpression_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 := ElnaTreeEnumerationTypeExpression_get_length(parser_node); + + ElnaType_set_kind(result, TypeKind.enumeration); + ElnaType_set_size(result, 4); + ElnaTypeEnumeration_set_members(result, member_array_start); + ElnaTypeEnumeration_set_length(result, member_count); + + return result +end; + +proc elna_name_pointer_type_expression(parser_node: Word); +var + base: Word; + result: Word; +begin + result := malloc(ElnaTypePointer_size()); + base := ElnaTreePointerTypeExpression_get_base(parser_node); + base := elna_name_type_expression(base); + + ElnaType_set_kind(result, TypeKind.pointer); + ElnaType_set_size(result, 4); + ElnaTypePointer_set_base(result, base); + + return result +end; + +proc elna_name_array_type_expression(parser_node: Word); +var + base: Word; + result: Word; + length: Word; + size: Word; +begin + result := malloc(ElnaTypeArray_size()); + base := ElnaTreeArrayTypeExpression_get_base(parser_node); + base := elna_name_type_expression(base); + (* Size of the array base. *) + size := ElnaType_get_size(base); + + length := ElnaTreeArrayTypeExpression_get_length(parser_node); + (* Expected to be an integer literal for now. *) + length := ElnaTreeIntegerLiteral_get_value(length); + + ElnaType_set_kind(result, TypeKind.pointer); + ElnaType_set_size(result, size * length); + ElnaTypeArray_set_base(result, base); + ElnaTypeArray_set_size(result, length); + + return result +end; + +proc elna_name_record_type_expression(parser_node: Word); +var + result: Word; + memory_start: Word; + member_count: Word; + member_array_start: Word; + member_array_current: Word; +begin + result := malloc(ElnaTypeRecord_size()); + + memory_start := ElnaTreeRecordTypeExpression_get_members(parser_node); + member_count := ElnaTreeRecordTypeExpression_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 := ElnaTreeRecordTypeExpression_get_length(parser_node); + + ElnaType_set_kind(result, TypeKind._record); + ElnaType_set_size(result, member_count * 4); + ElnaTypeRecord_set_members(result, member_array_start); + ElnaTypeRecord_set_length(result, member_count); + + return result +end; + +proc elna_parser_named_type_expression(); +var + result: Word; + type_name: Word; + name_length: Word; +begin + result := malloc(ElnaTreeNamedTypeExpression_size()); + + ElnaTreeNode_set_kind(result, ElnaTreeKind.named_type_expression); + type_name := _elna_lexer_global_get_start(); + name_length := _elna_lexer_global_get_end() - type_name; + ElnaTreeNamedTypeExpression_set_name(result, type_name); + ElnaTreeNamedTypeExpression_set_length(result, name_length); + _elna_lexer_skip_token(); + + return result +end; + +proc elna_parser_pointer_type_expression(); +var + result: Word; + base: Word; +begin + _elna_lexer_skip_token(); + result := malloc(ElnaTreePointerTypeExpression_size()); + ElnaTreeNode_set_kind(result, ElnaTreeKind.pointer_type_expression); + + base := elna_parser_type_expression(); + ElnaTreePointerTypeExpression_set_base(result, base); + + return result +end; + +proc elna_parser_array_type_expression(); +var + result: Word; + base: Word; + size: Word; + token_kind: Word; +begin + _elna_lexer_skip_token(); + result := malloc(ElnaTreeArrayTypeExpression_size()); + ElnaTreeNode_set_kind(result, ElnaTreeKind.array_type_expression); + + size := elna_parser_binary_expression(); + ElnaTreePointerTypeExpression_set_base(result, size); + + (* Read and skip square bracket. *) + _elna_lexer_read_token(@token_kind); + _elna_lexer_skip_token(); + + base := elna_parser_type_expression(); + ElnaTreePointerTypeExpression_set_base(result, base); + + 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() + elsif token_kind = ElnaLexerKind.hat then + result := elna_parser_pointer_type_expression() + elsif token_kind = ElnaLexerKind.left_square then + result := elna_parser_array_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 := ElnaTreeNode_get_kind(parser_node); + + if token_kind = ElnaTreeKind.named_type_expression then + type_name := ElnaTreeNamedTypeExpression_get_name(parser_node); + name_length := ElnaTreeNamedTypeExpression_get_length(parser_node); + + result := _symbol_table_lookup(@symbol_table_global, type_name, name_length); + result := _type_info_get__type(result) + elsif token_kind = ElnaTreeKind.enumeration_type_expression then + result := elna_name_enumeration_type_expression(parser_node) + elsif token_kind = ElnaTreeKind.record_type_expression then + result := elna_name_record_type_expression(parser_node) + elsif token_kind = ElnaTreeKind.pointer_type_expression then + result := elna_name_pointer_type_expression(parser_node) + elsif token_kind = ElnaTreeKind.array_type_expression then + result := elna_name_array_type_expression(parser_node) + end; + + return result +end; + +(** + * Parameters: + * parameter_index - Parameter index. + * parameter_type - Parameter type. + *) +proc _parameter_info_create(parameter_index: Word, parameter_type: 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); + _parameter_info_set_variable_type(result, parameter_type); + + 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 - Local variable index. + * temporary_type - Local variable type. + *) +proc _temporary_info_create(temporary_index: Word, temporary_type: 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); + _parameter_info_set_variable_type(result, temporary_type); + + 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; + variable_type: Word; +begin + name_position := _declaration_get_name(parser_node); + name_length := _declaration_get_length(parser_node); + + variable_type := _variable_declaration_get__type(parser_node); + variable_type := elna_name_type_expression(variable_type); + + info := _parameter_info_create(parameter_index, variable_type); + _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; + variable_type: Word; +begin + name_position := _declaration_get_name(parser_node); + name_length := _declaration_get_length(parser_node); + + variable_type := _variable_declaration_get__type(parser_node); + variable_type := elna_name_type_expression(variable_type); + + info := _temporary_info_create(variable_index, variable_type); + _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()); + + ElnaTreeNode_set_kind(result, ElnaTreeKind.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()); + + ElnaTreeNode_set_kind(result, ElnaTreeKind.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); + type_info := _type_info_create(type_info); + + _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()); + + ElnaTreeNode_set_kind(result, ElnaTreeKind.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 := ElnaTreeNamedTypeExpression_get_name(variable_type); + name_length := ElnaTreeNamedTypeExpression_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 := ElnaType_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 := ElnaTypeRecord_get_length(type_representation); + field_pointer := ElnaTypeRecord_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 := ElnaType_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()); + + ElnaTreeNode_set_kind(result, ElnaTreeKind.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_conditional_statements(parser_node: Word, symbol_table: Word); +var + condition: Word; + statements: Word; +begin + .elna_type_conditional_statements_loop; + condition := _conditional_statements_get_condition(parser_node); + elna_type_binary_expression(condition, symbol_table); + + statements := _conditional_statements_get_statements(parser_node); + elna_type_statements(statements, symbol_table); + + parser_node := _conditional_statements_get_next(parser_node); + if parser_node <> nil then + goto elna_type_conditional_statements_loop + end +end; + +proc elna_type_if_statement(parser_node: Word, symbol_table: Word); +var + block: Word; +begin + block := _if_statement_get_conditionals(parser_node); + + .elna_type_if_statement_conditionals; + elna_type_conditional_statements(block, symbol_table); + + block := _conditional_statements_get_next(block); + if block <> nil then + goto elna_type_if_statement_conditionals + end; + block := _if_statement_get__else(parser_node); + if block <> nil then + elna_type_statements(block, symbol_table) + end +end; + +proc elna_type_return_statement(parser_node: Word, symbol_table: Word); +var + returned_expression: Word; +begin + returned_expression := _return_statement_get_returned(parser_node); + elna_type_binary_expression(returned_expression, symbol_table) +end; + +proc elna_type_call(parser_node: Word, symbol_table: Word); +var + current_argument: Word; + argument_tree: Word; +begin + current_argument := 1; + + .elna_type_call_argument; + argument_tree := _call_get_argument(parser_node, current_argument); + + if argument_tree <> nil then + elna_type_binary_expression(argument_tree, symbol_table); + current_argument := current_argument + 1; + + goto elna_type_call_argument + end +end; + +proc elna_type_assign_statement(parser_node: Word, symbol_table: Word); +var + lhs: Word; + rhs: Word; +begin + lhs := _assign_statement_get_assignee(parser_node); + rhs := _assign_statement_get_assignment(parser_node); + + elna_type_designator(lhs, symbol_table); + elna_type_binary_expression(rhs, symbol_table) +end; + +proc elna_type_statement(parser_node: Word, symbol_table: Word); +var + statement_kind: Word; +begin + statement_kind := ElnaTreeNode_get_kind(parser_node); + + (* Skipping goto and label declarations. *) + if statement_kind = ElnaTreeKind.if_statement then + elna_type_if_statement(parser_node, symbol_table) + elsif statement_kind = ElnaTreeKind.return_statement then + elna_type_return_statement(parser_node, symbol_table) + elsif statement_kind = ElnaTreeKind.call then + elna_type_call(parser_node, symbol_table) + elsif statement_kind = ElnaTreeKind.assign_statement then + elna_type_assign_statement(parser_node, symbol_table) + end +end; + +proc elna_type_statements(parser_node: Word, symbol_table: Word); +begin + .elna_type_statements_loop; + if parser_node <> 0 then + elna_type_statement(parser_node, symbol_table); + + parser_node := _statement_get_next(parser_node); + goto elna_type_statements_loop + end +end; + +proc elna_type_character_literal(parser_node: Word); +var + symbol_info: Word; + symbol_type: Word; +begin + symbol_info := _symbol_table_lookup(@symbol_table_global, "Word", 4); + symbol_type := _type_info_get__type(symbol_info); + + ElnaTreeExpression_set_type_decoration(parser_node, symbol_type) +end; + +proc elna_type_integer_literal(parser_node: Word); +var + symbol_info: Word; + symbol_type: Word; +begin + symbol_info := _symbol_table_lookup(@symbol_table_global, "Word", 4); + symbol_type := _type_info_get__type(symbol_info); + + ElnaTreeExpression_set_type_decoration(parser_node, symbol_type) +end; + +proc elna_type_string_literal(parser_node: Word); +var + symbol_info: Word; + symbol_type: Word; +begin + symbol_info := _symbol_table_lookup(@symbol_table_global, "Word", 4); + symbol_type := _type_info_get__type(symbol_info); + + ElnaTreeExpression_set_type_decoration(parser_node, symbol_type) +end; + +proc elna_type_nil_literal(parser_node: Word); +var + symbol_info: Word; + symbol_type: Word; +begin + symbol_info := _symbol_table_lookup(@symbol_table_global, "Word", 4); + symbol_type := _type_info_get__type(symbol_info); + + ElnaTreeExpression_set_type_decoration(parser_node, symbol_type) +end; + +proc elna_type_variable_expression(parser_node: Word, symbol_table: Word); +var + variable_info: Word; + name_pointer: Word; + name_length: Word; + variable_kind: Word; + variable_type: Word; +begin + name_pointer := ElnaTreeVariableExpression_get_name(parser_node); + name_length := ElnaTreeVariableExpression_get_length(parser_node); + + variable_info := _symbol_table_lookup(symbol_table, name_pointer, name_length); + + if variable_info = nil then + variable_info := _symbol_table_lookup(@symbol_table_global, name_pointer, name_length) + end; + variable_kind := _info_get_kind(variable_info); + variable_type := nil; + + if variable_kind = InfoKind.parameter_info then + variable_type := _parameter_info_get_variable_type(variable_info) + elsif variable_kind = InfoKind.temporary_info then + variable_type := _temporary_info_get_variable_type(variable_info) + end; + ElnaTreeExpression_set_type_decoration(parser_node, variable_type) +end; + +proc elna_type_simple_expression(parser_node: Word, symbol_table: Word); +var + expression_kind: Word; +begin + expression_kind := ElnaTreeNode_get_kind(parser_node); + + if expression_kind = ElnaTreeKind.integer_literal then + elna_type_integer_literal(parser_node) + elsif expression_kind = ElnaTreeKind.character_literal then + elna_type_character_literal(parser_node) + elsif expression_kind = ElnaTreeKind.string_literal then + elna_type_string_literal(parser_node) + elsif expression_kind = ElnaTreeKind.null then + elna_type_nil_literal(parser_node) + elsif expression_kind = ElnaTreeKind.variable_expression then + elna_type_variable_expression(parser_node, symbol_table) + end +end; + +proc elna_type_designator(parser_node: Word, symbol_table: Word); +var + expression_kind: Word; + designator_base: Word; + base_type: Word; + type_kind: Word; + name_pointer: Word; + name_length: Word; + symbol_info: Word; +begin + expression_kind := ElnaTreeNode_get_kind(parser_node); + + if expression_kind = ElnaTreeKind.dereference_expression then + designator_base := ElnaTreeDereferenceExpression_get_pointer(parser_node); + elna_type_simple_expression(designator_base, symbol_table); + + base_type := ElnaTreeExpression_get_type_decoration(designator_base); + type_kind := ElnaType_get_kind(base_type); + + (* If check for compatibility, should be removed later. *) + if type_kind = TypeKind.pointer then + base_type := ElnaTypePointer_get_base(base_type) + end; + + ElnaTreeExpression_set_type_decoration(parser_node, base_type) + elsif expression_kind = ElnaTreeKind.field_access_expression then + base_type := nil; + designator_base := ElnaTreeFieldAccessExpression_get_aggregate(parser_node); + + (* Check whether the field access is an enumeration value. *) + if ElnaTreeNode_get_kind(designator_base) = ElnaTreeKind.variable_expression then + name_pointer := ElnaTreeVariableExpression_get_name(designator_base); + name_length := ElnaTreeVariableExpression_get_length(designator_base); + + symbol_info := _symbol_table_lookup(@symbol_table_global, name_pointer, name_length); + if symbol_info <> nil then + type_kind := _info_get_kind(symbol_info); + + if type_kind = InfoKind.type_info then + base_type := _type_info_get__type(symbol_info) + end + end + end; + (* If the base_type is still nil this is record field access. *) + if base_type = nil then + (* elna_type_simple_expression(designator_base, symbol_table); + base_type := ElnaTreeExpression_get_type_decoration(designator_base); *) + else + end; + + (* Change my type. *) + ElnaTreeExpression_set_type_decoration(parser_node, base_type) + elsif expression_kind = ElnaTreeKind.call then + elna_type_call(parser_node, symbol_table) + else + elna_type_simple_expression(parser_node, symbol_table) + end +end; + +proc elna_type_unary_expression(parser_node: Word, symbol_table: Word); +var + expression_kind: Word; + unary_operand: Word; + operand_type: Word; +begin + expression_kind := ElnaTreeNode_get_kind(parser_node); + + if expression_kind = ElnaTreeKind.unary_expression then + unary_operand := ElnaTreeUnaryExpression_get_operand(parser_node); + elna_type_designator(unary_operand, symbol_table); + + operand_type := ElnaTreeExpression_get_type_decoration(unary_operand); + ElnaTreeExpression_set_type_decoration(parser_node, operand_type) + else + elna_type_designator(parser_node, symbol_table) + end +end; + +proc elna_type_binary_expression(parser_node: Word, symbol_table: Word); +var + expression_kind: Word; + binary_operand: Word; + operand_type: Word; +begin + expression_kind := ElnaTreeNode_get_kind(parser_node); + + if expression_kind = ElnaTreeKind.binary_expression then + binary_operand := ElnaTreeBinaryExpression_get_rhs(parser_node); + elna_type_unary_expression(binary_operand, symbol_table); + + binary_operand := ElnaTreeBinaryExpression_get_lhs(parser_node); + elna_type_unary_expression(binary_operand, symbol_table); + + operand_type := ElnaTreeExpression_get_type_decoration(binary_operand); + + ElnaTreeExpression_set_type_decoration(parser_node, operand_type) + else + elna_type_unary_expression(parser_node, symbol_table) + end +end; + +proc elna_type_procedure_declaration(parser_node: Word); +var + body: Word; + name_pointer: Word; + name_length: Word; + procedure_info: Word; + symbol_table: Word; +begin + body := _procedure_declaration_get_body(parser_node); + name_pointer := _procedure_declaration_get_name(parser_node); + name_length := _procedure_declaration_get_length(parser_node); + + procedure_info := _symbol_table_lookup(@symbol_table_global, name_pointer, name_length); + symbol_table := _procedure_info_get_symbol_table(procedure_info); + + elna_type_statements(body, symbol_table) +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 <> nil 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_globals(parser_node); + .elna_name_module_declaration_global; + if current_part <> nil then + elna_name_procedure_temporary(current_part, 0, @symbol_table_global); + current_part := _declaration_get_next(current_part); + + goto elna_name_module_declaration_global + end; + current_part := _module_declaration_get_procedures(parser_node); + .elna_name_module_declaration_procedure; + if current_part <> nil 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(ElnaType_size()); + ElnaType_set_kind(current_type, TypeKind.primitive); + ElnaType_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; + +proc f(x: ElnaTreeExpression); +var + y: Word; +begin + y := x.kind +end; diff --git a/boot/stage17/linker.arg b/boot/stage17/linker.arg new file mode 100644 index 0000000..e69de29