Implement if-else

This commit is contained in:
2025-09-13 21:15:09 +02:00
parent 216dc59f0b
commit ee3b733a81
4 changed files with 1859 additions and 84 deletions

View File

@@ -6,7 +6,9 @@
require 'open3'
require 'rake/clean'
STAGES = Dir.glob('boot/stage*.elna').collect { |stage| File.basename stage, '.elna' }.sort
STAGES = Dir.glob('boot/stage*.elna')
.collect { |stage| File.basename stage, '.elna' }
.sort { |a, b| a.delete_prefix('stage').to_i <=> b.delete_prefix('stage').to_i }
CLEAN.include 'build/boot', 'build/valid'
@@ -39,17 +41,9 @@ end
desc 'Convert previous stage language into the current stage language'
task :convert do
File.open('boot/stage9.elna', 'w') do |current_stage|
File.readlines('boot/stage8.elna').each do |line|
comment_match = /^(\s*)#(.*)/.match line
if comment_match.nil?
current_stage << line
elsif comment_match[2].empty?
current_stage << "\n"
else
current_stage << "#{comment_match[1]}(* #{comment_match[2].strip} *)\n"
end
File.open('boot/stage10.elna', 'w') do |current_stage|
File.readlines('boot/stage9.elna').each do |line|
current_stage << line
end
end
end

1722
boot/stage10.elna Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -7,6 +7,10 @@
# - Procedure calls in expressions.
# - Comments between (* and *) are supported. These are still single line
# comments and they should be on a separate line.
# - _syscall builtin. _syscall takes 7 arguments,
# the 7th argument gets stored in a7 before invoking ecall.
# Other arguments are saved in a0 through a5.
# - New intrinsics: _load_byte, _load_word, _store_byte, _store_word.
const
symbol_builtin_name_int := "Int";
symbol_builtin_name_word := "Word";
@@ -1403,7 +1407,13 @@ begin
_skip_empty_lines();
_compile_var_part();
_write_z(".section .text\n\0");
_write_z(".section .text\n\n\0");
_write_z(".type _syscall, @function\n_syscall:\n\tmv a7, a6\n\tecall\n\tret\n\n\0");
_write_z(".type _load_byte, @function\n_load_byte:\n\tlb a0, (a0)\nret\n\n\0");
_write_z(".type _load_word, @function\n_load_word:\n\tlw a0, (a0)\nret\n\n\0");
_write_z(".type _store_byte, @function\n_store_byte:\n\tsb a0, (a1)\nret\n\n\0");
_write_z(".type _store_word, @function\n_store_word:\n\tsw a0, (a1)\nret\n\n\0");
.compile_module_loop:
_skip_empty_lines();

View File

@@ -2,11 +2,12 @@
(* 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/. *)
(* Stage 8 compiler. *)
(* Stage 9 compiler. *)
(* - Procedure calls in expressions. *)
(* - Comments between (* and *) are supported. These are still single line *)
(* comments and they should be on a separate line. *)
(* - if-else statements. *)
const
symbol_builtin_name_int := "Int";
symbol_builtin_name_word := "Word";
@@ -45,6 +46,7 @@ var
compiler_strings_position: Pointer := @compiler_strings;
compiler_strings_length: Word := 0;
label_counter: Word := 0;
source_code_position: Pointer := @source_code;
(* Calculates and returns the string token length between quotes, including the *)
@@ -93,16 +95,12 @@ begin
beq t1, t2, .add_string_end
la t2, compiler_strings_position
lw t3, (t2)
sb t1, (t3)
addi t3, t3, 1
sw t3, (t2)
addi t0, t0, 1
sw t0, 0(sp)
v8 := _load_byte(v0);
_store_byte(v8, compiler_strings_position);
_store_word(compiler_strings_position + 1, @compiler_strings_position);
v0 := v0 + 1;
lb t1, 8(sp)
li t2, '\\'
bne t1, t2, .add_string_increment
@@ -127,13 +125,7 @@ end;
(* Returns the amount of bytes written in a0. *)
proc _read_file();
begin
mv a2, a1
mv a1, a0
(* STDIN. *)
li a0, 0
(* SYS_READ. *)
li a7, 63
ecall
_syscall(0, v88, v84, 0, 0, 0, 63);
end;
(* Writes to the standard output. *)
@@ -143,13 +135,7 @@ end;
(* a1 - Buffer length. *)
proc _write_s();
begin
mv a2, a1
mv a1, a0
(* STDOUT. *)
li a0, 1
(* SYS_WRITE. *)
li a7, 64
ecall
_syscall(1, v88, v84, 0, 0, 0, 64);
end;
(* Writes a number to a string buffer. *)
@@ -317,9 +303,8 @@ begin
li t1, '.'
beq t0, t1, .read_token_next
lw a0, 0(sp)
lb a0, (a0)
_is_alnum();
v8 := _load_byte(v0);
_is_alnum(v8);
bnez a0, .read_token_next
goto .read_token_end;
@@ -464,10 +449,8 @@ begin
_advance_token(1);
.compile_character_literal_end:
la t0, source_code_position
lw t0, (t0)
lb a0, (t0)
_write_c();
v0 := _load_byte(source_code_position);
_write_c(v0);
_write_c('\'');
_write_c('\n');
@@ -523,10 +506,8 @@ end;
proc _compile_term();
begin
la t0, source_code_position
lw t0, (t0)
lb a0, (t0)
sw a0, 0(sp)
v0 := _load_byte(source_code_position);
lb a0, 0(sp)
li t1, '\''
beq a0, t1, .compile_term_character_literal
@@ -925,13 +906,72 @@ begin
_write_z("mv a0, t0\n\0");
end;
(* Writes a label, .Ln, where n is a unique number. *)
(* Parameters: *)
(* a0 - Label counter. *)
proc _write_label();
begin
_write_z(".L\0");
_write_i(v88);
end;
proc _compile_if();
begin
(* Skip "if ". *)
_advance_token(3);
(* Compile condition. *)
_compile_expression();
(* Skip " then" with newline. *)
_advance_token(6);
(* v0 is the label after the if statement. *)
v0 := label_counter;
_store_word(label_counter + 1, @label_counter);
(* v4 is the label in front of the next elsif condition or end. *)
v4 := label_counter;
_store_word(label_counter + 1, @label_counter);
_write_z("\tbeqz t0, \0");
_write_label(v4);
_write_c('\n');
_compile_procedure_body();
_write_z("\tj \0");
_write_label(v0);
_write_c('\n');
_write_label(v4);
_write_z(":\n\0");
_memcmp(source_code_position, "end", 3);
beqz a0, .compile_if_end
_memcmp(source_code_position, "else", 3);
beqz a0, .compile_if_else
.compile_if_else:
(* Skip "else" and newline. *)
_advance_token(5);
_compile_procedure_body();
.compile_if_end:
(* Skip "end". *)
_advance_token(3);
_write_label(v0);
_write_z(":\n\0");
end;
proc _compile_statement();
begin
_skip_spaces();
(* This is a call if the statement starts with an underscore. *)
la t0, source_code_position
lw t0, (t0)
(* First character after alignment tab. *)
addi t0, t0, 1
(* addi t0, t0, 1 *)
lb t0, (t0)
li t1, '_'
@@ -943,33 +983,42 @@ begin
li t1, 'v'
beq t0, t1, .compile_statement_assignment
li t1, 'i'
beq t0, t1, .compile_statement_if
(* keyword_ret contains "\tret", so it's 4 bytes long. *)
_memcmp(source_code_position, "\treturn", 7);
_memcmp(source_code_position, "return", 6);
beqz a0, .compile_statement_return
_compile_line();
goto .compile_statement_end;
.compile_statement_call:
_advance_token(1);
(* _advance_token(1); *)
_compile_call();
goto .compile_statement_semicolon;
.compile_statement_goto:
_advance_token(1);
(* _advance_token(1); *)
_compile_goto();
goto .compile_statement_semicolon;
.compile_statement_assignment:
_advance_token(1);
(* _advance_token(1); *)
_compile_assignment();
goto .compile_statement_semicolon;
.compile_statement_if:
(* _advance_token(1); *)
_compile_if();
goto .compile_statement_semicolon;
.compile_statement_return:
_advance_token(1);
(* _advance_token(1); *)
_compile_return_statement();
_write_c('\n');
@@ -986,10 +1035,12 @@ proc _compile_procedure_body();
begin
.compile_procedure_body_loop:
_skip_empty_lines();
_skip_spaces();
(* 3 is "end" length. *)
_memcmp(source_code_position, "end", 3);
beqz a0, .compile_procedure_body_epilogue
_memcmp(source_code_position, "else", 4);
beqz a0, .compile_procedure_body_epilogue
_compile_statement();
@@ -1065,24 +1116,24 @@ begin
_advance_token(5);
end;
proc _skip_newlines();
proc _skip_spaces();
begin
(* Skip newlines. *)
la t0, source_code_position
lw t1, (t0)
.skip_newlines_loop:
.skip_spaces_loop:
lb t2, (t1)
li t3, '\n'
bne t2, t3, .skip_newlines_end
beqz t2, .skip_newlines_end
li t3, '\t'
bne t2, t3, .skip_spaces_end
beqz t2, .skip_spaces_end
addi t1, t1, 1
sw t1, (t0)
goto .skip_newlines_loop;
goto .skip_spaces_loop;
.skip_newlines_end:
.skip_spaces_end:
end;
(* Prints and skips a line. *)
@@ -1121,9 +1172,6 @@ begin
lw t2, 0(sp)
lb t0, (t2)
li t1, '#'
beq t0, t1, .skip_empty_lines_comment
li t1, '\n'
beq t0, t1, .skip_empty_lines_newline
@@ -1175,10 +1223,8 @@ begin
li t1, '@'
beq t0, t1, .compile_global_initializer_pointer
la a0, source_code_position
lw a0, (a0)
lb a0, (a0)
_is_digit();
v0 := _load_byte(source_code_position);
_is_digit(v0);
bnez a0, .compile_global_initializer_number
unimp
@@ -1373,7 +1419,13 @@ begin
_skip_empty_lines();
_compile_var_part();
_write_z(".section .text\n\0");
_write_z(".section .text\n\n\0");
_write_z(".type _syscall, @function\n_syscall:\n\tmv a7, a6\n\tecall\n\tret\n\n\0");
_write_z(".type _load_byte, @function\n_load_byte:\n\tlb a0, (a0)\nret\n\n\0");
_write_z(".type _load_word, @function\n_load_word:\n\tlw a0, (a0)\nret\n\n\0");
_write_z(".type _store_byte, @function\n_store_byte:\n\tsb a0, (a1)\nret\n\n\0");
_write_z(".type _store_word, @function\n_store_word:\n\tsw a0, (a1)\nret\n\n\0");
.compile_module_loop:
_skip_empty_lines();
@@ -1413,10 +1465,9 @@ begin
lw t1, 4(sp)
bge t0, t1, .compile_end
lb a0, (t0)
addi t0, t0, 1
sw t0, 0(sp)
_write_c();
v8 := _load_byte(v0);
v0 := v0 + 1;
_write_c(v8);
j .compile_loop
@@ -1431,8 +1482,7 @@ end;
(* a0 - Status code. *)
proc _exit();
begin
li a7, 93 # SYS_EXIT
ecall
_syscall(0, 0, 0, 0, 0, 0, 93);
end;
(* Inserts a symbol into the table. *)
@@ -1554,7 +1604,7 @@ begin
sw t1, (t0)
end;
proc _initialize_classification();
proc _create_classification();
begin
_assign_at(@classification, 1, 15);
_assign_at(@classification, 2, 1);
@@ -1688,13 +1738,13 @@ begin
v0 := 129;
(* Set the remaining 129 - 256 bytes to transitionClassOther. *)
.initialize_classification_loop:
.create_classification_loop:
_assign_at(@classification, v0, 22);
v0 := v0 + 1;
lw t0, 0(sp)
li t1, 257
blt t0, t1, .initialize_classification_loop
blt t0, t1, .create_classification_loop
end;
(* Parameters: *)
@@ -1772,7 +1822,7 @@ end;
(* - The next byte is the action that should be performed when transitioning. *)
(* For the meaning of actions see labels in the lex_next function, which *)
(* handles each action. *)
proc _initialize_transitions();
proc _create_transitions();
begin
(* Start state. *)
_set_transition(1, 1, 1, 16);
@@ -1896,8 +1946,7 @@ end;
(* Gets pointer to the current source text. *)
proc _lexer_get_current();
begin
_lexer_get_state();
sw a0, 0(sp)
v0 := _lexer_get_state();
return v0 + 4
end;
@@ -1925,8 +1974,8 @@ end;
(* One time lexer initialization. *)
proc _lexer_initialize();
begin
_initialize_classification();
_initialize_transitions();
_create_classification();
_create_transitions();
end;
(* Entry point. *)