## Copyright (C) 2017 Jeremiah Orians
## Copyright (C) 2021 Andrius Štikonas
## This file is part of stage0.
##
## stage0 is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## stage0 is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with stage0.  If not, see <http://www.gnu.org/licenses/>.

; Register use:
; s1: malloc pointer
; s2: input fd
; s3: output fd
; s4: C, global_token
; s5: string_index, global_constant_list
; s6: Token, global_symbol_list
; s7: globals_list
; s8: global_function_list
; s9: output_list
; s10: current_count
; s11: function

; Struct TYPE format: (size 56)
; NEXT => 0
; SIZE => 8
; OFFSET => 16
; INDIRECT => 24
; MEMBERS => 32
; TYPE => 40
; NAME => 48

; Struct TOKEN_LIST format: (size 40)
; NEXT => 0
; LOCALS/PREV => 8
; S => 16
; TYPE => 24
; ARGS/DEPTH => 32

:_start

    RD_A2 RS1_SP !16 LD               ; Input file name

    ; Open input file and store FD in s2
    RD_A7 !56 ADDI                    ; sys_openat
    RD_A0 !-100 ADDI                  ; AT_FDCWD
    RD_A1 RS1_A2 MV                   ; file name
    RD_A2 ADDI                        ; read only
    ECALL                             ; syscall
    RS1_A0 @Fail BLTZ                 ; Error opening file
    RD_S2 RS1_A0 MV                   ; Save fd in for later

    ; Set default FD for output file to stdout
    RD_S3 !1 ADDI

    ; If we only have 2 arguments, don't use the third (it's not set)
    RD_T0 !2 ADDI
    RD_A0 RS1_SP LD                   ; Get number of the args
    RS1_A0 RS2_T0 @Fail BLT           ; No input file provided
    RS1_A0 RS2_T0 @after_open BEQ     ; No output file provided. Use stdout

    ; Open output file and store the FD in s3
    RD_A7 !56 ADDI                    ; sys_openat
    RD_A0 !-100 ADDI                  ; AT_FDCWD
    RD_A1 RS1_SP !24 LD               ; Output file (argument 3)
    RD_A2 !577 ADDI                   ; octal 00001101
    ; O_TRUNC   00001000
    ; O_CREAT   00000100
    ; O_WRONLY  00000001
    ; OCTAL!
    RD_A3 !384 ADDI                   ; Set read and write permission on user
    ECALL                             ; syscall
    RD_S3 RS1_A0 MV                   ; Save fd in for later

:after_open
    ; Prepare heap memory
    RD_A7 !214 ADDI                   ; sys_brk
    RD_A0 ADDI                        ; Get current brk
    ECALL                             ; syscall
    RD_S1 RS1_A0 MV                   ; Set our malloc pointer
    RD_A0 MV                          ; HEAD = NULL

    RD_RA $read_all_tokens JAL        ; Read all tokens
    RD_RA $Reverse_List JAL           ; Reverse order
    ;$debug_list JAL                  ; Try to figure out what is wrong
    RD_S4 RS1_A0 MV                   ; Set global_token
    RD_S5 MV                          ; Initialize global_constant_list
    RD_S6 MV                          ; Initialize global_symbol_list
    RD_RA $program JAL                ; Convert into program
    RD_A0 ~header_string1 AUIPC       ; Our header string
    RD_A0 RS1_A0 !header_string1 ADDI ; Our header string
    RD_RA $File_Print JAL             ; Print it
    RD_A0 RS1_S9 MV                   ; Set output list
    RD_RA $recursive_output JAL       ; Print them
    RD_A0 ~header_string2 AUIPC       ; Our header string
    RD_A0 RS1_A0 !header_string2 ADDI ; Our header string
    RD_RA $File_Print JAL             ; Print it
    RD_A0 ~header_string3 AUIPC       ; Our second label
    RD_A0 RS1_A0 !header_string3 ADDI ; Our second label
    RD_RA $File_Print JAL             ; Print it
    RD_A0 RS1_S7 MV                   ; Get globals
    RD_RA $recursive_output JAL       ; Print them
    RD_A0 ~header_string4 AUIPC       ; Our third label
    RD_A0 RS1_A0 !header_string4 ADDI ; Our third label
    RD_RA $File_Print JAL             ; Print it
    RD_A0 ~strings_list AUIPC         ; Our strings
    RD_A0 RS1_A0 !strings_list ADDI
    RD_A0 RS1_A0 LD
    RD_RA $recursive_output JAL       ; Print them
    RD_A0 ~header_string5 AUIPC       ; Our final header
    RD_A0 RS1_A0 !header_string5 ADDI ; Our final header
    RD_RA $File_Print JAL             ; Print it

:Done
    ; Terminate program with 0 return code
    RD_A7 !93 ADDI                    ; sys_exit
    RD_A0 MV                          ; Return code 0
    ECALL                             ; exit(0)

:Fail
    ; Terminate program with 1 return code
    RD_A7 !93 ADDI                    ; sys_exit
    RD_A0 !1 ADDI                     ; Return code 1
    ECALL                             ; exit(1)


;; read_all_tokens function
;; Receives FILE* in s2 and Token_List* in a0
;; Tokenizes all input and returns updated list in a0
;; Returns TOKEN in a0
;; Uses a0 for C
:read_all_tokens
    RD_SP RS1_SP !-8 ADDI                   ; allocate stack
    RS1_SP RS2_RA SD                        ; protect ra

    RD_S6 RS1_A0 MV                         ; Store Token
    RD_RA $fgetc JAL
:read_all_tokens_loop
    RD_T0 !-4 ADDI                          ; Check for EOF
    RS1_A0 RS2_T0 @read_all_tokens_done BEQ ; Stop if found
    RD_RA $get_token JAL                    ; Read all tokens
    $read_all_tokens_loop JAL               ; Loop
:read_all_tokens_done
    RD_A0 RS1_S6 MV                         ; Return Token

    RD_RA RS1_SP LD                         ; restore ra
    RD_SP RS1_SP !8 ADDI                    ; deallocate stack
    RETURN


; get_token function
; Receives INT in a0 and FILE* in s2
; Makes a list of TOKEN_LIST
; s4 is used for C, s5 for STRING_INDEX,
; a2 is used for S and a3 is used for current
; Returns C in a0
:get_token
    RD_SP RS1_SP !-32 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2
    RS1_SP RS2_A3 @24 SD              ; protect a3

    RD_S4 RS1_A0 MV                   ; Set C

    RD_A0 !40 MV                      ; Malloc CURRENT
    RD_RA $malloc JAL                 ; Get Pointer
    RD_A3 RS1_A0 MV                   ; Set CURRENT

    RD_A0 !256 MV                     ; Malloc the string
    RD_RA $malloc JAL                 ; Get Pointer to S
    RD_A2 RS1_A0 MV                   ; Set S
    RS1_A3 RS2_A2 @16 SD              ; CURRENT->S = S
:reset
    RD_S5 RS1_A2 MV                   ; S[0]
    RD_A0 RS1_S4 MV                   ; Using C

    RD_RA $clear_white_space JAL      ; Clear WhiteSpace
    RD_S4 RS1_A0 MV                   ; Set C

    RD_T0 !-4 ADDI                    ; Check for EOF
    RS1_A0 RS2_T0 @get_token_abort BEQ ; abort if EOF

    RD_T0 !35 ADDI                    ; Check for '#'
    RS1_A0 RS2_T0 @get_token_alpha BNE

    ; Deal with # line comments
    ; purge_macro function (inlined)
    ; Reads chars until LF is read and returns LF
:purge_macro
    RD_RA $fgetc JAL                  ; read next char
    RD_T0 !10 ADDI                    ; Check for '\n'
    RS1_A0 RS2_T0 @purge_macro BNE    ; Keep going

    RD_S4 RS1_A0 MV                   ; Set C
    $reset JAL                        ; Try again

:get_token_alpha
    RD_A0 RS1_S4 MV                   ; Send C
    RD_A1 ~alphas AUIPC               ; Get alphanumerics
    RD_A1 RS1_A1 !alphas ADDI         ; Get alphanumerics
    RD_RA $In_Set JAL                 ; See if in set
    RS1_A0 @get_token_symbol BEQZ     ; Otherwise process symbols

    ; Store keywords
    RD_A0 RS1_S4 MV                   ; Send C
    RD_RA $preserve_keyword JAL       ; Store
    RD_S4 RS1_A0 MV                   ; Set C
    $get_token_done JAL               ; Done with this token

:get_token_symbol
    RD_A0 RS1_S4 MV                   ; Send C
    RD_A1 ~symbols AUIPC              ; Get symbols
    RD_A1 RS1_A1 !symbols ADDI        ; Get symbols
    RD_RA $In_Set JAL                 ; See if in set
    RS1_A0 @get_token_strings BEQZ    ; Otherwise process strings

    ; Store symbols
    RD_A0 RS1_S4 MV                   ; Send C
    RD_RA $preserve_symbol JAL        ; Store
    RD_S4 RS1_A0 MV                   ; Set C
    $get_token_done JAL               ; Done with this token

:get_token_strings
    RD_A0 RS1_S4 MV                   ; Send C
    RD_A1 ~strings AUIPC              ; Get strings
    RD_A1 RS1_A1 !strings ADDI        ; Get strings
    RD_RA $In_Set JAL                 ; See if in set
    RS1_A0 @get_token_comment BEQZ    ; Otherwise process comments

    ; Store String
    RD_A0 RS1_S4 MV                   ; Send C
    RD_RA $consume_word JAL           ; Store
    RD_S4 RS1_A0 MV                   ; Set C
    $get_token_done JAL               ; Done with this token

:get_token_comment
    RD_A0 RS1_S4 MV                   ; Send C
    RD_T0 !47 ADDI                    ; '/'
    RS1_A0 RS2_T0 @get_token_else BNE ; Check if C == '/'

    RD_RA $consume_byte JAL           ; Hope it just is '/'
    RD_S4 RS1_A0 MV                   ; Set C

    RD_T0 !42 ADDI                    ; If '*' we have '/*'
    RS1_A0 RS2_T0 @get_token_comment_line BNE ; Check for '//'

    ; Deal with /* block comments */
    RD_RA $fgetc JAL                  ; get next C
    RD_S4 RS1_A0 MV                   ; Set C
:get_token_comment_block_outer
    RD_A0 RS1_S4 MV                   ; Using C
    RD_T0 !47 ADDI                    ; IF '/' != C
    RS1_A0 RS2_T0 @get_token_comment_block_done BEQ ; Done

:get_token_comment_block_inner
    RD_A0 RS1_S4 MV                   ; Using C
    RD_T0 !42 ADDI                    ; If '*' != C
    RS1_A0 RS2_T0 @get_token_comment_block_iter BEQ ; jump over

    ; Deal with inner loop
    RD_RA $fgetc JAL                  ; Get next C
    RD_S4 RS1_A0 MV                   ; Set C
    $get_token_comment_block_inner JAL ; keep going

:get_token_comment_block_iter
    RD_RA $fgetc JAL                  ; Get next C
    RD_S4 RS1_A0 MV                   ; Set C
    $get_token_comment_block_outer JAL

:get_token_comment_block_done
    RD_RA $fgetc JAL                  ; Get next C
    RD_S4 RS1_A0 MV                   ; Set C
    $reset JAL                        ; throw away, try again

:get_token_comment_line
    RD_T0 !47 ADDI                    ; IF '/' we have //
    RS1_A0 RS2_T0 @get_token_done BNE ; keep if just '/'

    ; Deal with // line comment
    RD_RA $fgetc JAL                  ; drop to match
    RD_S4 RS1_A0 MV                   ; Set C
    $reset JAL                        ; throw away, try again

:get_token_else
    RD_A0 RS1_S4 MV                   ; Send C
    RD_RA $consume_byte JAL
    RD_S4 RS1_A0 MV                   ; Set C

:get_token_done
    RS1_A3 RS2_S6 @8 SD               ; CURRENT->PREV = TOKEN
    RS1_A3 RS2_S6 SD                  ; CURRENT->NEXT = TOKEN
    RD_S6 RS1_A3 MV                   ; TOKEN = CURRENT

:get_token_abort
    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_A3 RS1_SP !24 LD               ; restore a3
    RD_SP RS1_SP !32 ADDI             ; deallocate stack
    RD_A0 RS1_S4 MV                   ; Return C
    RETURN


; clear_white_space function
; Receives INT in a0 and FILE* in s2
; Returns first non-whitespace char in a0
:clear_white_space
    RD_SP RS1_SP !-8 ADDI                     ; allocate stack
    RS1_SP RS2_RA SD                          ; protect ra

:clear_white_space_loop
    RD_T0 !32 ADDI                            ; Check for ' '
    RS1_A0 RS2_T0 @clear_white_space_wipe BEQ ; wipe it out

    RD_T0 !10 ADDI                            ; Check for '\n'
    RS1_A0 RS2_T0 @clear_white_space_wipe BEQ ; wipe it out

    RD_T0 !9 ADDI                             ; Check for '\t'
    RS1_A0 RS2_T0 @clear_white_space_done BNE ; looks like non-whitespace

:clear_white_space_wipe
    RD_RA $fgetc JAL                          ; Read a new byte
    RD_T0 !-4 ADDI                            ; Check for EOF
    RS1_A0 RS2_T0 @clear_white_space_done BEQ ; We are done
    $clear_white_space_loop JAL               ; iterate

:clear_white_space_done
    RD_RA RS1_SP LD                           ; restore ra
    RD_SP RS1_SP !8 ADDI                      ; deallocate stack
    RETURN


; preserve_keyword function
; Receives INT C in a0
; collects all chars in keyword
; Returns C in a0
; Uses a2 for INT C
:preserve_keyword
    RD_SP RS1_SP !-24 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2

    RD_A2 RS1_A0 MV                   ; Setup C
    RD_A1 ~alphas AUIPC               ; Get alphanumeric
    RD_A1 RS1_A1 !alphas ADDI         ; Get alphanumeric

:preserve_keyword_loop
    RD_RA $In_Set JAL                 ; Check if alphanumeric
    RS1_A0 @preserve_keyword_label BEQZ ; Otherwise check for label

    RD_A0 RS1_A2 MV                   ; Pass C
    RD_RA $consume_byte JAL           ; consume that byte
    RD_A2 RS1_A0 MV                   ; Update C
    $preserve_keyword_loop JAL        ; Keep looping

:preserve_keyword_label
    RD_A0 RS1_A2 MV                   ; Fix return
    RD_T0 !58 ADDI                    ; Check for ':'
    RS1_A0 RS2_T0 @preserve_keyword_done BNE ; Done

    ; Fix our goto label
    RD_RA $fixup_label JAL            ; Fix the label
    RD_A0 !32 ADDI                    ; Return Whitespace

:preserve_keyword_done
    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_SP RS1_SP !24 ADDI             ; deallocate stack
    RETURN


; preserve_symbol function
; Receives INT C in a0
; collects all chars in symbol
; Returns C in a0
; Uses a2 for INT C
:preserve_symbol
    RD_SP RS1_SP !-24 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2

    RD_A2 RS1_A0 MV                   ; Setup C
    RD_A1 ~symbols AUIPC              ; Get symbols
    RD_A1 RS1_A1 !symbols ADDI        ; Get symbols

:preserve_symbol_loop
    RD_RA $In_Set JAL                 ; Check if symbol
    RS1_A0 @preserve_symbol_done BEQZ ; Continue if symbol

    RD_A0 RS1_A2 MV                   ; Pass C
    RD_RA $consume_byte JAL           ; consume that byte
    RD_A2 RS1_A0 MV                   ; Update C
    $preserve_symbol_loop JAL         ; keep looping

:preserve_symbol_done
    RD_A0 RS1_A2 MV                   ; Fix return
    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_SP RS1_SP !24 ADDI             ; deallocate stack
    RETURN


; consume_word function
; receives INT C in a0
; returns INT C in a0
; Uses a0 for C, a1 for FREQ and a2 for ESCAPE
:consume_word
    RD_SP RS1_SP !-24 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2

    RD_A1 RS1_A0 MV                   ; FREQ = C
    RD_A2 MV                          ; ESCAPE = FALSE

:consume_word_loop
    RS1_A2 @consume_word_escape BNEZ  ; Enable escape if !ESCAPE

    RD_T0 !92 ADDI                    ; if '\\'
    RS1_A0 RS2_T0 @consume_word_iter BNE ; keep state

    RD_A2 !1 ADDI                     ; ESCAPE = TRUE
    $consume_word_iter JAL            ; keep going

:consume_word_escape
    RD_A2 MV                          ; ESCAPE = FALSE

:consume_word_iter
    RD_RA $consume_byte JAL           ; read next char

    RS2_A2 @consume_word_loop BNEZ    ; keep looping if ESCAPE

    RS1_A0 RS2_A1 @consume_word_loop BNEZ ; keep going if C != FREQ

    RD_RA $fgetc JAL                  ; return next char

    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_SP RS1_SP !24 ADDI             ; deallocate stack
    RETURN


; consume_byte function
; Receives INT C in a0
; Inserts C into string S, updates String S
; Returns Next char in a0
:consume_byte
    RD_SP RS1_SP !-16 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1

    RD_A1 RS1_S5 MV                   ; S[0]
    RS1_A1 RS2_A0 SB                  ; S[0] = C
    RD_A1 RS1_A1 !1 ADDI              ; S = S + 1
    RD_S5 RS1_A1 MV                   ; Update S
    RD_RA $fgetc JAL

    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_SP RS1_SP !16 ADDI             ; deallocate stack
    RETURN


; fixup_label function
; Receives S in a2
; prepends ':' to string
; Uses a0 for HOLD, a1 for PREV and a2 for S[0]
:fixup_label
    RD_SP RS1_SP !-24 ADDI            ; allocate stack
    RS1_SP RS2_A0 SD                  ; protect a0
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2

    RD_A0 !58 ADDI                    ; HOLD = ':'
    RD_A2 RS1_A3 !16 LD               ; HOLD_STRING[0]
:fixup_label_loop
    RD_A1 RS1_A0 MV                   ; PREV = HOLD
    RD_A0 RS1_A2 LBU                  ; HOLD = HOLD_STRING[I]
    RS1_A2 RS2_A1 SB                  ; HOLD_STRING[I] = PREV
    RD_A2 RS1_A2 !1 ADDI              ; I = I + 1
    RS1_A0 @fixup_label_loop BNEZ     ; Keep looping if NULL == HOLD

    RD_A0 RS1_SP LD                   ; restore a0
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_SP RS1_SP !24 ADDI             ; deallocate stack
    RETURN


; fgetc function
; Loads FILE* from s2
; Returns -4 (EOF) or char in a0
:fgetc
    RD_SP RS1_SP !-32 ADDI            ; allocate stack
    RS1_SP RS2_RA @8 SD               ; protect ra
    RS1_SP RS2_A1 @16 SD              ; protect a1
    RS1_SP RS2_A2 @24 SD              ; protect a2

    RD_A7 !63 ADDI                    ; sys_read
    RD_A1 RS1_SP MV                   ; Get stack address for buffer
    RD_A0 RS1_S2 MV                   ; read from input file
    RD_A2 !1 ADDI                     ; read 1 character
    ECALL                             ; syscall

    RS1_A0 @fgetc_done BNEZ           ; Check if nothing was read
    RD_A2 !-4 ADDI                    ; Use -4 as EOF
    RS1_A1 RS2_A2 SB                  ; Store EOF in *a1

:fgetc_done
    RD_A0 RS1_A1 LB                   ; return char in a0
    RD_RA RS1_SP !8 LD                ; restore ra
    RD_A1 RS1_SP !16 LD               ; restore a1
    RD_A2 RS1_SP !24 LD               ; restore a2
    RD_SP RS1_SP !32 ADDI             ; deallocate stack
    RETURN


; Reverse_List function
; Receives list in a0
; Returns the list reversed in a0
:Reverse_List
    RD_SP RS1_SP !-16 ADDI            ; allocate stack
    RS1_SP RS2_A1 SD                  ; protect a1
    RS1_SP RS2_A2 @8 SD               ; protect a2
    RD_A1 RS1_A0 MV                   ; Set HEAD
    RD_A0 MV                          ; ROOT = NULL
:Reverse_List_Loop
    RS1_A1 @Reverse_List_Done BEQZ    ; Stop if HEAD == NULL

    RD_A2 RS1_A1 LD                   ; NEXT = HEAD->NEXT
    RS1_A1 RS2_A0 SD                  ; HEAD->NEXT = ROOT
    RD_A0 RS1_A1 MV                   ; ROOT = HEAD
    RD_A1 RS1_A2 MV                   ; HEAD = NEXT
    $Reverse_List_Loop JAL            ; Continue looping

:Reverse_List_Done
    RD_A1 RS1_SP LD                   ; restore a1
    RD_A2 RS1_SP !8 LD                ; restore a2
    RD_SP RS1_SP !16 ADDI             ; deallocate stack
    RETURN


; Malloc isn't actually required if the program being built fits in the initial memory
; However, it doesn't take much to add it.
; Requires MALLOC pointer to be initialized and a0 to have the number of desired bytes
:malloc
    RD_SP RS1_SP !-16 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1

    RD_A1 RS1_S1 MV                   ; Store the current pointer
    RD_A0 RS1_A0 RS2_S1 ADD           ; Request the number of desired bytes
    RD_A7 !214 ADDI                   ; sys_brk
    ECALL                             ; syscall
    RD_S1 RS1_A0 MV                   ; Set our malloc pointer
    RD_A0 RS1_A1 MV                   ; Return the pointer

    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_SP RS1_SP !16 ADDI             ; deallocate stack
    RETURN


; recursive_output function
; Receives list in a0
; walks the list and prints the I->S for all nodes backwards
; Uses a1 for I
:recursive_output
    RD_SP RS1_SP !-24 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2

    RS1_A0 @recursive_output_done BEQZ ; Check if we are done
    RD_A1 RS1_A0 MV                   ; I = Head

    RD_A0 RS1_A1 LD                   ; I = I->next
    RD_RA $recursive_output JAL       ; Recurse

    RD_A0 RS1_A1 !16 LD               ; Using S
    RD_RA $File_Print JAL             ; Print it

:recursive_output_done
    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_SP RS1_SP !24 ADDI             ; deallocate stack
    RETURN


; File_Print function
; Receives CHAR* in a0
; calls fputc for every non-null char
:File_Print
    RD_SP RS1_SP !-24 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2
    RD_A1 RS1_A0 MV                   ; protect a0

    RS1_A0 @File_Print_Done BEQZ      ; Protect against nulls

:File_Print_Loop
    RD_A0 RS1_A1 LBU                  ; Read byte
    RS1_A0 @File_Print_Done BEQZ      ; Stop at NULL

    RD_RA $fputc JAL                  ; print it
    RD_A1 RS1_A1 !1 ADDI              ; S = S + 1
    $File_Print_Loop JAL              ; Keep printing

:File_Print_Done
    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_SP RS1_SP !24 ADDI             ; deallocate stack
    RETURN


; fputc function
; receives CHAR in a0 and load FILE* from s3
; writes char and returns
:fputc
    RD_SP RS1_SP !-32 ADDI            ; allocate stack
    RS1_SP RS2_A0 SD                  ; protect a0
    RS1_SP RS2_RA @8 SD               ; protect ra
    RS1_SP RS2_A1 @16 SD              ; protect a1
    RS1_SP RS2_A2 @24 SD              ; protect a2

    RD_A7 !64 ADDI                    ; sys_write
    RD_A0 RS1_S3 MV                   ; write to output
    RD_A1 RS1_SP MV                   ; Get stack address
    RD_A2 !1 ADDI                     ; write 1 character
    ECALL                             ; syscall

    RD_A0 RS1_SP LD                   ; restore a0
    RD_RA RS1_SP !8 LD                ; restore ra
    RD_A1 RS1_SP !16 LD               ; restore a1
    RD_A2 RS1_SP !24 LD               ; restore a2
    RD_SP RS1_SP !32 ADDI             ; deallocate stack
    RETURN


; match function
; Receives CHAR* in a0 and CHAR* in a1
; Returns 0 (TRUE) or 1 (FALSE) in a0
:match
    RD_SP RS1_SP !-24 ADDI            ; allocate stack
    RS1_SP RS2_A1 SD                  ; protect a1
    RS1_SP RS2_A2 @8 SD               ; protect a2
    RS1_SP RS2_A3 @16 SD              ; protect a3

    RD_A2 RS1_A0 MV                   ; S1 in place
    RD_A3 RS1_A1 MV                   ; S2 in place

:match_Loop
    RD_A0 RS1_A2 LBU                  ; S1[i]
    RD_A1 RS1_A3 LBU                  ; S2[i]
    RS1_A0 RS2_A1 @match_False BNE    ; Check if they match

    RD_A2 RS1_A2 !1 ADDI              ; S1 = S1 + 1
    RD_A3 RS1_A3 !1 ADDI              ; S2 = S2 + 1
    RS1_A0 @match_Done BEQZ           ; Match if we reached end of string
    $match_Loop JAL                   ; Otherwise keep looping

:match_False
    RD_A0 !1 ADDI                     ; Return false
:match_Done
    RD_A1 RS1_SP LD                   ; restore a1
    RD_A2 RS1_SP !8 LD                ; restore a2
    RD_A3 RS1_SP !16 LD               ; restore a3
    RD_SP RS1_SP !24 ADDI             ; deallocate stack
    RETURN


; In_Set function
; Receives char C in a0 and Char* in a1
; Returns 1 if true, zero if false in a0
:In_Set
    RD_SP RS1_SP !-8 ADDI             ; allocate stack
    RS1_SP RS2_A1 SD                  ; protect a1

:In_Set_loop
    RD_T0 RS1_A1 LBU                  ; Read char
    RS1_A0 RS2_T0 @In_Set_True BEQ    ; Return true
    RS1_T0 @In_Set_False BEQZ         ; Return False if NULL
    RD_A1 RS1_A1 !1 ADDI              ; s = s + 1
    $In_Set_loop JAL                  ; Continue looping

:In_Set_True
    RD_A0 !1 ADDI                     ; Set True
    RD_A1 RS1_SP LD                   ; restore a1
    RD_SP RS1_SP !8 ADDI              ; deallocate stack
    RETURN

:In_Set_False
    RD_A0 MV                          ; Set False
    RD_A1 RS1_SP LD                   ; restore a1
    RD_SP RS1_SP !8 ADDI              ; deallocate stack
    RETURN


; numerate_string function
; Receives CHAR* in a0
; Returns value of CHAR* in a0
; Uses a0 for VALUE, a1 for S, a2 for CH and a3 for NEGATIVE?
:numerate_string
    RD_SP RS1_SP !-24 ADDI            ; allocate stack
    RS1_SP RS2_A1 SD                  ; protect a1
    RS1_SP RS2_A2 @8 SD               ; protect a2
    RS1_SP RS2_A3 @16 SD              ; protect a3

    RD_A1 RS1_A0 MV                   ; put S in correct place
    RD_A0 MV                          ; Initialize to Zero
:numerate_string_loop
    RD_T0 RS1_A1 !1 ADDI              ; S + 1
    RD_A2 RS1_T0 LBU                  ; S[1]
    RD_T0 !120 ADDI                   ; 'x'
    RS1_A2 RS2_T0 @numerate_hex BEQ   ; Deal with hex_input

    ; Assume decimal input
    RD_A3 MV                          ; Assume no negation
    RD_A2 RS1_A1 LBU                  ; S[0]
    RD_T0 !45 ADDI                    ; '-'
    RS1_A2 RS2_T0 @numerate_decimal BNE ; Skip negation

    RD_A3 !1 ADDI                     ; Set FLAG
    RD_A1 RS1_A1 !1 ADDI              ; S = S + 1

:numerate_decimal
    RD_A2 RS1_A1 LBU                  ; S[i]
    RS1_A2 @numerate_decimal_done BEQZ ; We are done if NULL == S[i]

    ; a0 = a0 * 10 = (a0 << 3) + (a0 << 1)
    RD_T0 RS1_A0 RS2_X3 SLLI          ; a0 * 8
    RD_T1 RS1_A0 RS2_X1 SLLI          ; a0 * 2
    RD_A0 RS1_T0 RS2_T1 ADD           ; VALUE = VALUE * 10
    RD_A2 RS1_A2 !-48 ADDI            ; CH = CH - '0'
    RD_T0 !9 ADDI                     ; t0 = 9
    RS1_T0 RS2_A2 @numerate_string_fail BLT ; Check for illegal CH > 9
    RS1_A2 @numerate_string_fail BLTZ ; Check for illegal CH < 0
    RD_A0 RS1_A0 RS2_A2 ADD           ; VALUE = VALUE + CH
    RD_A1 RS1_A1 !1 ADDI              ; S = S + 1
    $numerate_decimal JAL

:numerate_decimal_done
    RD_T0 !1 ADDI                     ; Check for negative FLAG
    RS1_A3 RS2_T0 @numerate_string_done BNE ; Nope

    RD_A0 RS2_A0 SUB                  ; VALUE = -VALUE
    $numerate_string_done JAL         ; Done

:numerate_hex
    RD_A1 RS1_A1 !2 ADDI              ; S = S + 2
:numerate_hex_loop
    RD_A2 RS1_A1 LBU                  ; S[i]
    RS1_A2 @numerate_string_done BEQZ ; We are done if NULL == S[i]

    RD_A0 RS1_A0 RS2_X4 SLLI          ; VALUE = VALUE << 4
    RD_A2 RS1_A2 !-48 ADDI            ; CH = CH - '0'
    RD_T0 !10 ADDI                    ; t0 = 10
    RS1_A2 RS2_T0 @numerate_hex_digit BLT ; Check if we are dealing with number or letter
    RD_A2 RS1_A2 !-7 ADDI             ; Push A-F into range

:numerate_hex_digit
    RD_T0 !15 ADDI                    ; t0 = 15
    RS1_T0 RS2_A2 @numerate_string_fail BLT ; Check for CH > 'F'
    RS1_A2 @numerate_string_fail BLTZ ; Check for CH < 0
    RD_A0 RS1_A0 RS2_A2 ADD           ; VALUE = VALUE + CH
    RD_A1 RS1_A1 !1 ADDI              ; S = S + 1
    $numerate_hex_loop JAL            ; Keep looping

:numerate_string_fail
    RD_A0 MV                          ; return ZERO

:numerate_string_done
    RD_A1 RS1_SP LD                   ; restore a1
    RD_A2 RS1_SP !8 LD                ; restore a2
    RD_A3 RS1_SP !16 LD               ; restore a3
    RD_SP RS1_SP !24 ADDI             ; deallocate stack
    RS1_RA JALR                       ; return


; numerate_number function
; Receives an INT A in a0
; Returns char* result in a0
; Allocates 16 bytes of memory
; Behaves badly when given a negative number too large
; Uses a0 for temp, a1 for DIVISOR, a3 for mod/0, a4 for result[i] and a2 for A
:numerate_number
    RD_SP RS1_SP !-48 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2
    RS1_SP RS2_A3 @24 SD              ; protect a3
    RS1_SP RS2_A4 @32 SD              ; protect a4

    RD_A2 RS1_A0 MV                   ; Protect A
    RD_A0 !16 ADDI                    ; 16 bytes
    RD_RA $malloc JAL                 ; Get our pointer
    RS1_SP RS2_A0 @40 SD              ; protect our pointer
    RD_A4 RS1_A0 MV                   ; Put pointer in right place
    RD_A1 ~0x3B9ACA00 LUI             ; Set divisor to largest positive number that fits in 32bits
    RD_A1 RS1_A1 !0x3B9ACA00 ADDI     ; Set divisor to largest positive number that fits in 32bits

    RS1_A2 @numerate_number_ZERO BEQZ    ; Deal with 0 case
    RS2_A2 @numerate_number_positive BLT ; Check if positive

    ; Deal with negative case
    RD_A0 !45 ADDI                    ; Using "-"
    RS1_A4 RS2_A0 SB                  ; Write it
    RD_A4 RS1_A4 !1 ADDI              ; increment
    RD_A2 RS2_A2 SUB                  ; A = A * -1

:numerate_number_positive
    RD_T0 !10 ADDI                    ; 10
    RD_A0 RS1_A2 RS2_A1 DIVU          ; A / DIVISOR
    RS1_A0 @numerate_number_iter BNEZ ; Check if we have leading zeroes

    ; Clean up leading zeroes (DIVISOR = DIVISOR / 10)
    RD_A1 RS1_A1 RS2_T0 DIVU          ; Divide by 10
    $numerate_number_positive JAL     ; Keep collecting

:numerate_number_iter
    RS1_A1 @numerate_number_done BEQZ ; Done if DIVISOR = 0

    RD_A0 RS1_A2 RS2_A1 DIVU          ; A / DIVISOR
    RD_A0 RS1_A0 !48 ADDI             ; A / DIVISOR + 48
    RS1_A4 RS2_A0 SB                  ; Write it
    RD_A2 RS1_A2 RS2_A1 REMU          ; A % DIVISOR
    RD_A1 RS1_A1 RS2_T0 DIVU          ; Divide by 10
    RD_A4 RS1_A4 !1 ADDI              ; increment
    $numerate_number_iter JAL         ; Keep going

:numerate_number_done
    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_A3 RS1_SP !24 LD               ; restore a3
    RD_A4 RS1_SP !32 LD               ; restore a4
    RD_A0 RS1_SP !40 LD               ; restore our result
    RD_SP RS1_SP !48 ADDI             ; deallocate stack
    RETURN

:numerate_number_ZERO
    RD_A0 !48 ADDI                    ; Using '0'
    RS1_A4 RS2_A0 SB                  ; Write it
    RD_A4 RS1_A4 !1 ADDI              ; increment
    $numerate_number_done JAL         ; Be done


; escape_lookup function
; Receives char* c in a0
; Returns integer value of char in a0
; Aborts hard if unknown escape is received
; Uses a2 to hold char* C
:escape_lookup
    RD_SP RS1_SP !-24 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2

    RD_A2 RS1_A0 MV                   ; Put char* C in safe place
    RD_A0 RS1_A2 LBU                  ; c[0]
    RD_T0 !92 ADDI                    ; '\'
    RS1_A0 RS2_T0 @escape_lookup_done BNEZ ; We are done If '\\' != c[0]

    RD_A1 RS1_A2 MV                   ; Prepare for walk
    RD_A1 RS1_A1 !1 ADDI              ; increment
    RD_A1 RS1_A1 LBU                  ; load c[1]

    RD_T0 !120 ADDI                   ; Check for \x
    RS1_A1 RS2_T0 @escape_lookup_hex BEQ ; Deal with hex

    ; Deal with \? escapes
    RD_A0 !10 ADDI                    ; Guess "\n"
    RD_T0 !110 ADDI                   ; n
    RS1_A1 RS2_T0 @escape_lookup_done BEQ ; If n than we are done

    RD_A0 !9 ADDI                     ; Guess "\t"
    RD_T0 !116 ADDI                   ; t
    RS1_A1 RS2_T0 @escape_lookup_done BEQ ; If t than we are done

    RD_A0 RS1_A1 MV                   ; "\\", "'" and '"' all encode as themselves
    RD_T0 !92 ADDI                    ; If "\"
    RS1_A1 RS2_T0 @escape_lookup_done BEQ ; Be done
    RD_T0 !39 ADDI                    ; If "'"
    RS1_A1 RS2_T0 @escape_lookup_done BEQ ; Be done
    RD_T0 !34 ADDI                    ; If """
    RS1_A1 RS2_T0 @escape_lookup_done BEQ ; Be done

    RD_A0 !13 ADDI                    ; Guess "\r"
    RD_T0 !114 ADDI                   ; r
    RS1_A1 RS2_T0 @escape_lookup_done BEQ ; If r than we are done

    ; Looks like we have no clue what we are doing
    ; Aborting hard
    RD_S3 !2 ADDI                     ; write to standard error
    RD_A0 ~escape_lookup_string_0 AUIPC ; Using "Unknown escape received: "
    RD_A0 RS1_A0 !escape_lookup_string_0 ADDI
    RD_RA $File_Print JAL             ; Print it
    RD_A0 RS1_A2 MV                   ; Using C
    RD_RA $File_Print JAL             ; Print it
    RD_A0 ~escape_lookup_string_1 AUIPC ; Using " Unable to process\n"
    RD_A0 RS1_A0 !escape_lookup_string_1 ADDI
    RD_RA $File_Print JAL             ; Print it
    $Fail JAL                         ; Abort Hard

:escape_lookup_hex
    ; Give up on C and just assume they know what they are doing
    RD_A2 RS1_A2 !2 ADDI              ; increment
    RD_A0 RS1_A2 LBU                  ; c[2]
    RD_A2 RS1_A2 !1 ADDI              ; increment
    RD_RA $char2hex JAL               ; Get the hex value
    RD_A0 RS1_A0 RS2_X4 SLLI          ; c << 4
    RD_A1 RS1_A2 LBU                  ; c[3]
    RD_T3 RS1_A0 MV                   ; Protect c << 4
    RD_A0 RS1_A1 MV
    RD_A1 RS1_T3 MV
    RD_RA $char2hex JAL               ; Get the hex value
    RD_A0 RS1_A0 RS2_A1 ADD           ; hex(c[2]) << 4 + hex(c[3])

:escape_lookup_done
    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_SP RS1_SP !24 ADDI             ; deallocate stack
    RETURN


;; char2hex function
;; Receives char in RAX
;; Returns hex or aborts hard
:char2hex
    RD_A0 RS1_A0 !-48 ADDI            ; Try 0-9
    RD_T0 !10 ADDI
    RS1_A0 RS2_T0 @char2hex_done BLT  ; Done if digit

    ; Deal with A-F
    RD_T1 !0xDF ADDI                  ; Unset High bit turning a-f into A-F
    RD_A0 RS1_T1 RS2_A0 AND           ; Do it
    RD_A0 RS1_A0 !-7 ADDI             ; Shift down into position
    RS1_A0 RS2_T0 @char2hex_fail BLT  ; Everything below A is bad
    RD_T1 !16 ADDI
    RS1_A0 RS2_T0 @char2hex_done BLT  ; Make sure we are below F

:char2hex_fail
    ; Time to fail hard
    RD_S3 !2 ADDI                     ; Write to standard error
    RD_A0 ~char2hex_string_0 AUIPC    ; Using "Tried to print non-hex number\n"
    RD_A0 RS1_A0 !char2hex_string_0 ADDI
    RD_RA $File_Print JAL             ; Print it
    $Fail JAL                         ; Abort Hard

:char2hex_done
    RETURN


; parse_string function
; Receives char* string in a0
; Returns cleaned up string
; Protects char* string in a1
:parse_string
    RD_SP RS1_SP !-16 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1

    RD_A1 RS1_A0 MV                   ; Protect char *string
    RD_RA $weird JAL                  ; Determine if we have a weird string
    RS1_A0 @parse_string_weird BEQZ   ; Deal with it

    ; Dealing with regular string
    RD_A0 RS1_A1 MV                   ; Passing char* string
    RD_RA $collect_regular_string JAL ; Collect it
    $parse_string_done JAL            ; Be done

:parse_string_weird
    RD_A0 RS1_A1 MV                   ; Passing char* string
    RD_RA $collect_weird_string JAL   ; Collect it

:parse_string_done
    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_SP RS1_SP !16 ADDI             ; deallocate stack
    RETURN


; weird function
; Receives char* string in a0
; Returns true(0) or false(1) in a0
; Uses a2 to hold char* string
:weird
    RD_SP RS1_SP !-32 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2
    RS1_SP RS2_A3 @24 SD              ; protect a3

    RD_A2 RS1_A0 MV                   ; Place string in safe place
    RD_A2 RS1_A2 !1 ADDI              ; increment past the "

:weird_reset
    RD_A0 RS1_A2 LBU                  ; Load a char
    RS1_A0 @weird_false BEQZ          ; Nothing weird found

    RD_T0 !92 ADDI                    ; IF '\\'
    RS1_A0 RS2_T0 @weird_escaped BNE  ; Deal with escaping

    ; Deal with escape
    RD_A0 RS1_A2 MV                   ; We are passing the string
    RD_RA $escape_lookup JAL          ; to look it up

    RD_A2 RS1_A2 !1 ADDI              ; string = string + 1
    RD_A1 RS1_A2 LBU                  ; get string[1]
    RD_T0 !120 ADDI                   ; IF 'x' == string[1]
    RS1_A1 RS2_T0 @weird_escaped BNE  ; otherwise skip the gap

    RD_A2 RS1_A2 !2 ADDI              ; string = string + 2

:weird_escaped
    RD_A3 RS1_A0 MV                   ; Protect C in case we need it
    RD_A1 ~weird_string_0 AUIPC       ; Use "\t\n !#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
    RD_A1 RS1_A1 !weird_string_0 ADDI
    RD_RA $In_Set JAL                 ; To find if weird
    RD_T0 RS1_A0 MV                   ; Move result to temporary
    RD_A0 RS1_A3 MV                   ; Restore C
    RS1_T0 @weird_true BEQZ           ; If in set then not weird

    RD_A2 RS1_A2 !1 ADDI              ; string = string + 1

    ; Last chance for weird
    RD_A1 ~weird_string_1 AUIPC       ; Use "\t\n\r "
    RD_A1 RS1_A1 !weird_string_1 ADDI
    RD_RA $In_Set JAL                 ; Check for special case
    RS1_A0 @weird_reset BEQZ          ; Otherwise not in the special case

    ; Deal with possible special case
    RD_A0 RS1_A2 LBU                  ; Load string[1]
    RD_T0 !58 ADDI                    ; IF string[1] == ":"
    RS1_A0 RS2_T0 @weird_true BEQ     ; Then we git the special case
    $weird_reset JAL                  ; Keep trying

:weird_done
    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_A3 RS1_SP !24 LD               ; restore a3
    RD_SP RS1_SP !32 ADDI             ; deallocate stack
    RETURN

:weird_true
    RD_A0 MV                          ; Return true
    $weird_done JAL                   ; Be done

:weird_false
    RD_A0 !1 ADDI                     ; Return false
    $weird_done JAL                   ; Be done


; collect_regular_string function
; Receives char* string in a0
; Malloc and creates new string to return in a0 
; Uses a2 for return string and a3 for passed string
:collect_regular_string
    RD_SP RS1_SP !-40 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2
    RS1_SP RS2_A3 @24 SD              ; protect a3

    RD_A3 RS1_A0 MV                   ; Protect our passed string
    RD_A0 !256 ADDI                   ; We need 256 bytes of storage
    RD_RA $malloc JAL                 ; Get our new pointer
    RD_A2 RS1_A0 MV                   ; put it in place
    RS1_SP RS2_A0 @32 SD              ; protect until done

:collect_regular_string_reset
    RD_A0 RS1_A3 LBU                  ; string[0]
    RS1_A0 @collect_regular_string_done BEQZ ; See if we git the end

    RD_T0 !92 ADDI                    ; IF string[0] = '\\'
    RS1_A0 RS2_T0 @collect_regular_string_escaped BEQ ; Deal with escapes

    ; deal with boring char
    RS1_A2 RS2_A0 SB                  ; hold_string[index] = string[0]
    RD_A2 RS1_A2 !1 ADDI              ; Increment it
    RD_A3 RS1_A3 !1 ADDI              ; Increment it
    $collect_regular_string_reset JAL ; And keep going

:collect_regular_string_escaped
    RD_A0 RS1_A3 MV                   ; Using string
    RD_RA $escape_lookup JAL          ; Get the char
    RS1_A2 RS2_A0 SB                  ; hold_string[index] = escape_lookup(string)
    RD_A3 RS1_A3 !1 ADDI              ; Increment it
    RD_A2 RS1_A2 !1 ADDI              ; Increment it
    RD_A0 RS1_A3 LBU                  ; string[0]
    RD_A3 RS1_A3 !1 ADDI              ; Increment it
    RD_T0 !120 ADDI                   ; IF 'x' == string[1]
    RS1_A0 RS2_T0 @collect_regular_string_reset BNE ; Otherwise keep going

    RD_A3 RS1_A3 !2 ADDI              ; Increment it
    $collect_regular_string_reset JAL ; Keep going

:collect_regular_string_done
    RD_A0 !34 ADDI                    ; Using '"'
    RS1_A2 RS2_A0 SB                  ; hold_string[index] = '"'
    RD_A2 RS1_A2 !1 ADDI              ; Increment it
    RD_A0 !10 ADDI                    ; Using "\n"
    RS1_A2 RS2_A0 SB                  ; hold_string[index] = '\n'

    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_A3 RS1_SP !24 LD               ; restore a3
    RD_A0 RS1_SP !32 LD               ; restore a0
    RD_SP RS1_SP !40 ADDI             ; deallocate stack
    RETURN


; collect_weird_string function
; Receives char* string in a0
; Mallocs and returns char* hold in a0
; Uses a2 for char* hold and a3 for char* string
:collect_weird_string
    RD_SP RS1_SP !-40 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2
    RS1_SP RS2_A3 @24 SD              ; protect a3

    RD_A3 RS1_A0 MV                   ; Protect our passed string
    RD_A0 !512 ADDI                   ; We need 512 bytes of storage
    RD_RA $malloc JAL                 ; Get our new pointer
    RD_A2 RS1_A0 MV                   ; put it in place
    RS1_SP RS2_A0 @32 SD              ; protect until done

    RD_A0 !39 ADDI                    ; Using "'"
    RS1_A2 RS2_A0 SB                  ; hold_string[index] = "'"
    RD_A2 RS1_A2 !1 ADDI              ; Increment it
    RD_A3 RS1_A3 !1 ADDI              ; Increment it

:collect_weird_string_reset
    RD_A0 RS1_A3 LBU                  ; Read a byte
    RS1_A0 @collect_weird_string_done BEQZ ; Be done IF NULL == string[0]

    RD_A0 !32 ADDI                    ; Using ' '
    RS1_A2 RS2_A0 SB                  ; hold_string[index] = ' '
    RD_A2 RS1_A2 !1 ADDI              ; Increment it

    RD_A0 RS1_A3 MV                   ; Using string
    RD_RA $escape_lookup JAL          ; Get the char
    RD_RA $hex8 JAL                   ; Update a3

    RD_A0 RS1_A3 LBU                  ; Read a byte
    RD_A3 RS1_A3 !1 ADDI              ; Increment it
    RD_T0 !92 ADDI                    ; IF string[0] == '\\'
    RS1_A0 RS2_T0 @collect_weird_string_reset BNE ; Otherwise keep going

    RD_A0 RS1_A3 LBU                  ; Read a byte
    RD_A3 RS1_A3 !1 ADDI              ; Increment it
    RD_T0 !120 ADDI                   ; IF x == string[1]
    RS1_A0 RS2_T0 @collect_weird_string_reset BNE ; Otherwise keep going

    RD_A3 RS1_A3 !2 ADDI              ; Increment it
    $collect_weird_string_reset JAL   ; Keep going

:collect_weird_string_done
    RD_A0 !32 ADDI                    ; Using ' '
    RS1_A2 RS2_A0 SB                  ; hold_string[index] = ' '
    RD_A2 RS1_A2 !1 ADDI              ; Increment it
    RD_A0 !48 ADDI                    ; Using '0'
    RS1_A2 RS2_A0 SB                  ; hold_string[index] = '0'
    RD_A2 RS1_A2 !1 ADDI              ; Increment it
    RS1_A2 RS2_A0 SB                  ; hold_string[index] = '0'
    RD_A2 RS1_A2 !1 ADDI              ; Increment it
    RD_A0 !39 ADDI                    ; Using '''
    RS1_A2 RS2_A0 SB                  ; hold_string[index] = '''
    RD_A2 RS1_A2 !1 ADDI              ; Increment it
    RD_A0 !10 ADDI                    ; Using '\n'
    RS1_A2 RS2_A0 SB                  ; hold_string[index] = '\n'

    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_A3 RS1_SP !24 LD               ; restore a3
    RD_A0 RS1_SP !32 LD               ; restore a0
    RD_SP RS1_SP !40 ADDI             ; deallocate stack
    RETURN


; HEX to ascii routine
; Receives INT in a0 and CHAR* in a2
; Stores ascii of INT in CHAR*
; Returns only modifying a0 and a2
:hex8
    RD_SP RS1_SP !-16 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; Protect ra
    RS1_SP RS2_A0 @8 SD               ; Protect bottom nibble
    RD_A0 RS1_A0 RS2_X4 SRLI          ; do high nibble first
    RD_RA $hex4 JAL                   ; Store it
    RD_A0 RS1_SP !8 LD                ; do low nibble
    RD_RA $hex4 JAL                   ; Store it
    RD_RA RS1_SP LD                   ; restore ra
    RD_SP RS1_SP !16 ADDI             ; deallocate stack
    RETURN

:hex4
    RD_T0 !0xF ADDI
    RD_A0 RS1_A0 RS2_T0 AND           ; isolate nibble
    RD_A0 RS1_A0 !0x30 ADDI           ; convert to ascii
    RD_T0 !0x39 ADDI                  ; t0 = '9'
    RS1_T0 RS2_A0 @hex1 BGE           ; check if valid digit
    RD_A0 RS1_A0 !7 ADDI              ; use alpha range
:hex1
    RS1_A2 RS2_A0 SB                  ; store result
    RD_A2 RS1_A2 !1 ADDI              ; next position
    RETURN


; program function
; receives nothing, returns nothing
; Uses a0 for type_size
:program
    RD_SP RS1_SP !-24 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2

    ; The binary initialized the globals to null, so we can skip those steps

:new_type
    RS1_S4 @program_done BEQZ         ; Be done if null

    RD_A1 RS1_S4 !16 LD               ; GLOBAL_TOKEN->S
    RD_A0 ~constant AUIPC             ; "CONSTANT"
    RD_A0 RS1_A0 !constant ADDI
    RD_RA $match JAL                  ; Check if GLOBAL_TOKEN->S == "CONSTANT"
    RS1_A0 @program_else BNEZ

    ; Deal with minimal constant case
    RD_S4 RS1_S4 LD                   ; global_token = global_token->next
    RD_A0 RS1_S4 !16 LD               ; global_token->S
    RD_A1 MV                          ; NULL
    RD_A2 RS1_S5 MV                   ; global_constant_list
    RD_RA $sym_declare JAL            ; Declare that constant
    RD_S5 RS1_A0 MV                   ; global_constant_list = sym_declare(global_token->s, NULL, global_constant_list);

    RD_A1 RS1_S4 LD                   ; global_token->next
    RS1_S5 RS2_A1 @32 SD              ; global_constant_list->arguments = global_token->next

    RD_S4 RS1_A1 LD                   ; global_token = global_token->next->next
    $new_type JAL                     ; go around again

:program_else
    RD_RA $type_name JAL              ; Figure out the type_size
    RS1_A0 @new_type BEQZ             ; new type if NULL == type_size

    ; Add to global symbol table
    RD_A1 RS1_A0 MV                   ; put type_size in the right spot
    RD_A0 RS1_S4 !16 LD               ; global_token->S
    RD_A2 RS1_S6 MV                   ; Using global_symbol_list
    RD_RA $sym_declare JAL            ; Declare symbol
    RD_S6 RS1_A0 MV                   ; global_symbol_list = sym_declare(global_token->s, type_size, global_symbol_list);
    RD_S4 RS1_S4 LD                   ; global_token = global_token->next

    RD_A1 RS1_S4 !16 LD               ; global_token->S
    RD_A0 ~semicolon AUIPC            ; ";"
    RD_A0 RS1_A0 !semicolon ADDI
    RD_RA $match JAL                  ; match(";", global_token->s)
    RS1_A0 @program_function BNEZ     ; Check if we have a match

    ; Deal with the global variable
    RD_A1 RS1_S7 MV                   ; Using globals_list
    RD_A0 ~program_string_0 AUIPC     ; ":GLOBAL_"
    RD_A0 RS1_A0 !program_string_0 ADDI
    RD_RA $emit JAL                   ; Emit it
    RD_A1 RS1_A0 MV                   ; Update globals_list

    RD_A0 RS1_S4 !8 LD                ; global_token->prev
    RD_A0 RS1_A0 !16 LD               ; global_token->prev->S
    RD_RA $emit JAL                   ; Emit it

    RD_A1 RS1_A0 MV                   ; update globals_list
    RD_A0 ~program_string_1 AUIPC     ; "\nNULL\n"
    RD_A0 RS1_A0 !program_string_1 ADDI
    RD_RA $emit JAL                   ; Emit it
    RD_S7 RS1_A0 MV                   ; update globals_list

    RD_S4 RS1_S4 LD                   ; global_token = global_token->next
    $new_type JAL                     ; go around again

:program_function
    RD_A1 RS1_S4 !16 LD               ; global_token->S
    RD_A0 ~open_paren AUIPC           ; "("
    RD_A0 RS1_A0 !open_paren ADDI
    RD_RA $match JAL                  ; (match(";", global_token->s)
    RS1_A0 @program_error BNEZ        ; Make sure we have a match

    ; Deal with function definition
    RD_RA $declare_function JAL       ; Lets get the parsing rolling
    $new_type JAL                     ; Keep looping through functions

:program_error
    ; Deal with the case of something we don't support

:program_done
    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_SP RS1_SP !24 ADDI             ; deallocate stack
    RETURN


; sym_declare function
; Receives char *s in a0, struct type* t in a1, and struct token_list* list in a2
; Returns struct token_list* in a0
; Uses a0 for A
:sym_declare
    RD_SP RS1_SP !-16 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A3 @8 SD               ; protect a3
    RD_A3 RS1_A0 MV                   ; Get char *S safely out of the way

    RD_A0 !40 ADDI                    ; Using sizeof(struct token_list)
    RD_RA $malloc JAL                 ; Get pointer to A
    RS1_A0 RS2_A2 SD                  ; A->NEXT = LIST
    RS1_A0 RS2_A3 @16 SD              ; A->S = S
    RS1_A0 RS2_A1 @24 SD              ; A->TYPE = T

    RD_RA RS1_SP LD                   ; restore ra
    RD_A3 RS1_SP !8 LD                ; restore a3
    RD_SP RS1_SP !16 ADDI             ; deallocate stack
    RETURN


; emit_out function
; Receives char* S in a0
; Returns nothing
; Updates output_list
; MUST NOT ALTER REGISTERS
:emit_out
    RD_SP RS1_SP !-24 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A0 @8 SD               ; protect a0
    RS1_SP RS2_A1 @16 SD              ; protect a1

    RD_A1 RS1_S9 MV                   ; Using output_list
    RD_RA $emit JAL                   ; emit it
    RD_S9 RS1_A0 MV                   ; update it

    RD_RA RS1_SP LD                   ; restore ra
    RD_A0 RS1_SP !8 LD                ; restore a0
    RD_A1 RS1_SP !16 LD               ; restore a1
    RD_SP RS1_SP !24 ADDI             ; deallocate stack
    RETURN


; emit function
; Receives char *s in a0 and struct token_list* head in a1
; Returns struct token_list* T in a0
:emit
    RD_SP RS1_SP !-16 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A2 @8 SD               ; protect a2

    RD_A2 RS1_A0 MV                   ; Put S out of the way
    RD_A0 !40 ADDI                    ; sizeof(struct token_list)
    RD_RA $malloc JAL                 ; get T
    RS1_A0 RS2_A1 SD                  ; t->next = head
    RS1_A0 RS2_A2 @16 SD              ; t->s = s

    RD_RA RS1_SP LD                   ; restore ra
    RD_A2 RS1_SP !8 LD                ; restore a2
    RD_SP RS1_SP !16 ADDI             ; deallocate stack
    RETURN


; type_name function
; Receives nothing
; Returns type_size in a0
; Uses a2 for STRUCT TYPE* RET
:type_name
    RD_SP RS1_SP !-24 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2

    RD_A1 RS1_S4 !16 LD               ; global_token->S
    RD_A0 ~struct AUIPC               ; Using "struct"
    RD_A0 RS1_A0 !struct ADDI
    RD_RA $match JAL                  ; Check if global_token->S == "struct"
    RD_A2 RS1_A0 MV                   ; Protect structure
    RS1_A0 @type_name_native BNEZ     ; skip over "struct"

    ; Deal with possible STRUCTs
    RD_S4 RS1_S4 LD                   ; global_token = global_token->next
    RD_A0 RS1_S4 !16 LD               ; global_token->S
    RD_A1 ~global_types AUIPC         ; get all known types
    RD_A1 RS1_A1 !global_types ADDI
    RD_A1 RS1_A1 LD                   ; Dereference pointer
    RD_RA $lookup_type JAL            ; Find type if possible
    RD_A2 RS1_A0 MV                   ; Set ret

    RS1_A0 @type_name_common BNEZ     ; We have to create a struct if NULL == ret

    ; Create a struct
    RD_RA $create_struct JAL          ; Create a new struct
    RD_A2 MV                          ; Return NULL
    $type_name_done JAL               ; Done

:type_name_native
    ; Deal only with native types
    RD_A0 RS1_A1 MV                   ; Put global_token->S in the right place
    RD_A1 ~global_types AUIPC         ; get all known types
    RD_A1 RS1_A1 !global_types ADDI
    RD_A1 RS1_A1 LD                   ; Dereference pointer
    RD_RA $lookup_type JAL            ; Find the type if possible
    RD_A2 RS1_A0 MV                   ; Set ret

    RS1_A0 @type_name_common BNEZ     ; Abort if NULL == ret

    ; Aborting hard
    RD_S3 !2 ADDI                     ; write to standard error
    RD_A0 ~type_name_string_0 AUIPC   ; "Print header"
    RD_A0 RS1_A0 !type_name_string_0 ADDI
    RD_RA $File_Print JAL

    RD_A0 RS1_S4 !16 LD               ; global_token->S
    RD_RA $File_Print JAL             ; Print it

    RD_A0 ~type_name_string_1 AUIPC   ; "Print footer"
    RD_A0 RS1_A0 !type_name_string_1 ADDI
    RD_RA $File_Print JAL             ; Print it

    $Fail JAL                         ; Abort

:type_name_common
    RD_S4 RS1_S4 LD                   ; global_token = global_token->next

:type_name_iter
    RD_A0 RS1_S4 !16 LD               ; global_token->S
    RD_A0 RS1_A0 LBU                  ; global_token->S[0]
    RD_T0 !42 ADDI                    ; '*'
    RS1_A0 RS2_T0 @type_name_done BNE ; recurse

    ; Deal with char**
    RD_A2 RS1_A2 !24 LD               ; ret = ret->indirect
    RD_S4 RS1_S4 LD                   ; global_token = global_token->next
    $type_name_iter JAL               ; keep looping

:type_name_done
    RD_A0 RS1_A2 MV                   ; put ret in the right place
    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_SP RS1_SP !24 ADDI             ; deallocate stack
    RETURN


; lookup_type function
; Receives char* S in a0 and struct type* start in a1
; Returns struct type* in a0
; Uses a1 for S and a2 for I
:lookup_type
    RD_SP RS1_SP !-24 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2

    RD_A2 RS1_A1 MV                   ; I = Start
    RD_A1 RS1_A0 MV                   ; Put S in place

:lookup_type_iter
    RS1_A2 @lookup_type_done BEQZ     ; return NULL

    RD_A0 RS1_A2 !48 LD               ; I->NAME
    RD_RA $match JAL                  ; Check if matching
    RS1_A0 @lookup_type_done BEQZ     ; Return it if I->NAME == S

    RD_A2 RS1_A2 LD                   ; Otherwise I = I->NEXT
    $lookup_type_iter JAL             ; Keep looping

:lookup_type_done
    RD_A0 RS1_A2 MV                   ; return either I or NULL
    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_SP RS1_SP !24 ADDI             ; deallocate stack
    RETURN


; create_struct function
; Receives nothing
; Returns nothing
; Uses global_token to malloc a struct's definition
; Uses a6 for int OFFSET, a2 for struct type* head, a3 for struct type* I,
; a4 for member_size (Which is passed) and a5 for LAST
; a0 and a1 are is used for scratch
:create_struct
    RD_SP RS1_SP !-56 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2
    RS1_SP RS2_A3 @24 SD              ; protect a3
    RS1_SP RS2_A4 @32 SD              ; protect a4
    RS1_SP RS2_A5 @40 SD              ; protect a5
    RS1_SP RS2_A6 @48 SD              ; protect a6

    RD_A6 MV                          ; OFFSET = 0
    RD_A4 MV                          ; member_size = 0

    RD_A0 !56 ADDI                    ; sizeof(struct type)
    RD_RA $malloc JAL                 ; malloc(sizeof(struct type))
    RD_A2 RS1_A0 MV                   ; Set HEAD

    RD_A0 !56 ADDI                    ; sizeof(struct type)
    RD_RA $malloc JAL                 ; malloc(sizeof(struct type))
    RD_A3 RS1_A0 MV                   ; Set I

    RD_A0 RS1_S4 !16 LD               ; global_token->S
    RS1_A2 RS2_A0 @48 SD              ; HEAD->NAME = global_token->S
    RS1_A3 RS2_A0 @48 SD              ; I->NAME = global_token->S

    RS1_A2 RS2_A3 @24 SD              ; HEAD->INDIRECT = I
    RS1_A3 RS2_A2 @24 SD              ; I->INDIRECT = HEAD

    RD_A0 ~global_types AUIPC         ; Using global_types
    RD_A0 RS1_A0 !global_types ADDI
    RD_A1 RS1_A0 LD                   ; Dereference pointer
    RS1_A2 RS2_A1 SD                  ; HEAD->NEXT = global_types
    RS1_A0 RS2_A2 SD                  ; global_types = HEAD

    RD_S4 RS1_S4 LD                   ; global_token = global_token->next

    RD_A0 !8 ADDI                     ; Using register size
    RS1_A3 RS2_A0 @8 SD               ; I->SIZE = register size

    RD_A0 ~create_struct_string_0 AUIPC ; Using "ERROR in create_struct\n Missing {\n"
    RD_A0 RS1_A0 !create_struct_string_0 ADDI
    RD_A1 ~open_curly_brace AUIPC    ; Using "{"
    RD_A1 RS1_A1 !open_curly_brace ADDI
    RD_RA $require_match JAL          ; Make sure we have it

    RD_A5 MV                          ; LAST = NULL

:create_struct_iter
    RD_A0 RS1_S4 !16 LD               ; global_token->S
    RD_A0 RS1_A0 LBU                  ; global_token->S[0]
    RD_T0 !125 ADDI                   ; "}"
    RS1_A0 RS2_T0 @create_struct_done BEQ ; We are done

    ; Looks like we are adding members
    ; Lets see if it is a union
    RD_A0 RS1_S4 !16 LD               ; global_token->S
    RD_A1 ~union AUIPC                ; Using "union"
    RD_A1 RS1_A1 !union ADDI
    RD_RA $match JAL                  ; match(global_token->s, "union")
    RD_T0 RS1_A0 MV                   ; Protect match result

    RD_A0 RS1_A5 MV                   ; put last in right place
    RD_A1 RS1_A6 MV                   ; put offset in right place

    RS1_T0 @create_struct_single BNE  ; Deal with singles if no match

    ; Deal with union
    RD_RA $build_union JAL            ; ASSEMBLE
    $create_struct_common JAL

:create_struct_single
    ; Deal with singles
    RD_RA $build_member JAL           ; ASSEMBLE
:create_struct_common
    RD_A5 RS1_A0 MV                   ; last = build_union(last, offset)
    RD_A6 RS1_A6 RS2_A4 ADD           ; offset = offset + member_size

    RD_A0 ~create_struct_string_1 AUIPC ; Using "ERROR in create_struct\n Missing ;\n"
    RD_A0 RS1_A0 !create_struct_string_1 ADDI
    RD_A1 ~semicolon AUIPC            ; Using ";"
    RD_A1 RS1_A1 !semicolon ADDI
    RD_RA $require_match JAL          ; Make sure we have it
    $create_struct_iter JAL           ; Keep going

:create_struct_done
    RD_S4 RS1_S4 LD                   ; global_token = global_token->next
    RD_A0 ~create_struct_string_1 AUIPC ; Using "ERROR in create_struct\n Missing ;\n"
    RD_A0 RS1_A0 !create_struct_string_1 ADDI
    RD_A1 ~semicolon AUIPC            ; Using ";"
    RD_A1 RS1_A1 !semicolon ADDI
    RD_RA $require_match JAL          ; Make sure we have it

    RS1_A2 RS2_A6 @8 SD               ; HEAD->SIZE = OFFSET
    RS1_A2 RS2_A5 @32 SD              ; HEAD->MEMBERS = LAST
    RS1_A3 RS2_A5 @32 SD              ; I->MEMBERS = LAST

    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_A3 RS1_SP !24 LD               ; restore a3
    RD_A4 RS1_SP !32 LD               ; restore a4
    RD_A5 RS1_SP !40 LD               ; restore a5
    RD_A6 RS1_SP !40 LD               ; restore a6
    RD_SP RS1_SP !56 ADDI             ; deallocate stack
    RETURN



; lookup_member function
; Receives struct type* parent in a0 and char* name in a1
; Returns struct type* I in a0
; Uses char* NAME in a1, a2 for struct type* I and a3 to hold parent for errors
; Aborts hard if not found
:lookup_member
    RD_SP RS1_SP !-32 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2
    RS1_SP RS2_A3 @24 SD              ; protect a3

    RD_A3 RS1_A0 MV                   ; Protect Parent
    RD_A2 RS1_A0 !32 LD               ; struct type* I = parent->MEMBERS

:lookup_member_iter
    RS1_A2 @lookup_member_fail BEQZ   ; Abort HARD IF I == NULL

    RD_A0 RS1_A2 !48 LD               ; Using I->NAME
    RD_RA $match JAL                  ; IF I->NAME == NAME
    RD_T0 RS1_A0 MV                   ; Protect result
    RD_A0 RS1_A2 MV                   ; Prepare for return
    RD_A2 RS1_A2 !32 LD               ; Prepare for loop I = I->MEMBERS
    RS1_T0 @lookup_member_iter BNEZ   ; Looks like we are looping

    ; I is aready in a0
    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_A3 RS1_SP !24 LD               ; restore a3
    RD_SP RS1_SP !32 ADDI             ; deallocate stack
    RETURN

:lookup_member_fail
    RD_S3 !2 ADDI                     ; write to standard error
    RD_A0 ~lookup_member_string_0 AUIPC ; Using "ERROR in lookup_member "
    RD_A0 RS1_A0 !lookup_member_string_0 ADDI
    RD_RA $File_Print JAL             ; print it

    RD_A0 RS1_A3 !48 LD               ; PARENT->NAME
    RD_RA $File_Print JAL             ; print it

    RD_A1 RS1_S4 !16 LD               ; global_token->S

    RD_A0 ~lookup_member_string_1 AUIPC ; Using " does not exist\n"
    RD_A0 RS1_A0 !lookup_member_string_1 ADDI
    RD_RA $File_Print JAL             ; print it

    RD_A0 ~lookup_member_string_2 AUIPC ; Using "\n"
    RD_A0 RS1_A0 !lookup_member_string_2 ADDI
    RD_RA $File_Print JAL             ; print it
    $Fail JAL                         ; Abort Hard


; build_member function
; Receives struct type* last in a0, int offset in a1 and global member_size in a4
; Updates member_size in a4 and returns struct type* I in a0
; Uses a2 for struct type* member_type and a3 for struct type* I
:build_member
    RD_SP RS1_SP !-32 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2
    RS1_SP RS2_A3 @24 SD              ; protect a3

    RD_A3 RS1_A0 MV                   ; Put last out of the way
    RD_A0 !56 ADDI                    ; Allocate type
    RD_RA $malloc JAL                 ; Get I
    RS1_A0 RS2_A3 @32 SD              ; I->MEMBERS = LAST
    RS1_A0 RS2_A1 @16 SD              ; I->OFFSET = OFFSET
    RD_A3 RS1_A0 MV                   ; Put I in place

    RD_RA $type_name JAL              ; Get member_type
    RD_A2 RS1_A0 MV                   ; Put in place
    RD_A1 RS1_S4 !16 LD               ; global_token->S
    RS1_A3 RS2_A1 @48 SD              ; I->NAME = global_token->S
    RD_S4 RS1_S4 LD                   ; global_token = global_token->next

    ; Check if we have an array
    RD_A1 RS1_S4 !16 LD               ; global_token->S
    RD_A0 ~open_bracket AUIPC         ; Using "["
    RD_A0 RS1_A0 !open_bracket ADDI
    RD_RA $match JAL                  ; If global_token->S == "["
    RS1_A0 $build_member_array BEQZ   ; Then we have to deal with arrays in our struct

    ; Deal with non-array case
    RD_A0 RS1_A2 !8 LD                ; member_type->SIZE
    RS1_A3 RS2_A0 @8 SD               ; I->SIZE = member_type->SIZE
    $build_member_done JAL            ; We are done

:build_member_array
    RD_S4 RS1_S4 LD                   ; global_token = global_token->next
    RD_A0 RS1_S4 !16 LD               ; global_token->S
    RD_RA $numerate_string JAL        ; convert number
    RD_A1 RS1_A2 !40 LD               ; member_type->TYPE
    RD_A1 RS1_A1 !8 LD                ; member_type->TYPE->SIZE
    RD_A0 RS1_A0 RS2_A1 MUL           ; member_type->type->size * numerate_string(global_token->s)

    RD_S4 RS1_S4 LD                   ; global_token = global_token->next

    RD_A0 ~build_member_string_0 AUIPC ; Using "Struct only supports [num] form\n"
    RD_A0 RS1_A0 !build_member_string_0 ADDI
    RD_A1 ~close_bracket AUIPC        ; Using "]"
    RD_A1 RS1_A1 !close_bracket ADDI
    RD_RA $require_match JAL          ; Make sure we have it

:build_member_done
    RD_A4 RS1_A3 !8 LD                ; MEMBER_SIZE = I->SIZE
    RS1_A3 RS2_A2 @40 SD              ; I->TYPE = MEMBER_TYPE
    RD_A0 RS1_A3 MV                   ; Return I

    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_A3 RS1_SP !24 LD               ; restore a3
    RD_SP RS1_SP !32 ADDI             ; deallocate stack
    RETURN


; build_union function
; Receives struct type* last in a0, int offset in a1 and global member_size in a4
; Updates member_size in a4 and returns struct type* LAST in a0
; Uses a2 for struct type* last, a3 for int offset, a5 for int size and a4 for int member_size
:build_union
    RD_SP RS1_SP !-40 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2
    RS1_SP RS2_A3 @24 SD              ; protect a3
    RS1_SP RS2_A5 @32 SD              ; protect a5

    RD_A2 RS1_A0 MV                   ; Put LAST in the right spot
    RD_A3 RS1_A1 MV                   ; Put OFFSET in the right spot
    RD_A5 MV                          ; SIZE = 0

    RD_S4 RS1_S4 LD                   ; global_token = global_token->next

    RD_A0 ~build_union_string_0 AUIPC ; Using "ERROR in build_union\nMissing {\n"
    RD_A0 RS1_A0 !build_union_string_0 ADDI
    RD_A1 ~open_curly_brace AUIPC     ; Using "{"
    RD_A1 RS1_A1 !open_curly_brace ADDI
    RD_RA $require_match JAL          ; Make sure we have it

:build_union_iter
    RD_A0 RS1_S4 !16 LD               ; global_token->S
    RD_A0 RS1_A0 LBU                  ; global_token->S[0]
    RD_T0 !125 ADDI                   ; "}"
    RS1_A0 RS2_T0 @build_union_done BEQ ; We are done

    ; Collect union member
    RD_A0 RS1_A2 MV                   ; Passing LAST
    RD_A1 RS1_A3 MV                   ; Passing offset
    RD_RA $build_member JAL           ; build_member(last, offset)
    RD_A2 RS1_A0 MV                   ; last = build_member(last, offset)

    RS1_A4 RS2_A5 @build_union_size BLT ; If member_size > size then update size

    ; deal with member_size > size
    RD_A5 RS1_A4 MV                   ; SIZE = MEMBER_SIZE

:build_union_size
    RD_A0 ~build_union_string_1 AUIPC ; Using "ERROR in build_union\nMissing ;\n"
    RD_A0 RS1_A0 !build_union_string_1 ADDI
    RD_A1 ~semicolon AUIPC     ; Using ";"
    RD_A1 RS1_A1 !semicolon ADDI
    RD_RA $require_match JAL          ; Make sure we have it
    $build_union_iter JAL             ; Keep going

:build_union_done
    RD_A4 RS1_A5 MV                   ; MEMBER_SIZE = SIZE

    RD_S4 RS1_S4 LD                   ; global_token = global_token->next

    RD_A0 RS1_A2 MV                   ; return last

    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_A3 RS1_SP !24 LD               ; restore a3
    RD_A5 RS1_SP !32 LD               ; restore a5
    RD_SP RS1_SP !40 ADDI             ; deallocate stack
    RETURN


; require_match function
; Receives char* message in a0 and char* required in a1
; Returns nothing
; Uses a2 to hold message and updates global_token
:require_match
    RD_SP RS1_SP !-24 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2

    RD_A2 RS1_A0 MV                   ; put the message somewhere safe
    RD_A0 RS1_S4 !16 LD               ; global_token->S
    RD_RA $match JAL                  ; IF required == global_token->S
    RS1_A0 @require_match_good BEQZ   ; otherwise print error

    ; Deal with bad times
    RD_S3 !2 ADDI                     ; write to standard error
    RD_A0 RS1_A2 MV                   ; using out message
    RD_RA $File_Print JAL             ; Print it
    $Fail JAL                         ; Abort HARD

:require_match_good
    RD_S4 RS1_S4 LD                   ; global_token = global_token->next
    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_SP RS1_SP !24 ADDI             ; deallocate stack
    RETURN


; uniqueID Function
; Receives char *S in a0, struct token_list* l in a1 and char* num in a2
; Returns updated struct token_list* L in a0
:uniqueID
    RD_SP RS1_SP !-24 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2

    RD_RA $emit JAL                   ; emit(s, l)
    RD_A1 RS1_A0 MV                   ; Put L in correct place
    RD_A0 ~underline AUIPC            ; Using "_"
    RD_A0 RS1_A0 !underline ADDI
    RD_RA $emit JAL                   ; emit("_", l)
    RD_A1 RS1_A0 MV                   ; Put L in correct place
    RD_A0 RS1_A2 MV                   ; Put num in correct place
    RD_RA $emit JAL                   ; emit(num, l)
    RD_A1 RS1_A0 MV                   ; Put L in correct place
    RD_A0 ~uniqueID_string_0 AUIPC    ; Using "\n"
    RD_A0 RS1_A0 !uniqueID_string_0 ADDI
    RD_RA $emit JAL                   ; emit("\n", l)

    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_SP RS1_SP !24 ADDI             ; deallocate stack
    RETURN


; uniqueID_out function
; Receives char* S in a0 and char* num in a1
; Returns nothing
:uniqueID_out
    RD_SP RS1_SP !-32 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A0 @8 SD               ; protect a0
    RS1_SP RS2_A1 @16 SD              ; protect a1
    RS1_SP RS2_A2 @24 SD              ; protect a2

    RD_A2 RS1_A1 MV                   ; Put num in right spot
    RD_A1 RS1_S9 MV                   ; Using output_list
    RD_RA $uniqueID JAL               ; Get updated list
    RD_S9 RS1_A0 MV                   ; output_list = uniqueID(s, output_list, num)

    RD_RA RS1_SP LD                   ; restore ra
    RD_A0 RS1_SP !8 LD                ; restore a0
    RD_A1 RS1_SP !16 LD               ; restore a1
    RD_A2 RS1_SP !24 LD               ; restore a2
    RD_SP RS1_SP !32 ADDI             ; deallocate stack
    RETURN


; declare_function function
; Receives nothing and returns nothing
; Sets current function and adds it to the global function list
:declare_function
    RD_SP RS1_SP !-24 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2

    RD_S10 MV                         ; current_count = 0
    RD_A0 RS1_S4 !8 LD                ; global_token->prev
    RD_A0 RS1_A0 !16 LD               ; global_token->prev->s
    RD_A1 MV                          ; NULL
    RD_A2 RS1_S8 MV                   ; global_function_list
    RD_RA $sym_declare JAL            ; sym_declare(global_token->prev->s, NULL, global_function_list);
    RD_S11 RS1_A0 MV                  ; function = sym_declare(global_token->prev->s, NULL, global_function_list);
    RD_S8 RS1_A0 MV                   ; global_function_list = function

    RD_RA $collect_arguments JAL      ; collect all of the function arguments

    RD_A0 RS1_S4 !16 LD               ; global_token->S
    RD_A1 ~semicolon AUIPC            ; ";"
    RD_A1 RS1_A1 !semicolon ADDI
    RD_RA $match JAL                  ; If global_token->s == ";"
    RS1_A0 @declare_function_full BNEZ ; then it is a function prototype

    ; Deal with prototypes
    RD_S4 RS1_S4 LD                   ; global_token = global_token->next
    $declare_function_done JAL        ; Move on

:declare_function_full
    ; Deal with full function definitions
    RD_A0 ~declare_function_string_0 AUIPC ; "# Defining function "
    RD_A0 RS1_A0 !declare_function_string_0 ADDI
    RD_RA $emit_out JAL               ; emit it

    RD_A0 RS1_S11 !16 LD              ; function->s
    RD_RA $emit_out JAL               ; emit it

    RD_A0 ~declare_function_string_1 AUIPC ; "\n:FUNCTION_"
    RD_A0 RS1_A0 !declare_function_string_1 ADDI
    RD_RA $emit_out JAL               ; emit it

    RD_A0 RS1_S11 !16 LD              ; function->s
    RD_RA $emit_out JAL               ; emit it

    RD_A0 ~declare_function_string_3 AUIPC ; "\n"
    RD_A0 RS1_A0 !declare_function_string_3 ADDI
    RD_RA $emit_out JAL               ; emit it

    RD_RA $statement JAL              ; Recursively get the function pieces

    RD_A0 RS1_S9 !16 LD               ; output->s
    RD_A1 ~declare_function_string_2 AUIPC ; "RETURN\n"
    RD_A1 RS1_A1 !declare_function_string_2 ADDI
    RD_RA $match JAL                  ; If output->s == "RETURN\n"
    RS1_A0 @declare_function_done BEQZ ; Then skip adding it

    ; Add the return to the end of a function lacking a return
    RD_A0 ~declare_function_string_2 AUIPC ; "RETURN\n"
    RD_A0 RS1_A0 !declare_function_string_2 ADDI
    RD_RA $emit_out JAL               ; emit it

:declare_function_done
    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_SP RS1_SP !24 ADDI             ; deallocate stack
    RETURN


; collect_arguments function
; Receives nothing
; Returns Nothing
; Adds arguments to the function definition
; holds struct type* type_size in a2, then replace with struct token_list* A in a2 when type_size is used
:collect_arguments
    RD_SP RS1_SP !-24 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2
    RD_S4 RS1_S4 LD                   ; global_token = global_token->next
:collect_arguments_loop
    RD_A1 RS1_S4 !16 LD               ; global_token->S
    RD_A0 ~close_paren AUIPC          ; ")"
    RD_A0 RS1_A0 !close_paren ADDI
    RD_RA $match JAL                  ; IF global_token->S == ")"
    RS1_A0 @collect_arguments_done BEQZ ; then we are done

    ; deal with the case of there are arguments
    RD_RA $type_name JAL              ; Get the type
    RD_A2 RS1_A0 MV                   ; put type_size safely out of the way

    RD_A1 RS1_S4 !16 LD               ; global_token->S
    RD_A0 ~close_paren AUIPC          ; ")"
    RD_A0 RS1_A0 !close_paren ADDI
    RD_RA $match JAL                  ; IF global_token->S == ")"
    RS1_A0 @collect_arguments_common BEQZ ; is a foo(int, char,void) case

    ; Trying second else
    RD_A1 RS1_S4 !16 LD               ; global_token->S
    RD_A0 ~comma AUIPC                ; ","
    RD_A0 RS1_A0 !comma ADDI
    RD_RA $match JAL                  ; IF global_token->S == ","
    RS1_A0 @collect_arguments_common BEQZ ; then deal with commas between arguments

    ; deal with foo(int a, char b)
    RD_A0 RS1_S4 !16 LD               ; global_token->S
    RD_A1 RS1_A2 MV                   ; put type_size in the right place
    RD_A2 RS1_S11 !32 LD              ; function->args
    RD_RA $sym_declare JAL            ; sym_declare(global_token->s, type_size, function->arguments)
    RD_A2 RS1_A0 MV                   ; put A in a safe place

    RD_A0 RS1_S11 !32 LD              ; function->args
    RS1_A0 @collect_arguments_another BNEZ ; Check if it's the first argument (IF function->args == NULL)

    ; Deal with the case of first argument in the function
    RD_T0 !8 ADDI                     ; 8
    RS1_A2 RS2_T0 @32 SD              ; a->depth = 8
    $collect_arguments_next JAL       ; get to next

:collect_arguments_another
    RD_A0 RS1_S11 !32 LD              ; function->args
    RD_A0 RS1_A0 !32 LD               ; function->args->depth
    RD_A0 RS1_A0 !8 ADDI              ; function->args->depth + 8
    RS1_A2 RS2_A0 @32 SD              ; a->depth = function->args->depth + 8

:collect_arguments_next
    RD_S4 RS1_S4 LD                   ; global_token = global_token->next
    RS1_S11 RS2_A2 @32 SD             ; function->args = a

:collect_arguments_common
    RD_A1 RS1_S4 !16 LD               ; global_token->S
    RD_A0 ~comma AUIPC                ; ","
    RD_A0 RS1_A0 !comma ADDI
    RD_RA $match JAL                  ; IF global_token->S == ","
    RS1_A0 @collect_arguments_loop BNEZ ; then deal with comma, else loop

    ; keep foo(bar(), 1) expressions working
    RD_S4 RS1_S4 LD                   ; global_token = global_token->next
    $collect_arguments_loop JAL       ; keep going

:collect_arguments_done
    RD_S4 RS1_S4 LD                   ; global_token = global_token->next
    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_SP RS1_SP !24 ADDI             ; deallocate stack
    RETURN

; statement function
; Receives nothing
; Returns nothing
; Walks down global_token recursively to collect the contents of the function
:statement
    RD_SP RS1_SP !-24 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2

    RD_A1 RS1_S4 !16 LD               ; global_token->S
    RD_A0 ~open_curly_brace AUIPC     ; "{"
    RD_A0 RS1_A0 !open_curly_brace ADDI
    RD_RA $match JAL                  ; IF global_token->S == "{"
    RS1_A0 @statement_label BNEZ      ; otherwise try label

    ; deal with { statement }
    RD_RA $recursive_statement JAL    ; Statements inside of statements for days
    $statement_done JAL               ; We are done

:statement_label
    RD_A0 RS1_S4 !16 LD               ; global_token->S
    RD_A0 RS1_A0 LBU                  ; global_token->S[0]
    RD_T0 !58 ADDI                    ; Check if global_token->S == ':'
    RS1_A0 RS2_T0 @statement_local BNE ; otherwise try locals

    ; deal with labels
    RD_A0 RS1_A1 MV                   ; put global_token->S in the right spot
    RD_RA $emit_out JAL               ; emit it

    RD_A0 ~statement_string_0 AUIPC   ; Using "\t#C goto label\n"
    RD_A0 RS1_A0 !statement_string_0 ADDI
    RD_RA $emit_out JAL               ; emit it

    RD_S4 RS1_S4 LD                   ; global_token = global_token->next
    $statement_done JAL               ; We are done

:statement_local
    RD_A0 RS1_A1 MV                   ; put global_token->S in the right place
    RD_A1 ~prim_types AUIPC           ; pointer to primative types
    RD_A1 RS1_A1 !prim_types ADDI
    RD_RA $lookup_type JAL            ; See if found
    RS1_A0 @statement_local_success BNEZ ; Collect new local

    ; Second chance
    RD_A1 RS1_S4 !16 LD               ; global_token->S
    RD_A0 ~struct AUIPC               ; "struct"
    RD_A0 RS1_A0 !struct ADDI
    RD_RA $match JAL                  ; IF global_token->S == "struct"
    RS1_A0 @statement_if BNEZ         ; then we have a new local, otherwise try IF

:statement_local_success
    RD_RA $collect_local JAL          ; Grab those locals
    $statement_done JAL               ; We are done

:statement_if
    RD_A0 ~if_string AUIPC            ; Using "if"
    RD_A0 RS1_A0 !if_string ADDI
    RD_RA $match JAL                  ; IF global_token->S == "if"
    RS1_A0 @statement_do BNEZ         ; then we have a if statement, else try do

    ; Deal with IF statement
    RD_RA $process_if JAL             ; Process if
    $statement_done JAL               ; We are done

:statement_do
    RD_A0 ~do_string AUIPC            ; Using "do"
    RD_A0 RS1_A0 !do_string ADDI
    RD_RA $match JAL                  ; IF global_token->S == "do"
    RS1_A0 @statement_while BNEZ      ; then we have a do statement, else try while

    ; Deal with DO statement
    RD_RA $process_do JAL             ; Process do
    $statement_done JAL               ; We are done

:statement_while
    RD_A0 ~while_string AUIPC         ; Using "while"
    RD_A0 RS1_A0 !while_string ADDI
    RD_RA $match JAL                  ; IF global_token->S == "while"
    RS1_A0 @statement_for BNEZ        ; then we have a while statement, else try for

    ; Deal with WHILE statement
    RD_RA $process_while JAL          ; Process while
    $statement_done JAL               ; We are done

:statement_for
    RD_A0 ~for_string AUIPC           ; Using "for"
    RD_A0 RS1_A0 !for_string ADDI
    RD_RA $match JAL                  ; IF global_token->S == "for"
    RS1_A0 @statement_asm BNEZ        ; then we have a for statement, else try asm

    ; Deal with FOR statement
    RD_RA $process_for JAL            ; Process for
    $statement_done JAL               ; We are done

:statement_asm
    RD_A0 ~asm_string AUIPC           ; Using "asm"
    RD_A0 RS1_A0 !asm_string ADDI
    RD_RA $match JAL                  ; IF global_token->S == "asm"
    RS1_A0 @statement_goto BNEZ       ; then we have a asm statement, else try goto

    ; Deal with ASM statement
    RD_RA $process_asm JAL            ; Process asm
    $statement_done JAL               ; We are done

:statement_goto
    RD_A0 ~goto_string AUIPC          ; Using "goto"
    RD_A0 RS1_A0 !goto_string ADDI
    RD_RA $match JAL                  ; IF global_token->S == "goto"
    RS1_A0 @statement_return BNEZ     ; then we have a goto statement, else try return

    ; Deal with GOTO statement
    RD_A0 ~statement_string_1 AUIPC   ; Using "$"
    RD_A0 RS1_A0 !statement_string_1 ADDI
    RD_RA $emit_out JAL               ; emit it

    RD_S4 RS1_S4 LD                   ; global_token = global_token->next
    RD_A0 RS1_S4 !16 LD               ; global_token->S
    RD_RA $emit_out JAL               ; emit it

    ; Deal with GOTO statement
    RD_A0 ~statement_string_2 AUIPC   ; Using " JAL\n"
    RD_A0 RS1_A0 !statement_string_2 ADDI
    RD_RA $emit_out JAL               ; emit it

    RD_S4 RS1_S4 LD                   ; global_token = global_token->next

    RD_A0 ~statement_string_4 AUIPC   ; Using "ERROR in statement\nMissing ;\n"
    RD_A0 RS1_A0 !statement_string_4 ADDI
    RD_A1 ~semicolon AUIPC            ; Using ";"
    RD_A1 RS1_A1 !semicolon ADDI
    RD_RA $require_match JAL          ; Make sure we have it
    $statement_done JAL               ; We are done

:statement_return
    RD_A0 ~return_string AUIPC        ; Using "return"
    RD_A0 RS1_A0 !return_string ADDI
    RD_RA $match JAL                  ; IF global_token->S == "return"
    RS1_A0 @statement_break BNEZ      ; then we have a return statement, else try break

    ; Deal with RETURN statement
    RD_RA $return_result JAL          ; Return anything they want
    $statement_done JAL               ; We are done

:statement_break
    RD_A0 ~break_string AUIPC         ; Using "break"
    RD_A0 RS1_A0 !break_string ADDI
    RD_RA $match JAL                  ; IF global_token->S == "break"
    RS1_A0 @statement_continue BNEZ   ; then we have a return break, else try continue

    ; Deal with BREAK statement
    RD_RA $process_break JAL          ; Process break
    $statement_done JAL               ; We are done

:statement_continue
    RD_A0 ~continue_string AUIPC      ; Using "continue"
    RD_A0 RS1_A0 !continue_string ADDI
    RD_RA $match JAL                  ; IF global_token->S == "continue"
    RS1_A0 @statement_else BNEZ       ; then we have a continue statement, else we are punting to an expression

    ; Deal with CONTINUE statement
    RD_S4 RS1_S4 LD                   ; global_token = global_token->next

    RD_A0 ~statement_string_3 AUIPC   ; Using "\n#continue statement\n"
    RD_A0 RS1_A0 !statement_string_3 ADDI
    RD_RA $emit_out JAL               ; emit it

    RD_A0 ~statement_string_4 AUIPC   ; Using "ERROR in statement\nMissing ;\n"
    RD_A0 RS1_A0 !statement_string_4 ADDI
    RD_A1 ~semicolon AUIPC            ; Using ";"
    RD_A1 RS1_A1 !semicolon ADDI
    RD_RA $require_match JAL          ; Make sure we have it
    $statement_done JAL               ; We are done

:statement_else
    RD_RA $expression JAL             ; Collect expression
    RD_A0 ~statement_string_4 AUIPC   ; Using "ERROR in statement\nMissing ;\n"
    RD_A0 RS1_A0 !statement_string_4 ADDI
    RD_A1 ~semicolon AUIPC            ; Using ";"
    RD_A1 RS1_A1 !semicolon ADDI
    RD_RA $require_match JAL          ; Make sure we have it

:statement_done
    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_SP RS1_SP !24 ADDI             ; deallocate stack
    RETURN


; recursive_statement function
; Receives nothing
; Returns nothing
; Walks the global_token list to build the contents of statements
; Uses struct token_list* frame in a2
:recursive_statement
    RD_SP RS1_SP !-24 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2

    RD_S4 RS1_S4 LD                   ; global_token = global_token->next
    RD_A2 RS1_S11 !8 LD               ; frame = function->locals

:recursive_statement_loop
    RD_A1 RS1_S4 !16 LD               ; global_token->S
    RD_A0 ~close_curly_brace AUIPC    ; "}"
    RD_A0 RS1_A0 !close_curly_brace ADDI
    RD_RA $match JAL                  ; IF global_token->S == "}"
    RS1_A0 @recursive_statement_cleanup BEQZ ; then we are done recursing

    ; Deal with recursive calls
    RD_RA $statement JAL              ; Deal with another statement
    $recursive_statement_loop JAL     ; keep looping

:recursive_statement_cleanup
    RD_S4 RS1_S4 LD                   ; global_token = global_token->next
    RD_A0 ~recursive_statement_string_0 AUIPC ; Using "RETURN\n"
    RD_A0 RS1_A0 !recursive_statement_string_0 ADDI
    RD_A1 RS1_S9 !16 LD               ; output->S
    RD_RA $match JAL                  ; IF output->S == "RETURN\n"
    RS1_A0 @recursive_statement_done BEQZ ; We are done

    ;; Deal with cleanup
    RD_A1 RS1_S11 !8 LD               ; i = function->locals
    RD_A0 ~recursive_statement_string_1 AUIPC ; Using "RD_A1 RS1_SP LD	# _recursive_statement_locals..."
    RD_A0 RS1_A0 !recursive_statement_string_1 ADDI

:recursive_statement_locals
    RS1_A1 RS2_A2 @recursive_statement_done BEQ ; Check if frame != i

    ; Let's emit
    RD_RA $emit_out JAL               ; emit it
    RD_A1 RS1_A1 LD                   ; i = i->next
    $recursive_statement_locals JAL   ; keep going

:recursive_statement_done
    RS1_S11 RS2_A2 @8 SD              ; function->locals = frame

    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_SP RS1_SP !24 ADDI             ; deallocate stack
    RETURN


; collect_local function
; Receives nothing
; Returns nothing
; Walks global_token list to create function locals
; Uses a2 for struct token_list* A
:collect_local
    RD_SP RS1_SP !-24 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2

    RD_RA $type_name JAL              ; Get the local's type

    RD_A1 RS1_A0 MV                   ; Put struct type* type_size in the right place
    RD_A0 RS1_S4 !16 LD               ; global_token->S
    RD_A2 RS1_S11 !8 LD               ; function->locals
    RD_RA $sym_declare JAL            ; Declare it
    RD_A2 RS1_A0 MV                   ; put it away safely

    ; Try for main
    RD_A0 ~main_string AUIPC          ; Using "main"
    RD_A0 RS1_A0 !main_string ADDI
    RD_A1 RS1_S11 !16 LD              ; function->S
    RD_RA $match JAL                  ; IF match("main", function->s)
    RS1_A0 @collect_local_fresh BNEZ  ; try to see if fresh function

    ; Ok we are in main, now to see if main is fresh
    RD_A0 RS1_S11 !8 LD               ; function->locals
    RS1_A0 @collect_local_fresh BNEZ  ; try to see if fresh function

    ; Sweet we are in a fresh main
    RD_A0 !32 ADDI                    ; Start at 32
    RS1_A2 RS2_A0 @32 SD              ; a->DEPTH = 32
    $collect_local_common JAL         ; Go to the commons

:collect_local_fresh
    RD_A0 RS1_S11 !32 LD              ; function->args
    RS1_A0 @collect_local_first BNEZ  ; Otherwise see if first

    RD_A0 RS1_S11 !8 LD               ; function->locals
    RS1_A0 @collect_local_first BNEZ  ; Otherwise try first

    ; Sweet we are in a fresh function
    RD_A0 !8 ADDI                     ; We start at 8
    RS1_A2 RS2_A0 @32 SD              ; a->DEPTH = 8
    $collect_local_common JAL         ; Go to the commons

:collect_local_first
    RD_A0 RS1_S11 !8 LD               ; function->locals
    RS1_A0 @collect_local_else BNEZ   ; Looks like we are just another local

    ; Ok we are the first local
    RD_A0 RS1_S11 !32 LD              ; function->args
    RD_A0 RS1_A0 !32 LD               ; function->args->depth
    RD_A0 RS1_A0 !8 ADDI              ; function->arguments->depth + 8
    RS1_A2 RS2_A0 @32 SD              ; a->DEPTH = function->arguments->depth + 8
    $collect_local_common JAL         ; Go to the commons

:collect_local_else
    ; Always the last to know
    RD_A0 RS1_S11 !8 LD               ; function->locals
    RD_A0 RS1_A0 !32 LD               ; function->locals->depth
    RD_A0 RS1_A0 !8 ADDI              ; function->locals->depth + 8
    RS1_A2 RS2_A0 @32 SD              ; a->DEPTH = function->locals->depth + 8

:collect_local_common
    RS1_S11 RS2_A2 @8 SD              ; function->locals = a
    RD_A2 RS1_A2 !16 LD               ; a->S

    RD_A0 ~collect_local_string_0 AUIPC ; Using "# Defining local "
    RD_A0 RS1_A0 !collect_local_string_0 ADDI
    RD_RA $emit_out JAL               ; emit it

    RD_A0 RS1_S4 !16 LD               ; global_token->S
    RD_RA $emit_out JAL               ; emit it

    RD_A0 ~collect_local_string_1 AUIPC ; Using "\n"
    RD_A0 RS1_A0 !collect_local_string_1 ADDI
    RD_RA $emit_out JAL               ; emit it

    RD_S4 RS1_S4 LD                   ; global_token = global_token->next

    RD_A1 RS1_S4 !16 LD               ; global_token->S
    RD_A0 ~equal AUIPC                ; Using "="
    RD_A0 RS1_A0 !equal ADDI
    RD_RA $match JAL                  ; IF match("=", global_token->s)
    RS1_A0 @collect_local_done BNEZ   ; Deal with assignment, else finish it

    ; Deal with assignment
    RD_S4 RS1_S4 LD                   ; global_token = global_token->next

    RD_RA $expression JAL             ; Recurse

:collect_local_done
    RD_A0 ~collect_local_string_2 AUIPC ; Using "ERROR in collect_local\nMissing ;\n"
    RD_A0 RS1_A0 !collect_local_string_2 ADDI
    RD_A1 ~semicolon AUIPC            ; Using ";"
    RD_A1 RS1_A1 !semicolon ADDI
    RD_RA $require_match JAL          ; Make sure we have it

    RD_A0 ~collect_local_string_3 AUIPC ; Using "RD_SP RS1_SP !-8 ADDI\nRS1_SP RS2_A0 SD\t#"
    RD_A0 RS1_A0 !collect_local_string_3 ADDI
    RD_RA $emit_out JAL               ; emit it

    RD_A0 RS1_A2 MV                   ; Put A->S where it belongs
    RD_RA $emit_out JAL               ; emit it

    RD_A0 ~collect_local_string_1 AUIPC ; Using "\n"
    RD_A0 RS1_A0 !collect_local_string_1 ADDI
    RD_RA $emit_out JAL               ; emit it

    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_SP RS1_SP !24 ADDI             ; deallocate stack
    RETURN


; process_if function
; Receives nothing
; Returns Nothing
; Increments current_count recurses into expression + statement
; Uses a2 for char* NUMBER_STRING
:process_if
    RD_SP RS1_SP !-24 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2

    RD_A0 RS1_S10 MV                  ; Get current_count
    RD_S10 RS1_S10 !1 ADDI            ; current_count = current_count + 1
    RD_RA $numerate_number JAL        ; convert to string
    RD_A2 RS1_A0 MV                   ; put NUMBER_STRING in place

    RD_A0 ~process_if_string_0 AUIPC  ; Using "# IF_"
    RD_A0 RS1_A0 !process_if_string_0 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 RS1_S11 !16 LD              ; function->S
    RD_A1 RS1_A2 MV                   ; Passing NUMBER_STRING
    RD_RA $uniqueID_out JAL           ; uniqueID_out(function->s, number_string)

    RD_S4 RS1_S4 LD                   ; global_token = global_token->next

    RD_A0 ~process_if_string_1 AUIPC  ; Using "ERROR in process_if\nMISSING (\n"
    RD_A0 RS1_A0 !process_if_string_1 ADDI
    RD_A1 ~open_paren AUIPC           ; Using "("
    RD_A1 RS1_A1 !open_paren ADDI
    RD_RA $require_match JAL          ; Make sure we have it

    RD_RA $expression JAL             ; Recurse to get the IF(...) part

    RD_A0 ~process_if_string_2 AUIPC  ; Using "RS1_A0 @8 BNEZ\n$ELSE_"
    RD_A0 RS1_A0 !process_if_string_2 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 RS1_S11 !16 LD              ; function->S
    RD_A1 RS1_A2 MV                   ; Passing NUMBER_STRING
    RD_RA $uniqueID_out JAL           ; uniqueID_out(function->s, number_string)

    RD_A0 ~process_if_string_2a AUIPC ; Using "JAL\n"
    RD_A0 RS1_A0 !process_if_string_2a ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 ~process_if_string_3 AUIPC  ; Using "ERROR in process_do\nMISSING )\n"
    RD_A0 RS1_A0 !process_if_string_3 ADDI
    RD_A1 ~close_paren AUIPC          ; Using ")"
    RD_A1 RS1_A1 !close_paren ADDI
    RD_RA $require_match JAL          ; Make sure we have it

    RD_RA $statement JAL              ; Recursive to get the IF(){...} part

    RD_A0 ~process_if_string_4 AUIPC  ; Using "$_END_IF_"
    RD_A0 RS1_A0 !process_if_string_4 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 RS1_S11 !16 LD              ; function->S
    RD_A1 RS1_A2 MV                   ; Passing NUMBER_STRING
    RD_RA $uniqueID_out JAL           ; uniqueID_out(function->s, number_string)

    RD_A0 ~process_if_string_5 AUIPC  ; Using "JAL\n:ELSE_"
    RD_A0 RS1_A0 !process_if_string_5 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 RS1_S11 !16 LD              ; function->S
    RD_A1 RS1_A2 MV                   ; Passing NUMBER_STRING
    RD_RA $uniqueID_out JAL           ; uniqueID_out(function->s, number_string)

    RD_A1 RS1_S4 !16 LD               ; global_token->S
    RD_A0 ~else_string AUIPC          ; Using "else"
    RD_A0 RS1_A0 !else_string ADDI
    RD_RA $match JAL                  ; IF global_token->S == "else"
    RS1_A0 @process_if_done BNEZ      ; Then we need to collect the else too, otherwise done

    ; deal with else statement
    RD_S4 RS1_S4 LD                   ; global_token = global_token->next

    RD_RA $statement JAL              ; Recurse to get the ELSE {...} part

:process_if_done
    RD_A0 ~process_if_string_6 AUIPC  ; Using ":_END_IF_"
    RD_A0 RS1_A0 !process_if_string_6 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 RS1_S11 !16 LD              ; function->S
    RD_A1 RS1_A2 MV                   ; Passing NUMBER_STRING
    RD_RA $uniqueID_out JAL           ; uniqueID_out(function->s, number_string)

    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_SP RS1_SP !24 ADDI             ; deallocate stack
    RETURN


; process_do function
; Receives Nothing
; Returns Nothing
; Increments current_count and leverages save/restore_break_frame pieces
; Uses a2 for char* NUMBER_STRING
:process_do
    RD_SP RS1_SP !-24 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2

    RD_RA $save_break_frame JAL       ; Save the frame
    RD_A0 RS1_S10 MV                  ; Get current_count
    RD_S10 RS1_S10 !1 ADDI            ; current_count = current_count + 1
    RD_RA $numerate_number JAL        ; convert to string
    RD_A2 RS1_A0 MV                   ; put NUMBER_STRING in place

    RD_A0 ~process_do_string_0 AUIPC  ; Using "DO_END_"
    RD_A0 RS1_A0 !process_do_string_0 ADDI
    RD_A1 RS1_A2 MV                   ; Passing NUMBER_STRING
    RD_RA $set_break_frame JAL        ; Set the frame

    RD_A0 ~process_do_string_1 AUIPC  ; Using ":DO_"
    RD_A0 RS1_A0 !process_do_string_1 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 RS1_S11 !16 LD              ; function->S
    RD_A1 RS1_A2 MV                   ; Passing NUMBER_STRING
    RD_RA $uniqueID_out JAL           ; uniqueID_out(function->s, number_string)

    RD_S4 RS1_S4 LD                   ; global_token = global_token->next

    RD_RA $statement JAL              ; Do the DO {...} part

    RD_A0 ~process_do_string_2 AUIPC  ; Using "ERROR in process_do\nMISSING while\n"
    RD_A0 RS1_A0 !process_do_string_2 ADDI
    RD_A1 ~while_string AUIPC         ; Using "while"
    RD_A1 RS1_A1 !while_string ADDI
    RD_RA $require_match JAL          ; Make sure we have it

    RD_A0 ~process_do_string_3 AUIPC  ; Using "ERROR in process_do\nMISSING (\n"
    RD_A0 RS1_A0 !process_do_string_3 ADDI
    RD_A1 ~open_paren AUIPC           ; Using "("
    RD_A1 RS1_A1 !open_paren ADDI
    RD_RA $require_match JAL          ; Make sure we have it

    RD_RA $expression JAL             ; Do the WHILE (...) part

    RD_A0 ~process_do_string_4 AUIPC  ; Using "ERROR in process_do\nMISSING )\n"
    RD_A0 RS1_A0 !process_do_string_4 ADDI
    RD_A1 ~close_paren AUIPC          ; Using ")"
    RD_A1 RS1_A1 !close_paren ADDI
    RD_RA $require_match JAL          ; Make sure we have it

    RD_A0 ~process_do_string_5 AUIPC  ; Using "ERROR in process_do\nMISSING ;\n"
    RD_A0 RS1_A0 !process_do_string_5 ADDI
    RD_A1 ~semicolon AUIPC            ; Using ";"
    RD_A1 RS1_A1 !semicolon ADDI
    RD_RA $require_match JAL          ; Make sure we have it

    RD_A0 ~process_do_string_6 AUIPC  ; Using "RS1_A0 @DO_END_"
    RD_A0 RS1_A0 !process_do_string_6 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 RS1_S11 !16 LD              ; function->S
    RD_A1 RS1_A2 MV                   ; Passing NUMBER_STRING
    RD_RA $uniqueID_out JAL           ; uniqueID_out(function->s, number_string)

    RD_A0 ~process_do_string_6a AUIPC ; Using "BEQZ\n$DO_"
    RD_A0 RS1_A0 !process_do_string_6a ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 RS1_S11 !16 LD              ; function->S
    RD_A1 RS1_A2 MV                   ; Passing NUMBER_STRING
    RD_RA $uniqueID_out JAL           ; uniqueID_out(function->s, number_string)

    RD_A0 ~process_do_string_7 AUIPC  ; Using "JAL\n:DO_END_"
    RD_A0 RS1_A0 !process_do_string_7 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 RS1_S11 !16 LD              ; function->S
    RD_RA $uniqueID_out JAL           ; uniqueID_out(function->s, number_string)

    RD_RA $restore_break_frame JAL    ; Restore the old break frame

    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_SP RS1_SP !24 ADDI             ; deallocate stack
    RETURN


; process_while function
; Receives nothing
; Returns nothing
; Increments current_count and leverages save/restore_break_frame pieces
; Uses a2 for char* NUMBER_STRING
:process_while
    RD_SP RS1_SP !-24 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2

    RD_RA $save_break_frame JAL       ; Save the frame
    RD_A0 RS1_S10 MV                  ; Get current_count
    RD_S10 RS1_S10 !1 ADDI            ; current_count = current_count + 1
    RD_RA $numerate_number JAL        ; convert to string
    RD_A2 RS1_A0 MV                   ; put NUMBER_STRING in place

    RD_A0 ~process_while_string_0 AUIPC ; Using "END_WHILE_"
    RD_A0 RS1_A0 !process_while_string_0 ADDI
    RD_A1 RS1_A2 MV                   ; Passing NUMBER_STRING
    RD_RA $set_break_frame JAL        ; Set it and forget it

    RD_A0 ~process_while_string_1 AUIPC ; Using ":WHILE_"
    RD_A0 RS1_A0 !process_while_string_1 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 RS1_S11 !16 LD              ; function->S
    RD_A1 RS1_A2 MV                   ; Passing NUMBER_STRING
    RD_RA $uniqueID_out JAL           ; uniqueID_out(function->s, number_string)

    RD_S4 RS1_S4 LD                   ; global_token = global_token->next

    RD_A0 ~process_while_string_2 AUIPC ; Using "ERROR in process_while\nMISSING (\n"
    RD_A0 RS1_A0 !process_while_string_2 ADDI
    RD_A1 ~open_paren AUIPC           ; Using "("
    RD_A1 RS1_A1 !open_paren ADDI
    RD_RA $require_match JAL          ; Make sure we have it

    RD_RA $expression JAL             ; Deal with the WHILE (...) part

    RD_A0 ~process_while_string_3 AUIPC ; Using "RS1_A0 @8 BNEZ\n$END_WHILE_"
    RD_A0 RS1_A0 !process_while_string_3 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 RS1_S11 !16 LD              ; function->S
    RD_A1 RS1_A2 MV                   ; Passing NUMBER_STRING
    RD_RA $uniqueID_out JAL           ; uniqueID_out(function->s, number_string)

    RD_A0 ~process_while_string_4 AUIPC ; Using "JAL\n# THEN_while_"
    RD_A0 RS1_A0 !process_while_string_4 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 RS1_S11 !16 LD              ; function->S
    RD_A1 RS1_A2 MV                   ; Passing NUMBER_STRING
    RD_RA $uniqueID_out JAL           ; uniqueID_out(function->s, number_string)

    RD_A0 ~process_while_string_5 AUIPC ; Using "ERROR in process_while\nMISSING )\n"
    RD_A0 RS1_A0 !process_while_string_5 ADDI
    RD_A1 ~close_paren AUIPC           ; Using ")"
    RD_A1 RS1_A1 !close_paren ADDI
    RD_RA $require_match JAL          ; Make sure we have it

    RD_RA $statement JAL              ; Deal with the {....} part

    RD_A0 ~process_while_string_6 AUIPC ; Using "$WHILE_"
    RD_A0 RS1_A0 !process_while_string_6 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 RS1_S11 !16 LD              ; function->S
    RD_A1 RS1_A2 MV                   ; Passing NUMBER_STRING
    RD_RA $uniqueID_out JAL           ; uniqueID_out(function->s, number_string)

    RD_A0 ~process_while_string_7 AUIPC ; Using "JAL\n:END_WHILE_"
    RD_A0 RS1_A0 !process_while_string_7 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 RS1_S11 !16 LD              ; function->S
    RD_A1 RS1_A2 MV                   ; Passing NUMBER_STRING
    RD_RA $uniqueID_out JAL           ; uniqueID_out(function->s, number_string)

    RD_RA $restore_break_frame JAL    ; Restore the old break frame

    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_SP RS1_SP !24 ADDI             ; deallocate stack
    RETURN


; process_for function
; Receives Nothing
; Returns Nothing
; Increments current_count and leverages save/restore_break_frame pieces
; Uses a2 for char* NUMBER_STRING
:process_for
    RD_SP RS1_SP !-24 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2

    RD_RA $save_break_frame JAL       ; Save the frame
    RD_A0 RS1_S10 MV                  ; Get current_count
    RD_S10 RS1_S10 !1 ADDI            ; current_count = current_count + 1
    RD_RA $numerate_number JAL        ; convert to string
    RD_A2 RS1_A0 MV                   ; put NUMBER_STRING in place

    RD_A0 ~process_for_string_0 AUIPC ; Using "FOR_END_"
    RD_A0 RS1_A0 !process_for_string_0 ADDI
    RD_A1 RS1_A2 MV                   ; Passing NUMBER_STRING
    RD_RA $set_break_frame JAL        ; Set it and forget it

    RD_A0 ~process_for_string_1 AUIPC ; Using "# FOR_initialization_"
    RD_A0 RS1_A0 !process_for_string_1 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 RS1_S11 !16 LD              ; function->S
    RD_A1 RS1_A2 MV                   ; Passing NUMBER_STRING
    RD_RA $uniqueID_out JAL           ; uniqueID_out(function->s, number_string)

    RD_S4 RS1_S4 LD                   ; global_token = global_token->next

    RD_A0 ~process_for_string_2 AUIPC ; Using "ERROR in process_for\nMISSING (\n"
    RD_A0 RS1_A0 !process_for_string_2 ADDI
    RD_A1 ~open_paren AUIPC           ; Using "("
    RD_A1 RS1_A1 !open_paren ADDI
    RD_RA $require_match JAL          ; Make sure we have it

    RD_A1 RS1_S4 !16 LD               ; global_token->S
    RD_A0 ~semicolon AUIPC            ; Using ";"
    RD_A0 RS1_A0 !semicolon ADDI
    RD_RA $match JAL                  ; IF global_token->S == ";"
    RS1_A0 @process_for_terminator BEQZ ; Then no initializer, skip getting the expression

    ; Deal with FOR (...; case
    RD_RA $expression JAL             ; Get the FOR ( ... ; part

:process_for_terminator
    RD_A0 ~process_for_string_3 AUIPC ; Using ":FOR_"
    RD_A0 RS1_A0 !process_for_string_3 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 RS1_S11 !16 LD              ; function->S
    RD_A1 RS1_A2 MV                   ; Passing NUMBER_STRING
    RD_RA $uniqueID_out JAL           ; uniqueID_out(function->s, number_string)

    RD_A0 ~process_for_string_4 AUIPC ; Using "ERROR in process_for\nMISSING ;1\n"
    RD_A0 RS1_A0 !process_for_string_4 ADDI
    RD_A1 ~semicolon AUIPC            ; Using ";"
    RD_A1 RS1_A1 !semicolon ADDI
    RD_RA $require_match JAL          ; Make sure we have it

    RD_RA $expression JAL             ; Get the FOR ( ; ... ; Part

    RD_A0 ~process_for_string_5 AUIPC ; Using "RS1_A0 @8 BNEZ\n$FOR_END_"
    RD_A0 RS1_A0 !process_for_string_5 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 RS1_S11 !16 LD              ; function->S
    RD_A1 RS1_A2 MV                   ; Passing NUMBER_STRING
    RD_RA $uniqueID_out JAL           ; uniqueID_out(function->s, number_string)

    RD_A0 ~process_for_string_6 AUIPC ; Using "JAL\n$FOR_THEN_"
    RD_A0 RS1_A0 !process_for_string_6 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 RS1_S11 !16 LD              ; function->S
    RD_RA $uniqueID_out JAL           ; uniqueID_out(function->s, number_string)

    RD_A0 ~process_for_string_7 AUIPC ; Using "JAL\n:FOR_ITER_"
    RD_A0 RS1_A0 !process_for_string_7 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 RS1_S11 !16 LD              ; function->S
    RD_RA $uniqueID_out JAL           ; uniqueID_out(function->s, number_string)

    RD_A0 ~process_for_string_8 AUIPC ; Using "ERROR in process_for\nMISSING ;2\n"
    RD_A0 RS1_A0 !process_for_string_8 ADDI
    RD_A1 ~semicolon AUIPC            ; Using ";"
    RD_A1 RS1_A1 !semicolon ADDI
    RD_RA $require_match JAL          ; Make sure we have it

    RD_RA $expression JAL             ; Get the FOR (;;...) part

    RD_A0 ~process_for_string_9 AUIPC ; Using "$FOR_"
    RD_A0 RS1_A0 !process_for_string_9 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 RS1_S11 !16 LD              ; function->S
    RD_A1 RS1_A2 MV                   ; Passing NUMBER_STRING
    RD_RA $uniqueID_out JAL           ; uniqueID_out(function->s, number_string)

    RD_A0 ~process_for_string_10 AUIPC ; Using "JAL\n:FOR_THEN_"
    RD_A0 RS1_A0 !process_for_string_10 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 RS1_S11 !16 LD              ; function->S
    RD_A1 RS1_A2 MV                   ; Passing NUMBER_STRING
    RD_RA $uniqueID_out JAL           ; uniqueID_out(function->s, number_string)

    RD_A0 ~process_for_string_11 AUIPC ; Using "ERROR in process_for\nMISSING )\n"
    RD_A0 RS1_A0 !process_for_string_11 ADDI
    RD_A1 ~close_paren AUIPC          ; Using ")"
    RD_A1 RS1_A1 !close_paren ADDI
    RD_RA $require_match JAL          ; Make sure we have it

    RD_RA $statement JAL              ; Get FOR (;;) {...} part

    RD_A0 ~process_for_string_12 AUIPC ; Using "$FOR_ITER_"
    RD_A0 RS1_A0 !process_for_string_12 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 RS1_S11 !16 LD              ; function->S
    RD_A1 RS1_A2 MV                   ; Passing NUMBER_STRING
    RD_RA $uniqueID_out JAL           ; uniqueID_out(function->s, number_string)

    RD_A0 ~process_for_string_13 AUIPC ; Using "JAL\n:FOR_END_"
    RD_A0 RS1_A0 !process_for_string_13 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 RS1_S11 !16 LD              ; function->S
    RD_A1 RS1_A2 MV                   ; Passing NUMBER_STRING
    RD_RA $uniqueID_out JAL           ; uniqueID_out(function->s, number_string)

    RD_RA $restore_break_frame JAL    ; Restore the old break frame

    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_SP RS1_SP !24 ADDI             ; deallocate stack
    RETURN


; process_asm function
; Receives nothing
; Returns nothing
; Simply inlines the asm statements
; Uses a1 for global_token temp storage
:process_asm
    RD_SP RS1_SP !-16 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1

    RD_S4 RS1_S4 LD                   ; global_token = global_token->next

    RD_A0 ~process_asm_string_0 AUIPC ; Using "ERROR in process_asm\nMISSING (\n"
    RD_A0 RS1_A0 !process_asm_string_0 ADDI
    RD_A1 ~open_paren AUIPC           ; Using "("
    RD_A1 RS1_A1 !open_paren ADDI
    RD_RA $require_match JAL          ; Make sure we have it

:process_asm_iter
    RD_A0 RS1_S4 !16 LD               ; global_token->S
    RD_A0 RS1_A0 LBU                  ; global_token->S[0]
    RD_T0 !34 ADDI                    ; IF global_token->S[0] == '"'
    RS1_A0 RS2_T0 @process_asm_done BNE ; Otherwise be done

    RD_A0 RS1_S4 !16 LD               ; global_token->S
    RD_A0 RS1_A0 !1 ADDI              ; global_token->S + 1
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 ~process_asm_string_1 AUIPC ; ; Using "\n"
    RD_A0 RS1_A0 !process_asm_string_1 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_S4 RS1_S4 LD                   ; global_token = global_token->next
    $process_asm_iter JAL             ; keep going

:process_asm_done
    RD_A0 ~process_asm_string_2 AUIPC ; Using "ERROR in process_asm\nMISSING )\n"
    RD_A0 RS1_A0 !process_asm_string_2 ADDI
    RD_A1 ~close_paren AUIPC          ; Using ")"
    RD_A1 RS1_A1 !close_paren ADDI
    RD_RA $require_match JAL          ; Make sure we have it

    RD_A0 ~process_asm_string_3 AUIPC ; Using "ERROR in process_asm\nMISSING ;\n"
    RD_A0 RS1_A0 !process_asm_string_3 ADDI
    RD_A1 ~semicolon AUIPC            ; Using ";"
    RD_A1 RS1_A1 !semicolon ADDI
    RD_RA $require_match JAL          ; Make sure we have it

    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_SP RS1_SP !16 ADDI             ; deallocate stack
    RETURN


; return_result function
; Receives nothing
; Returns nothing
; Cleans up function and generates return
; Also handles returning expressions
:return_result
    RD_SP RS1_SP !-24 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2

    RD_S4 RS1_S4 LD                   ; global_token = global_token->next

    RD_A0 RS1_S4 !16 LD               ; global_token->S
    RD_A0 RS1_A0 LBU                  ; global_token->S[0]
    RD_T0 !59 ADDI                    ; ';'
    RS1_A0 RS2_T0 @return_result_cleanup BEQ ; Go straight to cleanup

    RD_RA $expression JAL             ; get the expression we are returning

:return_result_cleanup
    RD_A0 ~return_result_string_0 AUIPC ; Using "ERROR in return_result\nMISSING ;\n"
    RD_A0 RS1_A0 !return_result_string_0 ADDI
    RD_A1 ~semicolon AUIPC            ; Using ";"
    RD_A1 RS1_A1 !semicolon ADDI
    RD_RA $require_match JAL          ; Make sure we have it

    RD_A1 RS1_S11 !8 LD               ; i = function->locals
    RD_A0 ~return_result_string_1 AUIPC ; Using "RD_A1 RS1_SP LD\t# _return_result_locals..."
    RD_A0 RS1_A0 !return_result_string_1 ADDI

:return_result_locals
    RS1_A1 @return_result_done BEQZ   ; We are done if NULL == i

    RD_RA $emit_out JAL               ; Emit out pop
    RD_A1 RS1_A1 LD                   ; i = i->NEXT
    $return_result_locals JAL         ; Keep going

:return_result_done
    RD_A0 ~return_result_string_2 AUIPC ; Using "RETURN\n"
    RD_A0 RS1_A0 !return_result_string_2 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_SP RS1_SP !24 ADDI             ; deallocate stack
    RETURN


; process_break function
; Receives nothing
; Returns nothing
; Handles the break out of loops case
; Uses a1 for struct token_list* break_frame and a2 for struct token_list* I
:process_break
    RD_SP RS1_SP !-24 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2

    RD_T0 ~break_target_head AUIPC    ; Get break_target_head
    RD_T0 RS1_T0 !break_target_head ADDI
    RD_A0 RS1_T0 LD                   ; Catch big error
    RS1_A0 @process_break_bad BEQZ     ; Exit if IF(NULL == break_target_head)

    RD_A2 RS1_S11 !8 LD               ; I = function->locals
    RD_T0 ~break_frame AUIPC          ; Get break_frame
    RD_T0 RS1_T0 !break_frame ADDI
    RD_A1 RS1_T0 LD                   ; Put break_frame in the right spot
    RD_A0 ~process_break_string_1 AUIPC ; Using "RD_A1 RS1_SP LD	# break_cleanup_locals..."
    RD_A0 RS1_A0 !process_break_string_1 ADDI

:process_break_iter
    RS1_A2 @process_break_cleaned BEQZ ; We are done IF (NULL == I)
    RS1_A1 RS2_A2 @process_break_cleaned BEQ ; We are done IF I != break_frame

    RD_RA $emit_out JAL               ; Emit it
    RD_A2 RS1_A2 LD                   ; I = I->NEXT
    $process_break_iter JAL           ; Keep looping

:process_break_cleaned
    RD_S4 RS1_S4 LD                   ; global_token = global_token->next
    RD_A0 ~process_break_string_2 AUIPC ; Using "$"
    RD_A0 RS1_A0 !process_break_string_2 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_T0 ~break_target_head AUIPC    ; Using break_target_head
    RD_T0 RS1_T0 !break_target_head ADDI
    RD_A0 RS1_T0 LD                   ; Get what we are in
    RD_RA $emit_out JAL               ; Emit it

    RD_T0 ~break_target_func AUIPC    ; Using break_target_func
    RD_T0 RS1_T0 !break_target_func ADDI
    RD_A0 RS1_T0 LD                   ; Get what function we are in
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 ~underline AUIPC            ; Using "_"
    RD_A0 RS1_A0 !underline ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_T0 ~break_target_num AUIPC    ; Using break_target_num
    RD_T0 RS1_T0 !break_target_num ADDI
    RD_A0 RS1_T0 LD                   ; Get the digits
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 ~process_break_string_3 AUIPC ; Using " JAL\n"
    RD_A0 RS1_A0 !process_break_string_3 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 ~process_break_string_4 AUIPC ; Using "ERROR in break statement\nMissing ;\n"
    RD_A0 RS1_A0 !process_break_string_4 ADDI
    RD_A1 ~semicolon AUIPC            ; Using ";"
    RD_A1 RS1_A1 !semicolon ADDI
    RD_RA $require_match JAL          ; Make sure we have it

    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_SP RS1_SP !24 ADDI             ; deallocate stack
    RETURN

:process_break_bad
    ; Breaking badly
    RD_S3 !2 ADDI                     ; write to standard error
    RD_A0 RS1_A2                      ; Put S in the right place
    RD_RA $File_Print JAL             ; print it

    RD_A0 ~process_break_string_0 AUIPC ; Ending string
    RD_A0 RS1_A0 !process_break_string_0 ADDI
    RD_RA $File_Print JAL             ; print it
    $Fail JAL                         ; Abort Hard


; expression function
; Receives Nothing
; Returns Nothing
; Walks global_token and updates output_list
; Uses a0 and a1 for match and a2 for char* store
:expression
    RD_SP RS1_SP !-24 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2

    RD_RA $bitwise_expr JAL           ; Collect bitwise expressions

    RD_A1 RS1_S4 !16 LD               ; global_token->S
    RD_A0 ~equal AUIPC                ; "="
    RD_A0 RS1_A0 !equal ADDI
    RD_RA $match JAL                  ; IF global_token->S == "="
    RS1_A0 @expression_done BNEZ      ; We have to deal with assignment

    ; Deal with possible assignment
    RD_A2 ~expression_string_1 AUIPC  ; Assume "RS1_A1 RS2_A0 SB\n" by default
    RD_A2 RS1_A2 !expression_string_1 ADDI
    RD_A1 RS1_S4 !8 LD                ; global_token->prev
    RD_A1 RS1_A1 !16 LD               ; global_token->prev->S
    RD_A0 ~close_bracket AUIPC        ; Using "]"
    RD_A0 RS1_A0 !close_bracket ADDI
    RD_RA $match JAL                  ; IF global_token->S == "]"
    RS1_A0 @expression_int BNEZ       ; Otherwise INT

    ; Deal with char
    RD_A1 ~current_target AUIPC       ; Using current_target
    RD_A1 RS1_A1 !current_target ADDI
    RD_A1 RS1_A1 LD                   ; current_target
    RD_A1 RS1_A1 !48 LD               ; current_target->NAME
    RD_A0 ~type_char_indirect_name AUIPC ; Using "char *"
    RD_A0 RS1_A0 !type_char_indirect_name ADDI
    RD_RA $match JAL                  ; Intentional inefficiency because I feel like it
    RS1_A0 @expression_int BNEZ       ; IF current_target->NAME == "char*"

    $expression_common JAL            ; Looks like we have to use "RS1_A1 RS2_A0 SB\n"

:expression_int
    RD_A2 ~expression_string_0 AUIPC  ; Using "RS1_A1 RS2_A0 SD\n"
    RD_A2 RS1_A2 !expression_string_0 ADDI

:expression_common
    RD_A0 ~expression AUIPC           ; Passing expression
    RD_A0 RS1_A0 !expression ADDI
    RD_RA $common_recursion JAL       ; Recurse
    RD_A0 RS1_A2 MV                   ; Using Store
    RD_RA $emit_out JAL               ; Emit it
    RD_T0 ~current_target AUIPC       ; Using current_target
    RD_T0 RS1_T0 !current_target ADDI
    RS1_T0 SD                         ; current_target = NULL
    
:expression_done
    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_SP RS1_SP !24 ADDI             ; deallocate stack
    RETURN


; bitwise_expr function
; Receives nothing
; Returns nothing
; Walks global_token list and updates output list
; Just calls other functions
:bitwise_expr
    RD_SP RS1_SP !-8 ADDI             ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra

    RD_RA $relational_expr JAL        ; Walk up the tree
    RD_RA $bitwise_expr_stub JAL      ; Let general recursion do the work

    RD_RA RS1_SP LD                   ; restore ra
    RD_SP RS1_SP !8 ADDI              ; deallocate stack
    RETURN


; bitwise_expr_stub function
; Receives nothing
; Returns Nothing
; Just calls general_recursion a bunch
; Uses a0, a1, a2 and a3 for passing constants to general recursion
:bitwise_expr_stub
    RD_SP RS1_SP !-32 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2
    RS1_SP RS2_A3 @24 SD              ; protect a3

    RD_A0 ~relational_expr AUIPC      ; Using relational_expr
    RD_A0 RS1_A0 !relational_expr ADDI
    RD_A1 ~bitwise_expr_stub_string_0 AUIPC ; Using "RD_A0 RS1_A1 RS2_A0 AND\n"
    RD_A1 RS1_A1 !bitwise_expr_stub_string_0 ADDI
    RD_A2 ~bitwise_and AUIPC          ; Using "&"
    RD_A2 RS1_A2 !bitwise_and ADDI
    RD_A3 ~bitwise_expr_stub AUIPC    ; And recurse
    RD_A3 RS1_A3 !bitwise_expr_stub ADDIW
    RD_RA $general_recursion JAL      ; Hit it

    RD_A0 ~relational_expr AUIPC      ; Using relational_expr
    RD_A0 RS1_A0 !relational_expr ADDI
    RD_A1 ~bitwise_expr_stub_string_0 AUIPC ; Using "RD_A0 RS1_A1 RS2_A0 AND\n"
    RD_A1 RS1_A1 !bitwise_expr_stub_string_0 ADDI
    RD_A2 ~logical_and AUIPC          ; Using "&&"
    RD_A2 RS1_A2 !logical_and ADDI
    RD_A3 ~bitwise_expr_stub AUIPC    ; And recurse
    RD_A3 RS1_A3 !bitwise_expr_stub ADDIW
    RD_RA $general_recursion JAL      ; Hit it

    RD_A0 ~relational_expr AUIPC      ; Using relational_expr
    RD_A0 RS1_A0 !relational_expr ADDI
    RD_A1 ~bitwise_expr_stub_string_1 AUIPC ; Using "RD_A0 RS1_A1 RS2_A0 OR\n"
    RD_A1 RS1_A1 !bitwise_expr_stub_string_1 ADDI
    RD_A2 ~bitwise_or AUIPC           ; Using "|"
    RD_A2 RS1_A2 !bitwise_or ADDI
    RD_A3 ~bitwise_expr_stub AUIPC    ; And recurse
    RD_A3 RS1_A3 !bitwise_expr_stub ADDIW
    RD_RA $general_recursion JAL      ; Hit it

    RD_A0 ~relational_expr AUIPC      ; Using relational_expr
    RD_A0 RS1_A0 !relational_expr ADDI
    RD_A1 ~bitwise_expr_stub_string_1 AUIPC ; Using "RD_A0 RS1_A1 RS2_A0 OR\n"
    RD_A1 RS1_A1 !bitwise_expr_stub_string_1 ADDI
    RD_A2 ~logical_or AUIPC           ; Using "||"
    RD_A2 RS1_A2 !logical_or ADDI
    RD_A3 ~bitwise_expr_stub AUIPC    ; And recurse
    RD_A3 RS1_A3 !bitwise_expr_stub ADDIW
    RD_RA $general_recursion JAL      ; Hit it

    RD_A0 ~relational_expr AUIPC      ; Using relational_expr
    RD_A0 RS1_A0 !relational_expr ADDI
    RD_A1 ~bitwise_expr_stub_string_2 AUIPC ; Using "RD_A0 RS1_A1 RS2_A0 XOR\n"
    RD_A1 RS1_A1 !bitwise_expr_stub_string_2 ADDI
    RD_A2 ~bitwise_xor AUIPC          ; Using "^"
    RD_A2 RS1_A2 !bitwise_xor ADDI
    RD_A3 ~bitwise_expr_stub AUIPC    ; And recurse
    RD_A3 RS1_A3 !bitwise_expr_stub ADDIW
    RD_RA $general_recursion JAL      ; Hit it

    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_A3 RS1_SP !24 LD               ; restore a3
    RD_SP RS1_SP !32 ADDI             ; deallocate stack
    RETURN


; relational_expr function
; Receives nothing
; Returns nothing
; Walks global_token list and updates output list
; Just calls other functions
:relational_expr
    RD_SP RS1_SP !-8 ADDI             ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra

    RD_RA $additive_expr JAL          ; Walk up the tree
    RD_RA $relational_expr_stub JAL   ; Recurse

    RD_RA RS1_SP LD                   ; restore ra
    RD_SP RS1_SP !8 ADDI              ; deallocate stack
    RETURN


; relational_expr_stub function
; Receives nothing
; Returns Nothing
; Just calls general_recursion a bunch
; Uses a0, a1, a2 and a3 for passing constants to general recursion
:relational_expr_stub
    RD_SP RS1_SP !-32 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2
    RS1_SP RS2_A3 @24 SD              ; protect a3

    RD_A0 ~additive_expr AUIPC        ; Using additive_expr
    RD_A0 RS1_A0 !additive_expr ADDI
    RD_A1 ~relational_expr_stub_string_0 AUIPC ; Using "RD_A0 RS1_A1 RS2_A0 SLT\n"
    RD_A1 RS1_A1 !relational_expr_stub_string_0 ADDI
    RD_A2 ~less_than_string AUIPC     ; Using "<"
    RD_A2 RS1_A2 !less_than_string ADDI
    RD_A3 ~relational_expr_stub AUIPC ; And recurse
    RD_A3 RS1_A3 !relational_expr_stub ADDIW
    RD_RA $general_recursion JAL      ; Hit it

    RD_A0 ~additive_expr AUIPC        ; Using additive_expr
    RD_A0 RS1_A0 !additive_expr ADDI
    RD_A1 ~relational_expr_stub_string_1 AUIPC ; Using "RD_A0 RS1_A0 RS2_A1 SLT\nRD_A0 RS1_A0 !1 XORI\n"
    RD_A1 RS1_A1 !relational_expr_stub_string_1 ADDI
    RD_A2 ~less_than_equal_string AUIPC ; Using "<="
    RD_A2 RS1_A2 !less_than_equal_string ADDI
    RD_A3 ~relational_expr_stub AUIPC ; And recurse
    RD_A3 RS1_A3 !relational_expr_stub ADDIW
    RD_RA $general_recursion JAL      ; Hit it

    RD_A0 ~additive_expr AUIPC        ; Using additive_expr
    RD_A0 RS1_A0 !additive_expr ADDI
    RD_A1 ~relational_expr_stub_string_2 AUIPC ; Using "RD_A0 RS1_A1 RS2_A0 SLT\nRD_A0 RS1_A0 !1 XORI\n"
    RD_A1 RS1_A1 !relational_expr_stub_string_2 ADDI
    RD_A2 ~greater_than_equal_string AUIPC ; Using ">="
    RD_A2 RS1_A2 !greater_than_equal_string ADDI
    RD_A3 ~relational_expr_stub AUIPC ; And recurse
    RD_A3 RS1_A3 !relational_expr_stub ADDIW
    RD_RA $general_recursion JAL      ; Hit it

    RD_A0 ~additive_expr AUIPC        ; Using additive_expr
    RD_A0 RS1_A0 !additive_expr ADDI
    RD_A1 ~relational_expr_stub_string_3 AUIPC ; Using "RD_A0 RS1_A0 RS2_A1 SLT\n"
    RD_A1 RS1_A1 !relational_expr_stub_string_3 ADDI
    RD_A2 ~greater_than_string AUIPC  ; Using ">"
    RD_A2 RS1_A2 !greater_than_string ADDI
    RD_A3 ~relational_expr_stub AUIPC ; And recurse
    RD_A3 RS1_A3 !relational_expr_stub ADDIW
    RD_RA $general_recursion JAL      ; Hit it

    RD_A0 ~additive_expr AUIPC        ; Using additive_expr
    RD_A0 RS1_A0 !additive_expr ADDI
    RD_A1 ~relational_expr_stub_string_4 AUIPC ; Using "RD_A0 RS1_A0 RS2_A1 SUB\nRD_A0 RS1_A0 !1 SLTIU\n"
    RD_A1 RS1_A1 !relational_expr_stub_string_4 ADDI
    RD_A2 ~equal_to_string AUIPC      ; Using "=="
    RD_A2 RS1_A2 !equal_to_string ADDI
    RD_A3 ~relational_expr_stub AUIPC ; And recurse
    RD_A3 RS1_A3 !relational_expr_stub ADDIW
    RD_RA $general_recursion JAL      ; Hit it

    RD_A0 ~additive_expr AUIPC        ; Using additive_expr
    RD_A0 RS1_A0 !additive_expr ADDI
    RD_A1 ~relational_expr_stub_string_5 AUIPC ; Using "RD_A0 RS1_A0 RS2_A1 SUB\nRD_A0 RS2_A0 SLTU\n"
    RD_A1 RS1_A1 !relational_expr_stub_string_5 ADDI
    RD_A2 ~not_equal_string AUIPC     ; Using "!="
    RD_A2 RS1_A2 !not_equal_string ADDI
    RD_A3 ~relational_expr_stub AUIPC ; And recurse
    RD_A3 RS1_A3 !relational_expr_stub ADDIW
    RD_RA $general_recursion JAL      ; Hit it

    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_A3 RS1_SP !24 LD               ; restore a3
    RD_SP RS1_SP !32 ADDI             ; deallocate stack
    RETURN


; additive_expr function
; Receives nothing
; Returns nothing
; Walks global_token list and updates output list
; Just calls other functions
:additive_expr
    RD_SP RS1_SP !-8 ADDI             ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra

    RD_RA $postfix_expr JAL           ; Walk up the tree
    RD_RA $additive_expr_stub JAL     ; Recurse

    RD_RA RS1_SP LD                   ; restore ra
    RD_SP RS1_SP !8 ADDI              ; deallocate stack
    RETURN


; additive_expr_stub function
; Receives nothing
; Returns Nothing
; Just calls general_recursion a bunch
; Uses a0, a1, a2 and a3 for passing constants to general recursion
:additive_expr_stub
    RD_SP RS1_SP !-32 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2
    RS1_SP RS2_A3 @24 SD              ; protect a3

    RD_A0 ~postfix_expr AUIPC         ; Using postfix_expr
    RD_A0 RS1_A0 !postfix_expr ADDI
    RD_A1 ~additive_expr_stub_string_0 AUIPC ; Using "RD_A0 RS1_A1 RS2_A0 ADD\n"
    RD_A1 RS1_A1 !additive_expr_stub_string_0 ADDI
    RD_A2 ~plus_string AUIPC          ; Using "+"
    RD_A2 RS1_A2 !plus_string ADDI
    RD_A3 ~additive_expr_stub AUIPC   ; And recurse
    RD_A3 RS1_A3 !additive_expr_stub ADDIW
    RD_RA $general_recursion JAL      ; Hit it

    RD_A0 ~postfix_expr AUIPC         ; Using postfix_expr
    RD_A0 RS1_A0 !postfix_expr ADDI
    RD_A1 ~additive_expr_stub_string_1 AUIPC ; Using "RD_A0 RS1_A1 RS2_A0 SUB\n"
    RD_A1 RS1_A1 !additive_expr_stub_string_1 ADDI
    RD_A2 ~minus_string AUIPC         ; Using "-"
    RD_A2 RS1_A2 !minus_string ADDI
    RD_A3 ~additive_expr_stub AUIPC   ; And recurse
    RD_A3 RS1_A3 !additive_expr_stub ADDIW
    RD_RA $general_recursion JAL      ; Hit it

    RD_A0 ~postfix_expr AUIPC         ; Using postfix_expr
    RD_A0 RS1_A0 !postfix_expr ADDI
    RD_A1 ~additive_expr_stub_string_2 AUIPC ; Using "RD_A0 RS1_A1 RS2_A0 MUL\n"
    RD_A1 RS1_A1 !additive_expr_stub_string_2 ADDI
    RD_A2 ~multiply_string AUIPC      ; Using "*"
    RD_A2 RS1_A2 !multiply_string ADDI
    RD_A3 ~additive_expr_stub AUIPC   ; And recurse
    RD_A3 RS1_A3 !additive_expr_stub ADDIW
    RD_RA $general_recursion JAL      ; Hit it

    RD_A0 ~postfix_expr AUIPC         ; Using postfix_expr
    RD_A0 RS1_A0 !postfix_expr ADDI
    RD_A1 ~additive_expr_stub_string_3 AUIPC ; Using "RD_A0 RS1_A1 RS2_A0 DIV\n"
    RD_A1 RS1_A1 !additive_expr_stub_string_3 ADDI
    RD_A2 ~divide_string AUIPC        ; Using "/"
    RD_A2 RS1_A2 !divide_string ADDI
    RD_A3 ~additive_expr_stub AUIPC   ; And recurse
    RD_A3 RS1_A3 !additive_expr_stub ADDIW
    RD_RA $general_recursion JAL      ; Hit it

    RD_A0 ~postfix_expr AUIPC         ; Using postfix_expr
    RD_A0 RS1_A0 !postfix_expr ADDI
    RD_A1 ~additive_expr_stub_string_4 AUIPC ; Using "RD_A0 RS1_A1 RS2_A0 REM\n"
    RD_A1 RS1_A1 !additive_expr_stub_string_4 ADDI
    RD_A2 ~modulus_string AUIPC       ; Using "%"
    RD_A2 RS1_A2 !modulus_string ADDI
    RD_A3 ~additive_expr_stub AUIPC   ; And recurse
    RD_A3 RS1_A3 !additive_expr_stub ADDIW
    RD_RA $general_recursion JAL      ; Hit it

    RD_A0 ~postfix_expr AUIPC         ; Using postfix_expr
    RD_A0 RS1_A0 !postfix_expr ADDI
    RD_A1 ~additive_expr_stub_string_5 AUIPC ; Using "RD_A0 RS1_A1 RS2_A0 SLL\n"
    RD_A1 RS1_A1 !additive_expr_stub_string_5 ADDI
    RD_A2 ~left_shift_string AUIPC    ; Using "<<"
    RD_A2 RS1_A2 !left_shift_string ADDI
    RD_A3 ~additive_expr_stub AUIPC   ; And recurse
    RD_A3 RS1_A3 !additive_expr_stub ADDIW
    RD_RA $general_recursion JAL      ; Hit it

    RD_A0 ~postfix_expr AUIPC         ; Using postfix_expr
    RD_A0 RS1_A0 !postfix_expr ADDI
    RD_A1 ~additive_expr_stub_string_6 AUIPC ; Using "RD_A0 RS1_A1 RS2_A0 SRL\n"
    RD_A1 RS1_A1 !additive_expr_stub_string_6 ADDI
    RD_A2 ~right_shift_string AUIPC   ; Using ">>"
    RD_A2 RS1_A2 !right_shift_string ADDI
    RD_A3 ~additive_expr_stub AUIPC   ; And recurse
    RD_A3 RS1_A3 !additive_expr_stub ADDIW
    RD_RA $general_recursion JAL      ; Hit it

    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_A3 RS1_SP !24 LD               ; restore a3
    RD_SP RS1_SP !32 ADDI             ; deallocate stack
    RETURN


; postfix_expr function
; Receives nothing
; Returns Nothing
; Walks global_token list and updates output list
; just calls other function
:postfix_expr
    RD_SP RS1_SP !-8 ADDI             ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra

    RD_RA $primary_expr JAL           ; Walk up the tree
    RD_RA $postfix_expr_stub JAL      ; Recurse

    RD_RA RS1_SP LD                   ; restore ra
    RD_SP RS1_SP !8 ADDI              ; deallocate stack
    RETURN


; postfix_expr_stub function
; Receives nothing
; Returns Nothing
; Checks for "[" and "->" and deals with them otherwise does nothing
; Uses a0, a1, a2 and a3 for passing constants to general recursion
:postfix_expr_stub
    RD_SP RS1_SP !-16 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1

    RD_A1 RS1_S4 !16 LD               ; global_token->S
    RD_A0 ~open_bracket AUIPC         ; Using "["
    RD_A0 RS1_A0 !open_bracket ADDI
    RD_RA $match JAL                  ; IF global_token->S == "["
    RS1_A0 @postfix_expr_stub_arrow BNEZ ; then we have an array, else try arrow

    ; Deal with array
    RD_RA $postfix_expr_array JAL     ; Get it
    RD_RA $postfix_expr_stub JAL      ; Recurse

:postfix_expr_stub_arrow
    RD_A0 ~arrow_string AUIPC         ; Using "->"
    RD_A0 RS1_A0 !arrow_string ADDI
    RD_RA $match JAL                  ; IF global_token->S == "->"
    RS1_A0 @postfix_expr_stub_done BNEZ ; then have to deal with struct offset, else done

    ; Deal with arrow
    RD_RA $postfix_expr_arrow JAL     ; Get it
    RD_RA $postfix_expr_stub JAL      ; Recurse

:postfix_expr_stub_done
    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_SP RS1_SP !16 ADDI             ; deallocate stack
    RETURN


; unary_expr_sizeof function
; Receives nothing
; Returns nothing
; Uses a2 for A->SIZE
:unary_expr_sizeof
    RD_SP RS1_SP !-24 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2

    RD_S4 RS1_S4 LD                   ; global_token = global_token->next

    RD_A0 ~unary_expr_sizeof_string_0 AUIPC ; Using "ERROR in unary_expr\nMissing (\n"
    RD_A0 RS1_A0 !unary_expr_sizeof_string_0 ADDI
    RD_A1 ~open_paren AUIPC           ; Using "("
    RD_A1 RS1_A1 !open_paren ADDI
    RD_RA $require_match JAL          ; Make sure we have it

    RD_RA $type_name JAL              ; Get the type
    RD_A2 RS1_A0 !8 LD                ; Set A->TYPE

    RD_A0 ~unary_expr_sizeof_string_1 AUIPC ; Using "ERROR in unary_expr\nMissing )\n"
    RD_A0 RS1_A0 !unary_expr_sizeof_string_1 ADDI
    RD_A1 ~close_paren AUIPC          ; Using ")"
    RD_A1 RS1_A1 !close_paren ADDI
    RD_RA $require_match JAL          ; Make sure we have it

    RD_A0 ~unary_expr_sizeof_string_2 AUIPC ; Using "RD_A0 !"
    RD_A0 RS1_A0 !unary_expr_sizeof_string_2 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 RS1_A2 MV                   ; Put A->SIZE in the right place
    RD_RA $numerate_number JAL        ; Turn into string
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 ~unary_expr_sizeof_string_3 AUIPC ; Using " ADDI\n"
    RD_A0 RS1_A0 !unary_expr_sizeof_string_3 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_SP RS1_SP !24 ADDI             ; deallocate stack
    RETURN


; postfix_expr_array function
; Receives Nothing
; Returns Nothing
; Uses a1 for struct type* ARRAY and a2 for char* ASSIGN
:postfix_expr_array
    RD_SP RS1_SP !-32 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2

    RD_A0 ~current_target AUIPC       ; Using current_target
    RD_A0 RS1_A0 !current_target ADDI
    RD_A0 RS1_A0 LD                   ; ARRAY = current_target
    RS1_SP RS2_A0 @24 SD              ; Protect it

    RD_A0 ~expression AUIPC           ; Using expression
    RD_A0 RS1_A0 !expression ADDI
    RD_RA $common_recursion JAL       ; Recurse

    RD_A1 RS1_SP !24 LD               ; Restore array
    RD_A0 ~current_target AUIPC       ; Using current_target
    RD_A0 RS1_A0 !current_target ADDI
    RS1_A0 RS2_A1 SD                  ; current_target = ARRAY
    RD_A1 RS1_A0 LD                   ; Dereferece current_target

    RD_A2 ~postfix_expr_array_string_0 AUIPC ; ASSIGN = "RD_A0 RS1_A0 LD\n"
    RD_A2 RS1_A2 !postfix_expr_array_string_0 ADDI

    RD_A0 ~type_char_indirect_name AUIPC ; Using "char*"
    RD_A0 RS1_A0 !type_char_indirect_name ADDI
    RD_A1 RS1_A1 !48 LD               ; current_target->NAME
    RD_RA $match JAL                  ; IF current_target->NAME == "char*"
    RS1_A0 @postfix_expr_array_large BNEZ ; load a byte. otherwise adjust

    ; Deal with loading a byte
    RD_A2 ~postfix_expr_array_string_1 AUIPC ; ASSIGN = "RD_A0 RS1_A0 LBU\n"
    RD_A2 RS1_A2 !postfix_expr_array_string_1 ADDI
    $postfix_expr_array_common JAL    ; Do the next bit

:postfix_expr_array_large
    RD_A0 ~postfix_expr_array_string_2 AUIPC ; Using "RD_A0 RS1_A0 RS2_X"
    RD_A0 RS1_A0 !postfix_expr_array_string_2 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 ~current_target AUIPC       ; Using current_target
    RD_A0 RS1_A0 !current_target ADDI
    RD_A0 RS1_A0 LD                   ; current_target
    RD_A0 RS1_A0 !24 LD               ; current_target->INDIRECT
    RD_A0 RS1_A0 !8 LD                ; current_target->INDIRECT->SIZE
    RD_RA $ceil_log2 JAL              ; ceil_log2(current_target->indirect->size)
    RD_RA $numerate_number JAL        ; numerate_number(ceil_log2(current_target->indirect->size))
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 ~postfix_expr_array_string_3 AUIPC ; Using " SLLI\n"
    RD_A0 RS1_A0 !postfix_expr_array_string_3 ADDI
    RD_RA $emit_out JAL               ; Emit it

:postfix_expr_array_common
    RD_A0 ~postfix_expr_array_string_4 AUIPC ; Using "RD_A0 RS1_A1 RS2_A0 ADD\n"
    RD_A0 RS1_A0 !postfix_expr_array_string_4 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 ~postfix_expr_array_string_5 AUIPC ; Using "ERROR in postfix_expr\nMissing ]\n"
    RD_A0 RS1_A0 !postfix_expr_array_string_5 ADDI
    RD_A1 ~close_bracket AUIPC        ; Using "]"
    RD_A1 RS1_A1 !close_bracket ADDI
    RD_RA $require_match JAL          ; Make sure we have it

    RD_A1 RS1_S4 !16 LD               ; global_token->S
    RD_A0 ~equal AUIPC                ; Using "="
    RD_A0 RS1_A0 !equal ADDI
    RD_RA $match JAL                  ; IF global_token->S == "="
    RS1_A0 @postfix_expr_array_done BNEZ ; We need to preserve address, otherwise be done

    ; Clearing out assign
    RD_A2 ~postfix_expr_array_string_6 AUIPC ; ASSIGN = ""
    RD_A2 RS1_A2 !postfix_expr_array_string_6 ADDI

:postfix_expr_array_done
    RD_A0 RS1_A2 MV                   ; Using ASSIGN
    RD_RA $emit_out JAL               ; Emit it

    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_SP RS1_SP !32 ADDI             ; deallocate stack
    RETURN


; ceil_log2 function
; Receives int a in a0
; Performs log2 on A and
; Returns result in a0
; Uses a1 for INT A and a2 for INT RESULT
:ceil_log2
    RD_SP RS1_SP !-24 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2

    RS2_A0 MV                         ; RESULT = 0

    RD_A1 RS1_A0 MV                   ; put A in right place
    RD_A0 RS1_A0 !-1 ADDI             ; (A - 1)
    RD_A0 RS1_A0 RS2_A1 AND           ; A & (A - 1)
    RS1_A0 @ceil_log2_iter BNEZ       ; IF 0 == (A & (A - 1)) then starting from -1

    RD_A2 !-1 ADDI                    ; Result = -1

:ceil_log2_iter
    RS1_A1 @ceil_log2_done BEQZ       ; IF A == 0 we are done

    RD_A2 RS1_A2 !1 ADDI              ; RESULT = RESULT + 1
    RD_A1 RS1_A1 RS2_X1 SRLI          ; A = A >> 1
    $ceil_log2_iter JAL               ; Keep looping

:ceil_log2_done
    RD_A0 RS1_A2 MV                   ; Return RESULT
    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_SP RS1_SP !24 ADDI             ; deallocate stack
    RETURN


; postfix_expr_arrow function
; Receives nothing
; Returns nothing
; Emits a bunch and updates current_target
; Uses a1 for struct type* I
:postfix_expr_arrow
    RD_SP RS1_SP !-16 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1

    RD_A0 ~postfix_expr_arrow_string_0 AUIPC ; Using "# looking up offset\n"
    RD_A0 RS1_A0 !postfix_expr_arrow_string_0 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_S4 RS1_S4 LD                   ; global_token = global_token->next
    RD_A1 RS1_S4 !16 LD               ; global_token->S
    RD_A0 ~current_target AUIPC       ; Using current_target
    RD_A0 RS1_A0 !current_target ADDI
    RD_A0 RS1_A0 LD                   ; current_target
    RD_RA $lookup_member JAL          ; lookup_member(current_target, global_token->s)
    RD_A1 RS1_A0 MV                   ; struct type* I = lookup_member(current_target, global_token->s)

    RD_A0 RS1_A0 !40 LD               ; I->TYPE
    RD_T0 ~current_target AUIPC       ; Using current_target
    RD_T0 RS1_T0 !current_target ADDI
    RS1_T0 RS2_A0 SD                  ; current_target = I->TYPE

    RD_S4 RS1_S4 LD                   ; global_token = global_token->next

    RD_A0 RS1_A1 !16 LD               ; I->OFFSET
    RS1_A0 @postfix_expr_arrow_first BEQZ ; IF 0 != I->OFFSET then we don't need to do an offset

    ; Deal with needing an offset
    RD_A0 ~postfix_expr_arrow_string_1 AUIPC ; Using "# -> offset calculation\nRD_A1 !"
    RD_A0 RS1_A0 !postfix_expr_arrow_string_1 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 RS1_A1 !16 LD               ; I->OFFSET
    RD_RA $numerate_number JAL        ; Convert to string
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 ~postfix_expr_arrow_string_2 AUIPC ; Using " ADDI\nRD_A0 RS1_A1 RS2_A0 ADD\n"
    RD_A0 RS1_A0 !postfix_expr_arrow_string_2 ADDI
    RD_RA $emit_out JAL               ; Emit it

:postfix_expr_arrow_first
    RD_A0 RS1_A1 !8 LD                ; I->SIZE
    RD_T0 !4 ADDI                     ; 4
    RS1_A0 RS2_T0 @postfix_expr_arrow_done BLT ; Check IF I->SIZE >= 4, otherwise be done

    ; Last chance for load
    RD_A1 RS1_S4 !16 LD               ; global_token->S
    RD_A0 ~equal AUIPC                ; Using "="
    RD_A0 RS1_A0 !equal ADDI
    RD_RA $match JAL                  ; IF global_token->S == "="
    RS1_A0 @postfix_expr_arrow_done BEQZ ; Then we have assignment and should not load

    ; Deal with load case
    RD_A0 ~postfix_expr_arrow_string_3 AUIPC ; Using "RD_A0 RS1_A0 LD\n"
    RD_A0 RS1_A0 !postfix_expr_arrow_string_3 ADDI
    RD_RA $emit_out JAL               ; Emit it

:postfix_expr_arrow_done
    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_SP RS1_SP !16 ADDI             ; deallocate stack
    RETURN


; primary_expr function
; Receives nothing
; Returns nothing
:primary_expr
    RD_SP RS1_SP !-16 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1

    RD_A1 RS1_S4 !16 LD               ; global_token->S
    RD_A0 ~sizeof_string AUIPC        ; Using postfix_expr
    RD_A0 RS1_A0 !sizeof_string ADDI
    RD_RA $match JAL                  ; See if match
    RS1_A0 @primary_expr_neg BNEZ     ; Otherwise try negatives

    ; Deal with sizeof
    RD_RA $unary_expr_sizeof JAL      ; Lets do this
    $primary_expr_done JAL            ; We are done

:primary_expr_neg
    RD_A0 RS1_S4 !16 LD               ; global_token->S
    RD_A0 RS1_A0 LBU                  ; global_token->S[0]
    RD_T0 !45 ADDI                    ; "-"
    RS1_A0 RS2_T0 @primary_expr_not BNE ; Try negative IF global_token->S[0] == "-", else NOT

    ; Deal with negative numbers
    RD_A0 ~primary_expr_string_0 AUIPC ; Using "RD_A0 MV\n"
    RD_A0 RS1_A0 !primary_expr_string_0 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 ~postfix_expr AUIPC         ; Using postfix_expr
    RD_A0 RS1_A0 !postfix_expr ADDI
    RD_RA $common_recursion JAL       ; Get what it is notting

    RD_A0 ~primary_expr_string_1 AUIPC ; Using "RD_A0 RS1_A1 RS2_A0 SUB\n"
    RD_A0 RS1_A0 !primary_expr_string_1 ADDI
    RD_RA $emit_out JAL               ; Emit it
    $primary_expr_done JAL            ; We are done

:primary_expr_not
    RD_A0 RS1_S4 !16 LD               ; global_token->S
    RD_A0 RS1_A0 LBU                  ; global_token->S[0]
    RD_T0 !33 ADDI                    ; IF global_token->S[0] == "!"
    RS1_A0 RS2_T0 @primary_expr_bin BNE ; Otherwise try '~'

    ; Deal with logical not
    RD_A0 ~primary_expr_string_2 AUIPC ; Using "RD_A0 !1 ADDI\n"
    RD_A0 RS1_A0 !primary_expr_string_2 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 ~postfix_expr AUIPC         ; Using postfix_expr
    RD_A0 RS1_A0 !postfix_expr ADDI
    RD_RA $common_recursion JAL       ; Get what it is notting

    RD_A0 ~primary_expr_string_3 AUIPC ; ; Using "RD_A0 RS1_A1 RS2_A0 XOR\n"
    RD_A0 RS1_A0 !primary_expr_string_3 ADDI
    RD_RA $emit_out JAL               ; Emit it
    $primary_expr_done JAL            ; We are done

:primary_expr_bin
    RD_A0 RS1_S4 !16 LD               ; global_token->S
    RD_A0 RS1_A0 LBU                  ; global_token->S[0]
    RD_T0 !126 ADDI                   ; IF global_token->S[0] == "~"
    RS1_A0 RS2_T0 @primary_expr_paren BNE ; Otherwise try paren

    ; Deal with binary not_equal_string
    RD_A0 ~postfix_expr AUIPC         ; Using postfix_expr
    RD_A0 RS1_A0 !postfix_expr ADDI

    RD_A0 ~postfix_expr AUIPC         ; Using postfix_expr
    RD_A0 RS1_A0 !postfix_expr ADDI
    RD_RA $common_recursion JAL       ; Get what it is notting

    RD_A0 ~primary_expr_string_4 AUIPC ; Using "RD_A0 RS1_A0 NOT\n"
    RD_A0 RS1_A0 !primary_expr_string_4 ADDI
    RD_RA $emit_out JAL               ; Emit it
    $primary_expr_done JAL            ; We are done

:primary_expr_paren
    RD_A0 RS1_S4 !16 LD               ; global_token->S
    RD_A0 RS1_A0 LBU                  ; global_token->S[0]
    RD_T0 !40 ADDI                    ; IF global_token->S[0] == "("
    RS1_A0 RS2_T0 @primary_expr_ch BNE ; Otherwise try paren

    ; deal with nesting
    RD_S4 RS1_S4 LD                   ; global_token = global_token->next
    RD_RA $expression JAL             ; Lets recurse

    RD_A0 ~primary_expr_string_5 AUIPC ; Using Error in Primary expression\nDidn't get )\n"
    RD_A0 RS1_A0 !primary_expr_string_5 ADDI
    RD_A1 ~close_paren AUIPC          ; Using ")"
    RD_A1 RS1_A1 !close_paren ADDI
    RD_RA $require_match JAL          ; Make sure we have it
    $primary_expr_done JAL            ; We are done

:primary_expr_ch
    RD_A0 RS1_S4 !16 LD               ; global_token->S
    RD_A0 RS1_A0 LBU                  ; global_token->S[0]
    RD_T0 !39 ADDI                    ; IF global_token->S[0] == "'"
    RS1_A0 RS2_T0 @primary_expr_str BNE ; Otherwise try string

    ; Deal with chars
    RD_RA $primary_expr_char JAL      ; Handle that char
    $primary_expr_done JAL            ; We are done

:primary_expr_str
    RD_A0 RS1_S4 !16 LD               ; global_token->S
    RD_A0 RS1_A0 LBU                  ; global_token->S[0]
    RD_T0 !34 ADDI                    ; IF global_token->S[0] == '"'
    RS1_A0 RS2_T0 @primary_expr_var BNE ; Otherwise try variable

    ; Deal with strings
    RD_RA $primary_expr_string JAL    ; Handle that string
    $primary_expr_done JAL            ; We are done

:primary_expr_var
    RD_A0 RS1_S4 !16 LD               ; global_token->S
    RD_A0 RS1_A0 LBU                  ; global_token->S[0]
    RD_A1 ~primary_expr_string_6 AUIPC ; Using "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"
    RD_A1 RS1_A1 !primary_expr_string_6 ADDI
    RD_RA $In_Set JAL                 ; See if we have a match
    RS1_A0 @primary_expr_num BEQZ     ; otherwise try number

    ; Deal with variables
    RD_RA $primary_expr_variable JAL  ; Deal with variable
    $primary_expr_done JAL            ; We are done

:primary_expr_num
    RD_A0 RS1_S4 !16 LD               ; global_token->S
    RD_A0 RS1_A0 LBU                  ; global_token->S[0]
    RD_A1 ~primary_expr_string_7 AUIPC ; Using "0123456789"
    RD_A1 RS1_A1 !primary_expr_string_7 ADDI
    RD_RA $In_Set JAL                 ; See if we have a match
    RS1_A0 @primary_expr_fail BEQZ    ; Otherwise we failed

    ; Deal with numbers
    RD_RA $primary_expr_number JAL    ; Collect the number
    $primary_expr_done JAL            ; Be done

:primary_expr_fail
    ; looks like we hit bad input
    ; abort before it gets bad
    RD_RA $primary_expr_failure JAL   ; No match means failure

:primary_expr_done
    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_SP RS1_SP !16 ADDI             ; deallocate stack
    RETURN


; primary_expr_variable function
; Receives nothing
; Returns nothing
; Walks global and updates output
; Uses a0 for struct token_list* a and a2 for char* S
:primary_expr_variable
    RD_SP RS1_SP !-24 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2

    RD_A2 RS1_S4 !16 LD               ; global_token->S
    RD_S4 RS1_S4 LD                   ; global_token = global_token->next

    RD_A0 RS1_A2 MV                   ; Using S
    RD_A1 RS1_S5 MV                   ; Using global_constant_list
    RD_RA $sym_lookup JAL             ; sym_lookup(s, global_constant_list)
    RS1_A0 @primary_expr_variable_local BEQZ ; Try locals next

    ; Deal with constant load
    RD_A1 RS1_A0 !32 LD               ; a->ARGS
    RD_A0 ~primary_expr_variable_string_1 AUIPC ; Using "RD_A0 ~"
    RD_A0 RS1_A0 !primary_expr_variable_string_1 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 RS1_A1 !16 LD               ; a->ARGS->S
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 ~primary_expr_variable_string_2 AUIPC ; Using " LUI\nRD_A0 RS1_A0 !"
    RD_A0 RS1_A0 !primary_expr_variable_string_2 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 RS1_A1 !16 LD               ; a->ARGS->S
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 ~primary_expr_variable_string_3 AUIPC ; Using " ADDIW\n"
    RD_A0 RS1_A0 !primary_expr_variable_string_3 ADDI
    RD_RA $emit_out JAL               ; Emit it
    $primary_expr_variable_done JAL   ; Be done

:primary_expr_variable_local
    RD_A0 RS1_A2 MV                   ; Using S
    RD_A1 RS1_S11 !8 LD               ; function->locals
    RD_RA $sym_lookup JAL             ; sym_lookup(s, function->locals)
    RS1_A0 @primary_expr_variable_arguments BEQZ ; IF NULL == sym_lookup(s, function->locals)

    ; Deal with local load
    RD_RA $variable_load JAL          ; Collect it
    $primary_expr_variable_done JAL   ; Be done

:primary_expr_variable_arguments
    RD_A0 RS1_A2 MV                   ; Using S
    RD_A1 RS1_S11 !32 LD              ; function->args
    RD_RA $sym_lookup JAL             ; sym_lookup(s, function->args)
    RS1_A0 @primary_expr_variable_function BEQZ ; IF NULL == sym_lookup(s, function->args)

    ; Deal with argument load
    RD_RA $variable_load JAL          ; Collect it
    $primary_expr_variable_done JAL   ; Be done

:primary_expr_variable_function
    RD_A0 RS1_A2 MV                   ; Using S
    RD_A1 RS1_S8 MV                   ; global_function_list
    RD_RA $sym_lookup JAL             ; sym_lookup(s, global_function_list)
    RS1_A0 @primary_expr_variable_global BEQZ ; IF NULL == sym_lookup(s, global_function_list)

    ; Deal with functions
    RD_RA $function_load JAL          ; Deal with the function
    $primary_expr_variable_done JAL   ; Be done

:primary_expr_variable_global
    RD_A0 RS1_A2 MV                   ; Using S
    RD_A1 RS1_S6 MV                   ; Using global_symbol_list
    RD_RA $sym_lookup JAL             ; sym_lookup(s, global_symbol_list)
    RS1_A0 @primary_expr_variable_error BEQZ ; Give up IF NULL == sym_lookup(s, global_symbol_list)

    ; Deal with globals
    RD_RA $global_load JAL            ; Collect that global
    $primary_expr_variable_done JAL   ; Be done

:primary_expr_variable_error
    RD_S3 !2 ADDI                     ; write to standard error

    RD_A0 RS1_A2 MV                   ; Put S in the right place
    RD_RA $File_Print JAL             ; print it

    RD_A0 ~primary_expr_variable_string_0 AUIPC ; Ending string
    RD_A0 RS1_A0 !primary_expr_variable_string_0 ADDI
    RD_RA $File_Print JAL             ; print it
    $Fail JAL                         ; Abort Hard

:primary_expr_variable_done
    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_SP RS1_SP !24 ADDI             ; deallocate stack
    RETURN


; function_call function
; Receives char* S in a0 and int BOOL in a1
; Builds stack frames before and tears them down after function calls
; Uses a2 for char* S, a3 for int BOOL, a4 for PASSED
:function_call
    RD_SP RS1_SP !-40 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2
    RS1_SP RS2_A3 @24 SD              ; protect a3
    RS1_SP RS2_A4 @32 SD              ; protect a4

    RD_A2 RS1_A0 MV                   ; Put S in place
    RD_A3 RS1_A1 MV                   ; Put BOOL in place
    RD_A4 MV                          ; PASSED = 0

    RD_A0 ~function_call_string_0 AUIPC ; Using "ERROR in process_expression_list\nNo ( was found\n"
    RD_A0 RS1_A0 !function_call_string_0 ADDI
    RD_A1 ~open_paren AUIPC           ; Using "("
    RD_A1 RS1_A1 !open_paren ADDI
    RD_RA $require_match JAL          ; Make sure we have it

    RD_A0 ~function_call_string_1 AUIPC ; Using "RD_SP RS1_SP !-24 ADDI\nRS1_SP RS2_RA @8 SD\t# Protect the old return pointer\n"
    RD_A0 RS1_A0 !function_call_string_1 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 ~function_call_string_2 AUIPC ; Using "RS1_SP RS2_FP SD\t# Protect the old frame pointer\n"
    RD_A0 RS1_A0 !function_call_string_2 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 ~function_call_string_2a AUIPC ; Using "RS1_SP RS2_TP @16 SD\t# Protect temp register we are going to use\n"
    RD_A0 RS1_A0 !function_call_string_2a ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 ~function_call_string_2b AUIPC ; Using "RD_TP RS1_SP MV\t# The base pointer to-be\n"
    RD_A0 RS1_A0 !function_call_string_2b ADDI
    RD_RA $emit_out JAL               ; Emit it

    ;RD_A0 ~function_call_string_3 AUIPC ; Using "RD_FP RS1_SP MV\t# Copy new base pointer\n"
    ;RD_A0 RS1_A0 !function_call_string_3 ADDI
    ;RD_RA $emit_out JAL               ; Emit it

    RD_A0 RS1_S4 !16 LD               ; global_token->S
    RD_A0 RS1_A0 LBU                  ; global_token->S[0]
    RD_T0 !41 ADDI                    ; ")"
    RS1_A0 RS2_T0 @function_call_gen_done BEQ ; IF global_token->S[0] == ")" then no arguments to send

    ; looks like we have arguments to collect
    RD_RA $expression JAL             ; Collect the argument
    RD_A0 ~function_call_string_4 AUIPC ; Using "RD_SP RS1_SP !-8 ADDI\nRS1_SP RS2_A0 SD\t#_process_expression1\n"
    RD_A0 RS1_A0 !function_call_string_4 ADDI
    RD_RA $emit_out JAL               ; Emit it
    RD_A4 !1 ADDI                     ; PASSED = 1

:function_call_gen_iter
    RD_A0 RS1_S4 !16 LD               ; global_token->S
    RD_A0 RS1_A0 LBU                  ; global_token->S[0]
    RD_T0 !44 ADDI                    ; ","
    RS1_A0 RS2_T0 @function_call_gen_done BNE ; Check IF global_token->S[0] == ","

    RD_S4 RS1_S4 LD                   ; global_token = global_token->next

    RD_RA $expression JAL             ; Collect the argument

    RD_A0 ~function_call_string_5 AUIPC ; Using "RD_SP RS1_SP !-8 ADDI\nRS1_SP RS2_A0 SD\t#_process_expression2\n"
    RD_A0 RS1_A0 !function_call_string_5 ADDI
    RD_RA $emit_out JAL               ; Emit it
    RD_A4 RS1_A4 !1 ADDI              ; PASSED = PASSED + 1
    $function_call_gen_iter JAL       ; Keep trying

:function_call_gen_done
    ; All is collected
    RD_A0 ~function_call_string_6 AUIPC ; Using "ERROR in process_expression_list\nNo ) was found\n" SD\t#_process_expression2\n"
    RD_A0 RS1_A0 !function_call_string_6 ADDI
    RD_A1 ~close_paren AUIPC          ; Using ")"
    RD_A1 RS1_A1 !close_paren ADDI
    RD_RA $require_match JAL          ; Make sure we have it

    RS1_A3 @function_call_static BNEZ ; IF (BOOL == TRUE) then deal with function pointer, else static call

    ; Deal with a passed function pointer
    RD_A0 ~function_call_string_7 AUIPC ; Using "RD_A0 RS1_FP !-"
    RD_A0 RS1_A0 !function_call_string_7 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 RS1_A2 MV                   ; Using S
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 ~function_call_string_8 AUIPC ; Using " ADDI\nRD_A0 RS1_A0 LD\n"
    RD_A0 RS1_A0 !function_call_string_8 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 ~function_call_string_9 AUIPC ; Using "RD_FP RS1_TP MV\n"
    RD_A0 RS1_A0 !function_call_string_9 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 ~function_call_string_10 AUIPC ; Using "RD_RA RS1_A0 JALR\n"
    RD_A0 RS1_A0 !function_call_string_10 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 ~function_call_string_13 AUIPC ; Using "RD_A1 RS1_SP LD\t# _process_expression_locals\nRD_SP RS1_SP !8 ADDI\n"
    RD_A0 RS1_A0 !function_call_string_13 ADDI
    $function_call_cleanup JAL        ; Clean up

:function_call_static
    ; Deal with fixed function name

    RD_A0 ~function_call_string_10a AUIPC ; Using "RD_FP RS1_TP MV\n"
    RD_A0 RS1_A0 !function_call_string_10a ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 ~function_call_string_11 AUIPC ; Using "RD_RA $FUNCTION_"
    RD_A0 RS1_A0 !function_call_string_11 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 RS1_A2 MV                   ; Using S
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 ~function_call_string_12 AUIPC ; Using " JAL\n"
    RD_A0 RS1_A0 !function_call_string_12 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 ~function_call_string_13 AUIPC ; Using "RD_A1 RS1_SP LD\t# _process_expression_locals\nRD_SP RS1_SP !8 ADDI\n"
    RD_A0 RS1_A0 !function_call_string_13 ADDI

:function_call_cleanup
    RS1_A4 @function_call_done BEQZ   ; IF PASSED == 0 then we are done

    ; The desired string is already in a0
    RD_RA $emit_out JAL               ; Emit it

    RD_A4 RS1_A4 !-1 ADDI             ; PASSED = PASSED - 1
    $function_call_cleanup JAL        ; Keep going

:function_call_done
    RD_A0 ~function_call_string_14 AUIPC ; Using "RD_FP RS1_SP LD\t# Restore old frame pointer\n"
    RD_A0 RS1_A0 !function_call_string_14 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 ~function_call_string_14a AUIPC ; Using "RD_TP RS1_SP !16 LD\t# Restore temp register\n"
    RD_A0 RS1_A0 !function_call_string_14a ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 ~function_call_string_15 AUIPC ; Using "RD_RA RS1_SP !8 LD\t# Restore return address\nRD_SP RS1_SP !24 ADDI\n"
    RD_A0 RS1_A0 !function_call_string_15 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_A3 RS1_SP !24 LD               ; restore a3
    RD_A4 RS1_SP !32 LD               ; restore a4
    RD_SP RS1_SP !40 ADDI             ; deallocate stack
    RETURN


; variable_load function
; Receives struct token_list* A in a0
; Returns nothing
; Updates output and current_target
; Uses a2 for A
:variable_load
    RD_SP RS1_SP !-24 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2

    RD_A2 RS1_A0 MV                   ; Protect A

    RD_A1 RS1_S4 !16 LD               ; global_token->S
    RD_A0 ~open_paren AUIPC           ; "("
    RD_A0 RS1_A0 !open_paren ADDI
    RD_RA $match JAL                  ; Might be a function IF global_token->S == "("
    RS1_A0 @variable_load_regular BNEZ ; Otherwise it is regular

    RD_A1 RS1_A2 !24 LD               ; A->TYPE
    RD_A1 RS1_A1 !48 LD               ; A->TYPE->NAME
    RD_A0 ~type_function_name AUIPC   ; Using "FUNCTION"
    RD_A0 RS1_A0 !type_function_name ADDI
    RD_RA $match JAL                  ; IF A->TYPE->NAME == "FUNCTION"
    RS1_A0 @variable_load_regular BNEZ ; then it mus be a function, otherwise another regular

    ; deal with function
    RD_A0 RS1_A2 !32 LD               ; A->DEPTH
    RD_RA $numerate_number JAL        ; Convert to string
    RD_A1 MV                          ; pass 0 for true
    RD_RA $function_call JAL          ; Create the function call
    $variable_load_done JAL           ; Be done

:variable_load_regular
    RD_A0 RS1_A2 !24 LD               ; A->TYPE
    RD_T0 ~current_target AUIPC       ; Using current_target
    RD_T0 RS1_T0 !current_target ADDI
    RS1_T0 RS2_A0 SD                  ; current_target = A->TYPE

    RD_A0 ~variable_load_string_0 AUIPC ; Using "RD_A0 RS1_FP !-"
    RD_A0 RS1_A0 !variable_load_string_0 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 RS1_A2 MV
    RD_A0 RS1_A2 !32 LD               ; A->DEPTH
    RD_RA $numerate_number JAL        ; Convert to string
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 ~variable_load_string_1 AUIPC ; Using " ADDI\n"
    RD_A0 RS1_A0 !variable_load_string_1 ADDI
    RD_RA $emit_out JAL               ; Emit it

    ; Check for special case of assignment
    RD_A1 RS1_S4 !16 LD               ; global_token->S
    RD_A0 ~equal AUIPC                ; Using "="
    RD_A0 RS1_A0 !equal ADDI
    RD_RA $match JAL                  ; IF global_token->S == "="
    RS1_A0 @variable_load_done BEQZ   ; Then we skip loading

    ; Deal with common case
    RD_A0 ~variable_load_string_2 AUIPC ; Using "RD_A0 RS1_A0 LD\n"
    RD_A0 RS1_A0 !variable_load_string_2 ADDI
    RD_RA $emit_out JAL               ; Emit it

:variable_load_done
    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_SP RS1_SP !24 ADDI             ; deallocate stack
    RETURN


; function_load function
; Receives struct token_list* a in a0
; Returns nothing
; Uses a2 to hold A->S
:function_load
    RD_SP RS1_SP !-24 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2

    RD_A0 RS1_A0 !16 LD               ; A->S
    RD_A2 RS1_A0 MV                   ; Protect A->S
    RD_A1 RS1_S4 !16 LD               ; global_token->S
    RD_A0 ~open_paren AUIPC           ; Using "("
    RD_A0 RS1_A0 !open_paren ADDI
    RD_RA $match JAL                  ; IF global_token->S == "("
    RS1_A0 @function_load_regular BNEZ ; then do function call, otherwise load it's address

    ; Deal with function call
    RD_A0 RS1_A2 MV                   ; Using A->S
    RD_A1 !1 ADDI                     ; Using FALSE
    RD_RA $function_call JAL          ; Deal with it
    $function_load_done JAL           ; Be done

:function_load_regular
    RD_A0 ~function_load_string_0 AUIPC ; Using "RD_A0 ~FUNCTION_"
    RD_A0 RS1_A0 !function_load_string_0 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 RS1_A2 MV                   ; Using A->S
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 ~function_load_string_1 AUIPC ; Using " AUIPC\nRD_A0 RS1_A0 !FUNCTION_"
    RD_A0 RS1_A0 !function_load_string_1 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 RS1_A2 MV                   ; Using A->S
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 ~function_load_string_2 AUIPC ; Using " ADDIW\n"
    RD_A0 RS1_A0 !function_load_string_2 ADDI
    RD_RA $emit_out JAL               ; Emit it

:function_load_done
    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_SP RS1_SP !24 ADDI             ; deallocate stack
    RETURN


; global_load function
; Receives struct token_list* A in a0
; Returns nothing
; Uses a1 to hold A->S
:global_load
    RD_SP RS1_SP !-16 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1

    RD_A1 RS1_A0 MV                   ; Set as A
    RD_A1 RS1_A1 !16 LD               ; Set as A->S

    RD_A0 RS1_A0 !24 LD               ; A->TYPE
    RD_T0 ~current_target AUIPC       ; Using current_target
    RD_T0 RS1_T0 !current_target ADDI
    RS1_T0 RS2_A0 SD                  ; current_target = A->TYPE

    RD_A0 ~global_load_string_0 AUIPC ; Using "RD_A0 ~GLOBAL_"
    RD_A0 RS1_A0 !global_load_string_0 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 RS1_A1 MV                   ; Using A->S
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 ~global_load_string_1 AUIPC ; Using " AUIPC\nRD_A0 RS1_A0 !GLOBAL_"
    RD_A0 RS1_A0 !global_load_string_1 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 RS1_A1 MV                   ; Using A->S
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 ~global_load_string_2 AUIPC ; Using " ADDI\n"
    RD_A0 RS1_A0 !global_load_string_2 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A1 RS1_S4 !16 LD               ; global_token->S
    RD_A0 ~equal AUIPC ; "="
    RD_A0 RS1_A0 !equal ADDI
    RD_RA $match JAL                  ; IF global_token->S == "="
    RS1_A0 @global_load_done BEQZ     ; we need to skip for assignment and be done

    ; Otherwise we are loading contents
    RD_A0 ~global_load_string_3 AUIPC ; Using "RD_A0 RS1_A0 LD\n"
    RD_A0 RS1_A0 !global_load_string_3 ADDI
    RD_RA $emit_out JAL               ; Emit it

:global_load_done
    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_SP RS1_SP !16 ADDI             ; deallocate stack
    RETURN


; sym_lookup function
; Receives char* S in a0 and struct token_list* symbol_list in a1
; Uses I->S in a0, S in a1 and I in a2
; Returns match or NULL
:sym_lookup
    RD_SP RS1_SP !-24 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2

    RD_A2 RS1_A1 MV                   ; I = symbol_list
    RD_A1 RS1_A0 MV                   ; Put S in the right place

:sym_lookup_iter
    RS1_A2 @sym_lookup_done BEQZ      ; We failed to find match IF NULL == I

    RD_A0 RS1_A2 !16 LD               ; Using I->S
    RD_RA $match JAL                  ; IF I->S == S
    RS1_A0 @sym_lookup_done BEQZ      ; Then we are done, else failed

    RD_A2 RS1_A2 LD                   ; I = I->NEXT
    $sym_lookup_iter JAL              ; otherwise keep looping

:sym_lookup_done
    RD_A0 RS1_A2 MV                   ; Return I
    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_SP RS1_SP !24 ADDI             ; deallocate stack
    RETURN

; primary_expr_number function
; Receives nothing
; Returns nothing
; Simply uses current global token to update output and then steps to next global_token
:primary_expr_number
    RD_SP RS1_SP !-8 ADDI             ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra

    RD_A0 ~primary_expr_number_string_0 AUIPC ; Using "RD_A0 ~"
    RD_A0 RS1_A0 !primary_expr_number_string_0 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 RS1_S4 !16 LD               ; global_token->S
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 ~primary_expr_number_string_1 AUIPC ; Using " LUI\nRD_A0 RS1_A0 !"
    RD_A0 RS1_A0 !primary_expr_number_string_1 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 RS1_S4 !16 LD               ; global_token->S
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 ~primary_expr_number_string_2 AUIPC ; Using " ADDIW\n"
    RD_A0 RS1_A0 !primary_expr_number_string_2 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_S4 RS1_S4 LD                   ; global_token = global_token->next

    RD_RA RS1_SP LD                   ; restore ra
    RD_SP RS1_SP !8 ADDI              ; deallocate stack
    RETURN


; primary_expr_char function
; Receives nothing
; Returns nothing
; Updates output_list using global_token
:primary_expr_char
    RD_SP RS1_SP !-24 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2

    RD_A0 ~primary_expr_char_string_0 AUIPC ; Using "RD_A0 !"
    RD_A0 RS1_A0 !primary_expr_char_string_0 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 RS1_S4 !16 LD               ; global_token->S
    RD_A0 RS1_A0 !1 ADDI              ; global_token->S + 1
    RD_RA $escape_lookup JAL          ; Get the char
    RD_RA $numerate_number JAL        ; Convert to string
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 ~primary_expr_char_string_1 AUIPC ; Using " ADDI\n"
    RD_A0 RS1_A0 !primary_expr_char_string_1 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_S4 RS1_S4 LD                   ; global_token = global_token->next

    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_SP RS1_SP !24 ADDI             ; deallocate stack
    RETURN


; primary_expr_string function
; receives nothing
; Returns nothing
; creates entries for string and calls to generate string output
; uses a2 for char* number_string
:primary_expr_string
    RD_SP RS1_SP !-24 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2

    RD_A0 RS1_S10 MV                  ; Get current_count
    RD_S10 RS1_S10 !1 ADDI            ; current_count = current_count + 1
    RD_RA $numerate_number JAL        ; convert to string
    RD_A2 RS1_A0 MV                   ; put NUMBER_STRING in place

    RD_A0 ~primary_expr_string_string_0 AUIPC ; Using "RD_A0 ~STRING_"
    RD_A0 RS1_A0 !primary_expr_string_string_0 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 RS1_S11 !16 LD              ; function->S
    RD_A1 RS1_A2 MV                   ; Passing NUMBER_STRING
    RD_RA $uniqueID_out JAL           ; uniqueID_out(function->s, number_string)

    RD_A0 ~primary_expr_string_string_1 AUIPC ; Using "AUIPC\nRD_A0 RS1_A0 !STRING_"
    RD_A0 RS1_A0 !primary_expr_string_string_1 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 RS1_S11 !16 LD              ; function->S
    RD_A1 RS1_A2 MV                   ; Passing NUMBER_STRING
    RD_RA $uniqueID_out JAL           ; uniqueID_out(function->s, number_string)

    RD_A0 ~primary_expr_string_string_2 AUIPC ; Using "ADDI\n"
    RD_A0 RS1_A0 !primary_expr_string_string_2 ADDI
    RD_RA $emit_out JAL               ; Emit it

    ; Generate the target
    RD_A0 ~primary_expr_string_string_3 AUIPC ; Using ":STRING_"
    RD_A0 RS1_A0 !primary_expr_string_string_3 ADDI
    RD_A1 ~strings_list AUIPC         ; Using strings_list
    RD_A1 RS1_A1 !strings_list ADDI
    RD_A1 RS1_A1 LD
    RD_RA $emit JAL                   ; Emit it
    RD_A1 RS1_A0 MV                   ; Put new strings_list in place

    RD_A0 RS1_S11 !16 LD              ; function->S
    RD_RA $uniqueID JAL               ; uniqueID(function->s, number_string)
    RD_A1 RS1_A0 MV                   ; Put new strings_list in place

    ; Parse the string
    RD_A0 RS1_S4 !16 LD               ; global_token->S
    RD_RA $parse_string JAL           ; convert it to useful form
    RD_RA $emit JAL                   ; Emit it
    RD_T0 ~strings_list AUIPC         ; Using strings_list
    RD_T0 RS1_T0 !strings_list ADDI
    RS1_T0 RS2_A0 SD                  ; Update strings_list

    RD_S4 RS1_S4 LD                   ; global_token = global_token->next

    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_SP RS1_SP !24 ADDI             ; deallocate stack
    RETURN


; primary_expr_failure function
; Receives nothing
; Does not return but aborts hard
; Complains about the bad input
:primary_expr_failure
    RD_S3 !2 ADDI                     ; write to standard error
    RD_A0 ~primary_expr_failure_string_0 AUIPC ; Using "Received "
    RD_A0 RS1_A0 !primary_expr_failure_string_0 ADDI
    RD_RA $File_Print JAL             ; Print it

    RD_A0 RS1_S4 !16 LD               ; global_token->S
    RD_RA $File_Print JAL             ; Print it

    RD_A0 ~primary_expr_failure_string_1 AUIPC ; Using "Received "
    RD_A0 RS1_A0 !primary_expr_failure_string_1 ADDI
    RD_RA $File_Print JAL             ; Print it
    $Fail JAL                         ; Abort Hard


; promote_type function
; Receives struct type* a in a0 and struct type* b in a1
; Returns the most recent type in a0
; Uses a0 for struct type* I, a2 for struct type* A and a3 for struct type* B
:promote_type
    RD_SP RS1_SP !-32 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2
    RS1_SP RS2_A3 @24 SD              ; protect a3

    RS1_A1 @promote_type_done BEQZ    ; IF NULL == B, return A

    RD_A2 RS1_A0 MV                   ; Put A in place
    RD_A3 RS1_A1 MV                   ; Put B in place
    RD_A0 RS1_A1 MV                   ; If NULL == A
    RS1_A2 @promote_type_done BEQZ    ; IF NULL == A, return B

    ; Looks like we need to walk the list
    RD_A2 RS1_A2 !48 LD               ; A->NAME
    RD_A3 RS1_A3 !48 LD               ; B->NAME

    RD_A0 ~global_types AUIPC         ; Using global_types
    RD_A0 RS1_A0 !global_types ADDI
    RD_A0 RS1_A0 LD                   ; I = global_types

:promote_type_iter
    RS1_A0 @promote_type_done BEQZ    ; Be done IF NULL == I

    RD_A1 RS1_A0 !48 LD               ; I->NAME
    RS1_A1 RS2_A2 @promote_type_done BEQ ; Be done IF(A->NAME == I->NAME)

    RS1_A1 RS2_A3 @promote_type_done BEQ ; Be done IF(B->NAME == I->NAME)

    RD_A1 RS1_A0 !24 LD               ; I->INDIRECT
    RD_A1 RS1_A1 !48 LD               ; I->INDIRECT->NAME

    RS1_A1 RS2_A2 @promote_type_done BEQ ; Done IF(A->NAME == I->INDIRECT->NAME)

    RS1_A1 RS2_A3 @promote_type_done BEQ ; Done IF(B->NAME == I->INDIRECT->NAME)

    RD_A0 RS1_A0 LD                   ; I = I->NEXT
    $promote_type_iter JAL            ; Keep going

:promote_type_done

    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_A3 RS1_SP !24 LD               ; restore a3
    RD_SP RS1_SP !32 ADDI             ; deallocate stack
    RETURN


; common_recursion function
; Receives FUNCTION F in a0
; Returns Nothing
; Walks global_token list and update output_list
; Updates current_target
; Uses a1 to hold FUNCTION F and struct type* last_type
:common_recursion
    RD_SP RS1_SP !-16 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1

    RD_A1 RS1_A0 MV                   ; Put FUNCTION F safely out of the way
    RD_A0 ~common_recursion_string_0 AUIPC ; Using "RD_SP RS1_SP !-8 ADDI\t# _common_recursion\nRS1_SP RS2_A0 SD\n"
    RD_A0 RS1_A0 !common_recursion_string_0 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_S4 RS1_S4 LD                   ; global_token = global_token->next

    RD_A0 RS1_A1 MV                   ; Prepare for function call
    RD_T0 ~current_target AUIPC       ; Using current_target
    RD_T0 RS1_T0 !current_target ADDI
    RD_A1 RS1_T0 LD                   ; Get last_type
    RD_RA RS1_A0 JALR                 ; F()
    RD_T0 ~current_target AUIPC       ; Using current_target
    RD_T0 RS1_T0 !current_target ADDI
    RD_A0 RS1_T0 LD                   ; Get last current_target
    RD_RA $promote_type JAL           ; get the right type
    RS1_T0 RS2_A0 SD                  ; Set new current target

    RD_A0 ~common_recursion_string_1 AUIPC ; Using "RD_A1 RS1_SP LD..."
    RD_A0 RS1_A0 !common_recursion_string_1 ADDI
    RD_RA $emit_out JAL               ; Emit it

    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_SP RS1_SP !16 ADDI             ; deallocate stack
    RETURN


; general_recursion function
; Receives FUNCTION F in a0, char* S in a1, char* name in a2 and FUNCTION iterate in a3
; Returns nothing
; Uses a2 for char* S, a3 for FUNCTION iterate and a4 for FUNCTION F
:general_recursion
    RD_SP RS1_SP !-40 ADDI            ; allocate stack
    RS1_SP RS2_RA SD                  ; protect ra
    RS1_SP RS2_A1 @8 SD               ; protect a1
    RS1_SP RS2_A2 @16 SD              ; protect a2
    RS1_SP RS2_A3 @24 SD              ; protect a3
    RS1_SP RS2_A4 @32 SD              ; protect a4

    RD_A4 RS1_A0 MV                   ; Protect F
    RD_A0 RS1_A2 MV                   ; Put name in the right place
    RD_A2 RS1_A1 MV                   ; Protect S

    RD_A1 RS1_S4 !16 LD               ; global_token->S
    RD_RA $match JAL                  ; IF match(name, global_token->s)
    RS1_A0 @general_recursion_done BNEZ ; do recursion, else skip it

    ; Deal with the recursion
    RD_A0 RS1_A4 MV                   ; Put F in the right place
    RD_RA $common_recursion JAL       ; Recurse

    RD_A0 RS1_A2 MV                   ; Put S in the right place
    RD_RA $emit_out JAL               ; Emit it

    RD_A0 RS1_A3 MV                   ; Put iterate in the right place
    RD_RA RS1_A0 JALR                 ; Down the rabbit hole

:general_recursion_done
    RD_RA RS1_SP LD                   ; restore ra
    RD_A1 RS1_SP !8 LD                ; restore a1
    RD_A2 RS1_SP !16 LD               ; restore a2
    RD_A3 RS1_SP !24 LD               ; restore a3
    RD_A4 RS1_SP !32 LD               ; restore a4
    RD_SP RS1_SP !40 ADDI             ; deallocate stack
    RETURN


; save_break_frame microfunction
; Overwrites a0 and a1
; Saves break frame on stack
; Returns to caller
:save_break_frame
    RD_A1 RS1_SP LD                   ; Save return Address

    RD_T0 ~break_frame AUIPC          ; Get break_frame
    RD_T0 RS1_T0 !break_frame ADDI
    RD_A0 RS1_T0 LD
    RS1_SP RS2_A0 SD                  ; Store as nested_locals

    RD_T0 ~break_target_head AUIPC    ; Get break_target_head
    RD_T0 RS1_T0 !break_target_head ADDI
    RD_A0 RS1_T0 LD
    RD_SP RS1_SP !-8 ADDI             ; allocate stack
    RS1_SP RS2_A0 SD                  ; Store as nested_break_head

    RD_T0 ~break_target_func AUIPC    ; Get break_target_func
    RD_T0 RS1_T0 !break_target_func ADDI
    RD_A0 RS1_T0 LD
    RD_SP RS1_SP !-8 ADDI             ; allocate stack
    RS1_SP RS2_A0 SD                  ; Store as nested_break_func


    RD_T0 ~break_target_num AUIPC    ; Get break_target_num
    RD_T0 RS1_T0 !break_target_num ADDI
    RD_A0 RS1_T0 LD
    RD_SP RS1_SP !-8 ADDI             ; allocate stack
    RS1_SP RS2_A0 SD                  ; Store as break_target_num

    RD_SP RS1_SP !-8 ADDI             ; allocate stack
    RS1_SP RS2_A1 SD                  ; Put return back in place
    RETURN


; restore_break_frame microfunction
; Overwrites a0 and a1
; Restores break frame from stack
; Returns to caller
:restore_break_frame
    RD_A1 RS1_SP LD                   ; Save return Address
    RD_SP RS1_SP !8 ADDI              ; deallocate stack

    RD_A0 RS1_SP LD                   ; Get nested_break_num
    RD_SP RS1_SP !8 ADDI              ; deallocate stack
    RD_T0 ~break_target_num AUIPC
    RD_T0 RS1_T0 !break_target_num ADDI
    RS1_T0 RS2_A0 SD                  ; Restore as nested_break_num

    RD_A0 RS1_SP LD                   ; Get break_target_func
    RD_SP RS1_SP !8 ADDI              ; deallocate stack
    RD_T0 ~break_target_func AUIPC
    RD_T0 RS1_T0 !break_target_func ADDI
    RS1_T0 RS2_A0 SD                  ; Restore as break_target_func

    RD_A0 RS1_SP LD                   ; Get break_target_head
    RD_SP RS1_SP !8 ADDI              ; deallocate stack
    RD_T0 ~break_target_head AUIPC
    RD_T0 RS1_T0 !break_target_head ADDI
    RS1_T0 RS2_A0 SD                  ; Restore as break_target_head

    RD_A0 RS1_SP LD                   ; Get break_frame
    RD_T0 ~break_frame AUIPC
    RD_T0 RS1_T0 !break_frame ADDI
    RS1_T0 RS2_A0 SD                  ; Restore as break_frame

    RS1_SP RS2_A1 SD                  ; Put return back in place
    RETURN


; set_break_frame microfunction
; Receives char* head in a0 and char* num in a1
; Overwrites a0 and a1
; Returns to calling function
:set_break_frame
    RD_T0 ~break_target_head AUIPC    ; Using break_target_head
    RD_T0 RS1_T0 !break_target_head ADDI
    RS1_T0 RS2_A0 SD                  ; update break_target_head

    RD_T0 ~break_target_num AUIPC     ; Using break_target_num
    RD_T0 RS1_T0 !break_target_num ADDI
    RS1_T0 RS2_A1 SD                  ; update break_target_num

    RD_A0 RS1_S11 !8 LD               ; function->locals
    RD_T0 ~break_frame AUIPC          ; Using break_frame
    RD_T0 RS1_T0 !break_frame ADDI
    RS1_T0 RS2_A0 SD                  ; break_frame = function->locals

    RD_A0 RS1_S11 !16 LD              ; function->S
    RD_T0 ~break_target_func AUIPC    ; Using break_target_func
    RD_T0 RS1_T0 !break_target_func ADDI
    RS1_T0 RS2_A0 SD                  ; break_target_func = function->S

    RETURN


; debug_list function
; Receives struct token_list* in RAX
; Prints contents of list and exits
; Uses s11 as list pointer
; Does NOT return
:debug_list
    RD_S11 RS1_A0 MV                  ; Protect the list pointer
    RD_S3 !2 ADDI                     ; write to standard error

:debug_list_iter
    ; Header
    RD_A0 ~debug_list_string0 AUIPC   ; Using our first string
    RD_A0 RS1_A0 !debug_list_string0 ADDI
    RD_RA $File_Print JAL             ; Print it
    RD_A0 RS1_S11 MV                  ; Use address of pointer
    RD_RA $numerate_number JAL        ; Convert it into string
    RD_RA $File_Print JAL             ; Print it

    ;; NEXT
    RD_A0 ~debug_list_string1 AUIPC   ; Using our second string
    RD_A0 RS1_A0 !debug_list_string1 ADDI
    RD_RA $File_Print JAL             ; Print it
    RD_A0 RS1_S11 LD                  ; Use address of pointer
    RD_RA $numerate_number JAL        ; Convert it into string
    RD_RA $File_Print JAL             ; Print it

    ;; PREV
    RD_A0 ~debug_list_string2 AUIPC   ; Using our third string
    RD_A0 RS1_A0 !debug_list_string2 ADDI
    RD_RA $File_Print JAL             ; Print it
    RD_A0 RS1_S11 !8 LD               ; Use address of pointer
    RD_RA $numerate_number JAL        ; Convert it into string
    RD_RA $File_Print JAL             ; Print it

    ;; S
    RD_A0 ~debug_list_string3 AUIPC   ; Using our fourth string
    RD_A0 RS1_A0 !debug_list_string3 ADDI
    RD_RA $File_Print JAL             ; Print it
    RD_A0 RS1_S11 !16 LD              ; Use address of pointer
    RD_RA $numerate_number JAL        ; Convert it into string
    RD_RA $File_Print JAL             ; Print it

    ;; S Contents
    RD_A0 ~debug_list_string4 AUIPC   ; Using our fifth string
    RD_A0 RS1_A0 !debug_list_string4 ADDI
    RD_RA $File_Print JAL             ; Print it
    RD_A0 RS1_S11 !16 LD              ; Use address of pointer
    RS1_A0 @debug_list_null BNEZ      ; Display if not NULL Pointer
    RD_A0 ~debug_list_string_null AUIPC ; Give meaningful message instead
    RD_A0 RS1_A0 !debug_list_string_null ADDI
:debug_list_null
    RD_RA $File_Print JAL             ; Print it

    ;; TYPE
    RD_A0 ~debug_list_string5 AUIPC   ; Using our sixth string
    RD_A0 RS1_A0 !debug_list_string5 ADDI
    RD_RA $File_Print JAL             ; Print it
    RD_A0 RS1_S11 !24 LD              ; Use address of pointer
    RD_RA $numerate_number JAL        ; Convert it into string
    RD_RA $File_Print JAL             ; Print it

    ;; ARGS/DEPTH
    RD_A0 ~debug_list_string6 AUIPC   ; Using our seventh string
    RD_A0 RS1_A0 !debug_list_string6 ADDI
    RD_RA $File_Print JAL             ; Print it
    RD_A0 RS1_S11 !32 LD              ; Use address of pointer
    RD_RA $numerate_number JAL        ; Convert it into string
    RD_RA $File_Print JAL             ; Print it

    RD_A0 !10 ADDI                    ; Add "\n"
    RD_RA $fputc JAL                  ; print it
    RD_RA $fputc JAL                  ; print it

    RD_S11 RS1_S11 LD                 ; TOKEN = TOKEN->NEXT
    RS1_S11 @debug_list_iter BNEZ     ; iterate otherwise

    $Fail JAL

:ELF_data

:prim_types
:type_void
    &type_int %0                        ; NEXT
    %8 %0                               ; SIZE
    NULL                                ; OFFSET
    &type_void %0                       ; INDIRECT
    NULL                                ; MEMBERS
    &type_void %0                       ; TYPE
    &type_void_name %0                  ; NAME

:type_int
    &type_char %0                       ; NEXT
    %8 %0                               ; SIZE
    NULL                                ; OFFSET
    &type_int %0                        ; INDIRECT
    NULL                                ; MEMBERS
    &type_int %0                        ; TYPE
    &type_int_name %0                   ; NAME

:type_char
    &type_file %0                       ; NEXT
    %1 %0                               ; SIZE
    NULL                                ; OFFSET
    &type_char_indirect %0              ; INDIRECT
    NULL                                ; MEMBERS
    &type_char %0                       ; TYPE
    &type_char_name %0                  ; NAME

:type_char_indirect
    &type_file %0                       ; NEXT
    %8 %0                               ; SIZE
    NULL                                ; OFFSET
    &type_char_double_indirect %0       ; INDIRECT
    NULL                                ; MEMBERS
    &type_char_indirect %0              ; TYPE
    &type_char_indirect_name %0         ; NAME

:type_char_double_indirect
    &type_file %0                       ; NEXT
    %8 %0                               ; SIZE
    NULL                                ; OFFSET
    &type_char_double_indirect %0       ; INDIRECT
    NULL                                ; MEMBERS
    &type_char_indirect %0              ; TYPE
    &type_char_double_indirect_name %0  ; NAME

:type_file
    &type_function %0                   ; NEXT
    %8 %0                               ; SIZE
    NULL                                ; OFFSET
    &type_file %0                       ; INDIRECT
    NULL                                ; MEMBERS
    &type_file %0                       ; TYPE
    &type_file_name %0                  ; NAME

:type_function
    &type_unsigned %0                   ; NEXT
    %8 %0                               ; SIZE
    NULL                                ; OFFSET
    &type_function %0                   ; INDIRECT
    NULL                                ; MEMBERS
    &type_function %0                   ; TYPE
    &type_function_name %0              ; NAME

:type_unsigned
    &type_long %0                       ; NEXT
    %8 %0                               ; SIZE
    NULL                                ; OFFSET
    &type_unsigned %0                   ; INDIRECT
    NULL                                ; MEMBERS
    &type_unsigned %0                   ; TYPE
    &type_unsigned_name %0              ; NAME

:type_long
    NULL                                ; NEXT
    %8 %0                               ; SIZE
    NULL                                ; OFFSET
    &type_long %0                       ; INDIRECT
    NULL                                ; MEMBERS
    &type_long %0                       ; TYPE
    &type_long_name %0                  ; NAME

:type_void_name  "void"
:type_int_name  "int"
:type_char_name  "char"
:type_char_indirect_name  "char*"
:type_char_double_indirect_name  "char**"
:type_file_name  "FILE"
:type_function_name  "FUNCTION"
:type_unsigned_name  "unsigned"
:type_long_name  "long"

:break_frame NULL
:break_target_func NULL
:break_target_head NULL
:break_target_num NULL
:current_target NULL
:global_types &prim_types %0
:strings_list NULL

:alphas
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"

:symbols
"<=>|&!-"

:strings
'22 27 00'

;; Keywords
:union  "union"
:struct  "struct"
:constant  "CONSTANT"
:main_string  "main"
:argc_string  "argc"
:argv_string  "argv"
:if_string  "if"
:else_string  "else"
:do_string  "do"
:while_string  "while"
:for_string  "for"
:asm_string  "asm"
:goto_string  "goto"
:return_string  "return"
:break_string  "break"
:continue_string  "continue"
:sizeof_string  "sizeof"
:plus_string  "+"
:minus_string  "-"
:multiply_string  "*"
:divide_string  "/"
:modulus_string  "%"
:left_shift_string  "<<"
:right_shift_string  ">>"
:less_than_string  "<"
:less_than_equal_string  "<="
:greater_than_equal_string  ">="
:greater_than_string  ">"
:equal_to_string  "=="
:not_equal_string  "!="
:bitwise_and  "&"
:logical_and  "&&"
:bitwise_or  "|"
:logical_or  "||"
:bitwise_xor  "^"
:arrow_string  "->"

;; Frequently Used strings
;; Generally used by require_match
:open_curly_brace  "{"
:close_curly_brace  "}"
:open_paren  "("
:close_paren  ")"
:open_bracket  "["
:close_bracket  "]"
:comma  ","
:semicolon  ";"
:equal  "="
:percent  "%"
:underline  "_"

;; Strings needed by the program function
:program_string_0  ":GLOBAL_"
:program_string_1
"
NULL
"

:create_struct_string_0
"ERROR in create_struct
 Missing {
"

:create_struct_string_1
"ERROR in create_struct
 Missing ;
"

:header_string1
"
# Core program
"

:header_string2
"
:ELF_data
"

:header_string3
"
# Program global variables
"

:header_string4
"
# Program strings
"

:header_string5
"
:ELF_end
"

:escape_lookup_string_0
"Unknown escape received: "

:escape_lookup_string_1
" Unable to process
"

:type_name_string_0
"Unknown type "

:type_name_string_1
"
"

:lookup_member_string_0
"ERROR in lookup_member "

:lookup_member_string_1
" does not exist
"

:lookup_member_string_2
"
"

:build_member_string_0
"Struct only supports [num] form
"

:build_union_string_0
"ERROR in build_union
Missing {
"

:build_union_string_1
"ERROR in build_union
Missing ;
"

:declare_function_string_0
"# Defining function "

:declare_function_string_1
"
:FUNCTION_"

:declare_function_string_2
"RETURN
"

:declare_function_string_3
"
"

:common_recursion_string_0
"RD_SP RS1_SP !-8 ADDI	# _common_recursion
RS1_SP RS2_A0 SD
"

:common_recursion_string_1
"RD_A1 RS1_SP LD
RD_SP RS1_SP !8 ADDI	# _common_recursion
"

:recursive_statement_string_0
"RETURN
"

:recursive_statement_string_1
"RD_A1 RS1_SP LD	# _recursive_statement_locals
RD_SP RS1_SP !8 ADDI
"

:collect_local_string_0
"# Defining local "

:collect_local_string_1
"
"

:collect_local_string_2
"ERROR in collect_local
Missing ;
"

:collect_local_string_3
"RD_SP RS1_SP !-8 ADDI
RS1_SP RS2_A0 SD	#"

:return_result_string_0
"ERROR in return_result
MISSING ;
"

:return_result_string_1
"RD_A1 RS1_SP LD	# _return_result_locals
RD_SP RS1_SP !8 ADDI
"

:return_result_string_2
"RETURN
"

:statement_string_0
"	#C goto label
"

:statement_string_1
"$"

:statement_string_2
" JAL
"

:statement_string_3
"
#continue statement
"

:statement_string_4
"ERROR in statement
Missing ;
"

:process_if_string_0
"# IF_"

:process_if_string_1
"ERROR in process_if
MISSING (
"

:process_if_string_2
"RS1_A0 @8 BNEZ
$ELSE_"

:process_if_string_2a
"JAL
"

:process_if_string_3
"ERROR in process_if
MISSING )
"

:process_if_string_4
"$_END_IF_"

:process_if_string_5
"JAL
:ELSE_"

:process_if_string_6
":_END_IF_"

:process_do_string_0
"DO_END_"

:process_do_string_1
":DO_"

:process_do_string_2
"ERROR in process_do
MISSING while
"

:process_do_string_3
"ERROR in process_do
MISSING (
"

:process_do_string_4
"ERROR in process_do
MISSING )
"

:process_do_string_5
"ERROR in process_do
MISSING ;
"

:process_do_string_6
"RS1_A0 @DO_END_"

:process_do_string_6a
"BEQZ
$DO_"

:process_do_string_7
"JAL
:DO_END_"
process_for_string_6
:process_while_string_0
"END_WHILE_"

:process_while_string_1
":WHILE_"

:process_while_string_2
"ERROR in process_while
MISSING (
"

# Here @8 means jump 8 bytes forward, hence we skip one instruction.
# This slightly abuses M1 encoding since M1 uses @ for S-type instructions
# but we need B-type instruction. Fortunately, B and S encodings are
# identical for values up to 2047.

:process_while_string_3
"RS1_A0 @8 BNEZ
$END_WHILE_"

:process_while_string_4
"JAL
# THEN_while_"

:process_while_string_5
"ERROR in process_while
MISSING )
"

:process_while_string_6
"$WHILE_"

:process_while_string_7
"JAL
:END_WHILE_"

:process_for_string_0
"FOR_END_"

:process_for_string_1
"# FOR_initialization_"

:process_for_string_2
"ERROR in process_for
MISSING (
"

:process_for_string_3
":FOR_"

:process_for_string_4
"ERROR in process_for
MISSING ;1
"

:process_for_string_5
"RS1_A0 @8 BNEZ
$FOR_END_"

:process_for_string_6
"JAL
$FOR_THEN_"

:process_for_string_7
"JAL
:FOR_ITER_"

:process_for_string_8
"ERROR in process_for
MISSING ;2
"

:process_for_string_9
"$FOR_"

:process_for_string_10
"JAL
:FOR_THEN_"

:process_for_string_11
"ERROR in process_for
MISSING )
"

:process_for_string_12
"$FOR_ITER_"

:process_for_string_13
"JAL
:FOR_END_"


:process_asm_string_0
"ERROR in process_asm
MISSING (
"

:process_asm_string_1
"
"

:process_asm_string_2
"ERROR in process_asm
MISSING )
"


:process_asm_string_3
"ERROR in process_asm
MISSING ;
"

:process_break_string_0
"Not inside of a loop or case statement"

:process_break_string_1
"RD_A1 RS1_SP LD	# break_cleanup_locals
RD_SP RS1_SP !8 ADDI
"

:process_break_string_2
"$"

:process_break_string_3
" JAL
"

:process_break_string_4
"ERROR in break statement
Missing ;
"

:uniqueID_string_0
"
"

:expression_string_0
"RS1_A1 RS2_A0 SD
"

:expression_string_1
"RS1_A1 RS2_A0 SB
"

:primary_expr_string_0
"RD_A0 MV
"

:primary_expr_string_1
"RD_A0 RS1_A1 RS2_A0 SUB
"

:primary_expr_string_2
"RD_A0 !1 ADDI
"

:primary_expr_string_3
"RD_A0 RS1_A1 RS2_A0 XOR
"

:primary_expr_string_4
"RD_A0 RS1_A0 NOT
"

:primary_expr_string_5
"Error in Primary expression
Didn't get )
"

:primary_expr_string_6
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"

:primary_expr_string_7
"0123456789"

:primary_expr_variable_string_0
" is not a defined symbol
"

:primary_expr_variable_string_1
"RD_A0 ~"

:primary_expr_variable_string_2
" LUI
RD_A0 RS1_A0 !"

:primary_expr_variable_string_3
" ADDIW
"

:primary_expr_number_string_0
"RD_A0 ~"

:primary_expr_number_string_1
" LUI
RD_A0 RS1_A0 !"
:primary_expr_number_string_2
" ADDIW
"

:unary_expr_sizeof_string_0
"ERROR in unary_expr
Missing (
"

:unary_expr_sizeof_string_1
"ERROR in unary_expr
Missing )
"

:unary_expr_sizeof_string_2
"RD_A0 !"

:unary_expr_sizeof_string_3
" ADDI
"

:primary_expr_char_string_0
"RD_A0 !"

:primary_expr_char_string_1
" ADDI
"

:primary_expr_string_string_0
"RD_A0 ~STRING_"

:primary_expr_string_string_1
"AUIPC
RD_A0 RS1_A0 !STRING_"

:primary_expr_string_string_2
"ADDI
"

:primary_expr_string_string_3
":STRING_"

:primary_expr_failure_string_0
"Received "

:primary_expr_failure_string_1
" in primary_expr
"

:weird_string_0
"	
 !#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"

:weird_string_1
'09 0a 0d 20 00'

:bitwise_expr_stub_string_0  "RD_A0 RS1_A1 RS2_A0 AND
"
:bitwise_expr_stub_string_1  "RD_A0 RS1_A1 RS2_A0 OR
"
:bitwise_expr_stub_string_2  "RD_A0 RS1_A1 RS2_A0 XOR
"

:relational_expr_stub_string_0  "RD_A0 RS1_A1 RS2_A0 SLT
"

:relational_expr_stub_string_1  "RD_A0 RS1_A0 RS2_A1 SLT
RD_A0 RS1_A0 !1 XORI
"

:relational_expr_stub_string_2  "RD_A0 RS1_A1 RS2_A0 SLT
RD_A0 RS1_A0 !1 XORI
"

:relational_expr_stub_string_3  "RD_A0 RS1_A0 RS2_A1 SLT
"

:relational_expr_stub_string_4  "RD_A0 RS1_A0 RS2_A1 SUB
RD_A0 RS1_A0 !1 SLTIU
"
:relational_expr_stub_string_5  "RD_A0 RS1_A0 RS2_A1 SUB
RD_A0 RS2_A0 SLTU
"

:additive_expr_stub_string_0  "RD_A0 RS1_A1 RS2_A0 ADD
"
:additive_expr_stub_string_1  "RD_A0 RS1_A1 RS2_A0 SUB
"
:additive_expr_stub_string_2  "RD_A0 RS1_A1 RS2_A0 MUL
"
:additive_expr_stub_string_3  "RD_A0 RS1_A1 RS2_A0 DIV
"
:additive_expr_stub_string_4  "RD_A0 RS1_A1 RS2_A0 REM
"
:additive_expr_stub_string_5  "RD_A0 RS1_A1 RS2_A0 SLL
"
:additive_expr_stub_string_6  "RD_A0 RS1_A1 RS2_A0 SRL
"

:postfix_expr_array_string_0
"RD_A0 RS1_A0 LD
"

:postfix_expr_array_string_1
"RD_A0 RS1_A0 LBU
"

:postfix_expr_array_string_2
"RD_A0 RS1_A0 RS2_X"

:postfix_expr_array_string_3
" SLLI
"

:postfix_expr_array_string_4
"RD_A0 RS1_A1 RS2_A0 ADD
"

:postfix_expr_array_string_5
"ERROR in postfix_expr
Missing ]
"

:postfix_expr_array_string_6
'00'

:postfix_expr_arrow_string_0
"# looking up offset
"

:postfix_expr_arrow_string_1
"# -> offset calculation
RD_A1 !"

:postfix_expr_arrow_string_2
" ADDI
RD_A0 RS1_A1 RS2_A0 ADD
"

:postfix_expr_arrow_string_3
"RD_A0 RS1_A0 LD
"

:variable_load_string_0
"RD_A0 RS1_FP !-"

:variable_load_string_1
" ADDI
"

:variable_load_string_2
"RD_A0 RS1_A0 LD
"

:global_load_string_0
"RD_A0 ~GLOBAL_"

:global_load_string_1
" AUIPC
RD_A0 RS1_A0 !GLOBAL_"

:global_load_string_2
" ADDI
"

:global_load_string_3
"RD_A0 RS1_A0 LD
"

:function_load_string_0
"RD_A0 ~FUNCTION_"

:function_load_string_1
" AUIPC
RD_A0 RS1_A0 !FUNCTION_"

:function_load_string_2
" ADDIW
"

:function_call_string_0
"ERROR in process_expression_list
No ( was found
"

:function_call_string_1
"RD_SP RS1_SP !-24 ADDI
RS1_SP RS2_RA @8 SD	# Protect the old return pointer
"

:function_call_string_2
"RS1_SP RS2_FP SD	# Protect the old frame pointer
"

:function_call_string_2a
"RS1_SP RS2_TP @16 SD	# Protect temp register we are going to use
"

:function_call_string_2b
"RD_TP RS1_SP MV	# The base pointer to-be
"

:function_call_string_3
"RD_FP RS1_SP MV	# Copy new base pointer
"

:function_call_string_4
"RD_SP RS1_SP !-8 ADDI
RS1_SP RS2_A0 SD	#_process_expression1
"

:function_call_string_5
"RD_SP RS1_SP !-8 ADDI
RS1_SP RS2_A0 SD	#_process_expression2
"

:function_call_string_6
"ERROR in process_expression_list
No ) was found
"

:function_call_string_7
"RD_A0 RS1_FP !-"

:function_call_string_8
" ADDI
RD_A0 RS1_A0 LD
"

:function_call_string_9
"RD_FP RS1_TP MV
"

:function_call_string_10
"RD_RA RS1_A0 JALR
"

:function_call_string_10a
"RD_FP RS1_TP MV
"

:function_call_string_11
"RD_RA $FUNCTION_"

:function_call_string_12
" JAL
"

:function_call_string_13
"RD_A1 RS1_SP LD	# _process_expression_locals
RD_SP RS1_SP !8 ADDI
"

:function_call_string_14
"RD_FP RS1_SP LD	# Restore old frame pointer
"

:function_call_string_14a
"RD_TP RS1_SP !16 LD	# Restore temp register
"

:function_call_string_15
"RD_RA RS1_SP !8 LD	# Restore return address
RD_SP RS1_SP !24 ADDI
"

:char2hex_string_0
"Tried to print non-hex number
"

:debug_list_string0  "Token_list node at address: "
:debug_list_string1  "
NEXT address: "
:debug_list_string2  "
PREV address: "
:debug_list_string3  "
S address: "
:debug_list_string4  "
The contents of S are: "
:debug_list_string5  "
TYPE address: "
:debug_list_string6  "
ARGUMENTS address: "
:debug_list_string_null  ">::<NULL>::<"

:ELF_end
