diff options
author | pommicket <pommicket@gmail.com> | 2022-01-07 11:29:37 -0500 |
---|---|---|
committer | pommicket <pommicket@gmail.com> | 2022-01-07 11:30:14 -0500 |
commit | 479ff20704ecfb9e78dda1d52375368fbafbabbd (patch) | |
tree | 65a0f82549baed7a63ef0712eab345dac70b5ef2 | |
parent | 519069a89df7f2f704b9ba7052fc80660817115f (diff) |
add 04 to bootstrap, #line
-rw-r--r-- | 04/README.md | 3 | ||||
-rw-r--r-- | 04/in03 | 81 | ||||
-rw-r--r-- | 04a/in03 | 322 | ||||
-rw-r--r-- | 04a/in04 | 0 | ||||
-rwxr-xr-x | bootstrap.sh | 10 |
5 files changed, 81 insertions, 335 deletions
diff --git a/04/README.md b/04/README.md index 6f638b6..a0a7f8d 100644 --- a/04/README.md +++ b/04/README.md @@ -212,6 +212,9 @@ conditionally jump to the specified label. `{operator}` should be one of - `return {rvalue}` - `string {str}` - places a literal string in the code - `byte {number}` - places a literal byte in the code +- `#line {line number} {filename}` / `#line {line number}` - set line number and optionally the filename for future errors (no code is outputted from this) + +The `#line` directive (which also exists in C) seems a bit strange, but its use will be revealed soon. Now let's get down into the weeds: @@ -16,6 +16,13 @@ I=8S A=d2 ?I>A:argv_file_names ; use default input/output filenames + + ; set :filename appropriately + J=:filename + I=:input_filename + C=d0 + call :memccpy + ; open input file J=:input_filename I=d0 @@ -30,7 +37,18 @@ A=d2 J=A ?J<0:output_file_error !:second_pass_starting_point -:argv_file_names +:argv_file_names + ; get filenames from command-line arguments + + ; set :filename appropriately + I=S + ; argv[1] is at *(rsp+16) + I+=d16 + I=8I + J=:filename + C=d0 + call :memccpy + ; open input file J=S ; argv[1] is at *(rsp+16) @@ -174,6 +192,13 @@ D=A ?D!0:handle_string I=:line +J=:"#line" +C=x20 +call :string= +D=A +?D!0:handle_#line + +I=:line J=:"goto" C=x20 call :string= @@ -275,13 +300,18 @@ align ; 5 = length of "byte " I+=d5 call :read_number + ; store away number in rbp + R=A + ; make sure number is immediately followed by newline + C=1I + D=xa + ?C!D:bad_number ; make sure byte is 0-255 - C=A D=xff - ?CaD:bad_byte + ?RaD:bad_byte ; write byte I=:byte - 1I=C + 1I=R J=d4 D=d1 syscall x1 @@ -289,6 +319,35 @@ align :byte reserve d1 +:handle_#line + I=:line + ; 6 = length of "#line " + I+=d6 + call :read_number + ; store line number + D=A + C=:line_number + ; subtract one so that we specify the number of the *next* line + D-=d1 + 8C=D + ; check character after line number + C=1I + D=xa + ; if it's a newline, we're done + ?C=D:read_line + ; otherwise, it'd better be a space + D=x20 + ?C!D:bad_statement + ; set the filename + I+=d1 + J=:filename + C=xa + call :memccpy + ; we want a null-terminated, not newline-terminated filename + J-=d1 + 1J=0 + !:read_line + :handle_string I=:line ; 7 = length of "string " @@ -2139,7 +2198,9 @@ align :program_error R=B - B=:"Line" + B=:filename + call :eputs + B=:":" call :eputs D=:line_number @@ -2155,9 +2216,8 @@ align J=d1 syscall x3c -:"Line" - str Line - x20 +:":" + str : x0 :line_number_separator @@ -2346,6 +2406,9 @@ align :"string" str string x20 +:"#line" + str #line + x20 :"goto" str goto x20 @@ -2408,6 +2471,8 @@ align reserve d8 :line_number reserve d8 +:filename + reserve d80 :global_variables reserve d50000 :local_variables diff --git a/04a/in03 b/04a/in03 deleted file mode 100644 index ed965e6..0000000 --- a/04a/in03 +++ /dev/null @@ -1,322 +0,0 @@ -I=8S -A=d3 -?I!A:usage_error -; open input file - J=S - ; argv[1] is at *(rsp+16) - J+=d16 - J=8J - I=d0 - syscall x2 - J=A - ?J<0:input_file_error -; open output file - J=S - ; argv[2] is at *(rsp+24) - J+=d24 - J=8J - I=x241 - D=x1ed - syscall x2 - J=A - ?J<0:output_file_error -; initialize :definitions_end -J=:definitions_end -D=:definitions -8J=D - -:read_line -; use rbp to store line pointer -R=:line -:read_line_loop - ; read 1 byte into rbp - J=d3 - I=R - D=d1 - syscall x0 - D=A - ?D=0:eof - - ; check if the character was a newline: - C=1R - D=xa - ?C=D:read_line_loop_end - R+=d1 - !:read_line_loop -:read_line_loop_end - -; check if line = "#define " up to a terminator of ' '. -C=x20 -I=:#define -J=:line -call :string= -D=A -?D!0:handle_#define - -; handle a normal line -; R will store a pointer to the current character -R=:line -:process_line_loop - C=1R - B=C - call :isident - ?A!0:process_ident - ; if *R is not an identifier character, just output it to the file. - J=d4 - B=C - call :fputc - ; check if we reached the end of the line - C=1R - D=xa - ?C=D:read_line - ; increment R, keep looping - R+=d1 - !:process_line_loop - :process_ident - ; if *R is an ident char. look up this identifier in :definitions. - ; use C to keep pointer to definition - C=:definitions - :lookup_loop - D=1C - ; check if we reached end of definition table - ?D=0:lookup_loop_end - ; check if this entry matches our identifier - I=R - J=C - call :ident= - ?A!0:perform_substitution - ; if not, skip over this entry - :skip_definition_loop - D=1C - I=xa - C+=d1 - ?I!D:skip_definition_loop - !:lookup_loop - :lookup_loop_end - ; this identifier doesn't match anything in the definitions table; just write it. - ; first, figure out how long it is - J=R - :length_loop - C=1J - B=C - call :isident - ?A=0:length_loop_end - J+=d1 - !:length_loop - :length_loop_end - ; now write it. - I=R - R=J - J-=I - D=J - J=d4 - syscall x1 - ; keep looping - !:process_line_loop - -:perform_substitution - ; right now, I is a pointer to the end of the identifier in :line, - ; and J is a pointer to the end of the identifier in :definitions. - - ; advance :line pointer for next loop iteration - R=I - - J+=d1 - ; J now points to the definition. let's write it - I=J - :definition_end_loop - C=1I - D=xa - ?C=D:definition_end_loop_end - I+=d1 - !:definition_end_loop - :definition_end_loop_end - D=I - D-=J - I=J - J=d4 - syscall x1 - ; process the rest of this line - !:process_line_loop - -:eof - J=d0 - syscall x3c - -; can the character in rbx appear in an identifier? -:isident - A='0 - ?B<A:return_0 - ; note: 58 = '9' + 1 - A=d58 - ?B<A:return_1 - A='A - ?B<A:return_0 - ; note: 91 = 'z' + 1 - A=d91 - ?B<A:return_1 - A='z - ?B>A:return_0 - ; 96 = 'a' - 1 - A=d96 - ?B>A:return_1 - A='_ - ?B=A:return_1 - !:return_0 - - -:handle_#define - J=:definitions_end - J=8J - ; start copy from after "#define" - I=:line - I+=d8 - - :#define_copy_loop - D=1I - 1J=D - I+=d1 - J+=d1 - A=xa - ?D=A:#define_copy_loop_end - !:#define_copy_loop - :#define_copy_loop_end - - ; update end of definitions - D=:definitions_end - 8D=J - ; emit newline so we don't screw up line numbers - J=d4 - I=:newline - D=d1 - syscall x1 - - !:read_line - -:newline - xa - - -:usage_error - B=:usage_error_message - call :error - -:usage_error_message - str Please provide an input and an output file. - xa - x0 - -:input_file_error - B=:input_file_error_message - !:error - -:input_file_error_message - str Couldn't open input file. - xa - x0 - -:output_file_error - B=:output_file_error_message - !:error - -:output_file_error_message - str Couldn't open output file. - xa - x0 - -:error - J=B - call :strlen - D=A - I=J - J=d2 - syscall x1 - J=d1 - syscall x3c - -:strlen - I=B - D=B - :strlen_loop - C=1I - ?C=0:strlen_ret - I+=d1 - !:strlen_loop - :strlen_ret - I-=D - A=I - return - -:#define - str #define - x20 - x0 - -; check if strings in rdi and rsi are equal, up to terminator in rcx -:string= - D=1I - A=1J - ?D!A:return_0 - ?D=C:return_1 - I+=d1 - J+=d1 - !:string= - -; check if strings in rdi and rsi are equal, up to the first non-identifier character -:ident= - D=1I - B=D - call :isident - ; I ended - ?A=0:ident=_I_end - - D=1J - B=D - call :isident - ; J ended, but I didn't - ?A=0:return_0 - - ; we haven't reached the end of either - D=1I - A=1J - ?D!A:return_0 - I+=d1 - J+=d1 - !:ident= -:ident=_I_end - D=1J - B=D - call :isident - ; check if J also ended - ?A=0:return_1 - ; J didn't end - !:return_0 - -:return_0 - A=d0 - return -:return_1 - A=d1 - return - -; write the character in rbx to the file in rdi. -:fputc - C=B - I=S - I-=d1 - 1I=C - D=d1 - syscall x1 - return - - -align -:definitions_end - reserve d8 -:line - reserve d1000 -:definitions - reserve d200000 - -; we shouldn't end the file with a reserve; we don't handle that properly -x00 diff --git a/04a/in04 b/04a/in04 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/04a/in04 diff --git a/bootstrap.sh b/bootstrap.sh index 19410b9..161fa41 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -68,12 +68,12 @@ if [ "$(./out03)" != 'Hello, world!' ]; then fi cd .. -echo 'Processing stage 04a...' -cd 04a +echo 'Processing stage 04...' +cd 04 rm -f out* -make -s out04a -if [ "$(cat out04a)" != "$(printf '\n\nHello, world!')" ]; then - echo_red 'Stage 04a failed.' +make -s out04 +if [ "$(./out04)" != 'Hello, world!' ]; then + echo_red 'Stage 04 failed.' exit 1 fi cd .. |