(* * 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 19 compiler. *) type (** * List of intermediate representation items. *) ElnaInstructionList = record next: Word end; ElnaInstructionModule = record data: Word; code: Word end; ElnaInstructionDeclaration = record next: Word; name: Word; length: Word; body: Word; stack: Word; parameters: Word; count: Word end; (* Type representation. *) ElnaTypeKind = (primitive, enumeration, _record, pointer, array); ElnaType = record kind: ElnaTypeKind; size: Word end; ElnaTypeEnumeration = record kind: ElnaTypeKind; size: Word; members: Word; length: Word end; ElnaTypeField = record name: ElnaTypeKind; length: Word; field_type: ^ElnaType end; ElnaTypeRecord = record kind: ElnaTypeKind; size: Word; members: Word; length: Word end; ElnaTypePointer = record kind: ElnaTypeKind; size: Word; base: Word end; ElnaTypeArray = record kind: ElnaTypeKind; size: Word; base: Word; length: Word end; (* Abstract syntax tree nodes. *) ElnaTreeKind = ( integer_literal, string_literal, character_literal, boolean_literal, variable_expression, field_access_expression, dereference_expression, unary_expression, binary_expression, call, goto_statement, label_declaration, return_statement, assign_statement, if_statement, procedure_declaration, variable_declaration, enumeration_type_expression, named_type_expression, type_declaration, module_declaration, record_type_expression, pointer_type_expression, array_type_expression, null, trait_expression, array_access_expression ); ElnaTreeNode = record kind: ElnaTreeKind end; ElnaTreeExpression = record kind: ElnaTreeKind; type_decoration: ^ElnaType end; ElnaTreeIntegerLiteral = record kind: ElnaTreeKind; type_decoration: ^ElnaType; value: Word end; ElnaTreeCharacterLiteral = record kind: ElnaTreeKind; type_decoration: ^ElnaType; value: Word; length: Word end; ElnaTreeNilLiteral = record kind: ElnaTreeKind; type_decoration: ^ElnaType end; ElnaTreeBooleanLiteral = record kind: ElnaTreeKind; type_decoration: ^ElnaType; value: Word end; ElnaTreeVariableExpression = record kind: ElnaTreeKind; type_decoration: ^ElnaType; name: Word; length: Word end; ElnaTreeStringLiteral = record kind: ElnaTreeKind; type_decoration: ^ElnaType; value: Word; length: Word end; ElnaTreeDereferenceExpression = record kind: ElnaTreeKind; type_decoration: ^ElnaType; pointer: Word end; ElnaTreeBinaryExpression = record kind: ElnaTreeKind; type_decoration: ^ElnaType; lhs: Word; rhs: Word; operator: Word end; ElnaTreeUnaryExpression = record kind: ElnaTreeKind; type_decoration: ^ElnaType; operand: Word; operator: Word end; (** * All statements are chained into a list. Next contains a pointer to the next * statement in the statement list. *) ElnaTreeStatement = record kind: ElnaTreeKind; next: Word end; ElnaTreeIfStatement = record kind: ElnaTreeKind; next: Word; conditionals: Word; _else: ^ElnaTreeStatement end; ElnaTreeGotoStatement = record kind: ElnaTreeKind; next: Word; label: Word; length: Word end; ElnaTreeAssignStatement = record kind: ElnaTreeKind; next: Word; assignee: Word; assignment: ^ElnaTreeExpression end; ElnaTreeReturnStatement = record kind: ElnaTreeKind; next: Word; returned: ^ElnaTreeExpression end; ElnaTreeLabelDeclaration = record kind: ElnaTreeKind; next: Word; label: Word; length: Word end; ElnaTreeFieldAccessExpression = record kind: ElnaTreeKind; type_decoration: ^ElnaType; aggregate: Word; field: Word; length: Word end; ElnaTreeArrayAccessExpression = record kind: ElnaTreeKind; type_decoration: ^ElnaType; array: Word; index: Word end; ElnaTreeEnumerationTypeExpression = record kind: ElnaTreeKind; members: Word; length: Word end; ElnaTreeRecordTypeExpression = record kind: ElnaTreeKind; members: Word; length: Word end; ElnaTreeNamedTypeExpression = record kind: ElnaTreeKind; name: Word; length: Word end; ElnaTreePointerTypeExpression = record kind: ElnaTreeKind; base: ^ElnaTreeNode end; ElnaTreeArrayTypeExpression = record kind: ElnaTreeKind; base: ^ElnaTreeNode; length: Word end; ElnaTreeTraitExpression = record kind: ElnaTreeKind; name: Word; length: Word; argument: Word end; (** * Conditional statements is a list of pairs: condition and statements. * Used for example to represent if and elsif blocks with beloning statements. *) ElnaTreeConditionalStatements = record condition: ^ElnaTreeExpression; statements: ^ElnaTreeStatement; next: Word end; ElnaTreeDeclaration = record kind: ElnaTreeKind; next: Word; name: Word; length: Word end; ElnaTreeModuleDeclaration = record kind: ElnaTreeKind; types: Word; globals: Word; procedures: Word end; ElnaTreeProcedureDeclaration = record kind: ElnaTreeKind; next: Word; name: Word; length: Word; body: Word; temporaries: Word; parameters: Word end; ElnaTreeTypeDeclaration = record kind: ElnaTreeKind; next: Word; name: Word; length: Word; _type: Word end; ElnaTreeVariableDeclaration = record kind: ElnaTreeKind; next: Word; name: Word; length: Word; _type: Word end; ElnaTreeExpressionList = record expression: ^ElnaTreeBinaryExpression; next: Word end; ElnaTreeCall = record kind: ElnaTreeKind; next: Word; callee: ^ElnaTreeVariableExpression; arguments: ^ElnaTreeExpressionList; count: Word end; (* Symbol table information. *) ElnaSymbolInfoKind = (type_info, parameter_info, temporary_info, procedure_info); ElnaSymbolInfo = record kind: ElnaSymbolInfoKind end; ElnaSymbolTypeInfo = record kind: ElnaSymbolInfoKind; _type: ^ElnaType end; ElnaSymbolTemporaryInfo = record kind: ElnaSymbolInfoKind; offset: Word; variable_type: ^ElnaType end; ElnaSymbolProcedureInfo = record kind: ElnaSymbolInfoKind; symbol_table: Word end; ElnaErrorList = record first: Word; last: Word end; ElnaError = record next: 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, number_sign ); ElnaLexerState = ( start, colon, identifier, decimal, leading_zero, greater, minus, left_paren, less, dot, comment, closing_comment, character, character_escape, string, string_escape, number_sign, trait, finish ); ElnaLexerKind = ( identifier, _const, _var, _proc, _type, _begin, _end, _if, _then, _else, _elsif, _while, _do, _extern, _record, boolean, null, and, _or, _xor, pipe, not, _return, _module, _program, _import, _cast, _defer, _case, _of, trait, left_paren, right_paren, left_square, right_square, shift_left, shift_right, greater_equal, less_equal, greater_than, less_than, not_equal, equals, semicolon, dot, comma, plus, arrow, minus, multiplication, division, remainder, assignment, colon, hat, at, comment, string, character, integer, word, _goto, eof ); ElnaLexerCursor = record state: ElnaLexerState; start: Word; finish: Word end; 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 ); ElnaTacKind = (list, immediate, symbol, pseudo); ElnaTacOperand = record kind: ElnaTacKind; value: Word; length: Word end; ElnaTacInstruction = record next: Word; operator: ElnaTacOperator; operands: [3]ElnaTacOperand end; ElnaTacProcedure = record next: Word; name: Word; length: Word; body: Word; stack: Word; parameters: Word; count: Word end; ElnaTacStaticVariable = record next: Word; name: Word; length: Word; body: Word end; 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 ); ElnaRtlKind = (register, immediate, symbol, pseudo, offset, pseudo_mem); ElnaRtlOperand = record kind: ElnaRtlKind; value: Word; length: Word end; ElnaRtlInstruction = record next: Word; operator: ElnaRtlOperator; operands: [3]ElnaRtlOperand end; ElnaRtlRegister = ( zero, ra, sp, gp, tp, t0, t1, t2, s0, s1, a0, a1, a2, a3, a4, a5, a6, a7, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, t3, t4, t5, t6 ); ElnaRtlInfo = record counter: Word end; var symbol_table_global: [1024]Word; variable_map: [1024]Word; compiler_strings: [1024]Word; classification: [256]Word; (** * Lexer state is saved after the transition tables. * Each transition table entry is 8 bytes long. The table has 19 rows (transition states) * and 23 columns (character classes), so 3496 = 8 * 19 * 23. *) transition_table: [874]Word; lexer_state: ElnaLexerCursor; source_code: Word; compiler_strings_position: Word; compiler_strings_length: Word; label_counter: Word; symbol_table_store: Word; temporary_variable_counter: Word; pseudo_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; (** * Writes to the standard output. * * Parameters: * buffer - Buffer. * size - Buffer length. *) proc _write_s(buffer: Word, size: Word); begin write(1, buffer, size) end; (** * Writes a number to the standard output. * * Parameters: * number - Whole number. *) proc _write_i(number: Word); begin printf("%i\0", number); fflush(nil) end; (** * Writes a character from a0 into the standard output. * * Parameters: * character - Character to write. *) proc _write_c(character: Word); begin write(1, @character, 1) end; (** * Write null terminated string. * * Parameters: * string - String. *) proc _write_z(string: Word); begin printf("%s\0", string); fflush(nil) 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_underscore1: Word; begin is_underscore1 := character = '_'; return isalpha(character) or is_underscore1 end; proc _is_alnum(character: Word); return _is_alpha(character) or isdigit(character) end; proc elna_tac_generate_pseudo(operand_type: Word, operand_value: Word, operand_length: Word); var buffer: Word; begin pseudo_counter := pseudo_counter + 1; buffer := malloc(6); sprintf(buffer, "$%i\0", pseudo_counter); operand_type^ := ElnaTacKind.pseudo; operand_value^ := buffer; operand_length^ := strlen(buffer); end; proc elna_instruction_list_concatenate(this: ^ElnaInstructionList, 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_set_operand(this: ^ElnaTacInstruction, n: Word, operand_type: Word, operand_value: Word, operand_length: Word); begin this^.operands[n].kind := operand_type; this^.operands[n].value := operand_value; this^.operands[n].length := operand_length end; proc elna_tac_instruction_create(kind: Word); var result: ^ElnaTacInstruction; begin result := malloc(#size(ElnaTacInstruction)); result^.operator := kind; elna_instruction_list_concatenate(result, nil); return result end; proc elna_rtl_instruction_create(kind: Word); var result: ^ElnaRtlInstruction; begin result := malloc(#size(ElnaRtlInstruction)); result^.operator := kind; result^.next := nil; return result end; proc elna_rtl_instruction_set_operand(this: ^ElnaRtlInstruction, n: Word, operand_type: Word, operand_value: Word, operand_length: Word); begin this^.operands[n].kind := operand_type; this^.operands[n].value := operand_value; this^.operands[n].length := operand_length end; proc elna_rtl_load_operand_value(tac_instruction: ^ElnaTacInstruction, operand_number: Word, into: Word); var result: ^ElnaRtlInstruction; operand_value: Word; operand_length: Word; operand_type: Word; next_instruction: Word; begin operand_type := tac_instruction^.operands[operand_number].kind; operand_value := tac_instruction^.operands[operand_number].value; operand_length := tac_instruction^.operands[operand_number].length; if operand_type = ElnaTacKind.immediate then result := elna_rtl_instruction_create(ElnaRtlOperator.li); elna_rtl_instruction_set_operand(result, 1, ElnaRtlKind.register, into, 0); elna_rtl_instruction_set_operand(result, 2, ElnaRtlKind.immediate, operand_value, operand_length) elsif operand_type = ElnaTacKind.symbol then result := elna_rtl_instruction_create(ElnaRtlOperator.la); elna_rtl_instruction_set_operand(result, 1, ElnaRtlKind.register, into, 0); elna_rtl_instruction_set_operand(result, 2, ElnaRtlKind.symbol, operand_value, operand_length); next_instruction := elna_rtl_instruction_create(ElnaRtlOperator.lw); elna_rtl_instruction_set_operand(next_instruction, 1, ElnaRtlKind.register, into, 0); elna_rtl_instruction_set_operand(next_instruction, 2, ElnaRtlKind.offset, into, 0); result^.next := next_instruction elsif operand_type = ElnaTacKind.pseudo then result := elna_rtl_instruction_create(ElnaRtlOperator.move); elna_rtl_instruction_set_operand(result, 1, ElnaRtlKind.register, into, 0); elna_rtl_instruction_set_operand(result, 2, ElnaRtlKind.pseudo, operand_value, operand_length) end; return result end; proc elna_rtl_load_operand_address(tac_instruction: ^ElnaTacInstruction, operand_number: Word, into: Word); var result: Word; operand_value: Word; operand_length: Word; operand_type: Word; begin operand_type := tac_instruction^.operands[operand_number].kind; operand_value := tac_instruction^.operands[operand_number].value; operand_length := tac_instruction^.operands[operand_number].length; if operand_type = ElnaTacKind.symbol then result := elna_rtl_instruction_create(ElnaRtlOperator.la); elna_rtl_instruction_set_operand(result, 1, ElnaRtlKind.register, into, 0); elna_rtl_instruction_set_operand(result, 2, ElnaRtlKind.symbol, operand_value, operand_length) elsif operand_type = ElnaTacKind.pseudo then result := elna_rtl_instruction_create(ElnaRtlOperator.la); elna_rtl_instruction_set_operand(result, 1, ElnaRtlKind.register, into, 0); elna_rtl_instruction_set_operand(result, 2, ElnaRtlKind.pseudo, operand_value, operand_length) end; return result end; proc elna_rtl_binary_operands(tac_instruction: ^ElnaTacInstruction, next_instruction: ^^ElnaRtlInstruction); var lhs: ^ElnaRtlInstruction; rhs: ^ElnaRtlInstruction; 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^ := lhs^.next; if next_instruction^ = 0 then lhs^.next := rhs else next_instruction^^ := rhs end; next_instruction^ := rhs^.next; if next_instruction^ = 0 then next_instruction^ := rhs end; return lhs end; proc elna_rtl_binary_arithmetic(tac_instruction: ^ElnaTacInstruction, next_instruction: ^^ElnaRtlInstruction, operation: Word); var lhs: Word; binary_result: ^ElnaRtlInstruction; intermediate_instruction: ^ElnaRtlInstruction; 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, ElnaRtlKind.register, ElnaRtlRegister.t2, 0); elna_rtl_instruction_set_operand(binary_result, 3, ElnaRtlKind.register, ElnaRtlRegister.t3, 0); binary_result^.operator := operation; intermediate_instruction := next_instruction^; intermediate_instruction^.next := binary_result; next_instruction^ := binary_result; return lhs end; proc elna_rtl_binary_equality(tac_instruction: ^ElnaTacInstruction, instruction_kind: Word, next_instruction: ^^ElnaRtlInstruction); var operands: ^ElnaRtlInstruction; intermediate_instruction: ^ElnaRtlInstruction; operating_instruction: ^ElnaRtlInstruction; binary_result: ^ElnaRtlInstruction; begin operands := elna_rtl_binary_operands(tac_instruction, next_instruction); operating_instruction := elna_rtl_instruction_create(ElnaRtlOperator._xor); elna_rtl_instruction_set_operand(operating_instruction, 1, ElnaRtlKind.register, ElnaRtlRegister.t2, 0); elna_rtl_instruction_set_operand(operating_instruction, 2, ElnaRtlKind.register, ElnaRtlRegister.t2, 0); elna_rtl_instruction_set_operand(operating_instruction, 3, ElnaRtlKind.register, ElnaRtlRegister.t3, 0); intermediate_instruction := next_instruction^; intermediate_instruction^.next := operating_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, ElnaRtlKind.register, ElnaRtlRegister.t2, 0); intermediate_instruction := next_instruction^; operating_instruction^.next := binary_result; next_instruction^ := binary_result; return operands end; proc elna_rtl_copy_operand(tac_instruction: ^ElnaTacInstruction, number: Word, rtl_instruction: Word); var operand_length: Word; operand_type: Word; operand_value: Word; begin operand_type := tac_instruction^.operands[number].kind; operand_value := tac_instruction^.operands[number].value; operand_length := tac_instruction^.operands[number].length; if operand_type = ElnaTacKind.immediate then elna_rtl_instruction_set_operand(rtl_instruction, number, ElnaRtlKind.immediate, operand_value, operand_length) elsif operand_type = ElnaTacKind.symbol then elna_rtl_instruction_set_operand(rtl_instruction, number, ElnaRtlKind.symbol, operand_value, operand_length) elsif operand_type = ElnaTacKind.pseudo then elna_rtl_instruction_set_operand(rtl_instruction, number, ElnaRtlKind.pseudo, operand_value, operand_length) end 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: ^ElnaRtlInstruction; 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, ElnaRtlKind.register, ElnaRtlRegister.t0); result^.next := next_instruction^; return result end; proc elna_rtl_call(tac_instruction: ^ElnaTacInstruction, next_instruction: ^^ElnaRtlInstruction); var argument_count: Word; current_argument: ^ElnaTacOperand; argument_type: Word; argument_value: Word; argument_length: Word; argument_move: ^ElnaRtlInstruction; current_register: Word; first_instruction: Word; current_instruction: Word; begin current_register := 0; first_instruction := nil; current_argument := tac_instruction^.operands[2].value; argument_count := tac_instruction^.operands[2].length; .elna_rtl_call_loop; if argument_count > 0 then argument_count := argument_count - 1; argument_type := current_argument^.kind; argument_value := current_argument^.value; argument_length := current_argument^.length; current_argument := current_argument + #size(ElnaTacOperand); argument_move := elna_rtl_instruction_create(ElnaRtlOperator.move); elna_rtl_instruction_set_operand(argument_move, 1, ElnaRtlKind.register, ElnaRtlRegister.a0 + current_register, 0); elna_rtl_instruction_set_operand(argument_move, 2, argument_type, argument_value, argument_length); if first_instruction = nil then first_instruction := argument_move else elna_instruction_list_concatenate(current_instruction, argument_move) end; current_instruction := argument_move; current_register := current_register + 1; goto elna_rtl_call_loop end; argument_move := elna_rtl_instruction_create(ElnaRtlOperator.jal); elna_rtl_copy_operand(tac_instruction, 1, argument_move); if first_instruction = nil then first_instruction := argument_move else elna_instruction_list_concatenate(current_instruction, argument_move) end; current_instruction := argument_move; argument_move := elna_rtl_instruction_create(ElnaRtlOperator.move); elna_rtl_instruction_set_operand(argument_move, 1, tac_instruction^.operands[3].kind, tac_instruction^.operands[3].value, tac_instruction^.operands[3].length); elna_rtl_instruction_set_operand(argument_move, 2, ElnaRtlKind.register, ElnaRtlRegister.a0, 0); elna_instruction_list_concatenate(current_instruction, argument_move); next_instruction^ := argument_move; return first_instruction end; proc elna_rtl_store(tac_instruction: ^ElnaTacInstruction, next_instruction: ^^ElnaRtlInstruction); var result: ^ElnaRtlInstruction; operands: ^ElnaRtlInstruction; begin operands := elna_rtl_load_operand_value(tac_instruction, 1, ElnaRtlRegister.t0); next_instruction^ := elna_rtl_instruction_create(ElnaRtlOperator.sw); result := operands; operands := result^.next; elna_rtl_instruction_set_operand(next_instruction^, 1, ElnaRtlKind.register, ElnaRtlRegister.t0, 0); if tac_instruction^.operands[2].kind = ElnaTacKind.pseudo then elna_rtl_instruction_set_operand(next_instruction^, 2, ElnaRtlKind.pseudo, tac_instruction^.operands[2].value, tac_instruction^.operands[2].length) end; if operands = nil then result^.next := next_instruction^ else operands^.next := next_instruction^ end; return result end; proc elna_rtl_load(tac_instruction: ^ElnaTacInstruction, next_instruction: ^^ElnaRtlInstruction); var result: ^ElnaRtlInstruction; operands: ^ElnaRtlInstruction; begin operands := elna_rtl_load_operand_value(tac_instruction, 1, ElnaRtlRegister.t0); next_instruction^ := elna_rtl_instruction_create(ElnaRtlOperator.lw); result := operands; operands := result^.next; elna_rtl_instruction_set_operand(next_instruction^, 2, ElnaRtlKind.offset, ElnaRtlRegister.t0, 0); elna_rtl_instruction_set_operand(next_instruction^, 1, tac_instruction^.operands[2].kind, tac_instruction^.operands[2].value, tac_instruction^.operands[2].length); if operands = 0 then result^.next := next_instruction^ else operands^.next := next_instruction^ end; return result end; proc elna_rtl_instruction(tac_instruction: ^ElnaTacInstruction, next_instruction: ^^ElnaRtlInstruction); var result: ^ElnaRtlInstruction; operands: ^ElnaRtlInstruction; intermediate_instruction: ^ElnaRtlInstruction; begin result := malloc(#size(ElnaRtlInstruction)); next_instruction^ := nil; if tac_instruction^.operator = ElnaTacOperator.get_address 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 tac_instruction^.operator = ElnaTacOperator.add then result := elna_rtl_binary_arithmetic(tac_instruction, next_instruction, ElnaRtlOperator.add) elsif tac_instruction^.operator = ElnaTacOperator.load then result := elna_rtl_load(tac_instruction, next_instruction) elsif tac_instruction^.operator = ElnaTacOperator.store then result := elna_rtl_store(tac_instruction, next_instruction) elsif tac_instruction^.operator = ElnaTacOperator.proc_call then result := elna_rtl_call(tac_instruction, next_instruction) elsif tac_instruction^.operator = ElnaTacOperator.subtract then result := elna_rtl_binary_arithmetic(tac_instruction, next_instruction, ElnaRtlOperator.sub) elsif tac_instruction^.operator = ElnaTacOperator.multiply then result := elna_rtl_binary_arithmetic(tac_instruction, next_instruction, ElnaRtlOperator.mul) elsif tac_instruction^.operator = ElnaTacOperator.divide then result := elna_rtl_binary_arithmetic(tac_instruction, next_instruction, ElnaRtlOperator.div) elsif tac_instruction^.operator = ElnaTacOperator.remainder then result := elna_rtl_binary_arithmetic(tac_instruction, next_instruction, ElnaRtlOperator.rem) elsif tac_instruction^.operator = ElnaTacOperator._xor then result := elna_rtl_binary_arithmetic(tac_instruction, next_instruction, ElnaRtlOperator._xor) elsif tac_instruction^.operator = ElnaTacOperator._or then result := elna_rtl_binary_arithmetic(tac_instruction, next_instruction, ElnaRtlOperator._or) elsif tac_instruction^.operator = ElnaTacOperator.and then result := elna_rtl_binary_arithmetic(tac_instruction, next_instruction, ElnaRtlOperator.and) elsif tac_instruction^.operator = ElnaTacOperator.less_than then result := elna_rtl_binary_arithmetic(tac_instruction, next_instruction, ElnaRtlOperator.slt) elsif tac_instruction^.operator = 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, ElnaRtlKind.register, ElnaRtlRegister.t3, 0); elna_rtl_instruction_set_operand(result, 3, ElnaRtlKind.register, ElnaRtlRegister.t2, 0); intermediate_instruction := next_instruction^; intermediate_instruction^.next := result; next_instruction^ := result; result := operands; next_instruction^^.operator := ElnaRtlOperator.slt elsif tac_instruction^.operator = 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, ElnaRtlKind.register, ElnaRtlRegister.t2, 0); elna_rtl_instruction_set_operand(intermediate_instruction, 2, ElnaRtlKind.register, ElnaRtlRegister.t3, 0); elna_rtl_instruction_set_operand(intermediate_instruction, 3, ElnaRtlKind.register, ElnaRtlRegister.t2, 0); result := next_instruction^; result^.next := 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, ElnaRtlKind.register, ElnaRtlRegister.t2, 0); elna_rtl_instruction_set_operand(result, 3, ElnaRtlKind.immediate, 1, 0); intermediate_instruction := next_instruction^; intermediate_instruction^.next := result; next_instruction^ := result; result := operands elsif tac_instruction^.operator = 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, ElnaRtlKind.register, ElnaRtlRegister.t2, 0); elna_rtl_instruction_set_operand(intermediate_instruction, 2, ElnaRtlKind.register, ElnaRtlRegister.t2, 0); elna_rtl_instruction_set_operand(intermediate_instruction, 3, ElnaRtlKind.register, ElnaRtlRegister.t3, 0); result := next_instruction^; result^.next := 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, ElnaRtlKind.register, ElnaRtlRegister.t2, 0); elna_rtl_instruction_set_operand(result, 3, ElnaRtlKind.immediate, 1, 0); intermediate_instruction := next_instruction^; intermediate_instruction^.next := result; next_instruction^ := result; result := operands elsif tac_instruction^.operator = ElnaTacOperator.equal then result := elna_rtl_binary_equality(tac_instruction, ElnaRtlOperator.seqz, next_instruction) elsif tac_instruction^.operator = ElnaTacOperator.not_equal then result := elna_rtl_binary_equality(tac_instruction, ElnaRtlOperator.snez, next_instruction) elsif tac_instruction^.operator = ElnaTacOperator.negate then result := elna_rtl_unary(tac_instruction, ElnaRtlOperator.neg, next_instruction) elsif tac_instruction^.operator = ElnaTacOperator.complement then result := elna_rtl_unary(tac_instruction, ElnaRtlOperator.not, next_instruction) elsif tac_instruction^.operator = ElnaTacOperator.jump then result := elna_rtl_instruction_create(ElnaRtlOperator.j); elna_rtl_copy_operand(tac_instruction, 1, result) elsif tac_instruction^.operator = ElnaTacOperator.jump_if_zero then result := elna_rtl_conditional_jump(tac_instruction, ElnaRtlOperator.beqz) elsif tac_instruction^.operator = ElnaTacOperator.jump_if_not_zero then result := elna_rtl_conditional_jump(tac_instruction, ElnaRtlOperator.bnez) elsif tac_instruction^.operator = ElnaTacOperator._return then result := elna_rtl_load_operand_value(tac_instruction, 1, ElnaRtlRegister.a0) elsif tac_instruction^.operator = ElnaTacOperator.label then result := elna_rtl_instruction_create(ElnaRtlOperator.label); elna_rtl_copy_operand(tac_instruction, 1, result) elsif tac_instruction^.operator = ElnaTacOperator.copy then if tac_instruction^.operands[1].kind = ElnaTacKind.pseudo then operands := elna_rtl_load_operand_value(tac_instruction, 2, ElnaRtlRegister.t4); result := operands; operands := result^.next; next_instruction^ := elna_rtl_instruction_create(ElnaRtlOperator.move); elna_tac_instruction_set_operand(next_instruction^, 1, ElnaRtlKind.pseudo, tac_instruction^.operands[1].value, tac_instruction^.operands[1].length); elna_tac_instruction_set_operand(next_instruction^, 2, ElnaRtlKind.register, ElnaRtlRegister.t4, 0); if operands = nil then result^.next := next_instruction^ else operands^.next := next_instruction^ end end end; if next_instruction^ = nil then next_instruction^ := result; end; return result end; proc elna_instruction_module_create(data: Word, code: Word); var result: ^ElnaInstructionModule; begin result := malloc(#size(ElnaInstructionModule)); result^.data := data; result^.code := 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, ElnaTacKind.symbol, counter, length); return result end; proc elna_riscv_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) end; return argument_count end; proc elna_riscv_register(register: Word); begin printf("x%i\0", register - 1); fflush(nil) end; proc elna_riscv_operand(instruction: ^ElnaRtlInstruction, n: Word); var operand_type: Word; begin operand_type := instruction^.operands[n].kind; _write_c(' '); if operand_type = ElnaRtlKind.register then elna_riscv_register(instruction^.operands[n].value) elsif operand_type = ElnaRtlKind.offset then _write_i(instruction^.operands[n].length); _write_c('('); elna_riscv_register(instruction^.operands[n].value); _write_c(')') elsif operand_type = ElnaRtlKind.symbol then if instruction^.operands[n].length = 0 then _write_label(instruction^.operands[n].value, 0) else _write_s(instruction^.operands[n].value, instruction^.operands[n].length) end elsif instruction^.operands[n].length = 0 then (* ElnaRtlKind.immediate *) _write_i(instruction^.operands[n].value) else _write_s(instruction^.operands[n].value, instruction^.operands[n].length) end end; proc elna_alloc_variable(operand_value: Word, operand_length: Word); var pseudo_symbol: ^ElnaRtlInfo; begin pseudo_symbol := _symbol_table_lookup(@variable_map, operand_value, operand_length); if pseudo_symbol = nil then pseudo_symbol := malloc(#size(ElnaRtlInfo)); pseudo_symbol^.counter := temporary_variable_counter; _symbol_table_enter(@variable_map, operand_value, operand_length, pseudo_symbol); temporary_variable_counter := temporary_variable_counter + 4 end; return pseudo_symbol end; (** * If the first operand of an instruction is an operation target and expected * to be a register, but is not a register, then this procedure rewrites it * to a temporary register and preserves its value in the following instruction. *) proc elna_alloc_operation_target(instruction: ^ElnaRtlInstruction); var pseudo_symbol: ^ElnaRtlInfo; store_instruction: ^ElnaRtlInstruction; begin if instruction^.operands[1].kind = ElnaRtlKind.pseudo then store_instruction := elna_rtl_instruction_create(ElnaRtlOperator.sw); store_instruction^.next := instruction^.next; pseudo_symbol := elna_alloc_variable(instruction^.operands[1].value, instruction^.operands[1].length); elna_rtl_instruction_set_operand(store_instruction, 1, ElnaRtlKind.register, ElnaRtlRegister.t0, 0); elna_rtl_instruction_set_operand(store_instruction, 2, ElnaRtlKind.offset, ElnaRtlRegister.sp, pseudo_symbol^.counter); instruction^.next := store_instruction; elna_rtl_instruction_set_operand(instruction, 1, ElnaRtlKind.register, ElnaRtlRegister.t0, 0) end end; proc elna_alloc_load_address(instruction: ^ElnaRtlInstruction); var pseudo_symbol: ^ElnaRtlInfo; begin if instruction^.operands[2].kind = ElnaRtlKind.pseudo then pseudo_symbol := elna_alloc_variable(instruction^.operands[2].value, instruction^.operands[2].length); instruction^.operator := ElnaRtlOperator.addi; elna_rtl_instruction_set_operand(instruction, 2, ElnaRtlKind.register, ElnaRtlRegister.sp, 0); elna_rtl_instruction_set_operand(instruction, 3, ElnaRtlKind.immediate, pseudo_symbol^.counter, 0) end; if instruction^.operands[1].kind = ElnaRtlKind.pseudo then elna_alloc_operation_target(instruction) end end; proc elna_alloc_store(instruction: ^ElnaRtlInstruction); var old_instruction: ^ElnaRtlInstruction; pseudo_symbol: ^ElnaRtlInfo; begin if instruction^.operands[2].kind = ElnaRtlKind.pseudo then old_instruction := malloc(#size(ElnaRtlInstruction)); memcpy(old_instruction, instruction, #size(ElnaRtlInstruction)); pseudo_symbol := elna_alloc_variable(instruction^.operands[2].value, instruction^.operands[2].length); instruction^.operator := ElnaRtlOperator.lw; elna_rtl_instruction_set_operand(instruction, 1, ElnaRtlKind.register, ElnaRtlRegister.t1, 0); elna_rtl_instruction_set_operand(instruction, 2, ElnaRtlKind.offset, ElnaRtlRegister.sp, pseudo_symbol^.counter); instruction^.next := old_instruction; elna_rtl_instruction_set_operand(old_instruction, 2, ElnaRtlKind.offset, ElnaRtlRegister.t1, 0) end end; proc elna_alloc_load(instruction: ^ElnaRtlInstruction); var old_instruction: ^ElnaRtlInstruction; pseudo_symbol: ^ElnaRtlInfo; begin if instruction^.operands[1].kind = ElnaRtlKind.pseudo then old_instruction := malloc(#size(ElnaRtlInstruction)); memcpy(old_instruction, instruction, #size(ElnaRtlInstruction)); pseudo_symbol := elna_alloc_variable(instruction^.operands[1].value, instruction^.operands[1].length); old_instruction^.operator := ElnaRtlOperator.sw; elna_rtl_instruction_set_operand(old_instruction, 1, ElnaRtlKind.register, ElnaRtlRegister.t1, 0); elna_rtl_instruction_set_operand(old_instruction, 2, ElnaRtlKind.offset, ElnaRtlRegister.sp, pseudo_symbol^.counter); instruction^.next := old_instruction; elna_rtl_instruction_set_operand(instruction, 1, ElnaRtlKind.register, ElnaRtlRegister.t1, 0) end end; proc elna_alloc_instruction(instruction: ^ElnaRtlInstruction); var pseudo_symbol: ^ElnaRtlInfo; old_instruction: ^ElnaRtlInstruction; begin if instruction^.operator = ElnaRtlOperator.move then if instruction^.operands[1].kind = ElnaRtlKind.pseudo then pseudo_symbol := elna_alloc_variable(instruction^.operands[1].value, instruction^.operands[1].length); instruction^.operator = ElnaRtlOperator.sw; instruction^.operands[1].kind := instruction^.operands[2].kind; instruction^.operands[1].value := instruction^.operands[2].value; instruction^.operands[1].length := instruction^.operands[2].length; elna_rtl_instruction_set_operand(instruction, 2, ElnaRtlKind.offset, ElnaRtlRegister.sp, pseudo_symbol^.counter); goto elna_alloc_instruction_end end; if instruction^.operands[2].kind = ElnaRtlKind.pseudo then pseudo_symbol := elna_alloc_variable(instruction^.operands[2].value, instruction^.operands[2].length); instruction^.operator = ElnaRtlOperator.lw; elna_rtl_instruction_set_operand(instruction, 2, ElnaRtlKind.offset, ElnaRtlRegister.sp, pseudo_symbol^.counter); goto elna_alloc_instruction_end end elsif instruction^.operator = ElnaRtlOperator.la then elna_alloc_load_address(instruction) elsif instruction^.operator = ElnaRtlOperator.lw then elna_alloc_load(instruction) elsif instruction^.operator = ElnaRtlOperator.sw then elna_alloc_store(instruction) elsif instruction^.operator = ElnaRtlOperator._or then elna_alloc_operation_target(instruction) elsif instruction^.operator = ElnaRtlOperator.and then elna_alloc_operation_target(instruction) elsif instruction^.operator = ElnaRtlOperator.mul then elna_alloc_operation_target(instruction) elsif instruction^.operator = ElnaRtlOperator.sub then elna_alloc_operation_target(instruction) elsif instruction^.operator = ElnaRtlOperator.add then elna_alloc_operation_target(instruction) elsif instruction^.operator = ElnaRtlOperator._xor then elna_alloc_operation_target(instruction) elsif instruction^.operator = ElnaRtlOperator.rem then elna_alloc_operation_target(instruction) elsif instruction^.operator = ElnaRtlOperator.slt then elna_alloc_operation_target(instruction) elsif instruction^.operator = ElnaRtlOperator.beqz then if instruction^.operands[1].kind = ElnaRtlKind.pseudo then old_instruction := malloc(#size(ElnaRtlInstruction)); memcpy(old_instruction, instruction, #size(ElnaRtlInstruction)); pseudo_symbol := elna_alloc_variable(instruction^.operands[1].value, instruction^.operands[1].length); instruction^.operator := ElnaRtlOperator.lw; elna_rtl_instruction_set_operand(instruction, 1, ElnaRtlKind.register, ElnaRtlRegister.t0, 0); elna_rtl_instruction_set_operand(instruction, 2, ElnaRtlKind.offset, ElnaRtlRegister.sp, pseudo_symbol^.counter); instruction^.next := old_instruction; elna_rtl_instruction_set_operand(old_instruction, 1, ElnaRtlKind.register, ElnaRtlRegister.t0, 0); goto elna_alloc_instruction_end end elsif instruction^.operator = ElnaRtlOperator.seqz then elna_alloc_operation_target(instruction) elsif instruction^.operator = ElnaRtlOperator.snez then elna_alloc_operation_target(instruction) elsif instruction^.operator = ElnaRtlOperator.xori then elna_alloc_operation_target(instruction) end; .elna_alloc_instruction_end; end; proc elna_riscv_instruction(instruction: ^ElnaRtlInstruction); var argument_count: Word; current_argument: Word; operand_value: Word; begin argument_count := 0; if instruction^.operator = ElnaRtlOperator.label then _write_label(instruction^.operands[1].value, instruction^.operands[1].length); _write_c(':') elsif instruction^.operator = ElnaRtlOperator.allocate_stack then operand_value := instruction^.operands[1].value; (* Write the prologue. *) printf("\taddi sp, sp, -%i\n\tsw ra, %i(sp)\n\tsw s0, %i(sp)\n\taddi s0, sp, %i\n\0", operand_value + 8, operand_value + 4, operand_value, operand_value + 8); fflush(nil) elsif instruction^.operator = ElnaRtlOperator.ret then operand_value := instruction^.operands[1].value; (* Write the epilogue. *) printf("\tlw ra, %i(sp)\n\tlw s0, %i(sp)\n\taddi sp, sp, %i\n\0", operand_value + 4, operand_value, operand_value + 8); fflush(nil) else argument_count := elna_riscv_instruction_name(instruction^.operator) end; current_argument := 1; .elna_riscv_instruction_loop; if current_argument <= argument_count then elna_riscv_operand(instruction, current_argument); current_argument := current_argument + 1 end; if current_argument <= argument_count then _write_c(','); goto elna_riscv_instruction_loop end; _write_c('\n') end; proc elna_rtl_instructions(instruction: ^ElnaTacInstruction); var last_copy: ^ElnaRtlInstruction; current_copy: ^ElnaRtlInstruction; next_copy: ^ElnaRtlInstruction; first_copy: ^ElnaRtlInstruction; begin if instruction <> nil then first_copy := elna_rtl_instruction(instruction, @current_copy); instruction := instruction^.next else first_copy := nil; current_copy := nil end; .elna_rtl_instructions_start; if instruction <> nil then next_copy := elna_rtl_instruction(instruction, @last_copy); instruction := instruction^.next; current_copy^.next := next_copy; current_copy := last_copy; goto elna_rtl_instructions_start end; return first_copy end; proc elna_alloc_instructions(instruction: ^ElnaRtlInstruction); begin .elna_alloc_instructions_start; if instruction <> nil then elna_alloc_instruction(instruction); instruction := instruction^.next; goto elna_alloc_instructions_start end end; proc elna_riscv_instructions(instruction: ^ElnaRtlInstruction); begin .elna_riscv_instructions_start; if instruction <> nil then elna_riscv_instruction(instruction); instruction := instruction^.next; goto elna_riscv_instructions_start end end; proc elna_alloc_procedure(rtl_declaration: ^ElnaInstructionDeclaration); var stack_instruction: ^ElnaRtlInstruction; begin .elna_alloc_procedure_loop; temporary_variable_counter := 0; variable_map := 0; elna_alloc_instructions(rtl_declaration^.body); if rtl_declaration^.stack then stack_instruction := elna_rtl_instruction_create(ElnaRtlOperator.allocate_stack); stack_instruction^.next := rtl_declaration^.body; elna_rtl_instruction_set_operand(stack_instruction, 1, ElnaRtlKind.immediate, temporary_variable_counter, 0); rtl_declaration^.body := stack_instruction; stack_instruction := elna_rtl_instruction_create(ElnaRtlOperator.ret); elna_rtl_instruction_set_operand(stack_instruction, 1, ElnaRtlKind.immediate, temporary_variable_counter, 0); elna_instruction_list_concatenate(rtl_declaration^.body, stack_instruction) end; rtl_declaration := rtl_declaration^.next; if rtl_declaration <> nil then goto elna_alloc_procedure_loop end end; proc elna_riscv_procedure(procedure: ^ElnaInstructionDeclaration); begin .elna_riscv_procedure_loop; (* Write .type _procedure_name, @function. *) printf(".type %.*s, @function\n\0", procedure^.length, procedure^.name); (* Write procedure label, _procedure_name: *) printf("%.*s:\n\0", procedure^.length, procedure^.name); elna_riscv_instructions(procedure^.body); _write_z("\tret\n\0"); procedure := procedure^.next; if procedure <> nil then goto elna_riscv_procedure_loop end end; proc elna_riscv_variable(variable: ^ElnaInstructionDeclaration); begin .elna_riscv_variable_loop; if variable <> 0 then printf(".type %.*s, @object\n\0", variable^.length, variable^.name); printf("%.*s: .zero %i\n\0", variable^.length, variable^.name, variable^.body); variable := variable^.next; goto elna_riscv_variable_loop end end; proc elna_rtl_module_declaration(tac_module: ^ElnaInstructionModule); var code_part: ^ElnaInstructionDeclaration; data_part: ^ElnaInstructionDeclaration; begin data_part := elna_rtl_globals(tac_module^.data); code_part := elna_rtl_procedures(tac_module^.code); return elna_instruction_module_create(data_part, code_part) end; proc elna_alloc_module(pair: ^ElnaInstructionModule); begin elna_alloc_procedure(pair^.code) end; proc elna_riscv_module(pair: ^ElnaInstructionModule); var compiler_strings_copy: Word; compiler_strings_end: Word; current_byte: Word; begin _write_z(".globl main\n\n\0"); _write_z(".section .data\n\0"); elna_riscv_variable(pair^.data); _write_z(".section .text\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"); elna_riscv_procedure(pair^.code); _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_riscv_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_riscv_module_loop end; _write_c('"'); _write_c('\n'); end; proc elna_parser_integer_literal(); var integer_length: Word; result: ^ElnaTreeIntegerLiteral; buffer: Word; begin result := malloc(#size(ElnaTreeIntegerLiteral)); integer_length := lexer_state.finish - lexer_state.start; buffer := malloc(integer_length + 1); bzero(buffer, integer_length + 1); memcpy(buffer, lexer_state.start, integer_length); _elna_lexer_skip_token(); result^.kind := ElnaTreeKind.integer_literal; result^.value := atoi(buffer); result^.type_decoration := nil; return result end; proc elna_parser_boolean_literal(); var result: ^ElnaTreeBooleanLiteral; begin result := malloc(#size(ElnaTreeBooleanLiteral)); result^.kind := ElnaTreeKind.boolean_literal; result^.value := string_compare(lexer_state.start, 4, "true", 4); result^.type_decoration := nil; _elna_lexer_skip_token(); return result end; proc elna_parser_nil_literal(); var result: ^ElnaTreeNilLiteral; begin _elna_lexer_skip_token(); result := malloc(#size(ElnaTreeNilLiteral)); result^.kind := ElnaTreeKind.null; result^.type_decoration := nil; return result end; proc elna_tac_integer_literal(integer_literal_node: ^ElnaTreeIntegerLiteral, operand_type: Word, operand_value: Word, operand_length: Word); begin operand_type^ := ElnaTacKind.immediate; operand_value^ := integer_literal_node^.value; operand_length^ := 0; return nil end; proc elna_tac_boolean_literal(boolean_literal_node: ^ElnaTreeBooleanLiteral, operand_type: Word, operand_value: Word, operand_length: Word); begin operand_type^ := ElnaTacKind.immediate; operand_value^ := boolean_literal_node^.value; operand_length^ := 0; return nil end; proc elna_tac_nil_literal(nil_node: Word, operand_type: Word, operand_value: Word, operand_length: Word); begin operand_type^ := ElnaTacKind.immediate; operand_value^ := 0; operand_length^ := 0; return nil end; proc elna_parser_character_literal(); var result: ^ElnaTreeCharacterLiteral; begin result := malloc(#size(ElnaTreeCharacterLiteral)); result^.kind := ElnaTreeKind.character_literal; result^.value := lexer_state.start; result^.length := lexer_state.finish - lexer_state.start; result^.type_decoration := nil; _elna_lexer_skip_token(); return result end; proc elna_tac_character_literal(character_literal_node: ^ElnaTreeCharacterLiteral, operand_type: Word, operand_value: Word, operand_length: Word); begin operand_type^ := ElnaTacKind.immediate; operand_value^ := character_literal_node^.value; operand_length^ := character_literal_node^.length; return nil end; proc elna_parser_variable_expression(); var result: ^ElnaTreeVariableExpression; begin result := malloc(#size(ElnaTreeVariableExpression)); result^.kind := ElnaTreeKind.variable_expression; result^.name := lexer_state.start; result^.length := lexer_state.finish - lexer_state.start; result^.type_decoration := nil; _elna_lexer_skip_token(); return result end; proc elna_tac_variable_expression(variable_expression: ^ElnaTreeVariableExpression, symbol_table: Word, operand_type: Word, operand_value: Word, operand_length: Word); var lookup_result: ^ElnaSymbolTemporaryInfo; begin lookup_result := _symbol_table_lookup(symbol_table, variable_expression^.name, variable_expression^.length); if lookup_result <> nil then operand_type^ := ElnaTacKind.pseudo; operand_value^ := variable_expression^.name; operand_length^ := variable_expression^.length else operand_type^ := ElnaTacKind.symbol; operand_value^ := variable_expression^.name; operand_length^ := variable_expression^.length end; return nil end; proc elna_parser_string_literal(); var result: ^ElnaTreeStringLiteral; begin result := malloc(#size(ElnaTreeStringLiteral)); result^.kind := ElnaTreeKind.string_literal; result^.value := lexer_state.start; result^.length := lexer_state.finish - lexer_state.start; result^.type_decoration := nil; _elna_lexer_skip_token(); return result end; proc elna_tac_string_literal(string_literal_node: ^ElnaTreeStringLiteral, operand_type: Word, operand_value: Word, operand_length: Word); var offset: Word; first_instruction: Word; next_instruction: Word; begin offset := _add_string(string_literal_node^.value); elna_tac_generate_pseudo(operand_type, operand_value, operand_length); first_instruction := elna_tac_instruction_create(ElnaTacOperator.get_address); elna_tac_instruction_set_operand(first_instruction, 1, operand_type^, operand_value^, operand_length^); elna_tac_instruction_set_operand(first_instruction, 2, ElnaTacKind.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, operand_type^, operand_value^, operand_length^); elna_tac_instruction_set_operand(next_instruction, 2, operand_type^, operand_value^, operand_length^); elna_tac_instruction_set_operand(next_instruction, 3, ElnaTacKind.immediate, offset, 0); return elna_instruction_list_concatenate(first_instruction, next_instruction) end; proc elna_parser_trait_expression(); var result: ^ElnaTreeTraitExpression; token_kind: Word; begin result := malloc(#size(ElnaTreeTraitExpression)); result^.kind := ElnaTreeKind.trait_expression; result^.name := lexer_state.start; result^.length := lexer_state.finish - lexer_state.start; _elna_lexer_skip_token(); _elna_lexer_read_token(@token_kind); _elna_lexer_skip_token(); result^.argument := elna_parser_type_expression(); _elna_lexer_read_token(@token_kind); _elna_lexer_skip_token(); return result 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.boolean then parser_node := elna_parser_boolean_literal() elsif token_kind = ElnaLexerKind.null then parser_node := elna_parser_nil_literal() elsif token_kind = ElnaLexerKind.trait then parser_node := elna_parser_trait_expression() 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: ^ElnaTreeDereferenceExpression; begin result := malloc(#size(ElnaTreeDereferenceExpression)); result^.kind := ElnaTreeKind.dereference_expression; result^.pointer := simple_expression; result^.type_decoration := 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_parser_designator_loop; _elna_lexer_read_token(@token_kind); if token_kind = ElnaLexerKind.hat then simple_expression := elna_parser_dereference_expression(simple_expression); goto elna_parser_designator_loop elsif token_kind = ElnaLexerKind.dot then simple_expression := elna_parser_field_access_expression(simple_expression); goto elna_parser_designator_loop elsif token_kind = ElnaLexerKind.left_square then simple_expression := elna_parser_array_access_expression(simple_expression); goto elna_parser_designator_loop elsif token_kind = ElnaLexerKind.left_paren then simple_expression := elna_parser_call(simple_expression); goto elna_parser_designator_loop end; return simple_expression end; proc elna_tac_trait_expression(trait_node: ^ElnaTreeTraitExpression, operand_type: Word, operand_value: Word, operand_length: Word); var symbol: ^ElnaSymbolTypeInfo; info_type: ^ElnaType; parser_node: ^ElnaTreeNamedTypeExpression; begin parser_node := trait_node^.argument; symbol := _symbol_table_lookup(@symbol_table_global, parser_node^.name, parser_node^.length); info_type := symbol^._type; operand_type^ := ElnaTacKind.immediate; operand_value^ := info_type^.size; operand_length^ := 0; return nil end; proc elna_tac_simple_expression(parser_node: ^ElnaTreeNode, symbol_table: Word, operand_type: Word, operand_value: Word, operand_length: Word); var instruction: Word; begin if parser_node^.kind = ElnaTreeKind.character_literal then instruction := elna_tac_character_literal(parser_node, operand_type, operand_value, operand_length) elsif parser_node^.kind = ElnaTreeKind.string_literal then instruction := elna_tac_string_literal(parser_node, operand_type, operand_value, operand_length) elsif parser_node^.kind = ElnaTreeKind.integer_literal then instruction := elna_tac_integer_literal(parser_node, operand_type, operand_value, operand_length) elsif parser_node^.kind = ElnaTreeKind.boolean_literal then instruction := elna_tac_boolean_literal(parser_node, operand_type, operand_value, operand_length) elsif parser_node^.kind = ElnaTreeKind.null then instruction := elna_tac_nil_literal(parser_node, operand_type, operand_value, operand_length) elsif parser_node^.kind = ElnaTreeKind.trait_expression then instruction := elna_tac_trait_expression(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: ^ElnaTreeUnaryExpression; 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(#size(ElnaTreeUnaryExpression)); result^.kind := ElnaTreeKind.unary_expression; result^.operand := operand; result^.operator := operator; result^.type_decoration := nil end; return result end; proc elna_tac_copy_address(is_address: Word, from_type: Word, from_value: Word, from_length: Word, to_type: Word, to_value: Word, to_length: Word); var instruction: Word; begin if is_address then instruction := elna_tac_instruction_create(ElnaTacOperator.copy) else instruction := elna_tac_instruction_create(ElnaTacOperator.get_address) end; elna_tac_instruction_set_operand(instruction, 1, to_type, to_value, to_length); elna_tac_instruction_set_operand(instruction, 2, from_type, from_value, from_length); return instruction end; proc elna_tac_unary_expression(parser_node: ^ElnaTreeUnaryExpression, symbol_table: Word, operand_type: Word, operand_value: Word, operand_length: Word); var token_kind: Word; operator: Word; operand: ^ElnaTreeExpression; is_address: Word; first_instruction: Word; instruction: Word; base_type: Word; base_value: Word; base_length: Word; begin instruction := nil; if parser_node^.kind = ElnaTreeKind.unary_expression then operator := parser_node^.operator; operand := parser_node^.operand else operator := 0; operand := parser_node end; first_instruction := elna_tac_designator(operand, symbol_table, @is_address, @base_type, @base_value, @base_length); elna_tac_generate_pseudo(operand_type, operand_value, operand_length); if operator = '@' then instruction := elna_tac_copy_address(is_address, base_type, base_value, base_length, operand_type^, operand_value^, operand_length^) elsif operator = '-' then instruction := elna_tac_instruction_create(ElnaTacOperator.negate); elna_tac_instruction_set_operand(instruction, 1, operand_type^, operand_value^, operand_length^); elna_tac_instruction_set_operand(instruction, 2, base_type, base_value, base_length) elsif operator = '~' then instruction := elna_tac_instruction_create(ElnaTacOperator.complement); elna_tac_instruction_set_operand(instruction, 1, operand_type^, operand_value^, operand_length^); elna_tac_instruction_set_operand(instruction, 2, base_type, base_value, base_length) elsif is_address then instruction := elna_tac_instruction_create(ElnaTacOperator.load); elna_tac_instruction_set_operand(instruction, 1, base_type, base_value, base_length); elna_tac_instruction_set_operand(instruction, 2, operand_type^, operand_value^, operand_length^) else instruction := elna_tac_instruction_create(ElnaTacOperator.copy); elna_tac_instruction_set_operand(instruction, 1, operand_type^, operand_value^, operand_length^); elna_tac_instruction_set_operand(instruction, 2, base_type, base_value, base_length) end; return elna_instruction_list_concatenate(first_instruction, instruction) end; proc elna_parser_binary_expression(); var lhs_node: Word; rhs_node: Word; token_kind: Word; result: ^ElnaTreeBinaryExpression; 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(#size(ElnaTreeBinaryExpression)); result^.kind := ElnaTreeKind.binary_expression; result^.lhs := lhs_node; result^.rhs := rhs_node; result^.operator := token_kind; result^.type_decoration := nil else result := lhs_node end; return result end; proc elna_tac_binary_expression(parser_node: ^ElnaTreeBinaryExpression, symbol_table: Word, operand_type: Word, operand_value: Word, operand_length: Word); var first_instruction: Word; instruction: Word; lhs_type: Word; lhs_value: Word; lhs_length: Word; rhs_type: Word; rhs_value: Word; rhs_length: Word; begin if parser_node^.kind <> ElnaTreeKind.binary_expression then first_instruction := elna_tac_unary_expression(parser_node, symbol_table, operand_type, operand_value, operand_length) else first_instruction := elna_tac_unary_expression(parser_node^.lhs, symbol_table, @lhs_type, @lhs_value, @lhs_length); instruction := elna_tac_unary_expression(parser_node^.rhs, symbol_table, @rhs_type, @rhs_value, @rhs_length); first_instruction := elna_instruction_list_concatenate(first_instruction, instruction); if parser_node^.operator = ElnaLexerKind.plus then instruction := elna_tac_instruction_create(ElnaTacOperator.add) elsif parser_node^.operator = ElnaLexerKind.minus then instruction := elna_tac_instruction_create(ElnaTacOperator.subtract) elsif parser_node^.operator = ElnaLexerKind.multiplication then instruction := elna_tac_instruction_create(ElnaTacOperator.multiply) elsif parser_node^.operator = ElnaLexerKind.and then instruction := elna_tac_instruction_create(ElnaTacOperator.and) elsif parser_node^.operator = ElnaLexerKind._or then instruction := elna_tac_instruction_create(ElnaTacOperator._or) elsif parser_node^.operator = ElnaLexerKind._xor then instruction := elna_tac_instruction_create(ElnaTacOperator._xor) elsif parser_node^.operator = ElnaLexerKind.equals then instruction := elna_tac_instruction_create(ElnaTacOperator.equal) elsif parser_node^.operator = ElnaLexerKind.remainder then instruction := elna_tac_instruction_create(ElnaTacOperator.remainder) elsif parser_node^.operator = ElnaLexerKind.division then instruction := elna_tac_instruction_create(ElnaTacOperator.divide) elsif parser_node^.operator = ElnaLexerKind.less_than then instruction := elna_tac_instruction_create(ElnaTacOperator.less_than) elsif parser_node^.operator = ElnaLexerKind.greater_than then instruction := elna_tac_instruction_create(ElnaTacOperator.greater_than) elsif parser_node^.operator = ElnaLexerKind.less_equal then instruction := elna_tac_instruction_create(ElnaTacOperator.less_or_equal) elsif parser_node^.operator = ElnaLexerKind.greater_equal then instruction := elna_tac_instruction_create(ElnaTacOperator.greater_or_equal) elsif parser_node^.operator = ElnaLexerKind.not_equal then instruction := elna_tac_instruction_create(ElnaTacOperator.not_equal) end; elna_tac_generate_pseudo(operand_type, operand_value, operand_length); elna_tac_instruction_set_operand(instruction, 1, operand_type^, operand_value^, operand_length^); elna_tac_instruction_set_operand(instruction, 2, lhs_type, lhs_value, lhs_length); elna_tac_instruction_set_operand(instruction, 3, rhs_type, rhs_value, rhs_length); first_instruction := elna_instruction_list_concatenate(first_instruction, instruction) end; return first_instruction end; proc elna_parser_call(callee: Word); var result: ^ElnaTreeCall; argument_number: Word; token_kind: Word; argument_entry: ^ElnaTreeExpressionList; begin result := malloc(#size(ElnaTreeCall)); result^.kind := ElnaTreeKind.call; result^.next := nil; result^.arguments := nil; argument_number := 1; result^.callee := 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; if result^.arguments = nil then result^.arguments := malloc(#size(ElnaTreeExpressionList)); argument_entry := result^.arguments else argument_entry^.next := malloc(#size(ElnaTreeExpressionList)); argument_entry := argument_entry^.next end; argument_entry^.next := nil; argument_entry^.expression := elna_parser_binary_expression(); 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; (* Save the number of arguments. *) result^.count := argument_number - 1; return result end; proc elna_tac_call(parsed_call: ^ElnaTreeCall, symbol_table: Word, operand_type: Word, operand_value: Word, operand_length: Word); var parsed_expression: ^ElnaTreeVariableExpression; instruction: Word; first_instruction: Word; current_instruction: Word; argument_type: Word; argument_value: Word; argument_length: Word; arguments_operand: ^ElnaTacOperand; call_instruction: Word; argument_entry: ^ElnaTreeExpressionList; begin parsed_expression := parsed_call^.callee; first_instruction := nil; arguments_operand := malloc(parsed_call^.count * #size(ElnaTacOperand)); elna_tac_generate_pseudo(operand_type, operand_value, operand_length); call_instruction := elna_tac_instruction_create(ElnaTacOperator.proc_call); elna_tac_instruction_set_operand(call_instruction, 1, ElnaTacKind.symbol, parsed_expression^.name, parsed_expression^.length); elna_tac_instruction_set_operand(call_instruction, 2, ElnaTacKind.list, arguments_operand, parsed_call^.count); elna_tac_instruction_set_operand(call_instruction, 3, operand_type^, operand_value^, operand_length^); argument_entry := parsed_call^.arguments; .elna_tac_call_loop; if argument_entry <> nil then instruction := elna_tac_binary_expression(argument_entry^.expression, symbol_table, @argument_type, @argument_value, @argument_length); if first_instruction = nil then first_instruction := instruction else elna_instruction_list_concatenate(current_instruction, instruction) end; if instruction <> nil then current_instruction := instruction end; arguments_operand^.kind := argument_type; arguments_operand^.value := argument_value; arguments_operand^.length := argument_length; arguments_operand := arguments_operand + #size(ElnaTacOperand); argument_entry := argument_entry^.next; goto elna_tac_call_loop end; if first_instruction = nil then first_instruction := call_instruction else elna_instruction_list_concatenate(current_instruction, call_instruction) end; return first_instruction end; proc elna_parser_goto_statement(); var token_kind: Word; result: ^ElnaTreeGotoStatement; begin _elna_lexer_skip_token(); _elna_lexer_read_token(@token_kind); result := malloc(#size(ElnaTreeGotoStatement)); result^.kind := ElnaTreeKind.goto_statement; result^.next := nil; result^.label := lexer_state.start; result^.length := lexer_state.finish - lexer_state.start; _elna_lexer_skip_token(); return result end; proc elna_tac_goto_statement(parser_node: ^ElnaTreeGotoStatement); var label_length1: Word; label_with_dot1: Word; instruction1: Word; begin label_length1 := parser_node^.length + 1; label_with_dot1 := malloc(label_length1); _store_byte('.', label_with_dot1); memcpy(label_with_dot1 + 1, parser_node^.label, parser_node^.length); instruction1 := elna_tac_instruction_create(ElnaTacOperator.jump); elna_tac_instruction_set_operand(instruction1, 1, ElnaTacKind.symbol, label_with_dot1, label_length1); return instruction1 end; proc elna_parser_label_declaration(); var token_kind: Word; result: ^ElnaTreeGotoStatement; begin _elna_lexer_skip_token(); _elna_lexer_read_token(@token_kind); result := malloc(#size(ElnaTreeLabelDeclaration)); result^.kind := ElnaTreeKind.label_declaration; result^.next := nil; result^.label := lexer_state.start; result^.length := lexer_state.finish - lexer_state.start; _elna_lexer_skip_token(); return result end; proc elna_tac_label_declaration(parser_node: ^ElnaTreeLabelDeclaration); return elna_tac_label(parser_node^.label, parser_node^.length) end; proc elna_tac_enumeration_value(field_access_expression: ^ElnaTreeFieldAccessExpression, operand_type: Word, operand_value: Word, operand_length: Word); var enumeration_type: ^ElnaTypeEnumeration; members: Word; members_length: Word; token_type: Word; symbol_info: ^ElnaSymbolTypeInfo; member_name: Word; member_length: Word; counter: Word; enumeration_type_name: ^ElnaTreeVariableExpression; begin enumeration_type_name := field_access_expression^.aggregate; symbol_info := _symbol_table_lookup(@symbol_table_global, enumeration_type_name^.name, enumeration_type_name^.length); enumeration_type := symbol_info^._type; members := enumeration_type^.members; members_length := enumeration_type^.length; _elna_lexer_read_token(@token_type); counter := 1; .elna_tac_enumeration_value_members; if members_length > 0 then member_name := members^; member_length := members + 4; member_length := member_length^; if string_compare(field_access_expression^.field, field_access_expression^.length, member_name, member_length) = 0 then members_length := members_length - 1; members := members + 8; counter := counter + 1; goto elna_tac_enumeration_value_members end; (* Found. *) operand_type^ := ElnaTacKind.immediate; operand_value^ := counter; operand_length^ := 0 end; return nil end; proc elna_parser_field_access_expression(aggregate: Word); var token_kind: Word; result: ^ElnaTreeFieldAccessExpression; begin (* Skip dot. Read the enumeration value. *) _elna_lexer_skip_token(); _elna_lexer_read_token(@token_kind); result := malloc(#size(ElnaTreeFieldAccessExpression)); result^.kind := ElnaTreeKind.field_access_expression; result^.type_decoration := nil; result^.aggregate := aggregate; result^.field := lexer_state.start; result^.length := lexer_state.finish - lexer_state.start; _elna_lexer_skip_token(); return result end; proc elna_parser_array_access_expression(array: Word); var token_kind: Word; result: ^ElnaTreeArrayAccessExpression; begin _elna_lexer_skip_token(); result := malloc(#size(ElnaTreeArrayAccessExpression)); result^.kind := ElnaTreeKind.array_access_expression; result^.type_decoration := nil; result^.array := array; result^.index := elna_parser_binary_expression(); _elna_lexer_read_token(@token_kind); _elna_lexer_skip_token(); return result end; proc elna_tac_dereference_expression(dereference_expression: ^ElnaTreeDereferenceExpression, symbol_table: Word, operand_type: Word, operand_value: Word, operand_length: Word); var result_instructions: Word; load_instruction: Word; is_address: Word; begin result_instructions := elna_tac_designator(dereference_expression^.pointer, symbol_table, @is_address, operand_type, operand_value, operand_length); if is_address = true then load_instruction := elna_tac_instruction_create(ElnaTacOperator.load); elna_tac_instruction_set_operand(load_instruction, 1, operand_type^, operand_value^, operand_length^); elna_tac_instruction_set_operand(load_instruction, 2, operand_type^, operand_value^, operand_length^); result_instructions := elna_instruction_list_concatenate(result_instructions, load_instruction) end; return result_instructions end; proc elna_tac_designator(parser_node: ^ElnaTreeExpression, symbol_table: Word, is_address: Word, operand_type: Word, operand_value: Word, operand_length: Word); var field_access_expression: ^ElnaTreeFieldAccessExpression; result_instructions: Word; designator_base: ^ElnaTreeExpression; begin if parser_node^.kind = ElnaTreeKind.dereference_expression then result_instructions := elna_tac_dereference_expression(parser_node, symbol_table, operand_type, operand_value, operand_length); is_address^ := true elsif parser_node^.kind = ElnaTreeKind.field_access_expression then field_access_expression := parser_node; designator_base := field_access_expression^.aggregate; if designator_base^.type_decoration = nil then result_instructions := elna_tac_enumeration_value(field_access_expression, operand_type, operand_value, operand_length); is_address^ := false else result_instructions := elna_tac_field_access_expression(field_access_expression, symbol_table, operand_type, operand_value, operand_length); is_address^ := true end; elsif parser_node^.kind = ElnaTreeKind.array_access_expression then result_instructions := elna_tac_array_access_expression(parser_node, symbol_table, operand_type, operand_value, operand_length); is_address^ := true elsif parser_node^.kind = ElnaTreeKind.call then result_instructions := elna_tac_call(parser_node, symbol_table, operand_type, operand_value, operand_length); is_address^ := false else result_instructions := elna_tac_simple_expression(parser_node, symbol_table, operand_type, operand_value, operand_length); is_address^ := false end; return result_instructions end; proc elna_tac_field_access_expression(field_access_expression: ^ElnaTreeFieldAccessExpression, symbol_table: Word, operand_type: Word, operand_value: Word, operand_length: Word); var field_type: ^ElnaType; first_instruction: Word; last_instruction: Word; designator_base: ^ElnaTreeExpression; aggregate_type: ^ElnaTypeRecord; field_count: Word; current_field: ^ElnaTypeField; field_offset: Word; is_address: Word; base_type: Word; base_value: Word; base_length: Word; begin designator_base := field_access_expression^.aggregate; aggregate_type := designator_base^.type_decoration; first_instruction := elna_tac_designator(designator_base, symbol_table, @is_address, @base_type, @base_value, @base_length); field_count := aggregate_type^.length; current_field := aggregate_type^.members; field_offset := 0; .elna_tac_field_access_expression_field; if string_compare(field_access_expression^.field, field_access_expression^.length, current_field^.name, current_field^.length) = 0 then field_type := current_field^.field_type; field_count := field_count - 1; current_field := current_field + #size(ElnaTypeField); field_offset := field_offset + field_type^.size; goto elna_tac_field_access_expression_field end; elna_tac_generate_pseudo(operand_type, operand_value, operand_length); last_instruction := elna_tac_copy_address(is_address, base_type, base_value, base_length, operand_type^, operand_value^, operand_length^); first_instruction := elna_instruction_list_concatenate(first_instruction, last_instruction); last_instruction := elna_tac_instruction_create(ElnaTacOperator.add); elna_tac_instruction_set_operand(last_instruction, 1, operand_type^, operand_value^, operand_length^); elna_tac_instruction_set_operand(last_instruction, 2, ElnaTacKind.immediate, field_offset, 0); elna_tac_instruction_set_operand(last_instruction, 3, operand_type^, operand_value^, operand_length^); return elna_instruction_list_concatenate(first_instruction, last_instruction) end; proc elna_tac_array_access_expression(array_access_expression: ^ElnaTreeArrayAccessExpression, symbol_table: Word, operand_type: Word, operand_value: Word, operand_length: Word); var array_instructions: Word; index_instructions: Word; offset_instruction: Word; add_instruction: Word; is_address: Word; inter_type: Word; inter_value: Word; inter_length: Word; index_type: Word; index_value: Word; index_length: Word; aggregate_type: ^ElnaTypeArray; designator_base: ^ElnaTreeExpression; element_type: ^ElnaType; begin designator_base := array_access_expression^.array; aggregate_type := designator_base^.type_decoration; element_type := aggregate_type^.base; index_instructions := elna_tac_binary_expression(array_access_expression^.index, symbol_table, @inter_type, @inter_value, @inter_length); elna_tac_generate_pseudo(@index_type, @index_value, @index_length); add_instruction := elna_tac_instruction_create(ElnaTacOperator.subtract); elna_tac_instruction_set_operand(add_instruction, 1, index_type, index_value, index_length); elna_tac_instruction_set_operand(add_instruction, 2, inter_type, inter_value, inter_length); elna_tac_instruction_set_operand(add_instruction, 3, ElnaTacKind.immediate, 1, 0); offset_instruction := elna_tac_instruction_create(ElnaTacOperator.multiply); elna_tac_instruction_set_operand(offset_instruction, 1, index_type, index_value, index_length); elna_tac_instruction_set_operand(offset_instruction, 2, index_type, index_value, index_length); elna_tac_instruction_set_operand(offset_instruction, 3, ElnaTacKind.immediate, element_type^.size, 0); elna_instruction_list_concatenate(add_instruction, offset_instruction); index_instructions := elna_instruction_list_concatenate(index_instructions, add_instruction); array_instructions := elna_tac_designator(array_access_expression^.array, symbol_table, @is_address, @inter_type, @inter_value, @inter_length); elna_tac_generate_pseudo(operand_type, operand_value, operand_length); elna_instruction_list_concatenate(offset_instruction, array_instructions); array_instructions := elna_tac_copy_address(is_address, inter_type, inter_value, inter_length, operand_type^, operand_value^, operand_length^); elna_instruction_list_concatenate(offset_instruction, array_instructions); add_instruction := elna_tac_instruction_create(ElnaTacOperator.add); elna_tac_instruction_set_operand(add_instruction, 1, operand_type^, operand_value^, operand_length^); elna_tac_instruction_set_operand(add_instruction, 2, operand_type^, operand_value^, operand_length^); elna_tac_instruction_set_operand(add_instruction, 3, index_type, index_value, index_length); return elna_instruction_list_concatenate(index_instructions, add_instruction) end; proc elna_parser_assign_statement(assignee: Word); var result: ^ElnaTreeAssignStatement; token_kind: Word; begin result := malloc(#size(ElnaTreeAssignStatement)); result^.kind := ElnaTreeKind.assign_statement; result^.next := nil; result^.assignee := assignee; (* Skip the assignment sign (:=) with surrounding whitespaces. *) _elna_lexer_read_token(@token_kind); _elna_lexer_skip_token(); result^.assignment := elna_parser_binary_expression(); return result end; proc elna_tac_assign_statement(parser_tree: ^ElnaTreeAssignStatement, symbol_table: Word); var is_address: Word; first_instruction: Word; instruction: Word; current_instruction: Word; operand_type: Word; operand_value: Word; operand_length: Word; assignment_type: Word; assignment_value: Word; assignment_length: Word; assignee_type: Word; assignee_value: Word; assignee_length: Word; begin first_instruction := elna_tac_designator(parser_tree^.assignee, symbol_table, @is_address, @assignee_type, @assignee_value, @assignee_length); (* Compile the assignment. *) instruction := elna_tac_binary_expression(parser_tree^.assignment, symbol_table, @assignment_type, @assignment_value, @assignment_length); if assignee_type = ElnaTacKind.pseudo then current_instruction := elna_tac_instruction_create(ElnaTacOperator.copy); if is_address then elna_tac_generate_pseudo(@operand_type, @operand_value, @operand_length); (* Save the assignee address on the stack. *) elna_tac_instruction_set_operand(current_instruction, 1, operand_type, operand_value, operand_length); elna_tac_instruction_set_operand(current_instruction, 2, assignee_type, assignee_value, assignee_length); first_instruction := elna_instruction_list_concatenate(first_instruction, current_instruction); if instruction <> nil then elna_instruction_list_concatenate(current_instruction, instruction); current_instruction := instruction end; instruction := elna_tac_instruction_create(ElnaTacOperator.store); elna_tac_instruction_set_operand(instruction, 1, assignment_type, assignment_value, assignment_length); elna_tac_instruction_set_operand(instruction, 2, operand_type, operand_value, operand_length); elna_instruction_list_concatenate(current_instruction, instruction) else elna_tac_instruction_set_operand(current_instruction, 1, assignee_type, assignee_value, assignee_length); elna_tac_instruction_set_operand(current_instruction, 2, assignment_type, assignment_value, assignment_length); first_instruction := elna_instruction_list_concatenate(instruction, current_instruction) end else elna_tac_generate_pseudo(@operand_type, @operand_value, @operand_length); (* Save the assignee address on the stack. *) current_instruction := elna_tac_instruction_create(ElnaTacOperator.get_address); elna_tac_instruction_set_operand(current_instruction, 1, operand_type, operand_value, operand_length); elna_tac_instruction_set_operand(current_instruction, 2, assignee_type, assignee_value, assignee_length); first_instruction := elna_instruction_list_concatenate(first_instruction, current_instruction); if instruction <> nil then elna_instruction_list_concatenate(current_instruction, instruction); current_instruction := instruction end; instruction := elna_tac_instruction_create(ElnaTacOperator.store); elna_tac_instruction_set_operand(instruction, 1, assignment_type, assignment_value, assignment_length); elna_tac_instruction_set_operand(instruction, 2, operand_type, operand_value, operand_length); elna_instruction_list_concatenate(current_instruction, instruction) end; return first_instruction end; proc elna_parser_return_statement(); var token_kind: Word; returned: Word; label_length: Word; result: ^ElnaTreeReturnStatement; 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(#size(ElnaTreeReturnStatement)); result^.kind := ElnaTreeKind.return_statement; result^.next := nil; result^.returned := returned; return result end; proc elna_tac_return_statement(parser_node: ^ElnaTreeReturnStatement, symbol_table: Word); var first_instruction: Word; instruction: Word; operand_type: Word; operand_value: Word; operand_length: Word; begin operand_type := 0; operand_value := 0; operand_length := 0; first_instruction := elna_tac_binary_expression(parser_node^.returned, 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 printf(".L%i\0", counter) else printf(".%.*s\0", length, counter); end; fflush(nil) end; proc elna_parser_conditional_statements(); var token_kind: Word; result: ^ElnaTreeConditionalStatements; begin result := malloc(#size(ElnaTreeConditionalStatements)); (* Skip "if", "while" or "elsif". *) _elna_lexer_skip_token(); result^.condition := elna_parser_binary_expression(); (* Skip "then" or "do". *) _elna_lexer_read_token(@token_kind); _elna_lexer_skip_token(); result^.statements := elna_parser_statements(); result^.next := nil; return result end; proc elna_tac_conditional_statements(parser_node: ^ElnaTreeConditionalStatements, after_end_label: Word, symbol_table: Word); var condition_label: 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; first_instruction := elna_tac_binary_expression(parser_node^.condition, 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, ElnaTacKind.symbol, condition_label, 0); elna_instruction_list_concatenate(first_instruction, current_instruction); instruction := elna_tac_statements(parser_node^.statements, 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, ElnaTacKind.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 result: ^ElnaTreeIfStatement; token_kind: Word; previous_conditional: ^ElnaTreeConditionalStatements; next_conditional: ^ElnaTreeConditionalStatements; begin result := malloc(#size(ElnaTreeIfStatement)); result^.kind := ElnaTreeKind.if_statement; result^.next := nil; previous_conditional := elna_parser_conditional_statements(); result^.conditionals := 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(); previous_conditional^.next := next_conditional; previous_conditional = next_conditional; goto elna_parser_if_statement_loop elsif token_kind = ElnaLexerKind._else then _elna_lexer_skip_token(); result^._else := elna_parser_statements() else result^._else := nil end; _elna_lexer_skip_token(); return result end; proc elna_parser_statement(); var token_kind: Word; result : ^ElnaTreeNode; begin result := nil; _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(); if result^.kind <> ElnaTreeKind.call then result := elna_parser_assign_statement(result) end end; return result end; proc elna_parser_statements(); var token_kind: Word; previous_statement: ^ElnaTreeStatement; next_statement: ^ElnaTreeStatement; first_statement: ^ElnaTreeStatement; 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(); previous_statement^.next := next_statement; previous_statement := next_statement; if previous_statement <> 0 then goto elna_parser_statement_loop end end; .elna_parser_statements_end; _skip_empty_lines(); return first_statement end; proc elna_tac_statements(parser_node: Word, symbol_table: Word); var current_statement: ^ElnaTreeStatement; instruction: Word; first_instruction: Word; current_instruction: Word; begin current_statement := parser_node; first_instruction := nil; .elna_tac_statements_loop; pseudo_counter := 0; if current_statement <> nil then instruction := elna_tac_statement(current_statement, symbol_table); current_statement := current_statement^.next; if instruction = 0 then goto elna_tac_statements_loop end; if first_instruction = nil 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: ^ElnaTreeIfStatement, symbol_table: Word); var current_node: ^ElnaTreeConditionalStatements; 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 := parser_node^.conditionals; first_instruction := elna_tac_conditional_statements(current_node, after_end_label, symbol_table); current_instruction := first_instruction; .elna_tac_if_statement_loop; current_node := current_node^.next; if current_node <> nil 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 := parser_node^._else; if parser_node^._else <> nil then instruction := elna_tac_statements(parser_node^._else, 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: ^ElnaTreeNode, symbol_table: Word); var instruction: Word; operand_type: Word; operand_value: Word; operand_length: Word; begin if parser_node^.kind = ElnaTreeKind.goto_statement then instruction := elna_tac_goto_statement(parser_node) elsif parser_node^.kind = ElnaTreeKind.if_statement then instruction := elna_tac_if_statement(parser_node, symbol_table) elsif parser_node^.kind = ElnaTreeKind.return_statement then instruction := elna_tac_return_statement(parser_node, symbol_table) elsif parser_node^.kind = ElnaTreeKind.label_declaration then instruction := elna_tac_label_declaration(parser_node) elsif parser_node^.kind = ElnaTreeKind.call then instruction := elna_tac_call(parser_node, symbol_table, @operand_type, @operand_value, @operand_length) elsif parser_node^.kind = ElnaTreeKind.assign_statement then instruction := elna_tac_assign_statement(parser_node, symbol_table) else instruction := nil 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_type: Word; token_kind: Word; result: ^ElnaTreeEnumerationTypeExpression; 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; entry^ := lexer_state.start; entry := entry + 4; entry^ := lexer_state.finish - lexer_state.start; 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(#size(ElnaTreeEnumerationTypeExpression)); result^.kind := ElnaTreeKind.record_type_expression; result^.members := memory_start; result^.length := member_count; return result end; proc elna_parser_enumeration_type_expression(); var token_kind: Word; memory_start: Word; member_count: Word; result: ^ElnaTreeEnumerationTypeExpression; 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; entry^ := lexer_state.start; entry := entry + 4; entry^ := lexer_state.finish - lexer_state.start; 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(#size(ElnaTreeEnumerationTypeExpression)); result^.kind := ElnaTreeKind.enumeration_type_expression; result^.members := memory_start; result^.length := 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: ^ElnaTreeEnumerationTypeExpression); var result: ^ElnaTypeEnumeration; memory_start: Word; member_count: Word; member_array_start: Word; member_array_current: Word; begin result := malloc(#size(ElnaTypeEnumeration)); memory_start := parser_node^.members; member_count := parser_node^.length; (* Copy the list of enumeration members into an array of strings. *) member_array_start := malloc(member_count * 8); member_array_current := member_array_start; .elna_name_type_enumeration_loop; if member_count > 0 then member_array_current^ := memory_start^; member_array_current := member_array_current + 4; memory_start := memory_start + 4; member_array_current^ := memory_start^; member_array_current := member_array_current + 4; memory_start := memory_start + 4; memory_start := memory_start^; member_count := member_count - 1; goto elna_name_type_enumeration_loop end; member_count := parser_node^.length; result^.kind := ElnaTypeKind.enumeration; result^.size := 4; result^.members := member_array_start; result^.length := member_count; return result end; proc elna_name_pointer_type_expression(parser_node: ^ElnaTreePointerTypeExpression); var result: ^ElnaTypePointer; begin result := malloc(#size(ElnaTypePointer)); result^.kind := ElnaTypeKind.pointer; result^.size := 4; result^.base := elna_name_type_expression(parser_node^.base); return result end; proc elna_name_array_type_expression(parser_node: ^ElnaTreeArrayTypeExpression); var base: ^ElnaType; result: ^ElnaTypeArray; length: ^ElnaTreeIntegerLiteral; begin result := malloc(#size(ElnaTypeArray)); base := elna_name_type_expression(parser_node^.base); (* TODO: Expected to be an integer literal for now. *) length := parser_node^.length; result^.kind := ElnaTypeKind.array; (* Array size in bytes. *) result^.size := base^.size * length^.value; result^.base := base; result^.length := length^.value; return result end; proc elna_name_record_type_expression(parser_node: ^ElnaTreeRecordTypeExpression); var result: ^ElnaTypeRecord; memory_start: Word; member_count: Word; member_array_start: Word; member_array_current: ^ElnaTypeField; field_type: ^ElnaType; begin result := malloc(#size(ElnaTypeRecord)); result^.size := 0; memory_start := parser_node^.members; member_count := parser_node^.length; member_array_start := malloc(member_count * #size(ElnaTypeField)); member_array_current := member_array_start; .elna_name_type_record_loop; if member_count > 0 then member_array_current^.name := memory_start^; memory_start := memory_start + 4; member_array_current^.length := memory_start^; memory_start := memory_start + 4; field_type := elna_name_type_expression(memory_start^); result^.size := result^.size + field_type^.size; member_array_current^.field_type := field_type; member_array_current := member_array_current + #size(ElnaTypeField); memory_start := memory_start + 4; memory_start := memory_start^; member_count := member_count - 1; goto elna_name_type_record_loop end; result^.kind := ElnaTypeKind._record; result^.members := member_array_start; result^.length := parser_node^.length; return result end; proc elna_parser_named_type_expression(); var result: ^ElnaTreeNamedTypeExpression; begin result := malloc(#size(ElnaTreeNamedTypeExpression)); result^.kind := ElnaTreeKind.named_type_expression; result^.name := lexer_state.start; result^.length := lexer_state.finish - lexer_state.start; _elna_lexer_skip_token(); return result end; proc elna_parser_pointer_type_expression(); var result: ^ElnaTreePointerTypeExpression; begin _elna_lexer_skip_token(); result := malloc(#size(ElnaTreePointerTypeExpression)); result^.kind := ElnaTreeKind.pointer_type_expression; result^.base := elna_parser_type_expression(); return result end; proc elna_parser_array_type_expression(); var result: ^ElnaTreeArrayTypeExpression; token_kind: Word; begin _elna_lexer_skip_token(); result := malloc(#size(ElnaTreeArrayTypeExpression)); result^.kind := ElnaTreeKind.array_type_expression; result^.length := elna_parser_binary_expression(); (* Read and skip square bracket. *) _elna_lexer_read_token(@token_kind); _elna_lexer_skip_token(); result^.base := elna_parser_type_expression(); return result end; proc elna_parser_type_expression(); var token_kind: Word; result: Word; begin result := nil; _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: ^ElnaTreeNode); var named_type_expression: ^ElnaTreeNamedTypeExpression; type_symbol: ^ElnaSymbolTypeInfo; result: Word; begin if parser_node^.kind = ElnaTreeKind.named_type_expression then named_type_expression := parser_node; type_symbol := _symbol_table_lookup(@symbol_table_global, named_type_expression^.name, named_type_expression^.length); result := type_symbol^._type elsif parser_node^.kind = ElnaTreeKind.enumeration_type_expression then result := elna_name_enumeration_type_expression(parser_node) elsif parser_node^.kind = ElnaTreeKind.record_type_expression then result := elna_name_record_type_expression(parser_node) elsif parser_node^.kind = ElnaTreeKind.pointer_type_expression then result := elna_name_pointer_type_expression(parser_node) elsif parser_node^.kind = ElnaTreeKind.array_type_expression then result := elna_name_array_type_expression(parser_node) end; return result end; proc _type_info_create(type_representation: Word); var result: ^ElnaSymbolTypeInfo; begin result := malloc(#size(ElnaSymbolTypeInfo)); result^.kind := ElnaSymbolInfoKind.type_info; result^._type := 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: ^ElnaSymbolTemporaryInfo; begin result := malloc(#size(ElnaSymbolTemporaryInfo)); result^.kind := ElnaSymbolInfoKind.temporary_info; (* Calculate the stack offset: 4 * variable_counter. *) result^.offset := temporary_index * 4; result^.variable_type := temporary_type; return result end; (** * Parameters: * symbol_table - Local symbol table. *) proc _procedure_info_create(symbol_table: Word); var result: ^ElnaSymbolProcedureInfo; begin result := malloc(#size(ElnaSymbolProcedureInfo)); result^.kind := ElnaSymbolInfoKind.procedure_info; result^.symbol_table := symbol_table; return result end; (** * Parameters: * variable_index - Variable index. *) proc elna_name_procedure_temporary(parser_node: ^ElnaTreeVariableDeclaration, variable_index: Word, symbol_table: Word); var info: Word; variable_type: Word; begin variable_type := elna_name_type_expression(parser_node^._type); info := _temporary_info_create(variable_index, variable_type); _symbol_table_enter(symbol_table, parser_node^.name, parser_node^.length, info) end; proc elna_name_procedure_temporaries(parser_node: ^ElnaTreeVariableDeclaration, symbol_table: Word); var temporary_counter: Word; begin temporary_counter := 0; .elna_name_procedure_temporaries_loop; if parser_node <> nil then elna_name_procedure_temporary(parser_node, temporary_counter, symbol_table); temporary_counter := temporary_counter + 1; parser_node := parser_node^.next; goto elna_name_procedure_temporaries_loop end end; proc elna_parser_procedure_declaration(); var next_declaration: ^ElnaTreeDeclaration; current_declaration: ^ElnaTreeDeclaration; token_kind: Word; result: ^ElnaTreeProcedureDeclaration; parameter_head: ^ElnaTreeDeclaration; begin result := malloc(#size(ElnaTreeProcedureDeclaration)); result^.kind := ElnaTreeKind.procedure_declaration; result^.next := nil; (* Skip "proc ". *) _elna_lexer_skip_token(); _elna_lexer_read_token(@token_kind); result^.name := lexer_state.start; result^.length := lexer_state.finish - lexer_state.start; (* Skip procedure name. *) _elna_lexer_skip_token(); (* Skip open paren. *) _elna_lexer_read_token(@token_kind); _elna_lexer_skip_token(); parameter_head := nil; .elna_parser_procedure_declaration_parameter; _elna_lexer_read_token(@token_kind); if token_kind <> ElnaLexerKind.right_paren then next_declaration := elna_parser_variable_declaration(); if parameter_head = nil then parameter_head := next_declaration else current_declaration^.next := next_declaration end; current_declaration := next_declaration; _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(); result^.parameters := parameter_head; (* Skip semicolon and newline. *) _elna_lexer_read_token(@token_kind); _elna_lexer_skip_token(); parameter_head := elna_parser_var_part(); result^.temporaries := 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; result^.body := parameter_head; (* Skip the "end" keyword. *) _elna_lexer_read_token(@token_kind); _elna_lexer_skip_token(); return result end; proc elna_tac_parameters(ast_list: ^ElnaTreeDeclaration, parameter_count: ^Word); var ast_parameter: ^ElnaTreeDeclaration; parameter_index: Word; current_parameter: Word; parameter_list: Word; begin ast_parameter := ast_list; parameter_count^ := 0; .elna_tac_parameters_count; if ast_parameter <> nil then ast_parameter := ast_parameter^.next; parameter_count^ := parameter_count^ + 1; goto elna_tac_parameters_count end; (* The parameters are saved as an array of name pointer + name length. *) parameter_list := malloc(parameter_count^ * 8); current_parameter := parameter_list; ast_parameter := ast_list; parameter_index := 0; .elna_tac_parameters_loop; if parameter_index < parameter_count^ then current_parameter^ := ast_parameter^.name; current_parameter := current_parameter + 4; current_parameter^ := ast_parameter^.length; current_parameter := current_parameter + 4; parameter_index := parameter_index + 1; ast_parameter := ast_parameter^.next; goto elna_tac_parameters_loop end; return parameter_list end; proc elna_rtl_global_declaration(tac_declaration: ^ElnaTacStaticVariable); var result: ^ElnaInstructionDeclaration; begin result := malloc(#size(ElnaInstructionDeclaration)); result^.next := nil; result^.name := tac_declaration^.name; result^.length := tac_declaration^.length; result^.body := tac_declaration^.body; return result end; proc elna_rtl_parameters(parameters: Word, count: Word); var result: ^ElnaRtlInstruction; current_instruction: ^ElnaRtlInstruction; last_instruction: Word; parameter_index: Word; parameter_name: Word; name_length: Word; begin result := nil; parameter_index := 0; .elna_rtl_parameters_loop; if parameter_index < count then parameter_name := parameters^; parameters := parameters + 4; name_length := parameters^; parameters := parameters + 4; current_instruction := elna_rtl_instruction_create(ElnaRtlOperator.move); elna_rtl_instruction_set_operand(current_instruction, 1, ElnaRtlKind.pseudo, parameter_name, name_length); elna_rtl_instruction_set_operand(current_instruction, 2, ElnaRtlKind.register, 11 + parameter_index, 0); if result = nil then result := current_instruction else elna_instruction_list_concatenate(last_instruction, current_instruction) end; last_instruction := current_instruction; parameter_index := parameter_index + 1; goto elna_rtl_parameters_loop end; return result end; proc elna_rtl_procedure_declaration(tac_declaration: ^ElnaInstructionDeclaration); var result: ^ElnaInstructionDeclaration; body: ^ElnaRtlInstruction; parameters: ^ElnaRtlInstruction; begin result := malloc(#size(ElnaInstructionDeclaration)); result^.next := nil; result^.name := tac_declaration^.name; result^.length := tac_declaration^.length; result^.stack := tac_declaration^.stack; parameters = elna_rtl_parameters(tac_declaration^.parameters, tac_declaration^.count); body := elna_rtl_instructions(tac_declaration^.body); result^.body := elna_instruction_list_concatenate(parameters, body); return result end; proc elna_tac_procedure_declaration(parser_node: ElnaTreeProcedureDeclaration); var symbol_info: ^ElnaSymbolProcedureInfo; result: ^ElnaInstructionDeclaration; parameter_count: Word; begin result := malloc(#size(ElnaInstructionDeclaration)); result^.next := nil; result^.stack := 1; result^.name := parser_node^.name; result^.length := parser_node^.length; symbol_info := _symbol_table_lookup(@symbol_table_global, parser_node^.name, parser_node^.length); result^.parameters := elna_tac_parameters(parser_node^.parameters, @parameter_count); result^.count := parameter_count; result^.body := elna_tac_statements(parser_node^.body, symbol_info^.symbol_table); return result end; proc elna_parser_procedures(); var parser_node: ^ElnaTreeDeclaration; result: ^ElnaTreeDeclaration; current_declaration: ^ElnaTreeDeclaration; token_kind: Word; begin result := nil; .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 current_declaration^.next := 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: ^ElnaTacStaticVariable); var current_copy: ^ElnaInstructionDeclaration; next_copy: ^ElnaInstructionDeclaration; first_copy: ^ElnaInstructionDeclaration; begin if tac_procedure <> nil then first_copy := elna_rtl_global_declaration(tac_procedure); tac_procedure := tac_procedure^.next else first_copy := nil end; current_copy := first_copy; .elna_rtl_globals_start; if tac_procedure <> nil then next_copy := elna_rtl_global_declaration(tac_procedure); tac_procedure := tac_procedure^.next; current_copy^.next := next_copy; current_copy := next_copy; goto elna_rtl_globals_start end; return first_copy end; proc elna_rtl_procedures(tac_procedure: ^ElnaInstructionDeclaration); var current_copy: ^ElnaInstructionDeclaration; next_copy: ^ElnaInstructionDeclaration; first_copy: ^ElnaInstructionDeclaration; begin if tac_procedure <> nil then first_copy := elna_rtl_procedure_declaration(tac_procedure); tac_procedure := tac_procedure^.next else first_copy := nil; end; current_copy := first_copy; .elna_rtl_procedures_start; if tac_procedure <> nil then next_copy := elna_rtl_procedure_declaration(tac_procedure); tac_procedure := tac_procedure^.next; current_copy^.next := next_copy; current_copy := next_copy; goto elna_rtl_procedures_start end; return first_copy end; proc elna_tac_procedures(parser_node: ^ElnaTreeDeclaration); var result: ^ElnaInstructionDeclaration; current_procedure: ^ElnaInstructionDeclaration; first_procedure: ^ElnaInstructionDeclaration; begin first_procedure := nil; .elna_tac_procedures_loop; if parser_node = 0 then goto elna_tac_procedures_end end; result := elna_tac_procedure_declaration(parser_node); if first_procedure = nil then first_procedure := result else current_procedure^.next := result end; current_procedure := result; parser_node := parser_node^.next; goto elna_tac_procedures_loop; .elna_tac_procedures_end; return first_procedure end; (** * Skips comments. *) proc _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; result: ^ElnaTreeTypeDeclaration; begin _elna_lexer_read_token(@token_kind); type_name := lexer_state.start; name_length := lexer_state.finish - lexer_state.start; _elna_lexer_skip_token(); _elna_lexer_read_token(@token_kind); _elna_lexer_skip_token(); result := malloc(#size(ElnaTreeTypeDeclaration)); result^.kind := ElnaTreeKind.type_declaration; result^.next := nil; result^.name := type_name; result^.length := name_length; result^._type := elna_parser_type_expression(); _elna_lexer_read_token(@token_kind); _elna_lexer_skip_token(); return result end; proc elna_name_type_declaration(parser_node: ^ElnaTreeTypeDeclaration); var type_name: Word; name_length: Word; type_info: Word; begin type_name := parser_node^.name; name_length := parser_node^.length; parser_node := parser_node^._type; 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: ^ElnaTreeDeclaration; result: ^ElnaTreeDeclaration; current_declaration: ^ElnaTreeDeclaration; begin result := nil; _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 = nil then result := parser_node else current_declaration^.next := parser_node end; current_declaration := parser_node; goto elna_parser_type_part_loop end; .elna_parser_type_part_end; return result end; proc elna_parser_variable_declaration(); var token_kind: Word; name: Word; name_length: Word; variable_type: Word; result: ^ElnaTreeVariableDeclaration; begin _elna_lexer_read_token(@token_kind); name := lexer_state.start; name_length := lexer_state.finish - lexer_state.start; (* 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(#size(ElnaTreeVariableDeclaration)); result^.kind := ElnaTreeKind.variable_declaration; result^.next := nil; result^.name := name; result^.length := name_length; result^._type := variable_type; return result end; proc elna_tac_variable_declaration(parser_tree: ^ElnaTreeVariableDeclaration); var result: ^ElnaTacStaticVariable; variable_info: ^ElnaSymbolTemporaryInfo; begin result := malloc(#size(ElnaTacStaticVariable)); variable_info := _symbol_table_lookup(@symbol_table_global, parser_tree^.name, parser_tree^.length); result^.next := nil; result^.name := parser_tree^.name; result^.length := parser_tree^.length; result^.body := variable_info^.variable_type^.size; return result end; proc elna_parser_var_part(); var result: Word; token_kind: Word; variable_node: Word; current_declaration: ^ElnaTreeDeclaration; 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 current_declaration^.next := variable_node end; current_declaration := variable_node; goto elna_parser_var_part_loop end; .elna_parser_var_part_end; return result end; proc elna_tac_var_part(parser_node: ^ElnaTreeDeclaration); var node: ^ElnaTacStaticVariable; current_variable: ^ElnaTacStaticVariable; first_variable: ^ElnaTacStaticVariable; 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 current_variable^.next := node end; current_variable := node; parser_node := parser_node^.next; 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(error_list: ^ElnaErrorList); var parser_node: Word; result: ^ElnaTreeModuleDeclaration; token_kind: Word; parser_error: ^ElnaError; begin result := malloc(#size(ElnaTreeModuleDeclaration)); result^.kind := ElnaTreeKind.module_declaration; (* Skip "program;". *) _skip_empty_lines(); _elna_lexer_read_token(@token_kind); if token_kind <> ElnaLexerKind._program then parser_error := malloc(#size(ElnaError)); parser_error^.next := nil; error_list^.first := parser_error; error_list^.last := parser_error else _elna_lexer_skip_token(); _elna_lexer_read_token(@token_kind); _elna_lexer_skip_token(); result^.types := elna_parser_type_part(); result^.globals := elna_parser_var_part(); result^.procedures := elna_parser_procedures() end; return result end; (** * Process the source code and print the generated code. *) proc elna_tac_module_declaration(parser_node: ^ElnaTreeModuleDeclaration); var data_part: ^ElnaTacStaticVariable; code_part: Word; begin data_part := elna_tac_var_part(parser_node^.globals); code_part := elna_tac_procedures(parser_node^.procedures); return elna_instruction_module_create(data_part, code_part) end; proc elna_name_procedure_declaration(parser_node: ^ElnaTreeProcedureDeclaration); var new_symbol_table: Word; symbol_info: Word; begin new_symbol_table := _symbol_table_create(); symbol_info := _procedure_info_create(new_symbol_table); elna_name_procedure_temporaries(parser_node^.parameters, new_symbol_table); elna_name_procedure_temporaries(parser_node^.temporaries, new_symbol_table); _symbol_table_enter(@symbol_table_global, parser_node^.name, parser_node^.length, symbol_info) end; proc elna_type_conditional_statements(parser_node: ^ElnaTreeConditionalStatements, symbol_table: Word); begin .elna_type_conditional_statements_loop; elna_type_binary_expression(parser_node^.condition, symbol_table); elna_type_statements(parser_node^.statements, symbol_table); parser_node := parser_node^.next; if parser_node <> nil then goto elna_type_conditional_statements_loop end end; proc elna_type_if_statement(parser_node: ^ElnaTreeIfStatement, symbol_table: Word); var block: ^ElnaTreeConditionalStatements; begin block := parser_node^.conditionals; .elna_type_if_statement_conditionals; elna_type_conditional_statements(block, symbol_table); block := block^.next; if block <> nil then goto elna_type_if_statement_conditionals end; block := parser_node^._else; if block <> nil then elna_type_statements(block, symbol_table) end end; proc elna_type_return_statement(parser_node: ^ElnaTreeReturnStatement, symbol_table: Word); begin elna_type_binary_expression(parser_node^.returned, symbol_table) end; proc elna_type_call(parser_node: ^ElnaTreeCall, symbol_table: Word); var argument_tree: ^ElnaTreeExpressionList; begin argument_tree := parser_node^.arguments; .elna_type_call_argument; if argument_tree <> nil then elna_type_binary_expression(argument_tree^.expression, symbol_table); argument_tree := argument_tree^.next; goto elna_type_call_argument end end; proc elna_type_assign_statement(parser_node: ^ElnaTreeAssignStatement, symbol_table: Word); begin elna_type_designator(parser_node^.assignee, symbol_table); elna_type_binary_expression(parser_node^.assignment, symbol_table) end; proc elna_type_statement(parser_node: ^ElnaTreeNode, symbol_table: Word); begin (* Skipping goto and label declarations. *) if parser_node^.kind = ElnaTreeKind.if_statement then elna_type_if_statement(parser_node, symbol_table) elsif parser_node^.kind = ElnaTreeKind.return_statement then elna_type_return_statement(parser_node, symbol_table) elsif parser_node^.kind = ElnaTreeKind.call then elna_type_call(parser_node, symbol_table) elsif parser_node^.kind = ElnaTreeKind.assign_statement then elna_type_assign_statement(parser_node, symbol_table) end end; proc elna_type_statements(parser_node: ^ElnaTreeStatement, symbol_table: Word); begin .elna_type_statements_loop; if parser_node <> nil then elna_type_statement(parser_node, symbol_table); parser_node := parser_node^.next; goto elna_type_statements_loop end end; proc elna_type_character_literal(parser_node: ^ElnaTreeCharacterLiteral); var symbol_info: ^ElnaSymbolTypeInfo; begin symbol_info := _symbol_table_lookup(@symbol_table_global, "Word", 4); parser_node^.type_decoration := symbol_info^._type end; proc elna_type_integer_literal(parser_node: ^ElnaTreeIntegerLiteral); var symbol_info: ^ElnaSymbolTypeInfo; begin symbol_info := _symbol_table_lookup(@symbol_table_global, "Word", 4); parser_node^.type_decoration := symbol_info^._type end; proc elna_type_string_literal(parser_node: ^ElnaTreeStringLiteral); var symbol_info: ^ElnaSymbolTypeInfo; begin symbol_info := _symbol_table_lookup(@symbol_table_global, "Word", 4); parser_node^.type_decoration := symbol_info^._type end; proc elna_type_boolean_literal(parser_node: ^ElnaTreeBooleanLiteral); var symbol_info: ^ElnaSymbolTypeInfo; begin symbol_info := _symbol_table_lookup(@symbol_table_global, "Bool", 4); parser_node^.type_decoration := symbol_info^._type end; proc elna_type_nil_literal(parser_node: ^ElnaTreeNilLiteral); var symbol_info: ^ElnaSymbolTypeInfo; begin symbol_info := _symbol_table_lookup(@symbol_table_global, "Pointer", 7); parser_node^.type_decoration := symbol_info^._type end; proc elna_type_variable_expression(parser_node: ^ElnaTreeVariableExpression, symbol_table: Word); var variable_info: ^ElnaSymbolInfo; temporary_info: ^ElnaSymbolTemporaryInfo; begin variable_info := _symbol_table_lookup(symbol_table, parser_node^.name, parser_node^.length); if variable_info = nil then variable_info := _symbol_table_lookup(@symbol_table_global, parser_node^.name, parser_node^.length) end; if variable_info^.kind = ElnaSymbolInfoKind.temporary_info then temporary_info := variable_info; parser_node^.type_decoration := temporary_info^.variable_type end end; proc elna_type_simple_expression(parser_node: ^ElnaTreeNode, symbol_table: Word); begin if parser_node^.kind = ElnaTreeKind.integer_literal then elna_type_integer_literal(parser_node) elsif parser_node^.kind = ElnaTreeKind.character_literal then elna_type_character_literal(parser_node) elsif parser_node^.kind = ElnaTreeKind.string_literal then elna_type_string_literal(parser_node) elsif parser_node^.kind = ElnaTreeKind.boolean_literal then elna_type_boolean_literal(parser_node) elsif parser_node^.kind = ElnaTreeKind.null then elna_type_nil_literal(parser_node) elsif parser_node^.kind = ElnaTreeKind.variable_expression then elna_type_variable_expression(parser_node, symbol_table) end end; proc elna_type_dereference_expression(parser_node: ^ElnaTreeDereferenceExpression, symbol_table: Word); var base_type: ^ElnaType; pointer_type: ^ElnaTypePointer; dereferenced_expression: ^ElnaTreeExpression; begin elna_type_designator(parser_node^.pointer, symbol_table); dereferenced_expression := parser_node^.pointer; base_type := dereferenced_expression^.type_decoration; (* If check for compatibility, should be removed later. *) if base_type^.kind = ElnaTypeKind.pointer then pointer_type := base_type; base_type := pointer_type^.base; end; parser_node^.type_decoration := base_type end; proc elna_type_field_access_expression(parser_node: ^ElnaTreeFieldAccessExpression, symbol_table: Word); var variable_expression: ^ElnaTreeVariableExpression; base_type: Word; type_kind: ^ElnaSymbolTypeInfo; symbol_info: ^ElnaSymbolInfo; aggregate_type: ^ElnaTypeRecord; field_count: Word; current_field: ^ElnaTypeField; begin base_type := nil; variable_expression := parser_node^.aggregate; (* Check whether the field access is an enumeration value. *) if variable_expression^.kind = ElnaTreeKind.variable_expression then symbol_info := _symbol_table_lookup(@symbol_table_global, variable_expression^.name, variable_expression^.length); if symbol_info <> nil then type_kind := symbol_info; if symbol_info^.kind = ElnaSymbolInfoKind.type_info then base_type := type_kind^._type end end end; (* If the base_type is still nil this is record field access. *) if base_type = nil then elna_type_designator(parser_node^.aggregate, symbol_table); aggregate_type := variable_expression^.type_decoration; field_count := aggregate_type^.length; current_field := aggregate_type^.members; .elna_type_field_access_expression_field; if string_compare(parser_node^.field, parser_node^.length, current_field^.name, current_field^.length) = 0 then field_count := field_count - 1; current_field := current_field + #size(ElnaTypeField); goto elna_type_field_access_expression_field end; base_type := current_field^.field_type end; parser_node^.type_decoration := base_type end; proc elna_type_array_access_expression(parser_node: ^ElnaTreeArrayAccessExpression, symbol_table: Word); var aggregate_type: ^ElnaTypeArray; base_expression: ^ElnaTreeExpression; begin base_expression := parser_node^.array; elna_type_designator(base_expression, symbol_table); elna_type_binary_expression(parser_node^.index, symbol_table); aggregate_type := base_expression^.type_decoration; parser_node^.type_decoration := aggregate_type^.base end; proc elna_type_designator(parser_node: ^ElnaTreeNode, symbol_table: Word); begin if parser_node^.kind = ElnaTreeKind.dereference_expression then elna_type_dereference_expression(parser_node, symbol_table) elsif parser_node^.kind = ElnaTreeKind.field_access_expression then elna_type_field_access_expression(parser_node, symbol_table) elsif parser_node^.kind = ElnaTreeKind.array_access_expression then elna_type_array_access_expression(parser_node, symbol_table) elsif parser_node^.kind = ElnaTreeKind.call then elna_type_call(parser_node, symbol_table) else elna_type_simple_expression(parser_node, symbol_table) end end; proc elna_type_unary_expression(parser_node: ^ElnaTreeUnaryExpression, symbol_table: Word); var unary_operand: ^ElnaTreeExpression; begin if parser_node^.kind = ElnaTreeKind.unary_expression then unary_operand := parser_node^.operand; elna_type_designator(unary_operand, symbol_table); parser_node^.type_decoration := unary_operand^.type_decoration else elna_type_designator(parser_node, symbol_table) end end; proc elna_type_binary_expression(parser_node: ^ElnaTreeBinaryExpression, symbol_table: Word); var binary_operand: ^ElnaTreeExpression; begin if parser_node^.kind = ElnaTreeKind.binary_expression then elna_type_unary_expression(parser_node^.rhs, symbol_table); binary_operand := parser_node^.lhs; elna_type_unary_expression(binary_operand, symbol_table); parser_node^.type_decoration := binary_operand^.type_decoration else elna_type_unary_expression(parser_node, symbol_table) end end; proc elna_type_procedure_declaration(parser_node: ^ElnaTreeProcedureDeclaration); var procedure_info: ^ElnaSymbolProcedureInfo; symbol_table: Word; begin procedure_info := _symbol_table_lookup(@symbol_table_global, parser_node^.name, parser_node^.length); symbol_table := procedure_info^.symbol_table; elna_type_statements(parser_node^.body, symbol_table) end; proc elna_name_module_declaration(parser_node: ^ElnaTreeModuleDeclaration); var current_part: ^ElnaTreeDeclaration; begin current_part := parser_node^.types; .elna_name_module_declaration_type; if current_part <> nil then elna_name_type_declaration(current_part); current_part := current_part^.next; goto elna_name_module_declaration_type end; current_part := parser_node^.globals; .elna_name_module_declaration_global; if current_part <> nil then elna_name_procedure_temporary(current_part, 0, @symbol_table_global); current_part := current_part^.next; goto elna_name_module_declaration_global end; current_part := parser_node^.procedures; .elna_name_module_declaration_procedure; if current_part <> nil then elna_name_procedure_declaration(current_part); current_part := current_part^.next; goto elna_name_module_declaration_procedure end end; proc elna_type_module_declaration(parser_node: ^ElnaTreeModuleDeclaration); var current_part: ^ElnaTreeDeclaration; begin current_part := parser_node^.types; .elna_type_module_declaration_type; if current_part <> 0 then elna_type_type_declaration(current_part); current_part := current_part^.next; goto elna_type_module_declaration_type end; current_part := parser_node^.procedures; .elna_type_module_declaration_procedure; if current_part <> 0 then elna_type_procedure_declaration(current_part); current_part := current_part^.next; goto elna_type_module_declaration_procedure end end; proc _compile(); var parser_node: Word; tac: Word; rtl: Word; error_list: ^ElnaErrorList; compiled: Word; begin error_list := malloc(#size(ElnaErrorList)); error_list^.first := nil; error_list^.last := nil; parser_node := elna_parser_module_declaration(error_list); compiled := error_list^.first = nil; if compiled then elna_name_module_declaration(parser_node); elna_type_module_declaration(parser_node); tac := elna_tac_module_declaration(parser_node); rtl := elna_rtl_module_declaration(tac); elna_alloc_module(rtl); elna_riscv_module(rtl); end; return compiled end; (** * Looks for a symbol in the given symbol table. * * Parameters: * symbol_table - Symbol table. * symbol_name - Symbol name pointer. * name_length - Symbol name length. * * Returns the symbol pointer or 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: ^ElnaSymbolTypeInfo; current_type: ^ElnaType; begin (* Set the table length to 0. *) symbol_table_global := 0; (* Enter built-in symbols. *) current_type := malloc(#size(ElnaType)); current_type^.kind := ElnaTypeKind.primitive; current_type^.size := 4; current_info := _type_info_create(current_type); _symbol_table_enter(@symbol_table_global, "Word", 4, current_info); current_type := malloc(#size(ElnaType)); current_type^.kind := ElnaTypeKind.primitive; current_type^.size := 4; current_info := _type_info_create(current_type); _symbol_table_enter(@symbol_table_global, "Pointer", 7, current_info); current_type := malloc(#size(ElnaType)); current_type^.kind := ElnaTypeKind.primitive; current_type^.size := 1; current_info := _type_info_create(current_type); _symbol_table_enter(@symbol_table_global, "Bool", 4, current_info); end; (** * Initializes the array with character classes. *) proc _elna_lexer_classifications(); var code: Word; begin classification[1] := ElnaLexerClass.eof; classification[2] := ElnaLexerClass.invalid; classification[3] := ElnaLexerClass.invalid; classification[4] := ElnaLexerClass.invalid; classification[5] := ElnaLexerClass.invalid; classification[6] := ElnaLexerClass.invalid; classification[7] := ElnaLexerClass.invalid; classification[8] := ElnaLexerClass.invalid; classification[9] := ElnaLexerClass.invalid; classification[10] := ElnaLexerClass.space; classification[11] := ElnaLexerClass.space; classification[12] := ElnaLexerClass.invalid; classification[13] := ElnaLexerClass.invalid; classification[14] := ElnaLexerClass.space; classification[15] := ElnaLexerClass.invalid; classification[16] := ElnaLexerClass.invalid; classification[17] := ElnaLexerClass.invalid; classification[18] := ElnaLexerClass.invalid; classification[19] := ElnaLexerClass.invalid; classification[20] := ElnaLexerClass.invalid; classification[21] := ElnaLexerClass.invalid; classification[22] := ElnaLexerClass.invalid; classification[23] := ElnaLexerClass.invalid; classification[24] := ElnaLexerClass.invalid; classification[25] := ElnaLexerClass.invalid; classification[26] := ElnaLexerClass.invalid; classification[27] := ElnaLexerClass.invalid; classification[28] := ElnaLexerClass.invalid; classification[29] := ElnaLexerClass.invalid; classification[30] := ElnaLexerClass.invalid; classification[31] := ElnaLexerClass.invalid; classification[32] := ElnaLexerClass.invalid; classification[33] := ElnaLexerClass.space; classification[34] := ElnaLexerClass.single; classification[35] := ElnaLexerClass.double_quote; classification[36] := ElnaLexerClass.number_sign; classification[37] := ElnaLexerClass.other; classification[38] := ElnaLexerClass.single; classification[39] := ElnaLexerClass.single; classification[40] := ElnaLexerClass.single_quote; classification[41] := ElnaLexerClass.left_paren; classification[42] := ElnaLexerClass.right_paren; classification[43] := ElnaLexerClass.asterisk; classification[44] := ElnaLexerClass.single; classification[45] := ElnaLexerClass.single; classification[46] := ElnaLexerClass.minus; classification[47] := ElnaLexerClass.dot; classification[48] := ElnaLexerClass.single; classification[49] := ElnaLexerClass.zero; classification[50] := ElnaLexerClass.digit; classification[51] := ElnaLexerClass.digit; classification[52] := ElnaLexerClass.digit; classification[53] := ElnaLexerClass.digit; classification[54] := ElnaLexerClass.digit; classification[55] := ElnaLexerClass.digit; classification[56] := ElnaLexerClass.digit; classification[57] := ElnaLexerClass.digit; classification[58] := ElnaLexerClass.digit; classification[59] := ElnaLexerClass.colon; classification[60] := ElnaLexerClass.single; classification[61] := ElnaLexerClass.less; classification[62] := ElnaLexerClass.equals; classification[63] := ElnaLexerClass.greater; classification[64] := ElnaLexerClass.other; classification[65] := ElnaLexerClass.single; classification[66] := ElnaLexerClass.alpha; classification[67] := ElnaLexerClass.alpha; classification[68] := ElnaLexerClass.alpha; classification[69] := ElnaLexerClass.alpha; classification[70] := ElnaLexerClass.alpha; classification[71] := ElnaLexerClass.alpha; classification[72] := ElnaLexerClass.alpha; classification[73] := ElnaLexerClass.alpha; classification[74] := ElnaLexerClass.alpha; classification[75] := ElnaLexerClass.alpha; classification[76] := ElnaLexerClass.alpha; classification[77] := ElnaLexerClass.alpha; classification[78] := ElnaLexerClass.alpha; classification[79] := ElnaLexerClass.alpha; classification[80] := ElnaLexerClass.alpha; classification[81] := ElnaLexerClass.alpha; classification[82] := ElnaLexerClass.alpha; classification[83] := ElnaLexerClass.alpha; classification[84] := ElnaLexerClass.alpha; classification[85] := ElnaLexerClass.alpha; classification[86] := ElnaLexerClass.alpha; classification[87] := ElnaLexerClass.alpha; classification[88] := ElnaLexerClass.alpha; classification[89] := ElnaLexerClass.alpha; classification[90] := ElnaLexerClass.alpha; classification[91] := ElnaLexerClass.alpha; classification[92] := ElnaLexerClass.single; classification[93] := ElnaLexerClass.backslash; classification[94] := ElnaLexerClass.single; classification[95] := ElnaLexerClass.single; classification[96] := ElnaLexerClass.alpha; classification[97] := ElnaLexerClass.other; classification[98] := ElnaLexerClass.hex; classification[99] := ElnaLexerClass.hex; classification[100] := ElnaLexerClass.hex; classification[101] := ElnaLexerClass.hex; classification[102] := ElnaLexerClass.hex; classification[103] := ElnaLexerClass.hex; classification[104] := ElnaLexerClass.alpha; classification[105] := ElnaLexerClass.alpha; classification[106] := ElnaLexerClass.alpha; classification[107] := ElnaLexerClass.alpha; classification[108] := ElnaLexerClass.alpha; classification[109] := ElnaLexerClass.alpha; classification[110] := ElnaLexerClass.alpha; classification[111] := ElnaLexerClass.alpha; classification[112] := ElnaLexerClass.alpha; classification[113] := ElnaLexerClass.alpha; classification[114] := ElnaLexerClass.alpha; classification[115] := ElnaLexerClass.alpha; classification[116] := ElnaLexerClass.alpha; classification[117] := ElnaLexerClass.alpha; classification[118] := ElnaLexerClass.alpha; classification[119] := ElnaLexerClass.alpha; classification[120] := ElnaLexerClass.alpha; classification[121] := ElnaLexerClass.x; classification[122] := ElnaLexerClass.alpha; classification[123] := ElnaLexerClass.alpha; classification[124] := ElnaLexerClass.other; classification[125] := ElnaLexerClass.single; classification[126] := ElnaLexerClass.other; classification[127] := ElnaLexerClass.single; classification[128] := ElnaLexerClass.invalid; code := 129; (* Set the remaining 129 - 256 bytes to transitionClassOther. *) .create_classification_loop; 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 row_position: Word; column_position: Word; target: Word; begin (* Each state is 8 bytes long (2 words: action and next state). There are 23 character classes, so a transition row 8 * 23 = 184 bytes long. *) row_position := current_state - 1; row_position := row_position * 184; column_position := character_class - 1; column_position := column_position * 8; target := @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); _elna_lexer_set_transition(current_state, ElnaLexerClass.number_sign, default_action, next_state) end; (** * The transition table describes transitions from one state to another, given * a symbol (character class). * * The table has m rows and n columns, where m is the amount of states and n is * the amount of classes. So given the current state and a classified character * the table can be used to look up the next state. *) proc _elna_lexer_transitions(); begin (* Start state. *) _elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.invalid, ElnaLexerAction.none, ElnaLexerState.finish); _elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.digit, ElnaLexerAction.accumulate, ElnaLexerState.decimal); _elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.alpha, ElnaLexerAction.accumulate, ElnaLexerState.identifier); _elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.space, ElnaLexerAction.skip, ElnaLexerState.start); _elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.colon, ElnaLexerAction.accumulate, ElnaLexerState.colon); _elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.equals, ElnaLexerAction.single, ElnaLexerState.finish); _elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.left_paren, ElnaLexerAction.accumulate, ElnaLexerState.left_paren); _elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.right_paren, ElnaLexerAction.single, ElnaLexerState.finish); _elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.asterisk, ElnaLexerAction.single, ElnaLexerState.finish); _elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.backslash, ElnaLexerAction.none, ElnaLexerState.finish); _elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.single, ElnaLexerAction.single, ElnaLexerState.finish); _elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.hex, ElnaLexerAction.accumulate, ElnaLexerState.identifier); _elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.zero, ElnaLexerAction.accumulate, ElnaLexerState.leading_zero); _elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.x, ElnaLexerAction.accumulate, ElnaLexerState.identifier); _elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.eof, ElnaLexerAction.eof, ElnaLexerState.finish); _elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.dot, ElnaLexerAction.single, ElnaLexerState.finish); _elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.minus, ElnaLexerAction.accumulate, ElnaLexerState.minus); _elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.single_quote, ElnaLexerAction.accumulate, ElnaLexerState.character); _elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.double_quote, ElnaLexerAction.accumulate, ElnaLexerState.string); _elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.greater, ElnaLexerAction.accumulate, ElnaLexerState.greater); _elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.less, ElnaLexerAction.accumulate, ElnaLexerState.less); _elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.other, ElnaLexerAction.none, ElnaLexerState.finish); _elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.number_sign, ElnaLexerAction.accumulate, ElnaLexerState.number_sign); (* Colon state. *) _elna_lexer_default_transition(ElnaLexerState.colon, ElnaLexerAction.finalize, ElnaLexerState.finish); _elna_lexer_set_transition(ElnaLexerState.colon, ElnaLexerClass.equals, ElnaLexerAction.composite, ElnaLexerState.finish); (* Identifier state. *) _elna_lexer_default_transition(ElnaLexerState.identifier, ElnaLexerAction.key_id, ElnaLexerState.finish); _elna_lexer_set_transition(ElnaLexerState.identifier, ElnaLexerClass.digit, ElnaLexerAction.accumulate, ElnaLexerState.identifier); _elna_lexer_set_transition(ElnaLexerState.identifier, ElnaLexerClass.alpha, ElnaLexerAction.accumulate, ElnaLexerState.identifier); _elna_lexer_set_transition(ElnaLexerState.identifier, ElnaLexerClass.hex, ElnaLexerAction.accumulate, ElnaLexerState.identifier); _elna_lexer_set_transition(ElnaLexerState.identifier, ElnaLexerClass.zero, ElnaLexerAction.accumulate, ElnaLexerState.identifier); _elna_lexer_set_transition(ElnaLexerState.identifier, ElnaLexerClass.x, ElnaLexerAction.accumulate, ElnaLexerState.identifier); (* Decimal state. *) _elna_lexer_default_transition(ElnaLexerState.decimal, ElnaLexerAction.integer, ElnaLexerState.finish); _elna_lexer_set_transition(ElnaLexerState.decimal, ElnaLexerClass.digit, ElnaLexerAction.accumulate, ElnaLexerState.decimal); _elna_lexer_set_transition(ElnaLexerState.decimal, ElnaLexerClass.alpha, ElnaLexerAction.none, ElnaLexerState.finish); _elna_lexer_set_transition(ElnaLexerState.decimal, ElnaLexerClass.hex, ElnaLexerAction.none, ElnaLexerState.finish); _elna_lexer_set_transition(ElnaLexerState.decimal, ElnaLexerClass.zero, ElnaLexerAction.accumulate, ElnaLexerState.decimal); _elna_lexer_set_transition(ElnaLexerState.decimal, ElnaLexerClass.x, ElnaLexerAction.none, ElnaLexerState.finish); (* Leading zero. *) _elna_lexer_default_transition(ElnaLexerState.leading_zero, ElnaLexerAction.integer, ElnaLexerState.finish); _elna_lexer_set_transition(ElnaLexerState.leading_zero, ElnaLexerClass.digit, ElnaLexerAction.none, ElnaLexerState.finish); _elna_lexer_set_transition(ElnaLexerState.leading_zero, ElnaLexerClass.alpha, ElnaLexerAction.none, ElnaLexerState.finish); _elna_lexer_set_transition(ElnaLexerState.leading_zero, ElnaLexerClass.hex, ElnaLexerAction.none, ElnaLexerState.finish); _elna_lexer_set_transition(ElnaLexerState.leading_zero, ElnaLexerClass.zero, ElnaLexerAction.none, ElnaLexerState.finish); _elna_lexer_set_transition(ElnaLexerState.leading_zero, ElnaLexerClass.x, ElnaLexerAction.none, ElnaLexerState.dot); (* Greater state. *) _elna_lexer_default_transition(ElnaLexerState.greater, ElnaLexerAction.finalize, ElnaLexerState.finish); _elna_lexer_set_transition(ElnaLexerState.greater, ElnaLexerClass.equals, ElnaLexerAction.composite, ElnaLexerState.finish); (* Minus state. *) _elna_lexer_default_transition(ElnaLexerState.minus, ElnaLexerAction.finalize, ElnaLexerState.finish); _elna_lexer_set_transition(ElnaLexerState.minus, ElnaLexerClass.greater, ElnaLexerAction.composite, ElnaLexerState.finish); (* Left paren state. *) _elna_lexer_default_transition(ElnaLexerState.left_paren, ElnaLexerAction.finalize, ElnaLexerState.finish); _elna_lexer_set_transition(ElnaLexerState.left_paren, ElnaLexerClass.asterisk, ElnaLexerAction.accumulate, ElnaLexerState.comment); (* Less state. *) _elna_lexer_default_transition(ElnaLexerState.less, ElnaLexerAction.finalize, ElnaLexerState.finish); _elna_lexer_set_transition(ElnaLexerState.less, ElnaLexerClass.equals, ElnaLexerAction.composite, ElnaLexerState.finish); _elna_lexer_set_transition(ElnaLexerState.less, ElnaLexerClass.greater, ElnaLexerAction.composite, ElnaLexerState.finish); (* Hexadecimal after 0x. *) _elna_lexer_default_transition(ElnaLexerState.dot, ElnaLexerAction.finalize, ElnaLexerState.finish); _elna_lexer_set_transition(ElnaLexerState.dot, ElnaLexerClass.dot, ElnaLexerAction.composite, ElnaLexerState.finish); (* Comment. *) _elna_lexer_default_transition(ElnaLexerState.comment, ElnaLexerAction.accumulate, ElnaLexerState.comment); _elna_lexer_set_transition(ElnaLexerState.comment, ElnaLexerClass.asterisk, ElnaLexerAction.accumulate, ElnaLexerState.closing_comment); _elna_lexer_set_transition(ElnaLexerState.comment, ElnaLexerClass.eof, ElnaLexerAction.none, ElnaLexerState.finish); (* Closing comment. *) _elna_lexer_default_transition(ElnaLexerState.closing_comment, ElnaLexerAction.accumulate, ElnaLexerState.comment); _elna_lexer_set_transition(ElnaLexerState.closing_comment, ElnaLexerClass.invalid, ElnaLexerAction.none, ElnaLexerState.finish); _elna_lexer_set_transition(ElnaLexerState.closing_comment, ElnaLexerClass.right_paren, ElnaLexerAction.delimited, ElnaLexerState.finish); _elna_lexer_set_transition(ElnaLexerState.closing_comment, ElnaLexerClass.asterisk, ElnaLexerAction.accumulate, ElnaLexerState.closing_comment); _elna_lexer_set_transition(ElnaLexerState.closing_comment, ElnaLexerClass.eof, ElnaLexerAction.none, ElnaLexerState.finish); (* Character. *) _elna_lexer_default_transition(ElnaLexerState.character, ElnaLexerAction.accumulate, ElnaLexerState.character); _elna_lexer_set_transition(ElnaLexerState.character, ElnaLexerClass.invalid, ElnaLexerAction.none, ElnaLexerState.finish); _elna_lexer_set_transition(ElnaLexerState.character, ElnaLexerClass.eof, ElnaLexerAction.none, ElnaLexerState.finish); _elna_lexer_set_transition(ElnaLexerState.character, ElnaLexerClass.single_quote, ElnaLexerAction.delimited, ElnaLexerState.finish); _elna_lexer_set_transition(ElnaLexerState.character, ElnaLexerClass.backslash, ElnaLexerAction.accumulate, ElnaLexerState.character_escape); (* Escape sequence in a character. *) _elna_lexer_default_transition(ElnaLexerState.character_escape, ElnaLexerAction.accumulate, ElnaLexerState.character); _elna_lexer_set_transition(ElnaLexerState.character_escape, ElnaLexerClass.invalid, ElnaLexerAction.none, ElnaLexerState.finish); _elna_lexer_set_transition(ElnaLexerState.character_escape, ElnaLexerClass.eof, ElnaLexerAction.none, ElnaLexerState.finish); (* String. *) _elna_lexer_default_transition(ElnaLexerState.string, ElnaLexerAction.accumulate, ElnaLexerState.string); _elna_lexer_set_transition(ElnaLexerState.string, ElnaLexerClass.invalid, ElnaLexerAction.none, ElnaLexerState.finish); _elna_lexer_set_transition(ElnaLexerState.string, ElnaLexerClass.eof, ElnaLexerAction.none, ElnaLexerState.finish); _elna_lexer_set_transition(ElnaLexerState.string, ElnaLexerClass.double_quote, ElnaLexerAction.delimited, ElnaLexerState.finish); _elna_lexer_set_transition(ElnaLexerState.string, ElnaLexerClass.backslash, ElnaLexerAction.accumulate, ElnaLexerState.string_escape); (* Escape sequence in a string. *) _elna_lexer_default_transition(ElnaLexerState.string_escape, ElnaLexerAction.accumulate, ElnaLexerState.string); _elna_lexer_set_transition(ElnaLexerState.string_escape, ElnaLexerClass.invalid, ElnaLexerAction.none, ElnaLexerState.finish); _elna_lexer_set_transition(ElnaLexerState.string_escape, ElnaLexerClass.eof, ElnaLexerAction.none, ElnaLexerState.finish); (* Number sign state. *) _elna_lexer_default_transition(ElnaLexerState.number_sign, ElnaLexerAction.key_id, ElnaLexerState.finish); _elna_lexer_set_transition(ElnaLexerState.number_sign, ElnaLexerClass.alpha, ElnaLexerAction.accumulate, ElnaLexerState.trait); _elna_lexer_set_transition(ElnaLexerState.number_sign, ElnaLexerClass.hex, ElnaLexerAction.accumulate, ElnaLexerState.trait); _elna_lexer_set_transition(ElnaLexerState.number_sign, ElnaLexerClass.x, ElnaLexerAction.accumulate, ElnaLexerState.trait); (* Trait state. *) _elna_lexer_default_transition(ElnaLexerState.trait, ElnaLexerAction.key_id, ElnaLexerState.finish); _elna_lexer_set_transition(ElnaLexerState.trait, ElnaLexerClass.digit, ElnaLexerAction.accumulate, ElnaLexerState.trait); _elna_lexer_set_transition(ElnaLexerState.trait, ElnaLexerClass.alpha, ElnaLexerAction.accumulate, ElnaLexerState.trait); _elna_lexer_set_transition(ElnaLexerState.trait, ElnaLexerClass.hex, ElnaLexerAction.accumulate, ElnaLexerState.trait); _elna_lexer_set_transition(ElnaLexerState.trait, ElnaLexerClass.zero, ElnaLexerAction.accumulate, ElnaLexerState.trait); _elna_lexer_set_transition(ElnaLexerState.trait, ElnaLexerClass.x, ElnaLexerAction.accumulate, ElnaLexerState.trait) end; 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(); begin (* Transition start state is 1. *) lexer_state.state := ElnaLexerState.start; lexer_state.finish := lexer_state.start end; (** * One time lexer initialization. *) proc _elna_lexer_initialize(code_pointer: Word); begin _elna_lexer_classifications(); _elna_lexer_transitions(); lexer_state.start := code_pointer; lexer_state.finish := code_pointer end; proc _elna_lexer_next_transition(); var current_character: Word; begin current_character := _load_byte(lexer_state.finish); return _elna_lexer_get_transition(lexer_state.state, classification[current_character + 1]) 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 _load_byte(position_start) = '#' then result := ElnaLexerKind.trait elsif 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 elsif string_compare(position_start, token_length, "true", 4) then result := ElnaLexerKind.boolean elsif string_compare(position_start, token_length, "false", 5) then result := ElnaLexerKind.boolean 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 elsif character = '[' then result := ElnaLexerKind.left_square elsif character = ']' then result := ElnaLexerKind.right_square 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 intermediate: Word; begin if action_to_perform = ElnaLexerAction.none then elsif action_to_perform = ElnaLexerAction.accumulate then lexer_state.finish := lexer_state.finish + 1 elsif action_to_perform = ElnaLexerAction.skip then lexer_state.start := lexer_state.start + 1; lexer_state.finish := lexer_state.finish + 1 elsif action_to_perform = ElnaLexerAction.single then lexer_state.finish := lexer_state.finish + 1; intermediate := _elna_lexer_classify_single(lexer_state.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(lexer_state.start); kind^ := intermediate elsif action_to_perform = ElnaLexerAction.composite then intermediate := _elna_lexer_classify_composite(lexer_state.start, lexer_state.finish); kind^ := intermediate; lexer_state.finish := lexer_state.finish + 1 elsif action_to_perform = ElnaLexerAction.key_id then intermediate := _elna_lexer_classify_keyword(lexer_state.start, lexer_state.finish); kind^ := intermediate elsif action_to_perform = ElnaLexerAction.integer then intermediate := _elna_lexer_classify_integer(lexer_state.start, lexer_state.finish); kind^ := intermediate elsif action_to_perform = ElnaLexerAction.delimited then lexer_state.finish := lexer_state.finish + 1; intermediate := _elna_lexer_classify_delimited(lexer_state.start, lexer_state.finish); 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 := @lexer_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(); begin lexer_state.start := lexer_state.finish end; proc _initialize_global_state(); begin compiler_strings_position := @compiler_strings; source_code := malloc(495616); symbol_table_store := malloc(4194304); 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(0, offset, 409600); if last_read > 0 then offset := offset + last_read; goto start_read end; if _compile() then exit(0) else exit(4) end; end;