summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--04/README.md3
-rw-r--r--04/in0381
-rw-r--r--04a/in03322
-rw-r--r--04a/in040
-rwxr-xr-xbootstrap.sh10
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:
diff --git a/04/in03 b/04/in03
index c2f45ef..6daf3c5 100644
--- a/04/in03
+++ b/04/in03
@@ -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 ..