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 'open3'
require 'rake/clean' 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' CLEAN.include 'build/boot', 'build/valid'
@@ -39,17 +41,9 @@ end
desc 'Convert previous stage language into the current stage language' desc 'Convert previous stage language into the current stage language'
task :convert do task :convert do
File.open('boot/stage9.elna', 'w') do |current_stage| File.open('boot/stage10.elna', 'w') do |current_stage|
File.readlines('boot/stage8.elna').each do |line| File.readlines('boot/stage9.elna').each do |line|
comment_match = /^(\s*)#(.*)/.match line current_stage << 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
end end
end 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. # - Procedure calls in expressions.
# - Comments between (* and *) are supported. These are still single line # - Comments between (* and *) are supported. These are still single line
# comments and they should be on a separate 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 const
symbol_builtin_name_int := "Int"; symbol_builtin_name_int := "Int";
symbol_builtin_name_word := "Word"; symbol_builtin_name_word := "Word";
@@ -1403,7 +1407,13 @@ begin
_skip_empty_lines(); _skip_empty_lines();
_compile_var_part(); _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: .compile_module_loop:
_skip_empty_lines(); _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 *) (* 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/. *) (* obtain one at https://mozilla.org/MPL/2.0/. *)
(* Stage 8 compiler. *) (* Stage 9 compiler. *)
(* - Procedure calls in expressions. *) (* - Procedure calls in expressions. *)
(* - Comments between (* and *) are supported. These are still single line *) (* - Comments between (* and *) are supported. These are still single line *)
(* comments and they should be on a separate line. *) (* comments and they should be on a separate line. *)
(* - if-else statements. *)
const const
symbol_builtin_name_int := "Int"; symbol_builtin_name_int := "Int";
symbol_builtin_name_word := "Word"; symbol_builtin_name_word := "Word";
@@ -45,6 +46,7 @@ var
compiler_strings_position: Pointer := @compiler_strings; compiler_strings_position: Pointer := @compiler_strings;
compiler_strings_length: Word := 0; compiler_strings_length: Word := 0;
label_counter: Word := 0;
source_code_position: Pointer := @source_code; source_code_position: Pointer := @source_code;
(* Calculates and returns the string token length between quotes, including the *) (* Calculates and returns the string token length between quotes, including the *)
@@ -93,16 +95,12 @@ begin
beq t1, t2, .add_string_end beq t1, t2, .add_string_end
la t2, compiler_strings_position v8 := _load_byte(v0);
lw t3, (t2) _store_byte(v8, compiler_strings_position);
sb t1, (t3) _store_word(compiler_strings_position + 1, @compiler_strings_position);
v0 := v0 + 1;
addi t3, t3, 1
sw t3, (t2)
addi t0, t0, 1
sw t0, 0(sp)
lb t1, 8(sp)
li t2, '\\' li t2, '\\'
bne t1, t2, .add_string_increment bne t1, t2, .add_string_increment
@@ -127,13 +125,7 @@ end;
(* Returns the amount of bytes written in a0. *) (* Returns the amount of bytes written in a0. *)
proc _read_file(); proc _read_file();
begin begin
mv a2, a1 _syscall(0, v88, v84, 0, 0, 0, 63);
mv a1, a0
(* STDIN. *)
li a0, 0
(* SYS_READ. *)
li a7, 63
ecall
end; end;
(* Writes to the standard output. *) (* Writes to the standard output. *)
@@ -143,13 +135,7 @@ end;
(* a1 - Buffer length. *) (* a1 - Buffer length. *)
proc _write_s(); proc _write_s();
begin begin
mv a2, a1 _syscall(1, v88, v84, 0, 0, 0, 64);
mv a1, a0
(* STDOUT. *)
li a0, 1
(* SYS_WRITE. *)
li a7, 64
ecall
end; end;
(* Writes a number to a string buffer. *) (* Writes a number to a string buffer. *)
@@ -317,9 +303,8 @@ begin
li t1, '.' li t1, '.'
beq t0, t1, .read_token_next beq t0, t1, .read_token_next
lw a0, 0(sp) v8 := _load_byte(v0);
lb a0, (a0) _is_alnum(v8);
_is_alnum();
bnez a0, .read_token_next bnez a0, .read_token_next
goto .read_token_end; goto .read_token_end;
@@ -464,10 +449,8 @@ begin
_advance_token(1); _advance_token(1);
.compile_character_literal_end: .compile_character_literal_end:
la t0, source_code_position v0 := _load_byte(source_code_position);
lw t0, (t0) _write_c(v0);
lb a0, (t0)
_write_c();
_write_c('\''); _write_c('\'');
_write_c('\n'); _write_c('\n');
@@ -523,10 +506,8 @@ end;
proc _compile_term(); proc _compile_term();
begin begin
la t0, source_code_position v0 := _load_byte(source_code_position);
lw t0, (t0) lb a0, 0(sp)
lb a0, (t0)
sw a0, 0(sp)
li t1, '\'' li t1, '\''
beq a0, t1, .compile_term_character_literal beq a0, t1, .compile_term_character_literal
@@ -925,13 +906,72 @@ begin
_write_z("mv a0, t0\n\0"); _write_z("mv a0, t0\n\0");
end; 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(); proc _compile_statement();
begin begin
_skip_spaces();
(* This is a call if the statement starts with an underscore. *) (* This is a call if the statement starts with an underscore. *)
la t0, source_code_position la t0, source_code_position
lw t0, (t0) lw t0, (t0)
(* First character after alignment tab. *) (* First character after alignment tab. *)
addi t0, t0, 1 (* addi t0, t0, 1 *)
lb t0, (t0) lb t0, (t0)
li t1, '_' li t1, '_'
@@ -943,33 +983,42 @@ begin
li t1, 'v' li t1, 'v'
beq t0, t1, .compile_statement_assignment 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. *) (* 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 beqz a0, .compile_statement_return
_compile_line(); _compile_line();
goto .compile_statement_end; goto .compile_statement_end;
.compile_statement_call: .compile_statement_call:
_advance_token(1); (* _advance_token(1); *)
_compile_call(); _compile_call();
goto .compile_statement_semicolon; goto .compile_statement_semicolon;
.compile_statement_goto: .compile_statement_goto:
_advance_token(1); (* _advance_token(1); *)
_compile_goto(); _compile_goto();
goto .compile_statement_semicolon; goto .compile_statement_semicolon;
.compile_statement_assignment: .compile_statement_assignment:
_advance_token(1); (* _advance_token(1); *)
_compile_assignment(); _compile_assignment();
goto .compile_statement_semicolon; goto .compile_statement_semicolon;
.compile_statement_if:
(* _advance_token(1); *)
_compile_if();
goto .compile_statement_semicolon;
.compile_statement_return: .compile_statement_return:
_advance_token(1); (* _advance_token(1); *)
_compile_return_statement(); _compile_return_statement();
_write_c('\n'); _write_c('\n');
@@ -986,10 +1035,12 @@ proc _compile_procedure_body();
begin begin
.compile_procedure_body_loop: .compile_procedure_body_loop:
_skip_empty_lines(); _skip_empty_lines();
_skip_spaces();
(* 3 is "end" length. *)
_memcmp(source_code_position, "end", 3); _memcmp(source_code_position, "end", 3);
beqz a0, .compile_procedure_body_epilogue
_memcmp(source_code_position, "else", 4);
beqz a0, .compile_procedure_body_epilogue beqz a0, .compile_procedure_body_epilogue
_compile_statement(); _compile_statement();
@@ -1065,24 +1116,24 @@ begin
_advance_token(5); _advance_token(5);
end; end;
proc _skip_newlines(); proc _skip_spaces();
begin begin
(* Skip newlines. *) (* Skip newlines. *)
la t0, source_code_position la t0, source_code_position
lw t1, (t0) lw t1, (t0)
.skip_newlines_loop: .skip_spaces_loop:
lb t2, (t1) lb t2, (t1)
li t3, '\n' li t3, '\t'
bne t2, t3, .skip_newlines_end bne t2, t3, .skip_spaces_end
beqz t2, .skip_newlines_end beqz t2, .skip_spaces_end
addi t1, t1, 1 addi t1, t1, 1
sw t1, (t0) sw t1, (t0)
goto .skip_newlines_loop; goto .skip_spaces_loop;
.skip_newlines_end: .skip_spaces_end:
end; end;
(* Prints and skips a line. *) (* Prints and skips a line. *)
@@ -1121,9 +1172,6 @@ begin
lw t2, 0(sp) lw t2, 0(sp)
lb t0, (t2) lb t0, (t2)
li t1, '#'
beq t0, t1, .skip_empty_lines_comment
li t1, '\n' li t1, '\n'
beq t0, t1, .skip_empty_lines_newline beq t0, t1, .skip_empty_lines_newline
@@ -1175,10 +1223,8 @@ begin
li t1, '@' li t1, '@'
beq t0, t1, .compile_global_initializer_pointer beq t0, t1, .compile_global_initializer_pointer
la a0, source_code_position v0 := _load_byte(source_code_position);
lw a0, (a0) _is_digit(v0);
lb a0, (a0)
_is_digit();
bnez a0, .compile_global_initializer_number bnez a0, .compile_global_initializer_number
unimp unimp
@@ -1373,7 +1419,13 @@ begin
_skip_empty_lines(); _skip_empty_lines();
_compile_var_part(); _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: .compile_module_loop:
_skip_empty_lines(); _skip_empty_lines();
@@ -1413,10 +1465,9 @@ begin
lw t1, 4(sp) lw t1, 4(sp)
bge t0, t1, .compile_end bge t0, t1, .compile_end
lb a0, (t0) v8 := _load_byte(v0);
addi t0, t0, 1 v0 := v0 + 1;
sw t0, 0(sp) _write_c(v8);
_write_c();
j .compile_loop j .compile_loop
@@ -1431,8 +1482,7 @@ end;
(* a0 - Status code. *) (* a0 - Status code. *)
proc _exit(); proc _exit();
begin begin
li a7, 93 # SYS_EXIT _syscall(0, 0, 0, 0, 0, 0, 93);
ecall
end; end;
(* Inserts a symbol into the table. *) (* Inserts a symbol into the table. *)
@@ -1554,7 +1604,7 @@ begin
sw t1, (t0) sw t1, (t0)
end; end;
proc _initialize_classification(); proc _create_classification();
begin begin
_assign_at(@classification, 1, 15); _assign_at(@classification, 1, 15);
_assign_at(@classification, 2, 1); _assign_at(@classification, 2, 1);
@@ -1688,13 +1738,13 @@ begin
v0 := 129; v0 := 129;
(* Set the remaining 129 - 256 bytes to transitionClassOther. *) (* Set the remaining 129 - 256 bytes to transitionClassOther. *)
.initialize_classification_loop: .create_classification_loop:
_assign_at(@classification, v0, 22); _assign_at(@classification, v0, 22);
v0 := v0 + 1; v0 := v0 + 1;
lw t0, 0(sp) lw t0, 0(sp)
li t1, 257 li t1, 257
blt t0, t1, .initialize_classification_loop blt t0, t1, .create_classification_loop
end; end;
(* Parameters: *) (* Parameters: *)
@@ -1772,7 +1822,7 @@ end;
(* - The next byte is the action that should be performed when transitioning. *) (* - 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 *) (* For the meaning of actions see labels in the lex_next function, which *)
(* handles each action. *) (* handles each action. *)
proc _initialize_transitions(); proc _create_transitions();
begin begin
(* Start state. *) (* Start state. *)
_set_transition(1, 1, 1, 16); _set_transition(1, 1, 1, 16);
@@ -1896,8 +1946,7 @@ end;
(* Gets pointer to the current source text. *) (* Gets pointer to the current source text. *)
proc _lexer_get_current(); proc _lexer_get_current();
begin begin
_lexer_get_state(); v0 := _lexer_get_state();
sw a0, 0(sp)
return v0 + 4 return v0 + 4
end; end;
@@ -1925,8 +1974,8 @@ end;
(* One time lexer initialization. *) (* One time lexer initialization. *)
proc _lexer_initialize(); proc _lexer_initialize();
begin begin
_initialize_classification(); _create_classification();
_initialize_transitions(); _create_transitions();
end; end;
(* Entry point. *) (* Entry point. *)