add 04 to bootstrap, #line
This commit is contained in:
parent
519069a89d
commit
479ff20704
5 changed files with 81 additions and 335 deletions
|
@ -212,6 +212,9 @@ conditionally jump to the specified label. `{operator}` should be one of
|
||||||
- `return {rvalue}`
|
- `return {rvalue}`
|
||||||
- `string {str}` - places a literal string in the code
|
- `string {str}` - places a literal string in the code
|
||||||
- `byte {number}` - places a literal byte 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:
|
Now let's get down into the weeds:
|
||||||
|
|
||||||
|
|
79
04/in03
79
04/in03
|
@ -16,6 +16,13 @@ I=8S
|
||||||
A=d2
|
A=d2
|
||||||
?I>A:argv_file_names
|
?I>A:argv_file_names
|
||||||
; use default input/output filenames
|
; use default input/output filenames
|
||||||
|
|
||||||
|
; set :filename appropriately
|
||||||
|
J=:filename
|
||||||
|
I=:input_filename
|
||||||
|
C=d0
|
||||||
|
call :memccpy
|
||||||
|
|
||||||
; open input file
|
; open input file
|
||||||
J=:input_filename
|
J=:input_filename
|
||||||
I=d0
|
I=d0
|
||||||
|
@ -31,6 +38,17 @@ A=d2
|
||||||
?J<0:output_file_error
|
?J<0:output_file_error
|
||||||
!:second_pass_starting_point
|
!: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
|
; open input file
|
||||||
J=S
|
J=S
|
||||||
; argv[1] is at *(rsp+16)
|
; argv[1] is at *(rsp+16)
|
||||||
|
@ -173,6 +191,13 @@ call :string=
|
||||||
D=A
|
D=A
|
||||||
?D!0:handle_string
|
?D!0:handle_string
|
||||||
|
|
||||||
|
I=:line
|
||||||
|
J=:"#line"
|
||||||
|
C=x20
|
||||||
|
call :string=
|
||||||
|
D=A
|
||||||
|
?D!0:handle_#line
|
||||||
|
|
||||||
I=:line
|
I=:line
|
||||||
J=:"goto"
|
J=:"goto"
|
||||||
C=x20
|
C=x20
|
||||||
|
@ -275,13 +300,18 @@ align
|
||||||
; 5 = length of "byte "
|
; 5 = length of "byte "
|
||||||
I+=d5
|
I+=d5
|
||||||
call :read_number
|
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
|
; make sure byte is 0-255
|
||||||
C=A
|
|
||||||
D=xff
|
D=xff
|
||||||
?CaD:bad_byte
|
?RaD:bad_byte
|
||||||
; write byte
|
; write byte
|
||||||
I=:byte
|
I=:byte
|
||||||
1I=C
|
1I=R
|
||||||
J=d4
|
J=d4
|
||||||
D=d1
|
D=d1
|
||||||
syscall x1
|
syscall x1
|
||||||
|
@ -289,6 +319,35 @@ align
|
||||||
:byte
|
:byte
|
||||||
reserve d1
|
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
|
:handle_string
|
||||||
I=:line
|
I=:line
|
||||||
; 7 = length of "string "
|
; 7 = length of "string "
|
||||||
|
@ -2139,7 +2198,9 @@ align
|
||||||
:program_error
|
:program_error
|
||||||
R=B
|
R=B
|
||||||
|
|
||||||
B=:"Line"
|
B=:filename
|
||||||
|
call :eputs
|
||||||
|
B=:":"
|
||||||
call :eputs
|
call :eputs
|
||||||
|
|
||||||
D=:line_number
|
D=:line_number
|
||||||
|
@ -2155,9 +2216,8 @@ align
|
||||||
J=d1
|
J=d1
|
||||||
syscall x3c
|
syscall x3c
|
||||||
|
|
||||||
:"Line"
|
:":"
|
||||||
str Line
|
str :
|
||||||
x20
|
|
||||||
x0
|
x0
|
||||||
|
|
||||||
:line_number_separator
|
:line_number_separator
|
||||||
|
@ -2346,6 +2406,9 @@ align
|
||||||
:"string"
|
:"string"
|
||||||
str string
|
str string
|
||||||
x20
|
x20
|
||||||
|
:"#line"
|
||||||
|
str #line
|
||||||
|
x20
|
||||||
:"goto"
|
:"goto"
|
||||||
str goto
|
str goto
|
||||||
x20
|
x20
|
||||||
|
@ -2408,6 +2471,8 @@ align
|
||||||
reserve d8
|
reserve d8
|
||||||
:line_number
|
:line_number
|
||||||
reserve d8
|
reserve d8
|
||||||
|
:filename
|
||||||
|
reserve d80
|
||||||
:global_variables
|
:global_variables
|
||||||
reserve d50000
|
reserve d50000
|
||||||
:local_variables
|
:local_variables
|
||||||
|
|
322
04a/in03
322
04a/in03
|
@ -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
|
|
0
04a/in04
Normal file
0
04a/in04
Normal file
10
bootstrap.sh
10
bootstrap.sh
|
@ -68,12 +68,12 @@ if [ "$(./out03)" != 'Hello, world!' ]; then
|
||||||
fi
|
fi
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
echo 'Processing stage 04a...'
|
echo 'Processing stage 04...'
|
||||||
cd 04a
|
cd 04
|
||||||
rm -f out*
|
rm -f out*
|
||||||
make -s out04a
|
make -s out04
|
||||||
if [ "$(cat out04a)" != "$(printf '\n\nHello, world!')" ]; then
|
if [ "$(./out04)" != 'Hello, world!' ]; then
|
||||||
echo_red 'Stage 04a failed.'
|
echo_red 'Stage 04 failed.'
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
cd ..
|
cd ..
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue