;; Copyright (C) 2017 Jeremiah Orians
;; 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/>.

DEFINE ADDI8_to_RAX 4883C0
DEFINE ADDI8_to_RBX 4883C3
DEFINE ADDI8_to_RCX 4883C1
DEFINE ADDI8_to_RDX 4883C2
DEFINE ADDI8_to_RSI 4883C6
DEFINE ADD_RAX_to_RDI 4801C7
DEFINE ADD_RBX_to_RAX 4801D8
DEFINE ADD_RCX_to_RAX 4801C8
DEFINE ADD_RDI_to_RCX 4801F9
DEFINE AND_RBX_into_RAX 4821D8
DEFINE ANDI32_RAX 4825
DEFINE CALL_RAX FFD0
DEFINE CALLI32 E8
DEFINE CMP_AL_Immediate8 3C
DEFINE CMP_R12_Immediate8 4983FC
DEFINE CMP_RAX_Immediate8 4883F8
DEFINE CMP_RBP_Immediate8 4883FD
DEFINE CMP_RBX_Immediate8 4883FB
DEFINE CMP_RCX_Immediate8 4883F9
DEFINE CMP_RDX_Immediate8 4883FA
DEFINE CMP_RSI_Immediate8 4883FE
DEFINE CMP_RBX_to_RAX 4839D8
DEFINE CMP_RBX_to_RCX 4839D9
DEFINE CMP_RCX_to_RAX 4839C8
DEFINE CMP_RCX_to_RBX 4839CB
DEFINE CMP_RDI_to_RSI 4839FE
DEFINE CMP_RDX_to_RBX 4839D3
DEFINE COPY_R12_to_RAX 4C89E0
DEFINE COPY_R13_to_RAX 4C89E8
DEFINE COPY_R13_to_RDI 4C89EF
DEFINE COPY_R14_to_RDI 4C89F7
DEFINE COPY_R15_to_RDI 4C89FF
DEFINE COPY_RAX_to_R12 4989C4
DEFINE COPY_RAX_to_R13 4989C5
DEFINE COPY_RAX_to_R14 4989C6
DEFINE COPY_RAX_to_R15 4989C7
DEFINE COPY_RAX_to_RBP 4889C5
DEFINE COPY_RAX_to_RBX 4889C3
DEFINE COPY_RAX_to_RCX 4889C1
DEFINE COPY_RAX_to_RDX 4889C2
DEFINE COPY_RAX_to_RSI 4889C6
DEFINE COPY_RBP_to_RAX 4889E8
DEFINE COPY_RBX_to_RAX 4889D8
DEFINE COPY_RBX_to_RCX 4889D9
DEFINE COPY_RBX_to_RDX 4889DA
DEFINE COPY_RCX_to_RAX 4889C8
DEFINE COPY_RCX_to_RBX 4889CB
DEFINE COPY_RDI_to_R13 4989FD
DEFINE COPY_RDI_to_RSI 4889FE
DEFINE COPY_RDX_to_RAX 4889D0
DEFINE COPY_RDX_to_RBP 4889D5
DEFINE COPY_RDX_to_RBX 4889D3
DEFINE COPY_RSI_to_RAX 4889F0
DEFINE COPY_RSI_to_RDI 4889F7
DEFINE IDIV_RBX 48F7FB
DEFINE IMULI8_RAX 486BC0
DEFINE IMULI8_RBP 486BED
DEFINE IMUL_RBX_RAX 480FAFC3
DEFINE JE8 74
DEFINE JG8 7F
DEFINE JL8 7C
DEFINE JLE8 7E
DEFINE JMP8 EB
DEFINE JNE8 75
DEFINE JMP32 E9
DEFINE JBE8 76
DEFINE JE32 0F84
DEFINE JNE32 0F85
DEFINE LEA_RSI 488D3424
DEFINE LOAD8_AL_from_Address_RAX 8A00
DEFINE LOAD8_AL_from_Address_RBX 8A03
DEFINE LOAD8_AL_from_Address_RCX 8A01
DEFINE LOAD8_AL_from_Address_RDX 8A02
DEFINE LOAD8_BL_from_Address_RBX 8A1B
DEFINE LOAD8_BL_from_Address_RCX 8A19
DEFINE LOAD8_BL_from_Address_RDX 8A1A
DEFINE LOAD8_CL_from_Address_RBX 8A0B
DEFINE LOAD8_CL_from_Address_RBX_Immediate8 8A4B
DEFINE LOAD64_RAX_from_ABS32 488B0425
DEFINE LOAD64_RBX_from_ABS32 488B1C25
DEFINE LOAD64_RCX_from_ABS32 488B0C25
DEFINE LOADI32_R14 41BE
DEFINE LOADI32_RAX 48C7C0
DEFINE LOADI32_EAX B8
DEFINE LOADI32_RBX 48C7C3
DEFINE LOADI32_EBX BB
DEFINE LOADI32_RCX 48C7C1
DEFINE LOADI32_EDI BF
DEFINE LOADI32_EDX BA
DEFINE LOADI32_RDX 48C7C2
DEFINE LOADI32_RSI BE
DEFINE LOAD64_into_R12_from_Address_R12 4D8B2424
DEFINE LOAD64_into_RAX_from_Address_R12 498B0424
DEFINE LOAD64_into_RAX_from_Address_R12_Immediate8 498B4424
DEFINE LOAD64_into_RAX_from_Address_RAX 488B00
DEFINE LOAD64_into_RAX_from_Address_RBX 488B03
DEFINE LOAD64_into_RAX_from_Address_RAX_Immediate8 488B40
DEFINE LOAD64_into_RAX_from_Address_RBX_Immediate8 488B43
DEFINE LOAD64_into_RAX_from_Address_RCX_Immediate8 488B41
DEFINE LOAD64_into_RAX_from_Address_RDX_Immediate8 488B42
DEFINE LOAD64_into_RBX_from_Address_RAX_Immediate8 488B58
DEFINE LOAD64_into_RBX_from_Address_RBX 488B1B
DEFINE LOAD64_into_RBX_from_Address_RBX_Immediate8 488B5B
DEFINE LOAD64_into_RBX_from_Address_RCX_Immediate8 488B59
DEFINE LOAD64_into_RCX_from_Address_RAX_Immediate8 488B48
DEFINE LOAD64_into_RCX_from_Address_RCX_Immediate8 488B49
DEFINE LOAD64_into_RCX_from_Address_RDX_Immediate8 488B4A
DEFINE LOAD64_into_RCX_from_Address_RBX 488B0B
DEFINE LOAD64_into_RCX_from_Address_RCX 488B09
DEFINE LOAD64_into_RDX_from_Address_RDX_Immediate8 488B52
DEFINE LOAD64_into_RDI_from_Address_RDX_Immediate8 488B7A
DEFINE NULL 0000000000000000
DEFINE POP_R11 415B
DEFINE POP_RAX 58
DEFINE POP_RBP 5D
DEFINE POP_RBX 5B
DEFINE POP_RCX 59
DEFINE POP_RDX 5A
DEFINE POP_RDI 5F
DEFINE POP_RSI 5E
DEFINE PUSH_R11 4153
DEFINE PUSH_RAX 50
DEFINE PUSH_RBP 55
DEFINE PUSH_RBX 53
DEFINE PUSH_RCX 51
DEFINE PUSH_RDI 57
DEFINE PUSH_RDX 52
DEFINE PUSH_RSI 56
DEFINE RET C3
DEFINE SHIFT_LEFT_RAX_Immediate8 48C1E0
DEFINE SHIFT_RIGHT_RAX_Immediate8 48C1E8
DEFINE SHIFT_RIGHT_RBX_Immediate8 48C1EB
DEFINE STORE8_AL_into_Address_RBX 8803
DEFINE STORE8_AL_into_Address_RCX 8801
DEFINE STORE8_AL_into_Address_RSI 8806
DEFINE STORE8_BL_into_Address_RCX 8819
DEFINE STORE64_RAX_into_Address_RBP_Immediate8 488945
DEFINE STORE64_RAX_into_Address_RBX 488903
DEFINE STORE64_RAX_into_Address_RCX_Immediate8 488941
DEFINE STORE64_RAX_into_Address_RDX 488902
DEFINE STORE64_RAX_into_Address_RDX_Immediate8 488942
DEFINE STORE64_RBP_into_Address_RDX_Immediate8 48896A
DEFINE STORE64_RBX_into_Address_RAX 488918
DEFINE STORE64_RBX_into_Address_RAX_Immediate8 488958
DEFINE STORE64_RBX_into_Address_RDX_Immediate8 48895A
DEFINE STORE64_RCX_into_Address_RAX 488908
DEFINE STORE64_RCX_into_Address_RAX_Immediate8 488948
DEFINE STORE64_RCX_into_Address_RDX_Immediate8 48894A
DEFINE STORE64_RDX_into_Address_RAX_Immediate8 488950
DEFINE STORE64_RDX_into_Address_RBP_Immediate8 488955
DEFINE STORE64_RSI_into_Address_RBP_Immediate8 488975
DEFINE STORE64_RSI_into_Address_RDX_Immediate8 488972
DEFINE STORE64_from_RAX_into_ABS32 48890425
DEFINE STORE64_from_RBX_into_ABS32 48891C25
DEFINE STORE64_from_RCX_into_ABS32 48890C25
DEFINE STORE64_from_RDX_into_ABS32 48891425
DEFINE SUBI8_RAX 4883E8
DEFINE SUBI8_RCX 4883E9
DEFINE SUBI8_RSI 4883EE
DEFINE SWAP_RAX_RBX 4893
DEFINE SYSCALL 0F05
DEFINE ZERO_EXTEND_AL 480FB6C0
DEFINE ZERO_EXTEND_BL 480FB6DB
DEFINE ZERO_EXTEND_CL 480FB6C9



	;; Register usage:
	;; RAX, RSI, RDI => Temps
	;; R13 => MALLOC
	;; R14 => Output_file
	;; R15 => Input_file

	;; 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

; Where the ELF Header is going to hit
; Simply jump to _start
; Our main function

:_start
	POP_RAX                                     ; Get the number of arguments
	POP_RDI                                     ; Get the program name
	POP_RDI                                     ; Get the actual input name
	LOADI32_RSI %0                              ; prepare read_only
	LOADI32_EAX %2                              ; the syscall number for open()
	SYSCALL                                     ; Now open that damn file
	COPY_RAX_to_R15                             ; Preserve the file pointer we were given

	POP_RDI                                     ; Get the actual output name
	LOADI32_RSI %577                            ; Prepare file as O_WRONLY|O_CREAT|O_TRUNC
	LOADI32_EDX %384                            ; Prepare file as RW for owner only (600 in octal)
	LOADI32_EAX %2                              ; the syscall number for open()
	SYSCALL                                     ; Now open that damn file
	CMP_RAX_Immediate8 !0                       ; Check for missing output
	JG8 !_start_out                             ; Have real input
	LOADI32_EAX %1                              ; Use stdout

:_start_out
	COPY_RAX_to_R14                             ; Preserve the file pointer we were given

	LOADI32_EAX %12                             ; the Syscall # for SYS_BRK
	LOADI32_EDI %0                              ; Get current brk
	SYSCALL                                     ; Let the kernel do the work
	COPY_RAX_to_R13                             ; Set our malloc pointer
	LOADI32_EAX %0                              ; HEAD = NULL
	CALLI32 %read_all_tokens                    ; Read all tokens
	CALLI32 %Reverse_List                       ; Reverse order
;	CALLI32 %debug_list                         ; Try to figure out what is wrong
	STORE64_from_RAX_into_ABS32 &global_token   ; Set global_token
	CALLI32 %program                            ; Convert into program
	LOADI32_RAX &header_string1                 ; Our header string
	CALLI32 %File_Print                         ; Print it
	LOAD64_RAX_from_ABS32 &output_list          ; Our output_list
	CALLI32 %recursive_output                   ; Print core program
;	LOADI32_RAX &header_string2                 ; Our Enable debug
;	CALLI32 %File_Print                         ; Print it
	LOADI32_RAX &header_string3                 ; Our second label
	CALLI32 %File_Print                         ; Print it
	LOAD64_RAX_from_ABS32 &globals_list         ; Our globals
	CALLI32 %recursive_output                   ; Get them
	LOADI32_RAX &header_string4                 ; Our final header
	CALLI32 %File_Print                         ; Print it
	LOAD64_RAX_from_ABS32 &strings_list         ; Our strings
	CALLI32 %recursive_output                   ; Get them
	LOADI32_RAX &header_string5                 ; Our final header
	CALLI32 %File_Print                         ; Print it

:Done
	; program completed Successfully
	LOADI32_EDI %0                              ; All is well
	LOADI32_EAX %0x3C                           ; put the exit syscall number in eax
	SYSCALL                                     ; Call it a good day

:header_string1
"
# Core program
"

:header_string2
"
:ELF_data
"

:header_string3
"
# Program global variables
"

:header_string4
"
# Program strings
"

:header_string5
"
:ELF_end
"


;; read_all_tokens function
;; Receives FILE* in R15 and Token_List* in RAX
;; Tokenizes all input and returns updated list in RAX
;; Returns TOKEN in RAX
;; Uses RAX for C
:read_all_tokens
	STORE64_from_RAX_into_ABS32 &Token
	CALLI32 %fgetc
:read_all_tokens_loop
	CMP_RAX_Immediate8 !-4                      ; Check for EOF
	JE8 !read_all_tokens_done                   ; Stop if found
	CALLI32 %get_token                          ; Read all tokens
	JMP8 !read_all_tokens_loop                  ; Loop
:read_all_tokens_done
	LOAD64_RAX_from_ABS32 &Token
	RET


;; get_token function
;; Receives INT in RAX and FILE* in R15
;; Makes a list of TOKEN_LIST
;; C and STRING_INDEX are stored in memory, RCX is used for S and RDX is used for current
;; Returns C in RAX
:get_token
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	PUSH_RDX                                    ; Protect RDX

	STORE64_from_RAX_into_ABS32 &C              ; Set C

	LOADI32_EAX %40                             ; Malloc CURRENT
	CALLI32 %malloc                             ; Get Pointer
	COPY_RAX_to_RDX                             ; Set CURRENT

	LOADI32_EAX %256                            ; Malloc the string
	CALLI32 %malloc                             ; Get pointer to S
	COPY_RAX_to_RCX                             ; Set S
	STORE64_RCX_into_Address_RDX_Immediate8 !16 ; CURRENT->S = S
:reset
	STORE64_from_RCX_into_ABS32 &string_index   ; S[0]
	LOAD64_RAX_from_ABS32 &C                    ; Using C

	CALLI32 %clear_white_space                  ; Clear WhiteSpace
	STORE64_from_RAX_into_ABS32 &C              ; Set C

	CMP_RAX_Immediate8 !-4                      ; Check for EOF
	JE32 %get_token_abort                       ; if EOF abort

	CMP_RAX_Immediate8 !35                      ; Check for '#'
	JNE8 !get_token_alpha                       ; Nope

	;; Deal with # line comments
	CALLI32 %purge_macro                        ; Let it handle it
	STORE64_from_RAX_into_ABS32 &C              ; Set C
	JMP8 !reset                                 ; Try again

:get_token_alpha
	LOAD64_RAX_from_ABS32 &C                    ; Send C
	LOADI32_RBX &alphas                         ; Get alphanumerics
	CALLI32 %In_Set                             ; See if in set
	CMP_RAX_Immediate8 !1                       ; IF TRUE
	JNE8 !get_token_symbol                      ; Otherwise

	;; Store keywords
	LOAD64_RAX_from_ABS32 &C                    ; Send C
	CALLI32 %preserve_keyword                   ; Store
	STORE64_from_RAX_into_ABS32 &C              ; Set C
	JMP32 %get_token_done                       ; Be done with this token

:get_token_symbol
	LOAD64_RAX_from_ABS32 &C                    ; Send C
	LOADI32_RBX &symbols                        ; Get symbols
	CALLI32 %In_Set                             ; See if in set
	CMP_RAX_Immediate8 !1                       ; IF TRUE
	JNE8 !get_token_strings                     ; Otherwise

	;; Store symbols
	LOAD64_RAX_from_ABS32 &C                    ; Send C
	CALLI32 %preserve_symbol                    ; Store
	STORE64_from_RAX_into_ABS32 &C              ; Set C
	JMP32 %get_token_done                       ; Be done with this token

:get_token_strings
	LOAD64_RAX_from_ABS32 &C                    ; Send C
	LOADI32_RBX &strings                        ; Get strings
	CALLI32 %In_Set                             ; See if in set
	CMP_RAX_Immediate8 !1                       ; IF TRUE
	JNE8 !get_token_comment                     ; Otherwise

	;; Store String
	LOAD64_RAX_from_ABS32 &C                    ; Send C
	CALLI32 %consume_word                       ; Store
	STORE64_from_RAX_into_ABS32 &C              ; Set C
	JMP32 %get_token_done                       ; Be done with this token

:get_token_comment
	LOAD64_RAX_from_ABS32 &C                    ; Send C
	CMP_RAX_Immediate8 !47                      ; IF '/' == C
	JNE32 %get_token_else                       ; Otherwise

	CALLI32 %consume_byte                       ; Hope it just is '/'
	STORE64_from_RAX_into_ABS32 &C              ; Set C

	CMP_RAX_Immediate8 !42                      ; IF '*' we have '/*'
	JNE8 !get_token_comment_line                ; Check for '//'

	;; Deal with /* block comments */
	CALLI32 %fgetc                              ; get next C
	STORE64_from_RAX_into_ABS32 &C              ; Set C
:get_token_comment_block_outer
	LOAD64_RAX_from_ABS32 &C                    ; Using C
	CMP_RAX_Immediate8 !47                      ; IF '/' != C
	JE8 !get_token_comment_block_done           ; be done

:get_token_comment_block_inner
	LOAD64_RAX_from_ABS32 &C                    ; Using C
	CMP_RAX_Immediate8 !42                      ; IF '*' != C
	JE8 !get_token_comment_block_iter           ; jump over

	;; Deal with inner loop
	CALLI32 %fgetc                              ; get next C
	STORE64_from_RAX_into_ABS32 &C              ; Set C
	JMP8 !get_token_comment_block_inner         ; keep going

:get_token_comment_block_iter
	CALLI32 %fgetc                              ; get next C
	STORE64_from_RAX_into_ABS32 &C              ; Set C
	JMP8 !get_token_comment_block_outer

:get_token_comment_block_done
	CALLI32 %fgetc                              ; get next C
	STORE64_from_RAX_into_ABS32 &C              ; Set C
	JMP32 %reset                                ; throw away, try again

:get_token_comment_line
	CMP_RAX_Immediate8 !47                      ; IF '/' we have //
	JNE8 !get_token_done                        ; keep if just '/'

	;; Deal with // line comment
	CALLI32 %fgetc                              ; drop to match
	STORE64_from_RAX_into_ABS32 &C              ; Set C
	JMP32 %reset                                ; throw away, try again

:get_token_else
	LOAD64_RAX_from_ABS32 &C                    ; Send C
	CALLI32 %consume_byte
	STORE64_from_RAX_into_ABS32 &C              ; Set C

:get_token_done
	LOAD64_RAX_from_ABS32 &Token                ; TOKEN
	STORE64_RAX_into_Address_RDX_Immediate8 !8  ; CURRENT->PREV = TOKEN
	STORE64_RAX_into_Address_RDX                ; CURRENT->NEXT = TOKEN
	STORE64_from_RDX_into_ABS32 &Token          ; TOKEN = CURRENT

:get_token_abort
	POP_RDX                                     ; Restore RDX
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	LOAD64_RAX_from_ABS32 &C                    ; Return C
	RET


;; 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 R13 to be initialized and RAX to have the number of desired bytes
:malloc
	COPY_R13_to_RDI                             ; Using the current pointer
	ADD_RAX_to_RDI                              ; Request the number of desired bytes
	LOADI32_EAX %12                             ; the Syscall # for SYS_BRK
	PUSH_RCX                                    ; Protect rcx
	PUSH_R11                                    ; Protect r11
	SYSCALL                                     ; call the Kernel
	POP_R11                                     ; Restore r11
	POP_RCX                                     ; Restore rcx
	COPY_R13_to_RAX                             ; Return pointer
	COPY_RDI_to_R13                             ; Update pointer
	RET


;; clear_white_space function
;; Receives INT C in RAX and FILE* in R15
;; Returns first non-whitespace char in RAX
:clear_white_space
	CMP_RAX_Immediate8 !32                      ; Check for ' '
	JE8 !clear_white_space_wipe                 ; wipe it out

	CMP_RAX_Immediate8 !10                      ; Check for '\n'
	JE8 !clear_white_space_wipe                 ; wipe it output

	CMP_RAX_Immediate8 !9                       ; Check for '\t'
	JNE8 !clear_white_space_done                ; looks like non-whitespace

:clear_white_space_wipe
	CALLI32 %fgetc                              ; Read a new byte
	CMP_RAX_Immediate8 !-4                      ; Check for EOF
	JE8 !clear_white_space_done                 ; Short circuit
	JMP8 !clear_white_space                     ; iterate

:clear_white_space_done
	RET


;; In_Set function
;; Receives Char C in RAX and CHAR* in RBX
;; Returns 1 if true, zero if false in RAX
:In_Set
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
:In_Set_loop
	LOAD8_CL_from_Address_RBX                   ; Read char
	ZERO_EXTEND_CL                              ; Zero extend it

	CMP_RCX_to_RAX                              ; See if they match
	JE8 !In_Set_True                            ; return true

	CMP_RCX_Immediate8 !0                       ; Check for NULL
	JE8 !In_Set_False                           ; return false

	ADDI8_to_RBX !1                             ; s = s + 1
	JMP8 !In_Set_loop                           ; Keep looping

:In_Set_True
	LOADI32_EAX %1                              ; Set True
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET

:In_Set_False
	LOADI32_EAX %0                              ; Set FALSE
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET

:alphas
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"

:symbols
"<=>|&!-"

:strings
'22 27 00'


;; purge_macro function
;; Receives CH in RAX
;; Reads chars until Line feed is read
;; returns line feed
:purge_macro
	CALLI32 %fgetc                              ; read next char
	CMP_RAX_Immediate8 !10                      ; Check for '\n'
	JNE8 !purge_macro                           ; Keep going
	RET


;; preserve_keyword function
;; Receives INT C in RAX
;; collects all chars in keyword
;; Returns C in RAX
;; Uses RCX for INT C
:preserve_keyword
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	COPY_RAX_to_RCX                             ; Setup C
	LOADI32_RBX &alphas                         ; Concerning ourselves with "abc.."
:preserve_keyword_loop
	CALLI32 %In_Set                             ; Check if alphanumerics
	CMP_RAX_Immediate8 !1                       ; IF TRUE
	JNE8 !preserve_keyword_label                ; Otherwise check for label

	COPY_RCX_to_RAX                             ; Pass C
	CALLI32 %consume_byte                       ; consume that byte
	COPY_RAX_to_RCX                             ; Update C
	JMP8 !preserve_keyword_loop                 ; keep looping

:preserve_keyword_label
	COPY_RCX_to_RAX                             ; Fix return
	CMP_RAX_Immediate8 !58                      ; Check for ':'
	JNE8 !preserve_keyword_done                 ; be done

	;; Fix our goto label
	CALLI32 %fixup_label                        ; Fix the label
	LOADI32_EAX %32                             ; Return Whitespace

:preserve_keyword_done
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET


;; preserve_symbol function
;; Receives INT C in RAX
;; collects all chars in symbol
;; Returns C in RAX
;; Uses RCX for INT C
:preserve_symbol
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	COPY_RAX_to_RCX                             ; Setup C
	LOADI32_RBX &symbols                        ; Concerning ourselves with "<=>.."
:preserve_symbol_loop
	CALLI32 %In_Set                             ; Check if symbol
	CMP_RAX_Immediate8 !1                       ; IF TRUE
	JNE8 !preserve_symbol_done                  ; Otherwise be done

	COPY_RCX_to_RAX                             ; Pass C
	CALLI32 %consume_byte                       ; consume that byte
	COPY_RAX_to_RCX                             ; Update C
	JMP8 !preserve_symbol_loop                  ; keep looping

:preserve_symbol_done
	COPY_RCX_to_RAX                             ; Fix return
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET


;; consume_word function
;; receives INT C in RAX
;; returns INT C in RAX
;; Uses RAX for C, RBX for FREQ and RCX for ESCAPE
:consume_word
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	COPY_RAX_to_RBX                             ; FREQ = C
	LOADI32_RCX %0                              ; ESCAPE = FALSE
:consume_word_loop
	CMP_RCX_Immediate8 !0                       ; IF !ESCAPE
	JNE8 !consume_word_escape                   ; Enable escape

	CMP_RAX_Immediate8 !92                      ; if '\\'
	JNE8 !consume_word_iter                     ; keep state

	LOADI32_RCX %1                              ; ESCAPE = TRUE
	JMP8 !consume_word_iter                     ; keep going

:consume_word_escape
	LOADI32_RCX %0                              ; ESCAPE = FALSE

:consume_word_iter
	CALLI32 %consume_byte                       ; read next char

	CMP_RCX_Immediate8 !0                       ; IF ESCAPE
	JNE8 !consume_word_loop                     ; keep looping

	CMP_RBX_to_RAX                              ; IF C != FREQ
	JNE8 !consume_word_loop                     ; keep going

	CALLI32 %fgetc                              ; return next char
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET


;; consume_byte function
;; Receives INT C in RAX
;; Inserts C into string S, updates String S
;; Returns Next char in RAX
:consume_byte
	PUSH_RBX                                    ; Protect RBX
	LOAD64_RBX_from_ABS32 &string_index         ; S[0]
	STORE8_AL_into_Address_RBX                  ; S[0] = C
	ADDI8_to_RBX !1                             ; S = S + 1
	STORE64_from_RBX_into_ABS32 &string_index   ; Update S
	CALLI32 %fgetc
	POP_RBX                                     ; Restore RBX
	RET


;; fixup_label function
;; Receives S in RCX
;; prepends ':' to string and returns registers unchanged
;; Uses RAX for HOLD, RBX for PREV and RCX for S[0]
:fixup_label
	PUSH_RAX                                    ; Protect RAX
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	LOADI32_EAX %58                             ; HOLD = ':'
	LOAD64_into_RCX_from_Address_RDX_Immediate8 !16 ; HOLD_STRING[0]
:fixup_label_loop
	COPY_RAX_to_RBX                             ; PREV = HOLD
	LOAD8_AL_from_Address_RCX                   ; HOLD = HOLD_STRING[I]
	ZERO_EXTEND_AL                              ; make useful
	STORE8_BL_into_Address_RCX                  ; HOLD_STRING[I] = PREV
	ADDI8_to_RCX !1                             ; I = I + 1
	CMP_RAX_Immediate8 !0                       ; IF NULL == HOLD
	JNE8 !fixup_label_loop                      ; Keep looping

	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	POP_RAX                                     ; Restore RAX
	RET


;; fgetc function
;; Receives FILE* in R15
;; Returns -4 (EOF) or char in RAX
:fgetc
	LOADI32_RAX %-4                             ; Put EOF in rax
	PUSH_RAX                                    ; Assume bad (If nothing read, value will remain EOF)
	LEA_RSI                                     ; Get stack address
	COPY_R15_to_RDI                             ; Where are we reading from
	LOADI32_EAX %0                              ; the syscall number for read
	PUSH_RDX                                    ; Protect RDX
	LOADI32_EDX %1                              ; set the size of chars we want
	PUSH_RCX                                    ; Protect RCX
	PUSH_R11                                    ; Protect R11
	SYSCALL                                     ; call the Kernel
	POP_R11                                     ; Restore R11
	POP_RCX                                     ; Restore RCX
	POP_RDX                                     ; Restore RDX
	POP_RAX                                     ; Get either char or EOF
	CMP_RAX_Immediate8 !-4                      ; Check for EOF
	JE8 !fgetc_done                             ; Return as is
	ZERO_EXTEND_AL                              ; Make it useful
:fgetc_done
	RET


;; Reverse_List function
;; Receives List in RAX
;; Returns the list reversed in RAX
:Reverse_List
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	COPY_RAX_to_RBX                             ; Set HEAD
	LOADI32_EAX %0                              ; ROOT = NULL
:Reverse_List_Loop
	CMP_RBX_Immediate8 !0                       ; WHILE HEAD != NULL
	JE8 !Reverse_List_Done                      ; Stop otherwise

	LOAD64_into_RCX_from_Address_RBX            ; NEXT = HEAD->NEXT
	STORE64_RAX_into_Address_RBX                ; HEAD->NEXT = ROOT
	COPY_RBX_to_RAX                             ; ROOT = HEAD
	COPY_RCX_to_RBX                             ; HEAD = NEXT
	JMP8 !Reverse_List_Loop                     ; Keep Going

:Reverse_List_Done
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET


;; recursive_output function
;; Receives list in RAX
;; walks the list and prints the I->S for all nodes backwards
;; Uses RBX for I
:recursive_output
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	CMP_RAX_Immediate8 !0                       ; Check for NULL
	JE8 !recursive_output_done                  ; Skip the work
	COPY_RAX_to_RBX                             ; I = Head

	LOAD64_into_RAX_from_Address_RBX            ; Iterate to next Token
	CALLI32 %recursive_output                   ; Recurse

	LOAD64_into_RAX_from_Address_RBX_Immediate8 !16 ; Using S
	CALLI32 %File_Print                         ; Print it

:recursive_output_done
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET


;; File_Print function
;; Receives CHAR* in RAX
;; calls fputc for every non-null char
:File_Print
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	COPY_RAX_to_RBX                             ; Protect S
	CMP_RAX_Immediate8 !0                       ; Protect against nulls
	JE8 !File_Print_Done                        ; Simply don't try to print them
:File_Print_Loop
	LOAD8_AL_from_Address_RBX                   ; Read byte
	ZERO_EXTEND_AL                              ; zero extend
	CMP_RAX_Immediate8 !0                       ; Check for NULL
	JE8 !File_Print_Done                        ; Stop at NULL

	CALLI32 %fputc                              ; write it
	ADDI8_to_RBX !1                             ; S = S + 1
	JMP8 !File_Print_Loop                       ; Keep going

:File_Print_Done
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET


;; fputc function
;; receives CHAR in RAX and FILE* in R14
;; writes char and returns
:fputc
	PUSH_RAX                                    ; We are writing rax
	LEA_RSI                                     ; Get stack address
	COPY_R14_to_RDI                             ; Write to target file
	LOADI32_EAX %1                              ; the syscall number for write
	PUSH_RDX                                    ; Protect RDX
	LOADI32_EDX %1                              ; set the size of chars we want
	PUSH_RCX                                    ; Protect RCX
	PUSH_R11                                    ; Protect R11
	SYSCALL                                     ; call the Kernel
	POP_R11                                     ; Restore R11
	POP_RCX                                     ; Restore RCX
	POP_RDX                                     ; Restore RDX
	POP_RAX                                     ; Restore stack
	RET


;; program function
;; receives nothing, returns nothing
;; Uses RAX for type_size
:program
	;; The binary initialized the globals to null, so we can skip those steps
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX

:new_type
	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	CMP_RAX_Immediate8 !0                       ; Check if NULL
	JE32 %program_done                          ; Be done if null

	LOAD64_into_RBX_from_Address_RAX_Immediate8 !16 ; GLOBAL_TOKEN->S
	LOADI32_RAX &constant                       ; "CONSTANT"
	CALLI32 %match                              ; IF GLOBAL_TOKEN->S == "CONSTANT"
	CMP_RAX_Immediate8 !0                       ; If true
	JNE8 !program_else                          ; Looks like not a constant

	;; Deal with minimal constant case
	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX            ; global_token->next
	STORE64_from_RAX_into_ABS32 &global_token   ; global_token = global_token->next

	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; global_token->S
	LOADI32_EBX %0                              ; NULL
	LOAD64_RCX_from_ABS32 &global_constant_list ; global_constant_list
	CALLI32 %sym_declare                        ; Declare that constant
	STORE64_from_RAX_into_ABS32 &global_constant_list ; global_constant_list = sym_declare(global_token->s, NULL, global_constant_list);

	LOAD64_RBX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RBX_from_Address_RBX            ; global_token->next
	STORE64_RBX_into_Address_RAX_Immediate8 !32 ; global_constant_list->arguments = global_token->next

	LOAD64_into_RBX_from_Address_RBX            ; global_token->next->next
	STORE64_from_RBX_into_ABS32 &global_token   ; global_token = global_token->next->next;
	JMP8 !new_type                              ; go around again

:program_else
	CALLI32 %type_name                          ; Figure out the type_size
	CMP_RAX_Immediate8 !0                       ; IF NULL == type_size
	JE32 %new_type                              ; it was a new type

	;; Add to global symbol table
	COPY_RAX_to_RBX                             ; put type_size in the right spot
	LOAD64_RAX_from_ABS32 &global_token         ; Using global token
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; global_token->S
	LOAD64_RCX_from_ABS32 &global_symbol_list   ; Using global_symbol_list
	CALLI32 %sym_declare                        ; Declare symbol
	STORE64_from_RAX_into_ABS32 &global_symbol_list ; global_symbol_list = sym_declare(global_token->s, type_size, global_symbol_list);
	LOAD64_RBX_from_ABS32 &global_token         ; Using global token
	LOAD64_into_RBX_from_Address_RBX            ; global_token->next
	STORE64_from_RBX_into_ABS32 &global_token   ; global_token = global_token->next

	LOAD64_RBX_from_ABS32 &global_token         ; Using global token
	LOAD64_into_RBX_from_Address_RBX_Immediate8 !16 ; global_token->S
	LOADI32_RAX &semicolon                      ; ";"
	CALLI32 %match                              ; if(match(";", global_token->s))
	CMP_RAX_Immediate8 !0                       ; If true
	JNE8 !program_function                      ; looks like not a match

	;; Deal with the global variable
	LOAD64_RBX_from_ABS32 &globals_list         ; Using globals_list
	LOADI32_RAX &program_string_0               ; ":GLOBAL_"
	CALLI32 %emit                               ; Emit it
	COPY_RAX_to_RBX                             ; update globals_list

	LOAD64_RAX_from_ABS32 &global_token         ; Using global token
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !8  ; global token->prev
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; global token->prev->s
	CALLI32 %emit                               ; Emit it

	COPY_RAX_to_RBX                             ; update globals_list
	LOADI32_RAX &program_string_1               ; "\nNULL\n"
	CALLI32 %emit                               ; Emit it
	STORE64_from_RAX_into_ABS32 &globals_list   ; update globals_list

	LOAD64_RAX_from_ABS32 &global_token         ; Using global token
	LOAD64_into_RAX_from_Address_RAX            ; global_token->next
	STORE64_from_RAX_into_ABS32 &global_token   ; global_token = global_token->next
	JMP32 %new_type                             ; go around again

:program_function
	LOAD64_RBX_from_ABS32 &global_token         ; Using global token
	LOAD64_into_RBX_from_Address_RBX_Immediate8 !16 ; global_token->S
	LOADI32_RAX &open_paren                     ; "("
	CALLI32 %match                              ; if(match(";", global_token->s))
	CMP_RAX_Immediate8 !0                       ; If true
	JNE8 !program_error                         ; Otherwise deal with error case

	;; Deal with function definition
	CALLI32 %declare_function                   ; Lets get the parsing rolling
	JMP32 %new_type                             ; Keep looping through functions

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

:program_done
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET

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

:program_string_1
"
NULL
"


;; declare_function function
;; Receives nothing and returns nothing
;; Sets current function and adds it to the global function list
:declare_function
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	LOADI32_EAX %0                              ; Using NULL
	STORE64_from_RAX_into_ABS32 &current_count  ; current_count = 0

	LOAD64_RAX_from_ABS32 &global_token         ; Using global token
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !8  ; global token->prev
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; global token->prev->s
	LOADI32_EBX %0                              ; NULL
	LOAD64_RCX_from_ABS32 &global_function_list ; global_function_list
	CALLI32 %sym_declare                        ; sym_declare(global_token->prev->s, NULL, global_function_list);
	STORE64_from_RAX_into_ABS32 &function       ; function = sym_declare(global_token->prev->s, NULL, global_function_list);
	STORE64_from_RAX_into_ABS32 &global_function_list ; global_function_list = function

	CALLI32 %collect_arguments                  ; collect all of the function arguments

	LOAD64_RAX_from_ABS32 &global_token         ; Using global token
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; global token->s
	LOADI32_RBX &semicolon                      ; ";"
	CALLI32 %match                              ; IF global token->s == ";"
	CMP_RAX_Immediate8 !0                       ; If true
	JNE8 !declare_function_full                 ; It was a prototype

	;; Deal with prototypes
	LOAD64_RAX_from_ABS32 &global_token         ; Using global token
	LOAD64_into_RAX_from_Address_RAX            ; global token->next
	STORE64_from_RAX_into_ABS32 &global_token   ; global token = global token->next
	JMP32 %declare_function_done                ; Move on

:declare_function_full
	;; Deal with full function definitions
	LOADI32_RAX &declare_function_string_0      ; "# Defining function "
	CALLI32 %emit_out                           ; emit it

	LOAD64_RAX_from_ABS32 &function             ; function
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; function->s
	CALLI32 %emit_out                           ; emit it

	LOADI32_RAX &declare_function_string_1      ; "\n:FUNCTION_"
	CALLI32 %emit_out                           ; emit it

	LOAD64_RAX_from_ABS32 &function             ; function
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; function->s
	CALLI32 %emit_out                           ; emit it

	LOADI32_RAX &declare_function_string_3      ; "\n"
	CALLI32 %emit_out                           ; emit it

	CALLI32 %statement                          ; Recursively get the function pieces

	LOAD64_RAX_from_ABS32 &output_list          ; output
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; output->s
	LOADI32_RBX &declare_function_string_2      ; "RETURN\n"
	CALLI32 %match                              ; IF output->s == "RETURN\n"
	CMP_RAX_Immediate8 !0                       ; If true we can skip adding it
	JE8 !declare_function_done                  ; otherwise we need to add it

	;; Add the return to the end of a function lacking a return;
	LOADI32_RAX &declare_function_string_2      ; "RETURN\n"
	CALLI32 %emit_out                           ; emit it

:declare_function_done
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET


:declare_function_string_0
"# Defining function "

:declare_function_string_1
"
:FUNCTION_"

:declare_function_string_2
"RETURN
"

:declare_function_string_3
"
"


;; collect_arguments function
;; Receives nothing
;; Returns Nothing
;; Adds arguments to the function definition
;; holds struct type* type_size in RCX, then replace with struct token_list* a in RCX when type_size is used
:collect_arguments
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX            ; global_token->next
	STORE64_from_RAX_into_ABS32 &global_token   ; global_token = global_token->next
:collect_arguments_loop
	LOAD64_RBX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RBX_from_Address_RBX_Immediate8 !16 ; global_token->S
	LOADI32_RAX &close_paren                    ; ")"
	CALLI32 %match                              ; IF global_token->S == ")"
	CMP_RAX_Immediate8 !0                       ; we reached the end
	JE32 %collect_arguments_done                ; be done

	;; deal with the case of there are arguments
	CALLI32 %type_name                          ; Get the type
	COPY_RAX_to_RCX                             ; put type_size safely out of the way

	LOAD64_RBX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RBX_from_Address_RBX_Immediate8 !16 ; global_token->S
	LOADI32_RAX &close_paren                    ; ")"
	CALLI32 %match                              ; IF global_token->S == ")"
	CMP_RAX_Immediate8 !0                       ; is a foo(int, char,void) case
	JE32 %collect_arguments_common              ; deal with commas

	;; Trying second else
	LOAD64_RBX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RBX_from_Address_RBX_Immediate8 !16 ; global_token->S
	LOADI32_RAX &comma                          ; ","
	CALLI32 %match                              ; IF global_token->S == ","
	CMP_RAX_Immediate8 !0                       ; then deal with the common
	JE8 !collect_arguments_common               ; case of commas between arguments

	;; deal with foo(int a, char b)
	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; global_token->S
	COPY_RCX_to_RBX                             ; put type_size in the right place
	LOAD64_RCX_from_ABS32 &function             ; Using function
	LOAD64_into_RCX_from_Address_RCX_Immediate8 !32 ; function->args
	CALLI32 %sym_declare                        ; sym_declare(global_token->s, type_size, function->arguments);
	COPY_RAX_to_RCX                             ; put a in a safe place

	LOAD64_RAX_from_ABS32 &function             ; Using function
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !32 ; function->args
	CMP_RAX_Immediate8 !0                       ; IF function->args == NULL
	JNE8 !collect_arguments_another             ; otherwise it isn't the first

	;; Deal with the case of first argument in the function
	LOADI32_RAX %-8                             ; -8
	STORE64_RAX_into_Address_RCX_Immediate8 !32 ; a->depth = -8
	JMP8 !collect_arguments_next                ; get to next

:collect_arguments_another
	;; deal with the case of non-first arguments
	LOAD64_RAX_from_ABS32 &function             ; Using function
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !32 ; function->args
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !32 ; function->args->depth
	SUBI8_RAX !8                                ; function->args->depth - 8
	STORE64_RAX_into_Address_RCX_Immediate8 !32 ; a->depth = function->args->depth - 8

:collect_arguments_next
	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX            ; global_token->next
	STORE64_from_RAX_into_ABS32 &global_token   ; global_token = global_token->next

	LOAD64_RAX_from_ABS32 &function             ; Using function
	STORE64_RCX_into_Address_RAX_Immediate8 !32 ; function->args = a

:collect_arguments_common
	LOAD64_RBX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RBX_from_Address_RBX_Immediate8 !16 ; global_token->S
	LOADI32_RAX &comma                          ; ","
	CALLI32 %match                              ; IF global_token->S == ","
	CMP_RAX_Immediate8 !0                       ; then deal with the comma
	JNE32 %collect_arguments_loop               ; otherwise loop

	;; keep foo(bar(), 1) expressions working
	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX            ; global_token->next
	STORE64_from_RAX_into_ABS32 &global_token   ; global_token = global_token->next
	JMP32 %collect_arguments_loop               ; keep going

:collect_arguments_done
	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX            ; global_token->next
	STORE64_from_RAX_into_ABS32 &global_token   ; global_token = global_token->next
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET


;; statement function
;; Receives nothing
;; Returns nothing
;; Walks down global_token recursively to collect the contents of the function
:statement
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX

	LOAD64_RBX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RBX_from_Address_RBX_Immediate8 !16 ; global_token->S
	LOADI32_RAX &open_curly_brace               ; "{"
	CALLI32 %match                              ; IF global_token->S == "{"
	JNE8 !statement_label                       ; otherwise try label

	;; deal with { statement }
	CALLI32 %recursive_statement                ; Statements inside of statements for days
	JMP32 %statement_done                       ; Be done

:statement_label
	LOAD8_AL_from_Address_RBX                   ; global_token->S[0]
	ZERO_EXTEND_AL                              ; make it useful
	CMP_RAX_Immediate8 !58                      ; IF global_token->S == ':'
	JNE8 !statement_local                       ; otherwise try locals

	;; deal with labels
	COPY_RBX_to_RAX                             ; put global_token->S in the right spot
	CALLI32 %emit_out                           ; emit it

	LOADI32_RAX &statement_string_0             ; Using "\t#C goto label\n"
	CALLI32 %emit_out                           ; emit it

	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX            ; global_token->next
	STORE64_from_RAX_into_ABS32 &global_token   ; global_token = global_token->next
	JMP32 %statement_done                       ; be done

:statement_local
	COPY_RBX_to_RAX                             ; put global_token->S in the right place
	LOADI32_RBX &prim_types                     ; pointer to primative types
	CALLI32 %lookup_type                        ; See if found
	CMP_RAX_Immediate8 !0                       ; IF NULL == lookup_type(global_token->S, prim_types)
	JNE8 !statement_local_success               ; Sweet a new local

	;; Second chance
	LOAD64_RBX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RBX_from_Address_RBX_Immediate8 !16 ; global_token->S
	LOADI32_RAX &struct                         ; "struct"
	CALLI32 %match                              ; IF global_token->S == "struct"
	CMP_RAX_Immediate8 !0                       ; then we are a local
	JNE8 !statement_if                          ; otherwise try IF

:statement_local_success
	CALLI32 %collect_local                      ; Grab those locals
	JMP32 %statement_done                       ; be done

:statement_if
	LOADI32_RAX &if_string                      ; Using "if"
	CALLI32 %match                              ; IF global_token->S == "if"
	CMP_RAX_Immediate8 !0                       ; then we have an if statement
	JNE8 !statement_do                          ; otherwise try DO

	;; Deal with IF statement
	CALLI32 %process_if                         ; DO IT
	JMP32 %statement_done                       ; be done

:statement_do
	LOADI32_RAX &do_string                      ; Using "do"
	CALLI32 %match                              ; IF global_token->S == "do"
	CMP_RAX_Immediate8 !0                       ; then we have a do statement
	JNE8 !statement_while                       ; otherwise try WHILE

	;; Deal with DO statement
	CALLI32 %process_do                         ; DO IT
	JMP32 %statement_done                       ; be done

:statement_while
	LOADI32_RAX &while_string                   ; Using "while"
	CALLI32 %match                              ; IF global_token->S == "while"
	CMP_RAX_Immediate8 !0                       ; then we have a while statement
	JNE8 !statement_for                         ; otherwise try FOR

	;; Deal with WHILE statement
	CALLI32 %process_while                      ; DO IT
	JMP32 %statement_done                       ; be done

:statement_for
	LOADI32_RAX &for_string                     ; Using "for"
	CALLI32 %match                              ; IF global_token->S == "for"
	CMP_RAX_Immediate8 !0                       ; then we have a for statement
	JNE8 !statement_asm                         ; otherwise try ASM

	;; Deal with FOR statement
	CALLI32 %process_for                        ; DO IT
	JMP32 %statement_done                       ; be done

:statement_asm
	LOADI32_RAX &asm_string                     ; Using "asm"
	CALLI32 %match                              ; IF global_token->S == "asm"
	CMP_RAX_Immediate8 !0                       ; then we have an asm statement
	JNE8 !statement_goto                        ; otherwise try GOTO

	;; Deal with ASM statement
	CALLI32 %process_asm                        ; Hit it
	JMP32 %statement_done                       ; be done

:statement_goto
	LOADI32_RAX &goto_string                    ; Using "goto"
	CALLI32 %match                              ; IF global_token->S == "goto"
	CMP_RAX_Immediate8 !0                       ; then we have a goto statement
	JNE8 !statement_return                      ; Otherwise try RETURN

	;; Deal with GOTO statement
	LOADI32_RAX &statement_string_1             ; Using "JUMP %"
	CALLI32 %emit_out                           ; emit it

	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX            ; global_token->next
	STORE64_from_RAX_into_ABS32 &global_token   ; global_token = global_token->next

	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; global_token->S
	CALLI32 %emit_out                           ; emit it

	LOADI32_RAX &statement_string_2             ; Using "\n"
	CALLI32 %emit_out                           ; emit it

	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX            ; global_token->next
	STORE64_from_RAX_into_ABS32 &global_token   ; global_token = global_token->next

	LOADI32_RAX &statement_string_4             ; Using "ERROR in statement\nMissing ;\n"
	LOADI32_RBX &semicolon                      ; Using ";"
	CALLI32 %require_match                      ; Make sure it has the required
	JMP32 %statement_done                       ; Be done

:statement_return
	LOADI32_RAX &return_string                  ; Using "return"
	CALLI32 %match                              ; IF global_token->S == "return"
	CMP_RAX_Immediate8 !0                       ; then we have a return statement
	JNE8 !statement_break                       ; Otherwise try BREAK

	;; Deal with RETURN Statement
	CALLI32 %return_result                      ; Return anything they want
	JMP32 %statement_done                       ; be done

:statement_break
	LOADI32_RAX &break_string                   ; Using "break"
	CALLI32 %match                              ; IF global_token->S == "break"
	CMP_RAX_Immediate8 !0                       ; then we have a break statement
	JNE8 !statement_continue                    ; Otherwise try CONTINUE

	;; Deal with BREAK statement
	CALLI32 %process_break                      ; Lets do some damage
	JMP8 !statement_done                        ; be done

:statement_continue
	LOADI32_RAX &continue_string                ; Using "continue"
	CALLI32 %match                              ; IF global_token->S == "continue"
	CMP_RAX_Immediate8 !0                       ; then we have a continue statement
	JNE8 !statement_else                        ; Otherwise we are punting to an expression

	;; Deal with CONTINUE statement
	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX            ; global_token->next
	STORE64_from_RAX_into_ABS32 &global_token   ; global_token = global_token->next

	LOADI32_RAX &statement_string_3             ; Using "\n#continue statement\n"
	CALLI32 %emit_out                           ; emit it

	LOADI32_RAX &statement_string_4             ; Using "ERROR in statement\nMissing ;\n"
	LOADI32_RBX &semicolon                      ; Using ";"
	CALLI32 %require_match                      ; Don't forget the ";"
	JMP8 !statement_done                        ; Be done

:statement_else
	CALLI32 %expression                         ; Collect expression
	LOADI32_RAX &statement_string_4             ; Using "ERROR in statement\nMissing ;\n"
	LOADI32_RBX &semicolon                      ; Using ";"
	CALLI32 %require_match                      ; make sure we have it

:statement_done
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET

:statement_string_0
"	#C goto label
"

:statement_string_1
"JUMP %"

:statement_string_2
"
"

:statement_string_3
"
#continue statement
"

:statement_string_4
"ERROR in statement
Missing ;
"


;; recursive_statement function
;; Receives nothing
;; Returns nothing
;; Walks the global_token list to build the contents of statements
;; Uses struct token_list* frame in RCX
:recursive_statement
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX

	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX            ; global_token->next
	STORE64_from_RAX_into_ABS32 &global_token   ; global_token = global_token->next

	LOAD64_RCX_from_ABS32 &function             ; Using function
	LOAD64_into_RCX_from_Address_RCX_Immediate8 !8  ; frame = function->locals

:recursive_statement_loop
	LOAD64_RBX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RBX_from_Address_RBX_Immediate8 !16 ; global_token->S
	LOADI32_RAX &close_curly_brace              ; Using "}"
	CALLI32 %match                              ; IF global_token->S == "}"
	CMP_RAX_Immediate8 !0                       ; Then we are done recursing
	JE8 !recursive_statement_cleanup            ; and then we clean up

	;; Deal with the recursive calls
	CALLI32 %statement                          ; Deal with another statement
	JMP8 !recursive_statement_loop              ; loop some more

:recursive_statement_cleanup
	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX            ; global_token->next
	STORE64_from_RAX_into_ABS32 &global_token   ; global_token = global_token->next

	LOADI32_RAX &recursive_statement_string_0   ; Using "RETURN\n"
	LOAD64_RBX_from_ABS32 &output_list          ; Using output
	LOAD64_into_RBX_from_Address_RBX_Immediate8 !16 ; output->S
	CALLI32 %match                              ; IF output->S == "RETURN\n"
	CMP_RAX_Immediate8 !0                       ; Then we can skip the clean up
	JE8 !recursive_statement_done               ; and be done

	;; Deal with cleanup
	LOAD64_RBX_from_ABS32 &function             ; Using function
	LOAD64_into_RBX_from_Address_RBX_Immediate8 !8  ; i = function->locals
	LOADI32_RAX &recursive_statement_string_1   ; Using "POP_RBX\t# _recursive_statement_locals\n"

:recursive_statement_locals
	CMP_RCX_to_RBX                              ; IF frame != i
	JE8 !recursive_statement_done               ; Otherwise be done

	;; Lets emit
	CALLI32 %emit_out                           ; emit it
	LOAD64_into_RBX_from_Address_RBX            ; i = i->next
	JMP8 !recursive_statement_locals            ; keep going

:recursive_statement_done
	LOAD64_RAX_from_ABS32 &function             ; Using function
	STORE64_RCX_into_Address_RAX_Immediate8 !8  ; function->locals = frame
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET

:recursive_statement_string_0
"RETURN
"

:recursive_statement_string_1
"POP_RBX	# _recursive_statement_locals
"


;; return_result function
;; Receives nothing
;; Returns nothing
;; Cleans up function and generates return
;; Also handles returning expressions
:return_result
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX            ; global_token->next
	STORE64_from_RAX_into_ABS32 &global_token   ; global_token = global_token->next

	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; global_token->S
	LOAD8_AL_from_Address_RAX                   ; global_token->S[0]
	ZERO_EXTEND_AL                              ; make it useful
	CMP_RAX_Immediate8 !59                      ; If global_token->S[0] == ';'
	JE8 !return_result_cleanup                  ; Go straight to cleanup

	CALLI32 %expression                         ; get the expression we are returning

:return_result_cleanup
	LOADI32_RAX &return_result_string_0         ; Using "ERROR in return_result\nMISSING ;\n"
	LOADI32_RBX &semicolon                      ; Using ";"
	CALLI32 %require_match                      ; Make sure we have it

	LOAD64_RBX_from_ABS32 &function             ; Using function
	LOAD64_into_RBX_from_Address_RBX_Immediate8 !8  ; function->locals
	LOADI32_RAX &return_result_string_1         ; Using "POP_RBX\t# _return_result_locals\n"
:return_result_locals
	CMP_RBX_Immediate8 !0                       ; IF NULL == i
	JE8 !return_result_done                     ; Be done

	CALLI32 %emit_out                           ; Emit out pop
	LOAD64_into_RBX_from_Address_RBX            ; i = i->NEXT
	JMP8 !return_result_locals                  ; Keep going

:return_result_done
	LOADI32_RAX &return_result_string_2         ; Using "RETURN\n"
	CALLI32 %emit_out                           ; Emit it
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET

:return_result_string_0
"ERROR in return_result
MISSING ;
"

:return_result_string_1
"POP_RBX	# _return_result_locals
"

:return_result_string_2
"RETURN
"


;; collect_local function
;; Receives nothing
;; Returns nothing
;; Walks global_token list to create function locals
;; Uses RCX for struct token_list* A
:collect_local
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	CALLI32 %type_name                          ; Get the local's type

	COPY_RAX_to_RBX                             ; Put struct type* type_size in the right place
	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; global_token->S
	LOAD64_RCX_from_ABS32 &function             ; Using function
	LOAD64_into_RCX_from_Address_RCX_Immediate8 !8  ; function->locals
	CALLI32 %sym_declare                        ; Declare it
	COPY_RAX_to_RCX                             ; put it away safely

	;; Try for main
	LOADI32_RAX &main_string                    ; Using "main"
	LOAD64_RBX_from_ABS32 &function             ; Using function
	LOAD64_into_RBX_from_Address_RBX_Immediate8 !16 ; function->S
	CALLI32 %match                              ; IF match("main", function->s)
	CMP_RAX_Immediate8 !0                       ; possible
	JNE8 !collect_local_fresh                   ; try to see if fresh function

	;; Ok we are in main, now to see if main is fresh
	LOAD64_RAX_from_ABS32 &function             ; Using function
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !8  ; function->locals
	CMP_RAX_Immediate8 !0                       ; IF NULL == function->locals
	JNE8 !collect_local_fresh                   ; try to see if fresh function

	;; Sweet we are in a fresh main
	LOADI32_RAX %-40                            ; We start at -40
	STORE64_RAX_into_Address_RCX_Immediate8 !32 ; a->DEPTH = -40
	JMP8 !collect_local_common                  ; Go to the commons

:collect_local_fresh
	LOAD64_RAX_from_ABS32 &function             ; Using function
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !32 ; function->args
	CMP_RAX_Immediate8 !0                       ; IF NULL == function->args
	JNE8 !collect_local_first                   ; Otherwise see if first

	LOAD64_RAX_from_ABS32 &function             ; Using function
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !8  ; function->locals
	CMP_RAX_Immediate8 !0                       ; IF NULL == function->locals
	JNE8 !collect_local_first                   ; Otherwise try first

	;; Sweet we are in a fresh function
	LOADI32_RAX %-16                             ; We start at -16
	STORE64_RAX_into_Address_RCX_Immediate8 !32 ; a->DEPTH = -16
	JMP8 !collect_local_common                  ; Go to the commons

:collect_local_first
	LOAD64_RAX_from_ABS32 &function             ; Using function
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !8  ; function->locals
	CMP_RAX_Immediate8 !0                       ; IF NULL == function->locals
	JNE8 !collect_local_else                    ; Looks like we are just another local

	;; Ok we are the first local
	LOAD64_RAX_from_ABS32 &function             ; Using function
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !32 ; function->args
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !32 ; function->args->depth
	SUBI8_RAX !16                                ; function->arguments->depth - 16
	STORE64_RAX_into_Address_RCX_Immediate8 !32 ; a->DEPTH = function->arguments->depth - 16
	JMP8 !collect_local_common                  ; Go to the commons

:collect_local_else
	;; Always the last to know
	LOAD64_RAX_from_ABS32 &function             ; Using function
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !8  ; function->locals
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !32 ; function->locals->depth
	SUBI8_RAX !8                                ; function->locals->depth - 8
	STORE64_RAX_into_Address_RCX_Immediate8 !32 ; a->DEPTH = function->locals->depth - 8

:collect_local_common
	LOAD64_RAX_from_ABS32 &function             ; Using function

	STORE64_RCX_into_Address_RAX_Immediate8 !8  ; function->locals = a
	LOAD64_into_RCX_from_Address_RCX_Immediate8 !16 ; a->S

	LOADI32_RAX &collect_local_string_0         ; Using "# Defining local "
	CALLI32 %emit_out                           ; emit it

	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; global_token->S
	CALLI32 %emit_out                           ; emit it

	LOADI32_RAX &collect_local_string_1         ; Using "\n"
	CALLI32 %emit_out                           ; emit it

	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX            ; global_token->NEXT
	STORE64_from_RAX_into_ABS32 &global_token   ; global_token = global_token->NEXT

	LOAD64_into_RBX_from_Address_RAX_Immediate8 !16 ; global_token->S
	LOADI32_RAX &equal                          ; Using "="
	CALLI32 %match                              ; IF match("=", global_token->s)
	CMP_RAX_Immediate8 !0                       ; Deal with assignment
	JNE8 !collect_local_done                    ; Otherwise finish it

	;; Deal with assignment
	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX            ; global_token->NEXT
	STORE64_from_RAX_into_ABS32 &global_token   ; global_token = global_token->NEXT

	CALLI32 %expression                         ; Recurse

:collect_local_done
	LOADI32_RAX &collect_local_string_2         ; Using "ERROR in collect_local\nMissing ;\n"
	LOADI32_RBX &semicolon                      ; Using ";"
	CALLI32 %require_match                      ; Make sure we have it

	LOADI32_RAX &collect_local_string_3         ; Using "PUSH_RAX\t#"
	CALLI32 %emit_out                           ; emit it

	COPY_RCX_to_RAX                             ; put A->S where it belongs
	CALLI32 %emit_out                           ; emit it

	LOADI32_RAX &collect_local_string_1         ; Using "\n"
	CALLI32 %emit_out                           ; emit it

	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET

:collect_local_string_0
"# Defining local "

:collect_local_string_1
"
"

:collect_local_string_2
"ERROR in collect_local
Missing ;
"

:collect_local_string_3
"PUSH_RAX	#"


;; process_asm function
;; Receives nothing
;; Returns nothing
;; Simply inlines the asm statements
;; Uses RBX for global_token temp storage
:process_asm
	PUSH_RBX                                    ; Protect RBX
	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX            ; global_token->NEXT
	STORE64_from_RAX_into_ABS32 &global_token   ; global_token = global_token->NEXT

	LOADI32_RAX &process_asm_string_0           ; Using "ERROR in process_asm\nMISSING (\n"
	LOADI32_RBX &open_paren                     ; Using "("
	CALLI32 %require_match                      ; Make sure we have it

	LOAD64_RBX_from_ABS32 &global_token         ; Using global_token
:process_asm_iter
	LOAD64_into_RAX_from_Address_RBX_Immediate8 !16 ; global_token->S
	LOAD8_AL_from_Address_RAX                   ; global_token->S[0]
	ZERO_EXTEND_AL                              ; Make it useful
	CMP_RAX_Immediate8 !34                      ; IF global_token->S[0] == '\"'
	JNE8 !process_asm_done                      ; Otherwise be done

	LOAD64_into_RAX_from_Address_RBX_Immediate8 !16 ; global_token->S
	ADDI8_to_RAX !1                             ; global_token->S + 1
	CALLI32 %emit_out                           ; Emit it

	LOADI32_RAX &process_asm_string_1           ; Using "\n"
	CALLI32 %emit_out                           ; Emit it

	LOAD64_into_RBX_from_Address_RBX            ; global_token->NEXT
	STORE64_from_RBX_into_ABS32 &global_token   ; global_token = global_token->NEXT
	JMP8 !process_asm_iter                      ; keep going

:process_asm_done
	LOADI32_RAX &process_asm_string_2           ; Using "ERROR in process_asm\nMISSING )\n"
	LOADI32_RBX &close_paren                    ; Using ")"
	CALLI32 %require_match                      ; Make sure we have it

	LOADI32_RAX &process_asm_string_3           ; Using "ERROR in process_asm\nMISSING ;\n"
	LOADI32_RBX &semicolon                      ; Using ";"
	CALLI32 %require_match                      ; Make sure we have it

	POP_RBX                                     ; Restore RBX
	RET

: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_if function
;; Receives nothing
;; Returns Nothing
;; Increments current_count recurses into expression + statement
;; Uses RCX for char* NUMBER_STRING
:process_if
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	LOAD64_RAX_from_ABS32 &current_count        ; Using current count
	COPY_RAX_to_RBX                             ; Preparing for update
	ADDI8_to_RBX !1                             ; current_count + 1
	STORE64_from_RBX_into_ABS32 &current_count  ; current_count = current_count + 1
	CALLI32 %numerate_number                    ; convert to string
	COPY_RAX_to_RCX                             ; put NUMBER_STRING in place

	LOADI32_RAX &process_if_string_0            ; Using "# IF_"
	CALLI32 %emit_out                           ; Emit it

	LOAD64_RAX_from_ABS32 &function             ; Using function
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; function->S
	COPY_RCX_to_RBX                             ; Passing NUMBER_STRING
	CALLI32 %uniqueID_out                       ; uniqueID_out(function->s, number_string)

	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX            ; global_token->NEXT
	STORE64_from_RAX_into_ABS32 &global_token   ; global_token = global_token->NEXT

	LOADI32_RAX &process_if_string_1            ; Using "ERROR in process_if\nMISSING (\n"
	LOADI32_RBX &open_paren                     ; Using "("
	CALLI32 %require_match                      ; Make sure we have it

	CALLI32 %expression                         ; Recurse to get the IF(...) part

	LOADI32_RAX &process_if_string_2            ; Using "TEST\nJUMP_EQ %ELSE_"
	CALLI32 %emit_out                           ; Emit it

	LOAD64_RAX_from_ABS32 &function             ; Using function
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; function->S
	COPY_RCX_to_RBX                             ; Passing NUMBER_STRING
	CALLI32 %uniqueID_out                       ; uniqueID_out(function->s, number_string)

	LOADI32_RAX &process_if_string_3            ; Using "ERROR in process_if\nMISSING )\n"
	LOADI32_RBX &close_paren                    ; Using ")"
	CALLI32 %require_match                      ; Make sure we have it

	CALLI32 %statement                          ; Recursive to get the IF(){...} part

	LOADI32_RAX &process_if_string_4            ; Using "JUMP %_END_IF_"
	CALLI32 %emit_out                           ; Emit it

	LOAD64_RAX_from_ABS32 &function             ; Using function
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; function->S
	COPY_RCX_to_RBX                             ; Passing NUMBER_STRING
	CALLI32 %uniqueID_out                       ; uniqueID_out(function->s, number_string)

	LOADI32_RAX &process_if_string_5            ; Using ":ELSE_"
	CALLI32 %emit_out                           ; Emit it

	LOAD64_RAX_from_ABS32 &function             ; Using function
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; function->S
	CALLI32 %uniqueID_out                       ; uniqueID_out(function->s, number_string)

	LOAD64_RBX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RBX_from_Address_RBX_Immediate8 !16 ; global_token->S
	LOADI32_RAX &else_string                    ; Using "else"
	CALLI32 %match                              ; IF global_token->S == "else"
	CMP_RAX_Immediate8 !0                       ; Then we need to collect the else too
	JNE8 !process_if_done                       ; Otherwise finish up

	;; deal with else statement
	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX            ; global_token->NEXT
	STORE64_from_RAX_into_ABS32 &global_token   ; global_token = global_token->NEXT

	CALLI32 %statement                          ; Recurse to get the ELSE {...} part

:process_if_done
	LOADI32_RAX &process_if_string_6            ; Using ":_END_IF_"
	CALLI32 %emit_out                           ; Emit it

	LOAD64_RAX_from_ABS32 &function             ; Using function
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; function->S
	COPY_RCX_to_RBX                             ; Passing NUMBER_STRING
	CALLI32 %uniqueID_out                       ; uniqueID_out(function->s, number_string)

	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET

:process_if_string_0
"# IF_"

:process_if_string_1
"ERROR in process_if
MISSING (
"

:process_if_string_2
"TEST
JUMP_EQ %ELSE_"

:process_if_string_3
"ERROR in process_if
MISSING )
"

:process_if_string_4
"JUMP %_END_IF_"

:process_if_string_5
":ELSE_"

:process_if_string_6
":_END_IF_"


;; save_break_frame microfunction
;; Overwrites RAX and RBX
;; Saves break frame on stack
;; Returns to caller
:save_break_frame
	POP_RBX                                     ; Save return Address
	LOAD64_RAX_from_ABS32 &break_frame          ; Get break_frame
	PUSH_RAX                                    ; Store as nested_locals
	LOAD64_RAX_from_ABS32 &break_target_head    ; Get break_target_head
	PUSH_RAX                                    ; Store as nested_break_head
	LOAD64_RAX_from_ABS32 &break_target_func    ; Get break_target_func
	PUSH_RAX                                    ; Store as nested_break_func
	LOAD64_RAX_from_ABS32 &break_target_num     ; Get break_target_num
	PUSH_RAX                                    ; Store as nested_break_num
	PUSH_RBX                                    ; Put return back in place
	RET                                         ; Return to caller


;; restore_break_frame microfunction
;; Overwrites RAX and RBX
;; Restores break frame from stack
;; Returns to caller
:restore_break_frame
	POP_RBX                                     ; Save return Address
	POP_RAX                                     ; Get nested_break_num
	STORE64_from_RAX_into_ABS32 &break_target_num   ; Restore break_target_num
	POP_RAX                                     ; Get nested_break_func
	STORE64_from_RAX_into_ABS32 &break_target_func  ; Restore break_target_func
	POP_RAX                                     ; Get nested_break_head
	STORE64_from_RAX_into_ABS32 &break_target_head  ; Restore break_target_head
	POP_RAX                                     ; Get nested_locals
	STORE64_from_RAX_into_ABS32 &break_frame    ; Restore break_frame
	PUSH_RBX                                    ; Put return back in place
	RET                                         ; Return to caller


;; set_break_frame microfunction
;; Receives char* head in RAX and char* num in RBX
;; Overwrites RAX and RBX
;; Returns to calling function
:set_break_frame
	STORE64_from_RAX_into_ABS32 &break_target_head  ; update break_target_head
	STORE64_from_RBX_into_ABS32 &break_target_num   ; update break_target_num
	LOAD64_RBX_from_ABS32 &function             ; Using function
	LOAD64_into_RAX_from_Address_RBX_Immediate8 !8  ; function->LOCALS
	STORE64_from_RAX_into_ABS32 &break_frame    ; break_frame = function->LOCALS
	LOAD64_into_RAX_from_Address_RBX_Immediate8 !16 ; function->S
	STORE64_from_RAX_into_ABS32 &break_target_func  ; break_target_func = function->S
	RET                                         ; Return to sender


;; process_do function
;; Receives Nothing
;; Returns Nothing
;; Increments current_count and leverages save/restore_break_frame pieces
;; Uses RCX for char* NUMBER_STRING
:process_do
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	CALLI32 %save_break_frame                   ; Save the frame

	LOAD64_RAX_from_ABS32 &current_count        ; Using current count
	COPY_RAX_to_RBX                             ; Preparing for update
	ADDI8_to_RBX !1                             ; current_count + 1
	STORE64_from_RBX_into_ABS32 &current_count  ; current_count = current_count + 1
	CALLI32 %numerate_number                    ; convert to string
	COPY_RAX_to_RCX                             ; put NUMBER_STRING in place

	LOADI32_RAX &process_do_string_0            ; Using "DO_END_"
	COPY_RCX_to_RBX                             ; Passing NUMBER_STRING
	CALLI32 %set_break_frame                    ; Set the frame

	LOADI32_RAX &process_do_string_1            ; Using ":DO_"
	CALLI32 %emit_out                           ; Emit it

	LOAD64_RAX_from_ABS32 &function             ; Using function
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; function->S
	COPY_RCX_to_RBX                             ; Passing NUMBER_STRING
	CALLI32 %uniqueID_out                       ; uniqueID_out(function->s, number_string)

	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX            ; global_token->NEXT
	STORE64_from_RAX_into_ABS32 &global_token   ; global_token = global_token->NEXT

	CALLI32 %statement                          ; Do the DO {...} part

	LOADI32_RAX &process_do_string_2            ; Using "ERROR in process_do\nMISSING while\n"
	LOADI32_RBX &while_string                   ; Using "while"
	CALLI32 %require_match                      ; Make sure we have it

	LOADI32_RAX &process_do_string_3            ; Using "ERROR in process_do\nMISSING (\n"
	LOADI32_RBX &open_paren                     ; Using "("
	CALLI32 %require_match                      ; Make sure we have it

	CALLI32 %expression                         ; Do the WHILE (...) part

	LOADI32_RAX &process_do_string_4            ; Using "ERROR in process_do\nMISSING )\n"
	LOADI32_RBX &close_paren                    ; Using ")"
	CALLI32 %require_match                      ; Make sure we have it

	LOADI32_RAX &process_do_string_5            ; Using "ERROR in process_do\nMISSING ;\n"
	LOADI32_RBX &semicolon                      ; Using ";"
	CALLI32 %require_match                      ; Make sure we have it

	LOADI32_RAX &process_do_string_6            ; Using "TEST\nJUMP_NE %DO_"
	CALLI32 %emit_out                           ; Emit it

	LOAD64_RAX_from_ABS32 &function             ; Using function
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; function->S
	COPY_RCX_to_RBX                             ; Passing NUMBER_STRING
	CALLI32 %uniqueID_out                       ; uniqueID_out(function->s, number_string)

	LOADI32_RAX &process_do_string_7            ; Using ":DO_END_"
	CALLI32 %emit_out                           ; Emit it

	LOAD64_RAX_from_ABS32 &function             ; Using function
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; function->S
	CALLI32 %uniqueID_out                       ; uniqueID_out(function->s, number_string)

	CALLI32 %restore_break_frame                ; Restore the old break frame

	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET

: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
"TEST
JUMP_NE %DO_"

:process_do_string_7
":DO_END_"


;; process_while function
;; Receives nothing
;; Returns nothing
;; Increments current_count and leverages save/restore_break_frame pieces
;; Uses RCX for char* NUMBER_STRING
:process_while
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	CALLI32 %save_break_frame                   ; Save break_frame

	LOAD64_RAX_from_ABS32 &current_count        ; Using current count
	COPY_RAX_to_RBX                             ; Preparing for update
	ADDI8_to_RBX !1                             ; current_count + 1
	STORE64_from_RBX_into_ABS32 &current_count  ; current_count = current_count + 1
	CALLI32 %numerate_number                    ; convert to string
	COPY_RAX_to_RCX                             ; put NUMBER_STRING in place

	LOADI32_RAX &process_while_string_0         ; Using "END_WHILE_"
	COPY_RCX_to_RBX                             ; Passing NUMBER_STRING
	CALLI32 %set_break_frame                    ; Set it and forget it

	LOADI32_RAX &process_while_string_1         ; Using ":WHILE_"
	CALLI32 %emit_out                           ; Emit it

	LOAD64_RAX_from_ABS32 &function             ; Using function
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; function->S
	COPY_RCX_to_RBX                             ; Passing NUMBER_STRING
	CALLI32 %uniqueID_out                       ; uniqueID_out(function->s, number_string)

	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX            ; global_token->NEXT
	STORE64_from_RAX_into_ABS32 &global_token   ; global_token = global_token->NEXT

	LOADI32_RAX &process_while_string_2         ; Using "ERROR in process_while\nMISSING (\n"
	LOADI32_RBX &open_paren                     ; Using "("
	CALLI32 %require_match                      ; Make sure we have it

	CALLI32 %expression                         ; Deal with the WHILE (...) part

	LOADI32_RAX &process_while_string_3         ; Using "TEST\nJUMP_EQ %END_WHILE_"
	CALLI32 %emit_out                           ; Emit it

	LOAD64_RAX_from_ABS32 &function             ; Using function
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; function->S
	COPY_RCX_to_RBX                             ; Passing NUMBER_STRING
	CALLI32 %uniqueID_out                       ; uniqueID_out(function->s, number_string)

	LOADI32_RAX &process_while_string_4         ; Using "# THEN_while_"
	CALLI32 %emit_out                           ; Emit it

	LOAD64_RAX_from_ABS32 &function             ; Using function
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; function->S
	CALLI32 %uniqueID_out                       ; uniqueID_out(function->s, number_string)

	LOADI32_RAX &process_while_string_5         ; Using "ERROR in process_while\nMISSING )\n"
	LOADI32_RBX &close_paren                    ; Using ")"
	CALLI32 %require_match                      ; Make sure we have it

	CALLI32 %statement                          ; Deal with the {....} part

	LOADI32_RAX &process_while_string_6         ; Using "JUMP %WHILE_"
	CALLI32 %emit_out                           ; Emit it

	LOAD64_RAX_from_ABS32 &function             ; Using function
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; function->S
	COPY_RCX_to_RBX                             ; Passing NUMBER_STRING
	CALLI32 %uniqueID_out                       ; uniqueID_out(function->s, number_string)

	LOADI32_RAX &process_while_string_7         ; Using ":END_WHILE_"
	CALLI32 %emit_out                           ; Emit it

	LOAD64_RAX_from_ABS32 &function             ; Using function
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; function->S
	CALLI32 %uniqueID_out                       ; uniqueID_out(function->s, number_string)

	CALLI32 %restore_break_frame                ; Restore the old break frame

	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET

:process_while_string_0
"END_WHILE_"

:process_while_string_1
":WHILE_"

:process_while_string_2
"ERROR in process_while
MISSING (
"

:process_while_string_3
"TEST
JUMP_EQ %END_WHILE_"

:process_while_string_4
"# THEN_while_"

:process_while_string_5
"ERROR in process_while
MISSING )
"

:process_while_string_6
"JUMP %WHILE_"

:process_while_string_7
":END_WHILE_"


;; process_for function
;; Receives Nothing
;; Returns Nothing
;; Increments current_count and leverages save/restore_break_frame pieces
;; Uses RCX for char* NUMBER_STRING
:process_for
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	CALLI32 %save_break_frame                   ; Save the frame

	LOAD64_RAX_from_ABS32 &current_count        ; Using current count
	COPY_RAX_to_RBX                             ; Preparing for update
	ADDI8_to_RBX !1                             ; current_count + 1
	STORE64_from_RBX_into_ABS32 &current_count  ; current_count = current_count + 1
	CALLI32 %numerate_number                    ; convert to string
	COPY_RAX_to_RCX                             ; put NUMBER_STRING in place

	LOADI32_RAX &process_for_string_0           ; Using "FOR_END_"
	COPY_RCX_to_RBX                             ; Passing NUMBER_STRING
	CALLI32 %set_break_frame                    ; Set it and forget it

	LOADI32_RAX &process_for_string_1           ; Using "# FOR_initialization_"
	CALLI32 %emit_out                           ; Emit it

	LOAD64_RAX_from_ABS32 &function             ; Using function
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; function->S
	COPY_RCX_to_RBX                             ; Passing NUMBER_STRING
	CALLI32 %uniqueID_out                       ; uniqueID_out(function->s, number_string)

	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX            ; global_token->NEXT
	STORE64_from_RAX_into_ABS32 &global_token   ; global_token = global_token->NEXT

	LOADI32_RAX &process_for_string_2           ; Using "ERROR in process_for\nMISSING (\n"
	LOADI32_RBX &open_paren                     ; Using "("
	CALLI32 %require_match                      ; Make Sure we have it

	LOAD64_RBX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RBX_from_Address_RBX_Immediate8 !16 ; global_token->S
	LOADI32_RAX &semicolon                      ; Using ";"
	CALLI32 %match                              ; IF global_token->S == ";"
	CMP_RAX_Immediate8 !0                       ; Then no initializer
	JE8 !process_for_terminator                 ; And skip getting the expression

	;; Deal with FOR (...; case
	CALLI32 %expression                         ; Get the FOR ( ... ; part

:process_for_terminator
	LOADI32_RAX &process_for_string_3           ; Using ":FOR_"
	CALLI32 %emit_out                           ; Emit it

	LOAD64_RAX_from_ABS32 &function             ; Using function
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; function->S
	COPY_RCX_to_RBX                             ; Passing NUMBER_STRING
	CALLI32 %uniqueID_out                       ; uniqueID_out(function->s, number_string)

	LOADI32_RAX &process_for_string_4           ; Using "ERROR in process_for\nMISSING ;1\n"
	LOADI32_RBX &semicolon                      ; Using ";"
	CALLI32 %require_match                      ; Make sure we have it

	CALLI32 %expression                         ; Get the FOR ( ; ... ; Part

	LOADI32_RAX &process_for_string_5           ; Using "TEST\nJUMP_EQ %FOR_END_"
	CALLI32 %emit_out                           ; Emit it

	LOAD64_RAX_from_ABS32 &function             ; Using function
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; function->S
	COPY_RCX_to_RBX                             ; Passing NUMBER_STRING
	CALLI32 %uniqueID_out                       ; uniqueID_out(function->s, number_string)

	LOADI32_RAX &process_for_string_6           ; Using "JUMP %FOR_THEN_"
	CALLI32 %emit_out                           ; Emit it

	LOAD64_RAX_from_ABS32 &function             ; Using function
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; function->S
	CALLI32 %uniqueID_out                       ; uniqueID_out(function->s, number_string)

	LOADI32_RAX &process_for_string_7           ; Using ":FOR_ITER_"
	CALLI32 %emit_out                           ; Emit it

	LOAD64_RAX_from_ABS32 &function             ; Using function
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; function->S
	CALLI32 %uniqueID_out                       ; uniqueID_out(function->s, number_string)

	LOADI32_RAX &process_for_string_8           ; Using "ERROR in process_for\nMISSING ;2\n"
	LOADI32_RBX &semicolon                      ; Using ";"
	CALLI32 %require_match                      ; Make sure we have it

	CALLI32 %expression                         ; Get the FOR (;;...) part

	LOADI32_RAX &process_for_string_9           ; Using "JUMP %FOR_"
	CALLI32 %emit_out                           ; Emit it

	LOAD64_RAX_from_ABS32 &function             ; Using function
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; function->S
	COPY_RCX_to_RBX                             ; Passing NUMBER_STRING
	CALLI32 %uniqueID_out                       ; uniqueID_out(function->s, number_string)

	LOADI32_RAX &process_for_string_10          ; Using ":FOR_THEN_"
	CALLI32 %emit_out                           ; Emit it

	LOAD64_RAX_from_ABS32 &function             ; Using function
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; function->S
	CALLI32 %uniqueID_out                       ; uniqueID_out(function->s, number_string)

	LOADI32_RAX &process_for_string_11          ; Using "ERROR in process_for\nMISSING )\n"
	LOADI32_RBX &close_paren                    ; Using ")"
	CALLI32 %require_match                      ; Make sure we have it

	CALLI32 %statement                          ; Get FOR (;;) {...} part

	LOADI32_RAX &process_for_string_12          ; Using "JUMP %FOR_ITER_"
	CALLI32 %emit_out                           ; Emit it

	LOAD64_RAX_from_ABS32 &function             ; Using function
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; function->S
	COPY_RCX_to_RBX                             ; Passing NUMBER_STRING
	CALLI32 %uniqueID_out                       ; uniqueID_out(function->s, number_string)

	LOADI32_RAX &process_for_string_13          ; Using ":FOR_END_"
	CALLI32 %emit_out                           ; Emit it

	LOAD64_RAX_from_ABS32 &function             ; Using function
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; function->S
	CALLI32 %uniqueID_out                       ; uniqueID_out(function->s, number_string)

	CALLI32 %restore_break_frame                ; Restore the old break frame

	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET

: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
"TEST
JUMP_EQ %FOR_END_"

:process_for_string_6
"JUMP %FOR_THEN_"

:process_for_string_7
":FOR_ITER_"

:process_for_string_8
"ERROR in process_for
MISSING ;2
"

:process_for_string_9
"JUMP %FOR_"

:process_for_string_10
":FOR_THEN_"

:process_for_string_11
"ERROR in process_for
MISSING )
"

:process_for_string_12
"JUMP %FOR_ITER_"

:process_for_string_13
":FOR_END_"


;; process_break function
;; Receives nothing
;; Returns nothing
;; Handles the break out of loops case
;; Uses RBX for struct token_list* break_frame and RCX for struct token_list* I
:process_break
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	LOAD64_RAX_from_ABS32 &break_target_head    ; Catch big error
	CMP_RAX_Immediate8 !0                       ; IF(NULL == break_target_head)
	JE32 %process_break_bad                     ; I'm sorry Mr White but you have stage-3 lung cancer

	LOAD64_RAX_from_ABS32 &function             ; Using function
	LOAD64_into_RCX_from_Address_RAX_Immediate8 !8  ; I = function->LOCALS
	LOAD64_RBX_from_ABS32 &break_frame          ; Put break_frame in the right spot
	LOADI32_RAX &process_break_string_1         ; Using "POP_RBX\t# break_cleanup_locals\n"

:process_break_iter
	CMP_RCX_Immediate8 !0                       ; IF (NULL == I)
	JE8 !process_break_cleaned                  ; We are done

	CMP_RBX_to_RCX                              ; IF I != break_frame
	JE8 !process_break_cleaned                  ; We are done

	CALLI32 %emit_out                           ; Emit it
	LOAD64_into_RCX_from_Address_RCX            ; I = I->NEXT
	JMP8 !process_break_iter                    ; Keep looping

:process_break_cleaned
	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX            ; global_token->NEXT
	STORE64_from_RAX_into_ABS32 &global_token   ; global_token = global_token->NEXT

	LOADI32_RAX &process_break_string_2         ; Using "JUMP %"
	CALLI32 %emit_out                           ; Emit it

	LOAD64_RAX_from_ABS32 &break_target_head    ; Get what we are in
	CALLI32 %emit_out                           ; Emit it

	LOAD64_RAX_from_ABS32 &break_target_func    ; Get what function we are in
	CALLI32 %emit_out                           ; Emit it

	LOADI32_RAX &underline                      ; Using "_"
	CALLI32 %emit_out                           ; Emit it

	LOAD64_RAX_from_ABS32 &break_target_num     ; Get dem digits
	CALLI32 %emit_out                           ; Emit it

	LOADI32_RAX &process_break_string_3         ; Using "\n"
	CALLI32 %emit_out                           ; Emit it

	LOADI32_RAX &process_break_string_4         ; Using "ERROR in break statement\nMissing ;\n"
	LOADI32_RBX &semicolon                      ; Using ";"
	CALLI32 %require_match                      ; Make sure we have it

	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET


:process_break_bad
	;; Breaking badly
	LOADI32_R14 %2                              ; write to standard error
;	CALLI32 %line_error                         ; Write useful debug info
	COPY_RCX_to_RAX                             ; put S in the right place
	CALLI32 %File_Print                         ; print it

	LOADI32_RAX &process_break_string_0         ; Ending string
	CALLI32 %File_Print                         ; print it
	JMP32 %Exit_Failure                         ; Abort Hard

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

:process_break_string_1
"POP_RBX	# break_cleanup_locals
"

:process_break_string_2
"JUMP %"

:process_break_string_3
"
"

:process_break_string_4
"ERROR in break statement
Missing ;
"


;; expression function
;; Receives Nothing
;; Returns Nothing
;; Walks global_token and updates output_list
;; Uses RAX and RBX for match and RCX for char* store
:expression
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	CALLI32 %bitwise_expr                       ; Collect bitwise expressions

	LOAD64_RBX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RBX_from_Address_RBX_Immediate8 !16 ; global_token->S
	LOADI32_RAX &equal                          ; "="
	CALLI32 %match                              ; IF global_token->S == "="
	CMP_RAX_Immediate8 !0                       ; We have to deal with assignment
	JNE32 %expression_done                      ; Looks like nope

	;; Deal with possible assignment
	LOADI32_RCX &expression_string_1            ; Assume "STORE_CHAR\n" by default
	LOAD64_RBX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RBX_from_Address_RBX_Immediate8 !8  ; global_token->PREV
	LOAD64_into_RBX_from_Address_RBX_Immediate8 !16 ; global_token->PREV->S
	LOADI32_RAX &close_bracket                  ; Using "]"
	CALLI32 %match                              ; IF global_token->S == "]"
	CMP_RAX_Immediate8 !0                       ; Then we might have a char
	JNE8 !expression_int                        ; Otherwise INT

	LOAD64_RBX_from_ABS32 &current_target       ; Using current_target
	LOAD64_into_RBX_from_Address_RBX_Immediate8 !48 ; current_target->NAME
	LOADI32_RAX &type_char_indirect_name        ; Using "char*"
	CALLI32 %match                              ; Intentional inefficiency because I feel like it
	CMP_RAX_Immediate8 !0                       ; IF current_target->NAME == "char*"
	JNE8 !expression_int                        ; Do char anyway

	JMP8 !expression_common                     ; Looks like we have to use "STORE_CHAR\n"

:expression_int
	LOADI32_RCX &expression_string_0            ; Use "STORE_INTEGER\n"

:expression_common
	LOADI32_RAX &expression                     ; Passing expression
	CALLI32 %common_recursion                   ; Recurse
	COPY_RCX_to_RAX                             ; Using Store
	CALLI32 %emit_out                           ; Emit it
	LOADI32_EAX %0                              ; Using NULL
	STORE64_from_RAX_into_ABS32 &current_target ; current_target = NULL

:expression_done
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET

:expression_string_0
"STORE_INTEGER
"

:expression_string_1
"STORE_CHAR
"


;; bitwise_expr function
;; Receives nothing
;; Returns nothing
;; Walks global_token list and updates output list
;; Just calls other functions
:bitwise_expr
	CALLI32 %relational_expr                    ; Walk up the tree
	CALLI32 %bitwise_expr_stub                  ; Let general recursion do the work
	RET


;; bitwise_expr_stub function
;; Receives nothing
;; Returns Nothing
;; Just calls general_recursion a bunch
;; Uses RAX, RBX, RCX and RDX for passing constants to general recursion
:bitwise_expr_stub
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	PUSH_RDX                                    ; Protect RDX

	LOADI32_RAX &relational_expr                ; Using relational_expr
	LOADI32_RBX &bitwise_expr_stub_string_0     ; Using "AND_rax_rbx\n"
	LOADI32_RCX &bitwise_and                    ; Using "&"
	LOADI32_RDX &bitwise_expr_stub              ; And recurse
	CALLI32 %general_recursion                  ; Hit it

	LOADI32_RAX &relational_expr                ; Using relational_expr
	LOADI32_RBX &bitwise_expr_stub_string_0     ; Using "AND_rax_rbx\n"
	LOADI32_RCX &logical_and                    ; Using "&&"
	LOADI32_RDX &bitwise_expr_stub              ; And recurse
	CALLI32 %general_recursion                  ; Hit it

	LOADI32_RAX &relational_expr                ; Using relational_expr
	LOADI32_RBX &bitwise_expr_stub_string_1     ; Using "OR_rax_rbx\n"
	LOADI32_RCX &bitwise_or                     ; Using "|"
	LOADI32_RDX &bitwise_expr_stub              ; And recurse
	CALLI32 %general_recursion                  ; Hit it

	LOADI32_RAX &relational_expr                ; Using relational_expr
	LOADI32_RBX &bitwise_expr_stub_string_1     ; Using "OR_rax_rbx\n"
	LOADI32_RCX &logical_or                     ; Using "||"
	LOADI32_RDX &bitwise_expr_stub              ; And recurse
	CALLI32 %general_recursion                  ; Hit it

	LOADI32_RAX &relational_expr                ; Using relational_expr
	LOADI32_RBX &bitwise_expr_stub_string_2     ; Using "XOR_rbx_rax_into_rax\n"
	LOADI32_RCX &bitwise_xor                    ; Using "^"
	LOADI32_RDX &bitwise_expr_stub              ; And recurse
	CALLI32 %general_recursion                  ; Hit it

	POP_RDX                                     ; Restore RDX
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET

:bitwise_expr_stub_string_0
"AND_rax_rbx
"

:bitwise_expr_stub_string_1
"OR_rax_rbx
"

:bitwise_expr_stub_string_2
"XOR_rbx_rax_into_rax
"


;; relational_expr function
;; Receives nothing
;; Returns Nothing
;; Walks global_token list and updates output list
;; just calls other function
:relational_expr
	CALLI32 %additive_expr                      ; Walk up the tree
	CALLI32 %relational_expr_stub               ; Recurse
	RET


;; relational_expr_stub function
;; Receives nothing
;; Returns Nothing
;; Just calls general_recursion a bunch
;; Uses RAX, RBX, RCX and RDX for passing constants to general recursion
:relational_expr_stub
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	PUSH_RDX                                    ; Protect RDX

	LOADI32_RAX &additive_expr                  ; Using additive_expr
	LOADI32_RBX &relational_expr_stub_string_0  ; Using "CMP\nSETL\nMOVEZX\n"
	LOADI32_RCX &less_than_string               ; Using "<"
	LOADI32_RDX &relational_expr_stub           ; And recurse
	CALLI32 %general_recursion                  ; Hit it

	LOADI32_RAX &additive_expr                  ; Using additive_expr
	LOADI32_RBX &relational_expr_stub_string_1  ; Using "CMP\nSETLE\nMOVEZX\n"
	LOADI32_RCX &less_than_equal_string         ; Using "<="
	LOADI32_RDX &relational_expr_stub           ; And recurse
	CALLI32 %general_recursion                  ; Hit it

	LOADI32_RAX &additive_expr                  ; Using additive_expr
	LOADI32_RBX &relational_expr_stub_string_2  ; Using "CMP\nSETGE\nMOVEZX\n"
	LOADI32_RCX &greater_than_equal_string      ; Using ">="
	LOADI32_RDX &relational_expr_stub           ; And recurse
	CALLI32 %general_recursion                  ; Hit it

	LOADI32_RAX &additive_expr                  ; Using additive_expr
	LOADI32_RBX &relational_expr_stub_string_3  ; Using "CMP\nSETG\nMOVEZX\n"
	LOADI32_RCX &greater_than_string            ; Using ">"
	LOADI32_RDX &relational_expr_stub           ; And recurse
	CALLI32 %general_recursion                  ; Hit it

	LOADI32_RAX &additive_expr                  ; Using additive_expr
	LOADI32_RBX &relational_expr_stub_string_4  ; Using "CMP\nSETE\nMOVEZBL\n"
	LOADI32_RCX &equal_to_string                ; Using "=="
	LOADI32_RDX &relational_expr_stub           ; And recurse
	CALLI32 %general_recursion                  ; Hit it

	LOADI32_RAX &additive_expr                  ; Using additive_expr
	LOADI32_RBX &relational_expr_stub_string_5  ; Using "CMP\nSETNE\nMOVEZX\n"
	LOADI32_RCX &not_equal_string               ; Using "!="
	LOADI32_RDX &relational_expr_stub           ; And recurse
	CALLI32 %general_recursion                  ; Hit it

	POP_RDX                                     ; Restore RDX
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET

:relational_expr_stub_string_0
"CMP
SETL
MOVEZX
"

:relational_expr_stub_string_1
"CMP
SETLE
MOVEZX
"

:relational_expr_stub_string_2
"CMP
SETGE
MOVEZX
"

:relational_expr_stub_string_3
"CMP
SETG
MOVEZX
"

:relational_expr_stub_string_4
"CMP
SETE
MOVEZX
"

:relational_expr_stub_string_5
"CMP
SETNE
MOVEZX
"


;; additive_expr function
;; Receives nothing
;; Returns Nothing
;; Walks global_token list and updates output list
;; just calls other function
:additive_expr
	CALLI32 %postfix_expr                       ; Walk up the tree
	CALLI32 %additive_expr_stub                 ; Recurse
	RET


;; additive_expr_stub function
;; Receives nothing
;; Returns Nothing
;; Just calls general_recursion a bunch
;; Uses RAX, RBX, RCX and RDX for passing constants to general recursion
:additive_expr_stub
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	PUSH_RDX                                    ; Protect RDX

	LOADI32_RAX &postfix_expr                   ; Using postfix_expr
	LOADI32_RBX &additive_expr_stub_string_0    ; Using "ADD_rbx_to_rax\n"
	LOADI32_RCX &plus_string                    ; Using "+"
	LOADI32_RDX &additive_expr_stub             ; And recurse
	CALLI32 %general_recursion                  ; Hit it

	LOADI32_RAX &postfix_expr                   ; Using postfix_expr
	LOADI32_RBX &additive_expr_stub_string_1    ; Using "SUBTRACT_rax_from_rbx_into_rbx\nMOVE_rbx_to_rax\n"
	LOADI32_RCX &minus_string                   ; Using "-"
	LOADI32_RDX &additive_expr_stub             ; And recurse
	CALLI32 %general_recursion                  ; Hit it

	LOADI32_RAX &postfix_expr                   ; Using postfix_expr
	LOADI32_RBX &additive_expr_stub_string_2    ; Using "MULTIPLY_rax_by_rbx_into_rax\n"
	LOADI32_RCX &multiply_string                ; Using "*"
	LOADI32_RDX &additive_expr_stub             ; And recurse
	CALLI32 %general_recursion                  ; Hit it

	LOADI32_RAX &postfix_expr                   ; Using postfix_expr
	LOADI32_RBX &additive_expr_stub_string_3    ; Using "XCHG_rax_rbx\nLOAD_IMMEDIATE_rdx %0\nDIVIDE_rax_by_rbx_into_rax\n"
	LOADI32_RCX &divide_string                  ; Using "/"
	LOADI32_RDX &additive_expr_stub             ; And recurse
	CALLI32 %general_recursion                  ; Hit it

	LOADI32_RAX &postfix_expr                   ; Using postfix_expr
	LOADI32_RBX &additive_expr_stub_string_4    ; Using "XCHG_rax_rbx\nLOAD_IMMEDIATE_rdx %0\nMODULUS_rax_from_rbx_into_rbx\nMOVE_rdx_to_rax\n"
	LOADI32_RCX &modulus_string                 ; Using "%"
	LOADI32_RDX &additive_expr_stub             ; And recurse
	CALLI32 %general_recursion                  ; Hit it

	LOADI32_RAX &postfix_expr                   ; Using postfix_expr
	LOADI32_RBX &additive_expr_stub_string_5    ; Using "COPY_rax_to_rcx\nCOPY_rbx_to_rax\nSAL_rax_cl\n"
	LOADI32_RCX &left_shift_string              ; Using "<<"
	LOADI32_RDX &additive_expr_stub             ; And recurse
	CALLI32 %general_recursion                  ; Hit it

	LOADI32_RAX &postfix_expr                   ; Using postfix_expr
	LOADI32_RBX &additive_expr_stub_string_6    ; Using "COPY_rax_to_rcx\nCOPY_rbx_to_rax\nSAR_rax_cl\n"
	LOADI32_RCX &right_shift_string             ; Using ">>"
	LOADI32_RDX &additive_expr_stub             ; And recurse
	CALLI32 %general_recursion                  ; Hit it

	POP_RDX                                     ; Restore RDX
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET

:additive_expr_stub_string_0
"ADD_rbx_to_rax
"

:additive_expr_stub_string_1
"SUBTRACT_rax_from_rbx_into_rbx
MOVE_rbx_to_rax
"

:additive_expr_stub_string_2
"MULTIPLY_rax_by_rbx_into_rax
"

:additive_expr_stub_string_3
"XCHG_rax_rbx
LOAD_IMMEDIATE_rdx %0
DIVIDE_rax_by_rbx_into_rax
"

:additive_expr_stub_string_4
"XCHG_rax_rbx
LOAD_IMMEDIATE_rdx %0
MODULUS_rax_from_rbx_into_rbx
MOVE_rdx_to_rax
"

:additive_expr_stub_string_5
"COPY_rax_to_rcx
COPY_rbx_to_rax
SAL_rax_cl
"

:additive_expr_stub_string_6
"COPY_rax_to_rcx
COPY_rbx_to_rax
SAR_rax_cl
"


;; postfix_expr function
;; Receives nothing
;; Returns Nothing
;; Walks global_token list and updates output list
;; just calls other function
:postfix_expr
	CALLI32 %primary_expr                       ; Walk up the tree
	CALLI32 %postfix_expr_stub                  ; Recurse
	RET


;; postfix_expr_stub function
;; Receives nothing
;; Returns Nothing
;; Checks for "[" and "->" and deals with them otherwise does nothing
;; Uses RAX, RBX, RCX and RDX for passing constants to general recursion
:postfix_expr_stub
	PUSH_RBX                                    ; Protect RBX
	LOAD64_RBX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RBX_from_Address_RBX_Immediate8 !16 ; global_token->S
	LOADI32_RAX &open_bracket                   ; Using "["
	CALLI32 %match                              ; IF global_token->S == "["
	CMP_RAX_Immediate8 !0                       ; then we have an array
	JNE8 !postfix_expr_stub_arrow               ; Otherwise try arrow

	;; Deal with array
	CALLI32 %postfix_expr_array                 ; Get it
	CALLI32 %postfix_expr_stub                  ; Recurse

:postfix_expr_stub_arrow
	LOADI32_RAX &arrow_string                   ; Using "->"
	CALLI32 %match                              ; IF global_token->S == "->"
	CMP_RAX_Immediate8 !0                       ; Then we need to deal with struct offsets
	JNE8 !postfix_expr_stub_done                ; Otherwise be done

	;; Deal with arrow
	CALLI32 %postfix_expr_arrow                 ; Get it
	CALLI32 %postfix_expr_stub                  ; Recurse

:postfix_expr_stub_done
	POP_RBX                                     ; Restore RBX
	RET


;; unary_expr_sizeof function
;; Receives nothing
;; Returns nothing
;; Uses RCX for A->SIZE
:unary_expr_sizeof
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX            ; global_token->NEXT
	STORE64_from_RAX_into_ABS32 &global_token   ; global_token = global_token->NEXT

	LOADI32_RAX &unary_expr_sizeof_string_0     ; Using "ERROR in unary_expr\nMissing (\n"
	LOADI32_RBX &open_paren                     ; Using "("
	CALLI32 %require_match                      ; Make sure we have it

	CALLI32 %type_name                          ; Get the type
	LOAD64_into_RCX_from_Address_RAX_Immediate8 !8  ; Set A->TYPE

	LOADI32_RAX &unary_expr_sizeof_string_1     ; Using "ERROR in unary_expr\nMissing )\n"
	LOADI32_RBX &close_paren                    ; Using ")"
	CALLI32 %require_match                      ; Make sure we have it

	LOADI32_RAX &unary_expr_sizeof_string_2     ; Using "LOAD_IMMEDIATE_rax %"
	CALLI32 %emit_out                           ; Emit it

	COPY_RCX_to_RAX                             ; Put A->SIZE in the right place
	CALLI32 %numerate_number                    ; Turn into string
	CALLI32 %emit_out                           ; Emit it

	LOADI32_RAX &unary_expr_sizeof_string_3     ; Using "\n"
	CALLI32 %emit_out                           ; Emit it

	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET

: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
"LOAD_IMMEDIATE_rax %"

:unary_expr_sizeof_string_3
"
"


;; postfix_expr_array function
;; Receives Nothing
;; Returns Nothing
;; Uses RBX for struct type* ARRAY and RCX for char* ASSIGN
:postfix_expr_array
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	LOAD64_RAX_from_ABS32 &current_target       ; ARRAY = current_target
	PUSH_RAX                                    ; Protect it

	LOADI32_RAX &expression                     ; Using expression
	CALLI32 %common_recursion                   ; Recurse

	POP_RBX                                     ; Restore array
	STORE64_from_RBX_into_ABS32 &current_target ; current_target = ARRAY

	LOADI32_RCX &postfix_expr_array_string_0    ; ASSIGN = "LOAD_INTEGER\n"

	LOADI32_RAX &type_char_indirect_name        ; Using "char*"
	LOAD64_into_RBX_from_Address_RBX_Immediate8 !48 ; current_target->NAME
	CALLI32 %match                              ; IF current_target->NAME == "char*"
	CMP_RAX_Immediate8 !0                       ; load a byte
	JNE8 !postfix_expr_array_large              ; Otherwise adjust

	;; Deal with loading byte
	LOADI32_RCX &postfix_expr_array_string_1    ; ASSIGN = "LOAD_BYTE\n"
	JMP8 !postfix_expr_array_common             ; Do the next bit

:postfix_expr_array_large
	;; deal with arrays made of things other than chars
	LOADI32_RAX &postfix_expr_array_string_2    ; Using "SAL_rax_Immediate8 !"
	CALLI32 %emit_out                           ; Emit it

	LOAD64_RAX_from_ABS32 &current_target       ; Using current_target
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !24 ; current_target->INDIRECT
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !8  ; current_target->INDIRECT->SIZE
	CALLI32 %ceil_log2                          ; ceil_log2(current_target->indirect->size)
	CALLI32 %numerate_number                    ; numerate_number(ceil_log2(current_target->indirect->size))
	CALLI32 %emit_out                           ; Emit it

	LOADI32_RAX &postfix_expr_array_string_3    ; Using "\n"
	CALLI32 %emit_out                           ; Emit it

:postfix_expr_array_common
	LOADI32_RAX &postfix_expr_array_string_4    ; Using "ADD_rbx_to_rax\n"
	CALLI32 %emit_out                           ; Emit it

	LOADI32_RAX &postfix_expr_array_string_5    ; Using "ERROR in postfix_expr\nMissing ]\n"
	LOADI32_RBX &close_bracket                  ; Using "]"
	CALLI32 %require_match                      ; Make sure we have it

	LOAD64_RBX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RBX_from_Address_RBX_Immediate8 !16 ; global_token->S
	LOADI32_RAX &equal                          ; Using "="
	CALLI32 %match                              ; IF global_token->S == "="
	CMP_RAX_Immediate8 !0                       ; We need to preserve address
	JNE8 !postfix_expr_array_done               ; Otherwise be done

	;; Clearing out assign
	LOADI32_RCX &postfix_expr_array_string_6    ; ASSIGN = ""

:postfix_expr_array_done
	COPY_RCX_to_RAX                             ; Using ASSIGN
	CALLI32 %emit_out                           ; Emit it

	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET

:postfix_expr_array_string_0
"LOAD_INTEGER
"

:postfix_expr_array_string_1
"LOAD_BYTE
"

:postfix_expr_array_string_2
"SAL_rax_Immediate8 !"

:postfix_expr_array_string_3
"
"

:postfix_expr_array_string_4
"ADD_rbx_to_rax
"

:postfix_expr_array_string_5
"ERROR in postfix_expr
Missing ]
"

:postfix_expr_array_string_6
'00'


;; ceil_log2 function
;; Receives int a in RAX
;; Performs log2 on A and
;; Returns result in RAX
;; Uses RBX for INT A and RCX for INT RESULT
:ceil_log2
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	LOADI32_RCX %0                              ; RESULT = 0

	COPY_RAX_to_RBX                             ; put A in right place
	SUBI8_RAX !1                                ; (A - 1)
	AND_RBX_into_RAX                            ; A & (A - 1)
	CMP_RAX_Immediate8 !0                       ; IF 0 == (A & (A - 1))
	JNE8 !ceil_log2_iter                        ; Starting from -1

	LOADI32_RCX %-1                             ; RESULT = -1

:ceil_log2_iter
	CMP_RBX_Immediate8 !0                       ; IF A > 0
	JLE8 !ceil_log2_done                        ; Otherwise be done

	ADDI8_to_RCX !1                             ; RESULT = RESULT + 1
	SHIFT_RIGHT_RBX_Immediate8 !1               ; A = A >> 1
	JMP8 !ceil_log2_iter                        ; Keep looping

:ceil_log2_done
	COPY_RCX_to_RAX                             ; Return RESULT
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET


;; postfix_expr_arrow function
;; Receives nothing
;; Returns nothing
;; Emits a bunch and updates current_target
;; Uses RBX for struct type* I
:postfix_expr_arrow
	PUSH_RBX                                    ; Protect RBX
	LOADI32_RAX &postfix_expr_arrow_string_0    ; Using "# looking up offset\n"
	CALLI32 %emit_out                           ; Emit it

	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX            ; global_token->NEXT
	STORE64_from_RAX_into_ABS32 &global_token   ; global_token = global_token->NEXT

	LOAD64_into_RBX_from_Address_RAX_Immediate8 !16 ; Using global_token->S
	LOAD64_RAX_from_ABS32 &current_target       ; Using current_target
	CALLI32 %lookup_member                      ; lookup_member(current_target, global_token->s)
	COPY_RAX_to_RBX                             ; struct type* I = lookup_member(current_target, global_token->s)

	LOAD64_into_RAX_from_Address_RAX_Immediate8 !40 ; I->TYPE
	STORE64_from_RAX_into_ABS32 &current_target ; current_target = I->TYPE

	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX            ; global_token->NEXT
	STORE64_from_RAX_into_ABS32 &global_token   ; global_token = global_token->NEXT

	LOAD64_into_RAX_from_Address_RBX_Immediate8 !16 ; I->OFFSET
	CMP_RAX_Immediate8 !0                       ; IF 0 != I->OFFSET
	JE8 !postfix_expr_arrow_first               ; Then we don't need to do an offset

	;; Deal with needing an offset
	LOADI32_RAX &postfix_expr_arrow_string_1    ; Using "# -> offset calculation\nLOAD_IMMEDIATE_rbx %"
	CALLI32 %emit_out                           ; Emit it

	LOAD64_into_RAX_from_Address_RBX_Immediate8 !16 ; I->OFFSET
	CALLI32 %numerate_number                    ; Convert to string
	CALLI32 %emit_out                           ; Emit it

	LOADI32_RAX &postfix_expr_arrow_string_2    ; Using "\nADD_rbx_to_rax\n"
	CALLI32 %emit_out                           ; Emit it

:postfix_expr_arrow_first
	LOAD64_into_RAX_from_Address_RBX_Immediate8 !8  ; I->SIZE
	CMP_RAX_Immediate8 !4                       ; IF I->SIZE >= 4
	JL8 !postfix_expr_arrow_done                ; Otherwise be done

	;; Last chance for load
	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RBX_from_Address_RAX_Immediate8 !16 ; global_token->S
	LOADI32_RAX &equal                          ; Using "="
	CALLI32 %match                              ; IF global_token->S == "="
	CMP_RAX_Immediate8 !0                       ; Then we have assignment and should not load
	JE8 !postfix_expr_arrow_done                ; Be done

	;; Deal with load case
	LOADI32_RAX &postfix_expr_arrow_string_3    ; Using "LOAD_INTEGER\n"
	CALLI32 %emit_out                           ; Emit it

:postfix_expr_arrow_done
	POP_RBX                                     ; Restore RBX
	RET

:postfix_expr_arrow_string_0
"# looking up offset
"

:postfix_expr_arrow_string_1
"# -> offset calculation
LOAD_IMMEDIATE_rbx %"

:postfix_expr_arrow_string_2
"
ADD_rbx_to_rax
"

:postfix_expr_arrow_string_3
"LOAD_INTEGER
"


;; primary_expr function
;; Receives nothing
;; Returns nothing
:primary_expr
	PUSH_RBX                                    ; Protect RBX

	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RBX_from_Address_RAX_Immediate8 !16 ; global_token->S
	LOADI32_RAX &sizeof_string                  ; Using "sizeof"
	CALLI32 %match                              ; See if match
	CMP_RAX_Immediate8 !0                       ; IF match
	JNE8 !primary_expr_neg                      ; Otherwise try negatives

	;; Deal with sizeof
	CALLI32 %unary_expr_sizeof                  ; Lets do this
	JMP32 %primary_expr_done                    ; Be done

:primary_expr_neg
	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; global_token->S
	LOAD8_AL_from_Address_RAX                   ; global_token->S[0]
	ZERO_EXTEND_AL                              ; Make it useful
	CMP_RAX_Immediate8 !45                      ; IF global_token->S[0] == "-"
	JNE8 !primary_expr_not                      ; Otherwise try logical NOT

	;; Deal with negative numbers
	LOADI32_RAX &primary_expr_string_0          ; Using "LOAD_IMMEDIATE_rax %0\n"
	CALLI32 %emit_out                           ; Emit it

	LOADI32_RAX &postfix_expr                   ; Passing postfix_expr
	CALLI32 %common_recursion                   ; Get what it is notting

	LOADI32_RAX &primary_expr_string_1          ; Using "SUBTRACT_rax_from_rbx_into_rbx\nMOVE_rbx_to_rax\n"
	CALLI32 %emit_out                           ; Emit it
	JMP32 %primary_expr_done                    ; Be done

:primary_expr_not
	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; global_token->S
	LOAD8_AL_from_Address_RAX                   ; global_token->S[0]
	ZERO_EXTEND_AL                              ; Make it useful
	CMP_RAX_Immediate8 !33                      ; IF global_token->S[0] == "!"
	JNE8 !primary_expr_bin                      ; Otherwise try '~'

	;; Deal with logical not
	LOADI32_RAX &primary_expr_string_2          ; Using "LOAD_IMMEDIATE_rax %1\n"
	CALLI32 %emit_out                           ; Emit it

	LOADI32_RAX &postfix_expr                   ; Passing postfix_expr
	CALLI32 %common_recursion                   ; Get what it is notting

	LOADI32_RAX &primary_expr_string_3          ; Using "XOR_rbx_rax_into_rax\n"
	CALLI32 %emit_out                           ; Emit it
	JMP32 %primary_expr_done                    ; Be done

:primary_expr_bin
	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; global_token->S
	LOAD8_AL_from_Address_RAX                   ; global_token->S[0]
	ZERO_EXTEND_AL                              ; Make it useful
	CMP_RAX_Immediate8 !126                     ; IF global_token->S[0] == "~"
	JNE8 !primary_expr_paren                    ; Otherwise try paren

	;; Deal with binary NOT
	LOADI32_RAX &postfix_expr                   ; Passing postfix_expr
	CALLI32 %common_recursion                   ; Get what it is notting
	LOADI32_RAX &primary_expr_string_4          ; Using "NOT_rax\n"
	CALLI32 %emit_out                           ; Emit it
	JMP32 %primary_expr_done                    ; Be done

:primary_expr_paren
	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; global_token->S
	LOAD8_AL_from_Address_RAX                   ; global_token->S[0]
	ZERO_EXTEND_AL                              ; Make it useful
	CMP_RAX_Immediate8 !40                      ; IF global_token->S[0] == "("
	JNE8 !primary_expr_ch                       ; Otherwise try char

	;; deal with nesting
	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX            ; global_token->NEXT
	STORE64_from_RAX_into_ABS32 &global_token   ; global_token = global_token->NEXT
	CALLI32 %expression                         ; Lets recurse
	LOADI32_RAX &primary_expr_string_5          ; Using "Error in Primary expression\nDidn't get )\n"
	LOADI32_RBX &close_paren                    ; Using ")"
	CALLI32 %require_match                      ; Make sure we have it
	JMP32 %primary_expr_done                    ; Be done

:primary_expr_ch
	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; global_token->S
	LOAD8_AL_from_Address_RAX                   ; global_token->S[0]
	ZERO_EXTEND_AL                              ; Make it useful
	CMP_RAX_Immediate8 !39                      ; Using "'"
	JNE8 !primary_expr_str                      ; Otherwise try string

	CALLI32 %primary_expr_char                  ; Handle that char
	JMP32 %primary_expr_done                    ; Be done

:primary_expr_str
	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; global_token->S
	LOAD8_AL_from_Address_RAX                   ; global_token->S[0]
	ZERO_EXTEND_AL                              ; Make it useful
	CMP_RAX_Immediate8 !34                      ; Using '\"'
	JNE8 !primary_expr_var                      ; Otherwise try a variable

	;; Deal with strings
	CALLI32 %primary_expr_string                ; Handle that string
	JMP8 !primary_expr_done                     ; Be done

:primary_expr_var
	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; global_token->S
	LOAD8_AL_from_Address_RAX                   ; global_token->S[0]
	ZERO_EXTEND_AL                              ; Make it useful
	LOADI32_RBX &primary_expr_string_6          ; Using "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"
	CALLI32 %In_Set                             ; See if we have a match
	CMP_RAX_Immediate8 !1                       ; IF match
	JNE8 !primary_expr_num                      ; otherwise try number

	;; Deal with variables
	CALLI32 %primary_expr_variable              ; Deal with variable
	JMP8 !primary_expr_done                     ; Be done

:primary_expr_num
	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; global_token->S
	LOAD8_AL_from_Address_RAX                   ; global_token->S[0]
	ZERO_EXTEND_AL                              ; Make it useful
	LOADI32_RBX &primary_expr_string_7          ; Using "0123456789"
	CALLI32 %In_Set                             ; See if we have a match
	CMP_RAX_Immediate8 !1                       ; IF match
	JNE8 !primary_expr_fail                     ; otherwise we failed hard

	;; Deal with numbers
	CALLI32 %primary_expr_number                ; Collect the number
	JMP8 !primary_expr_done                     ; Be done

:primary_expr_fail
	;; looks like we hit bad input
	;; abort before it gets bad
	CALLI32 %primary_expr_failure               ; No match means failure
:primary_expr_done
	POP_RBX                                     ; Restore RBX
	RET

:primary_expr_string_0
"LOAD_IMMEDIATE_rax %0
"

:primary_expr_string_1
"SUBTRACT_rax_from_rbx_into_rbx
MOVE_rbx_to_rax
"

:primary_expr_string_2
"LOAD_IMMEDIATE_rax %1
"

:primary_expr_string_3
"XOR_rbx_rax_into_rax
"

:primary_expr_string_4
"NOT_rax
"

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

:primary_expr_string_6
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"

:primary_expr_string_7
"0123456789"


;; primary_expr_variable function
;; Receives nothing
;; Returns nothing
;; Walks global and updates output
;; Uses RAX for struct token_list* a and RCX for char* S
:primary_expr_variable
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RCX_from_Address_RAX_Immediate8 !16 ; S = global_token->S
	LOAD64_into_RAX_from_Address_RAX            ; global_token->NEXT
	STORE64_from_RAX_into_ABS32 &global_token   ; global_token = global_token->NEXT

	COPY_RCX_to_RAX                             ; Using S
	LOAD64_RBX_from_ABS32 &global_constant_list ; Using global_constant_list
	CALLI32 %sym_lookup                         ; sym_lookup(s, global_constant_list)
	CMP_RAX_Immediate8 !0                       ; IF NULL == sym_lookup(s, global_constant_list)
	JE8 !primary_expr_variable_local            ; Try locals next

	;; Deal with constant load
	LOAD64_into_RBX_from_Address_RAX_Immediate8 !32 ; a->ARGS
	LOADI32_RAX &primary_expr_variable_string_2 ; Using "LOAD_IMMEDIATE_rax %"
	CALLI32 %emit_out                           ; Emit it

	LOAD64_into_RAX_from_Address_RBX_Immediate8 !16 ; a->ARGS->S
	CALLI32 %emit_out                           ; Emit it

	LOADI32_RAX &primary_expr_variable_string_1 ; Using "\n"
	CALLI32 %emit_out                           ; Emit it
	JMP32 %primary_expr_variable_done           ; Be done

:primary_expr_variable_local
	COPY_RCX_to_RAX                             ; Using S
	LOAD64_RBX_from_ABS32 &function             ; Using function
	LOAD64_into_RBX_from_Address_RBX_Immediate8 !8  ; function->locals
	CALLI32 %sym_lookup                         ; sym_lookup(s, function->locals)
	CMP_RAX_Immediate8 !0                       ; IF NULL == sym_lookup(s, function->locals)
	JE8 !primary_expr_variable_arguments        ; try arguments next

	;; Deal with local load
	CALLI32 %variable_load                      ; Collect it
	JMP32 %primary_expr_variable_done           ; Be done

:primary_expr_variable_arguments
	COPY_RCX_to_RAX                             ; Using S
	LOAD64_RBX_from_ABS32 &function             ; Using function
	LOAD64_into_RBX_from_Address_RBX_Immediate8 !32 ; function->args
	CALLI32 %sym_lookup                         ; sym_lookup(s, function->args)
	CMP_RAX_Immediate8 !0                       ; IF NULL == sym_lookup(s, function->args)
	JE8 !primary_expr_variable_function         ; try functions next

	;; Deal with argument load
	CALLI32 %variable_load                      ; Collect it
	JMP8 !primary_expr_variable_done            ; Be done

:primary_expr_variable_function
	COPY_RCX_to_RAX                             ; Using S
	LOAD64_RBX_from_ABS32 &global_function_list ; Using global_function_list
	CALLI32 %sym_lookup                         ; sym_lookup(s, global_function_list)
	CMP_RAX_Immediate8 !0                       ; IF NULL == sym_lookup(s, global_function_list)
	JE8 !primary_expr_variable_global           ; try globals next

	;; Deal with functions
	CALLI32 %function_load                      ; Deal with the function
	JMP8 !primary_expr_variable_done            ; Be done

:primary_expr_variable_global
	COPY_RCX_to_RAX                             ; Using S
	LOAD64_RBX_from_ABS32 &global_symbol_list   ; Using global_symbol_list
	CALLI32 %sym_lookup                         ; sym_lookup(s, global_symbol_list)
	CMP_RAX_Immediate8 !0                       ; IF NULL == sym_lookup(s, global_symbol_list)
	JE8 !primary_expr_variable_error            ; Give up

	;; Deal with globals
	CALLI32 %global_load                        ; Collect that global
	JMP8 !primary_expr_variable_done            ; Be done

:primary_expr_variable_error
	LOADI32_R14 %2                              ; write to standard error
;	CALLI32 %line_error                         ; Write useful debug info
	COPY_RCX_to_RAX                             ; put S in the right place
	CALLI32 %File_Print                         ; print it

	LOADI32_RAX &primary_expr_variable_string_0 ; Ending string
	CALLI32 %File_Print                         ; print it
	JMP32 %Exit_Failure                         ; Abort Hard

:primary_expr_variable_done
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET

:primary_expr_variable_string_0
" is not a defined symbol
"

:primary_expr_variable_string_1
"
"

:primary_expr_variable_string_2
"LOAD_IMMEDIATE_rax %"


;; function_call function
;; Receives char* S in RAX and int BOOL in RBX
;; Builds stack frames before and tears them down after function calls
;; Uses RCX for char* S, RDX for int BOOL, RSI for PASSED
:function_call
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	PUSH_RDX                                    ; Protect RDX
	PUSH_RSI                                    ; Protect RSI
	COPY_RAX_to_RCX                             ; Put S in place
	COPY_RBX_to_RDX                             ; Put BOOL in place
	LOADI32_RSI %0                              ; PASSED = 0

	LOADI32_RAX &function_call_string_0         ; Using "ERROR in process_expression_list\nNo ( was found\n"
	LOADI32_RBX &open_paren                     ; Using "("
	CALLI32 %require_match                      ; Make sure we have it

	LOADI32_RAX &function_call_string_1         ; Using "PUSH_RDI\t# Prevent overwriting in recursion\n"
	CALLI32 %emit_out                           ; Emit it

	LOADI32_RAX &function_call_string_2         ; Using "PUSH_RBP\t# Protect the old base pointer\n"
	CALLI32 %emit_out                           ; Emit it

	LOADI32_RAX &function_call_string_3         ; Using "COPY_RSP_to_RDI\t# Copy new base pointer\n"
	CALLI32 %emit_out                           ; Emit it

	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; global_token->S
	LOAD8_AL_from_Address_RAX                   ; global_token->S[0]
	ZERO_EXTEND_AL                              ; Make it useful
	CMP_RAX_Immediate8 !41                      ; IF global_token->S[0] == ")"
	JE8 !function_call_gen_done                 ; Then no arguments to send

	;; looks like we have arguments to collect
	CALLI32 %expression                         ; Collect the argument

	LOADI32_RAX &function_call_string_4         ; Using "PUSH_RAX\t#_process_expression1\n"
	CALLI32 %emit_out                           ; Emit it
	LOADI32_RSI %1                              ; PASSED = 1

:function_call_gen_iter
	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; global_token->S
	LOAD8_AL_from_Address_RAX                   ; global_token->S[0]
	ZERO_EXTEND_AL                              ; Make it useful
	CMP_RAX_Immediate8 !44                      ; IF global_token->S[0] == ","
	JNE8 !function_call_gen_done                ; Otherwise we are done

	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX            ; global_token->NEXT
	STORE64_from_RAX_into_ABS32 &global_token   ; global_token = global_token->NEXT

	CALLI32 %expression                         ; Collect the argument

	LOADI32_RAX &function_call_string_5         ; Using "PUSH_RAX\t#_process_expression2\n"
	CALLI32 %emit_out                           ; Emit it
	ADDI8_to_RSI !1                             ; PASSED = PASSED + 1
	JMP8 !function_call_gen_iter                ; Keep trying

:function_call_gen_done
	;; All is collected
	LOADI32_RAX &function_call_string_6         ; Using "ERROR in process_expression_list\nNo ) was found\n"
	LOADI32_RBX &close_paren                    ; Using ")"
	CALLI32 %require_match                      ; Make sure we have it

	CMP_RDX_Immediate8 !0                       ; IF(BOOL == TRUE)
	JNE8 !function_call_static                  ; Otherwise it is a static call

	;; Deal with a passed function pointer
	LOADI32_RAX &function_call_string_7         ; Using "LOAD_BASE_ADDRESS_rax %"
	CALLI32 %emit_out                           ; Emit it

	COPY_RCX_to_RAX                             ; Using S
	CALLI32 %emit_out                           ; Emit it

	LOADI32_RAX &function_call_string_8         ; Using "\nLOAD_INTEGER\n"
	CALLI32 %emit_out                           ; Emit it

	LOADI32_RAX &function_call_string_9         ; Using "COPY_rdi_to_rbp\n"
	CALLI32 %emit_out                           ; Emit it

	LOADI32_RAX &function_call_string_10        ; Using "CALL_rax\n"
	CALLI32 %emit_out                           ; Emit it

	LOADI32_RAX &function_call_string_13        ; Using "POP_RBX\t# _process_expression_locals\n"
	JMP8 !function_call_cleanup                 ; Clean up

:function_call_static
	;; Deal with fixed function name
	LOADI32_RAX &function_call_string_9         ; Using "COPY_rdi_to_rbp\n"
	CALLI32 %emit_out                           ; Emit it

	LOADI32_RAX &function_call_string_11        ; Using "CALL_IMMEDIATE %FUNCTION_"
	CALLI32 %emit_out                           ; Emit it

	COPY_RCX_to_RAX                             ; Using S
	CALLI32 %emit_out                           ; Emit it

	LOADI32_RAX &function_call_string_12        ; Using "\n"
	CALLI32 %emit_out                           ; Emit it

	LOADI32_RAX &function_call_string_13        ; Using "POP_RBX\t# _process_expression_locals\n"

:function_call_cleanup
	CMP_RSI_Immediate8 !0                       ; IF PASSED > 0
	JLE8 !function_call_done                    ; Otherwise be done

	;; The desired string is already in RAX
	CALLI32 %emit_out                           ; Emit it

	SUBI8_RSI !1                                ; PASSED = PASSED - 1
	JMP8 !function_call_cleanup                 ; Keep going

:function_call_done
	LOADI32_RAX &function_call_string_14        ; Using "POP_RBP\t# Restore old base pointer\n"
	CALLI32 %emit_out                           ; Emit it

	LOADI32_RAX &function_call_string_15        ; Using "POP_RDI\t# Prevent overwrite\n"
	CALLI32 %emit_out                           ; Emit it

	POP_RSI                                     ; Restore RSI
	POP_RDX                                     ; Restore RDX
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET

:function_call_string_0
"ERROR in process_expression_list
No ( was found
"

:function_call_string_1
"PUSH_RDI	# Prevent overwriting in recursion
"

:function_call_string_2
"PUSH_RBP	# Protect the old base pointer
"

:function_call_string_3
"COPY_RSP_to_RDI	# Copy new base pointer
"

:function_call_string_4
"PUSH_RAX	#_process_expression1
"

:function_call_string_5
"PUSH_RAX	#_process_expression2
"

:function_call_string_6
"ERROR in process_expression_list
No ) was found
"

:function_call_string_7
"LOAD_BASE_ADDRESS_rax %"

:function_call_string_8
"
LOAD_INTEGER
"

:function_call_string_9
"COPY_rdi_to_rbp
"

:function_call_string_10
"CALL_rax
"

:function_call_string_11
"CALL_IMMEDIATE %FUNCTION_"

:function_call_string_12
"
"

:function_call_string_13
"POP_RBX	# _process_expression_locals
"

:function_call_string_14
"POP_RBP	# Restore old base pointer
"

:function_call_string_15
"POP_RDI	# Prevent overwrite
"


;; variable_load function
;; Receives struct token_list* A in RAX
;; Returns nothing
;; Updates output and current_target
;; Uses RCX for A
:variable_load
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	COPY_RAX_to_RCX                             ; Protect A

	LOAD64_RBX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RBX_from_Address_RBX_Immediate8 !16 ; global_token->S
	LOADI32_RAX &open_paren                     ; Using "("
	CALLI32 %match                              ; IF global_token->S == "("
	CMP_RAX_Immediate8 !0                       ; Then it might be a function
	JNE8 !variable_load_regular                 ; Otherwise it is regular

	LOAD64_into_RBX_from_Address_RCX_Immediate8 !24 ; A->TYPE
	LOAD64_into_RBX_from_Address_RBX_Immediate8 !48 ; A->TYPE->NAME
	LOADI32_RAX &type_function_name             ; Using "FUNCTION"
	CALLI32 %match                              ; IF A->TYPE->NAME == "FUNCTION"
	CMP_RAX_Immediate8 !0                       ; Then it must be a function
	JNE8 !variable_load_regular                 ; otherwise just another regular

	;; deal with function
	LOAD64_into_RAX_from_Address_RCX_Immediate8 !32 ; A->DEPTH
	CALLI32 %numerate_number                    ; Convert to string
	LOADI32_EBX %0                              ; pass 0 for true
	CALLI32 %function_call                      ; Create the function call
	JMP8 !variable_load_done                    ; Be done

:variable_load_regular
	LOAD64_into_RAX_from_Address_RCX_Immediate8 !24 ; A->TYPE
	STORE64_from_RAX_into_ABS32 &current_target ; current_target = A->TYPE

	LOADI32_RAX &variable_load_string_0         ; Using "LOAD_BASE_ADDRESS_rax %"
	CALLI32 %emit_out                           ; Emit it

	LOAD64_into_RAX_from_Address_RCX_Immediate8 !32 ; A->DEPTH
	CALLI32 %numerate_number                    ; Convert to string
	CALLI32 %emit_out                           ; Emit it

	LOADI32_RAX &variable_load_string_1         ; Using "\n"
	CALLI32 %emit_out                           ; Emit it

	;; Check for special case of assignment
	LOAD64_RBX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RBX_from_Address_RBX_Immediate8 !16 ; global_token->S
	LOADI32_RAX &equal                          ; Using "="
	CALLI32 %match                              ; IF global_token->S == "="
	CMP_RAX_Immediate8 !0                       ; Then we skip loading
	JE8 !variable_load_done                     ; And be done

	;; Deal with common case
	LOADI32_RAX &variable_load_string_2         ; Using "LOAD_INTEGER\n"
	CALLI32 %emit_out                           ; Emit it

:variable_load_done
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET

:variable_load_string_0
"LOAD_BASE_ADDRESS_rax %"

:variable_load_string_1
"
"

:variable_load_string_2
"LOAD_INTEGER
"


;; function_load function
;; Receives struct token_list* a in RAX
;; Returns nothing
;; Uses RCX to hold A->S
:function_load
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; A->S
	COPY_RAX_to_RCX                             ; Protect A->S
	LOAD64_RBX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RBX_from_Address_RBX_Immediate8 !16 ; global_token->S
	LOADI32_RAX &open_paren                     ; Using "("
	CALLI32 %match                              ; IF global_token->S == "("
	CMP_RAX_Immediate8 !0                       ; The we need to do a function call
	JNE8 !function_load_regular                 ; Otherwise just load it's address

	;; Deal with function call
	COPY_RCX_to_RAX                             ; Using A->S
	LOADI32_EBX %1                              ; Using FALSE
	CALLI32 %function_call                      ; Deal with it
	JMP8 !function_load_done                    ; Be done

:function_load_regular
	LOADI32_RAX &function_load_string_0         ; Using "LOAD_IMMEDIATE_rax &FUNCTION_"
	CALLI32 %emit_out                           ; Emit it

	COPY_RCX_to_RAX                             ; Using A->S
	CALLI32 %emit_out                           ; Emit it

	LOADI32_RAX &function_load_string_1         ; Using "\n"
	CALLI32 %emit_out                           ; Emit it

:function_load_done
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET

:function_load_string_0
"LOAD_IMMEDIATE_rax &FUNCTION_"

:function_load_string_1
"
"


;; global_load function
;; Receives struct token_list* A in RAX
;; Returns nothing
;; Uses RBX to hold A->S
:global_load
	PUSH_RBX                                    ; Protect RBX
	COPY_RAX_to_RBX                             ; Set as A
	LOAD64_into_RBX_from_Address_RBX_Immediate8 !16 ; Set as A->S

	LOAD64_into_RAX_from_Address_RAX_Immediate8 !24 ; A->TYPE
	STORE64_from_RAX_into_ABS32 &current_target ; current_target = A->TYPE

	LOADI32_RAX &global_load_string_0           ; Using "LOAD_IMMEDIATE_rax &GLOBAL_"
	CALLI32 %emit_out                           ; Emit it

	COPY_RBX_to_RAX                             ; Using A->S
	CALLI32 %emit_out                           ; Emit it

	LOADI32_RAX &global_load_string_1           ; Using "\n"
	CALLI32 %emit_out                           ; Emit it

	LOAD64_RBX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RBX_from_Address_RBX_Immediate8 !16 ; global_token->S
	LOADI32_RAX &equal                          ; "="
	CALLI32 %match                              ; IF global_token->S == "="
	CMP_RAX_Immediate8 !0                       ; We need to skip for assignment
	JE8 !global_load_done                       ; and be done

	;; Otherwise we are loading the contents
	LOADI32_RAX &global_load_string_2           ; Using "LOAD_INTEGER\n"
	CALLI32 %emit_out                           ; Emit it

:global_load_done
	POP_RBX                                     ; Restore RBX
	RET

:global_load_string_0
"LOAD_IMMEDIATE_rax &GLOBAL_"

:global_load_string_1
"
"

:global_load_string_2
"LOAD_INTEGER
"


;; sym_lookup function
;; Receives char* S in RAX and struct token_list* symbol_list in RBX
;; Uses I->S in RAX, S in RBX and I in RCX
;; Returns match or NULL
:sym_lookup
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	COPY_RBX_to_RCX                             ; I = symbol_list
	COPY_RAX_to_RBX                             ; Put S in the right place
:sym_lookup_iter
	CMP_RCX_Immediate8 !0                       ; IF NULL == I
	JE8 !sym_lookup_done                        ; We failed to find match

	LOAD64_into_RAX_from_Address_RCX_Immediate8 !16 ; Using I->S
	CALLI32 %match                              ; IF I->S == S
	CMP_RAX_Immediate8 !0                       ; then be done
	JE8 !sym_lookup_done                        ; Failed

	LOAD64_into_RCX_from_Address_RCX            ; I = I->NEXT
	JMP8 !sym_lookup_iter                       ; otherwise keep looping

:sym_lookup_done
	COPY_RCX_to_RAX                             ; Return I
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET


;; 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
	LOADI32_RAX &primary_expr_number_string_0   ; Using "LOAD_IMMEDIATE_rax %"
	CALLI32 %emit_out                           ; Emit it

	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; global_token->S
	CALLI32 %emit_out                           ; Emit it

	LOADI32_RAX &primary_expr_number_string_1   ; Using "\n"
	CALLI32 %emit_out                           ; Emit it

	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX            ; global_token->NEXT
	STORE64_from_RAX_into_ABS32 &global_token   ; global_token = global_token->NEXT
	RET

:primary_expr_number_string_0
"LOAD_IMMEDIATE_rax %"

:primary_expr_number_string_1
"
"


;; primary_expr_string function
;; receives nothing
;; Returns nothing
;; creates entries for string and calls to generate string output
;; uses RCX for char* number_string
:primary_expr_string
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	LOAD64_RBX_from_ABS32 &current_count        ; Using current_count
	COPY_RBX_to_RAX                             ; And putting it in the right place
	CALLI32 %numerate_number                    ; Get the string
	COPY_RAX_to_RCX                             ; protect number_string

	ADDI8_to_RBX !1                             ; current_count + 1
	STORE64_from_RBX_into_ABS32 &current_count  ; current_count = current_count + 1

	LOADI32_RAX &primary_expr_string_string_0   ; Using "LOAD_IMMEDIATE_rax &STRING_"
	CALLI32 %emit_out                           ; Emit it

	LOAD64_RAX_from_ABS32 &function             ; Using function
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; function->S
	COPY_RCX_to_RBX                             ; Put number_string in the right place
	CALLI32 %uniqueID_out                       ; Make it unique

	;; Generate the target
	LOADI32_RAX &primary_expr_string_string_1   ; Using ":STRING_"
	LOAD64_RBX_from_ABS32 &strings_list         ; Using strings_list
	CALLI32 %emit                               ; Emit it
	COPY_RAX_to_RBX                             ; put new strings_list in place

	LOAD64_RAX_from_ABS32 &function             ; Using function
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; function->S
	CALLI32 %uniqueID                           ; Make it unique
	COPY_RAX_to_RBX                             ; put new strings_list in place

	;; Parse the string
	LOAD64_RAX_from_ABS32 &global_token         ; Using global token
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; global_token->S
	CALLI32 %parse_string                       ; convert to useful form
	CALLI32 %emit                               ; Emit it
	STORE64_from_RAX_into_ABS32 &strings_list   ; Update Strings _list

	LOAD64_RAX_from_ABS32 &global_token         ; Using global token
	LOAD64_into_RAX_from_Address_RAX            ; global_token->NEXT
	STORE64_from_RAX_into_ABS32 &global_token   ; global_token = global_token->NEXT

	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET

:primary_expr_string_string_0
"LOAD_IMMEDIATE_rax &STRING_"

:primary_expr_string_string_1
":STRING_"


;; primary_expr_char function
;; Receives nothing
;; Returns nothing
;; Updates output_list using global_token
:primary_expr_char
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	LOADI32_RAX &primary_expr_char_string_0     ; Using "LOAD_IMMEDIATE_rax %"
	CALLI32 %emit_out                           ; Emit it

	LOAD64_RAX_from_ABS32 &global_token         ; Using global token
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; global_token->S
	ADDI8_to_RAX !1                             ; global_token->S + 1
	CALLI32 %escape_lookup                      ; Get the char
	CALLI32 %numerate_number                    ; Convert to string
	CALLI32 %emit_out                           ; emit it

	LOADI32_RAX &primary_expr_char_string_1     ; Using "\n"
	CALLI32 %emit_out                           ; Emit it

	LOAD64_RAX_from_ABS32 &global_token         ; Using global token
	LOAD64_into_RAX_from_Address_RAX            ; global_token->NEXT
	STORE64_from_RAX_into_ABS32 &global_token   ; global_token = global_token->NEXT
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET

:primary_expr_char_string_0
"LOAD_IMMEDIATE_rax %"

:primary_expr_char_string_1
"
"


;; primary_expr_failure function
;; Receives nothing
;; Does not return but aborts hard
;; Complains about the bad input
:primary_expr_failure
;	CALLI32 %line_error                         ; Get line of issue
	LOADI32_R14 %2                              ; write to standard error
	LOADI32_RAX &primary_expr_failure_string_0  ; Using "Received "
	CALLI32 %File_Print                         ; Print it

	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; global_token->S
	CALLI32 %File_Print                         ; Print it

	LOADI32_RAX &primary_expr_failure_string_1  ; Using " in primary_expr\n"
	CALLI32 %File_Print                         ; Print it
	JMP32 %Exit_Failure                         ; Abort Hard

:primary_expr_failure_string_0
"Received "

:primary_expr_failure_string_1
" in primary_expr
"


;; general_recursion function
;; Receives FUNCTION F in RAX, char* S in RBX, char* name in RCX and FUNCTION iterate in RDX
;; Returns nothing
;; Uses RCX for char* S, RDX for FUNCTION iterate and RBP for FUNCTION F
;; But generally recurses a shitload
:general_recursion
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	PUSH_RDX                                    ; Protect RDX
	PUSH_RBP                                    ; Protect RBP
	COPY_RAX_to_RBP                             ; Protect F
	COPY_RCX_to_RAX                             ; Put name in the right place
	COPY_RBX_to_RCX                             ; Protect S

	LOAD64_RBX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RBX_from_Address_RBX_Immediate8 !16 ; global_token->S
	CALLI32 %match                              ; IF match(name, global_token->s)
	CMP_RAX_Immediate8 !0                       ; If true we do
	JNE8 !general_recursion_done                ; Otherwise skip it

	;; Deal with the recursion
	COPY_RBP_to_RAX                             ; Put F in the right place
	CALLI32 %common_recursion                   ; Recurse

	COPY_RCX_to_RAX                             ; Put S in the right place
	CALLI32 %emit_out                           ; Emit it

	COPY_RDX_to_RAX                             ; Put iterate in the right place
	CALL_RAX                                    ; Down the rabbit hole

:general_recursion_done
	POP_RBP                                     ; Restore RBP
	POP_RDX                                     ; Restore RDX
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET


;; promote_type function
;; Receives struct type* a in RAX and struct type* b in RBX
;; Returns the most recent type in RAX
;; Uses RAX for struct type* I, RCX for struct type* A and RDX for struct type* B
:promote_type
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	PUSH_RDX                                    ; Protect RDX
	CMP_RBX_Immediate8 !0                       ; IF NULL == B
	JE8 !promote_type_done                      ; Just return A

	COPY_RAX_to_RCX                             ; Put A in place
	COPY_RBX_to_RDX                             ; Put B in place
	COPY_RBX_to_RAX                             ; IF NULL == A
	CMP_RCX_Immediate8 !0                       ; Then we just return B
	JE8 !promote_type_done                      ; Be done

	;; Looks like we need to walk the list
	LOAD64_into_RCX_from_Address_RCX_Immediate8 !48 ; A->NAME
	LOAD64_into_RDX_from_Address_RDX_Immediate8 !48 ; B->NAME
	LOAD64_RAX_from_ABS32 &global_types         ; I = global_types
:promote_type_iter
	CMP_RAX_Immediate8 !0                       ; IF NULL == I
	JE8 !promote_type_done                      ; Just be done

	LOAD64_into_RBX_from_Address_RAX_Immediate8 !48 ; I->NAME
	CMP_RCX_to_RBX                              ; IF(A->NAME == I->NAME)
	JE8 !promote_type_done                      ; Be done

	CMP_RDX_to_RBX                              ; IF(B->NAME == I->NAME)
	JE8 !promote_type_done                      ; Be done

	LOAD64_into_RBX_from_Address_RAX_Immediate8 !24 ; I->INDIRECT
	LOAD64_into_RBX_from_Address_RBX_Immediate8 !48 ; I->INDIRECT->NAME

	CMP_RCX_to_RBX                              ; IF(A->NAME == I->INDIRECT->NAME)
	JE8 !promote_type_done                      ; Be done

	CMP_RDX_to_RBX                              ; IF(B->NAME == I->INDIRECT->NAME)
	JE8 !promote_type_done                      ; Be done

	LOAD64_into_RAX_from_Address_RAX            ; I = I->NEXT
	JMP8 !promote_type_iter                     ; Keep going

:promote_type_done
	POP_RDX                                     ; Restore RDX
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET


;; common_recursion function
;; Receives FUNCTION F in RAX
;; Returns Nothing
;; Walks global_token list and update output_list
;; Updates current_target
;; Uses RBX to hold FUNCTION F and struct type* last_type
:common_recursion
	PUSH_RBX                                    ; Protect RBX
	COPY_RAX_to_RBX                             ; Put FUNCTION F safely out of the way
	LOADI32_RAX &common_recursion_string_0      ; Using "PUSH_RAX\t#_common_recursion\n"
	CALLI32 %emit_out                           ; Emit it

	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX            ; global_token->NEXT
	STORE64_from_RAX_into_ABS32 &global_token   ; global_token = global_token->NEXT

	COPY_RBX_to_RAX                             ; Prepare for function call
	LOAD64_RBX_from_ABS32 &current_target       ; Get last type
	CALL_RAX                                    ; F();
	LOAD64_RAX_from_ABS32 &current_target       ; Get current_target
	CALLI32 %promote_type                       ; get the right type
	STORE64_from_RAX_into_ABS32 &current_target ; Set new current_target

	LOADI32_RAX &common_recursion_string_1      ; Using "POP_RBX\t# _common_recursion\n"
	CALLI32 %emit_out                           ; Emit it
	POP_RBX                                     ; Restore RBX
	RET

:common_recursion_string_0
"PUSH_RAX	#_common_recursion
"

:common_recursion_string_1
"POP_RBX	# _common_recursion
"


;; require_match function
;; Receives char* message in RAX and char* required in RBX
;; Returns nothing
;; Uses RCX to hold message and updates global_token
:require_match
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	COPY_RAX_to_RCX                             ; put the message somewhere safe
	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; global_token->S
	CALLI32 %match                              ; IF required == global_token->S
	CMP_RAX_Immediate8 !0                       ; we are fine
	JE8 !require_match_good                     ; otherwise pain

	;; Deal with bad times
;	CALLI32 %line_error                         ; Tell user what went wrong
	LOADI32_R14 %2                              ; write to standard error
	COPY_RCX_to_RAX                             ; using our message
	CALLI32 %File_Print                         ; Print it
	JMP32 %Exit_Failure                         ; Abort HARD

:require_match_good
	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX            ; global_token->next
	STORE64_from_RAX_into_ABS32 &global_token   ; global_token = global_token->next
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET


;; uniqueID Function
;; Receives char *S in RAX, struct token_list* l in RBX and char* num in RCX
;; Returns updated struct token_list* L in RAX
:uniqueID
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	CALLI32 %emit                               ; emit(s, l)
	COPY_RAX_to_RBX                             ; Put L in correct place
	LOADI32_RAX &underline                      ; Using "_"
	CALLI32 %emit                               ; emit("_", l)
	COPY_RAX_to_RBX                             ; Put L in correct place
	COPY_RCX_to_RAX                             ; Put num in correct place
	CALLI32 %emit                               ; emit(num, l)
	COPY_RAX_to_RBX                             ; Put L in correct place
	LOADI32_RAX &uniqueID_string_0              ; Using "\n"
	CALLI32 %emit                               ; emit("\n", l)
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET

:uniqueID_string_0
"
"


;; uniqueID_out function
;; Receives char* S in RAX and char* num in RBX
;; Returns nothing
:uniqueID_out
	PUSH_RAX                                    ; Protect RAX
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	COPY_RBX_to_RCX                             ; Put num in right spot
	LOAD64_RBX_from_ABS32 &output_list          ; Using output_list
	CALLI32 %uniqueID                           ; Get updated list
	STORE64_from_RAX_into_ABS32 &output_list    ; output_list = uniqueID(s, output_list, num)
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	POP_RAX                                     ; Restore RAX
	RET


;; emit_out function
;; Receives char* S in RAX
;; Returns nothing
;; Updates output_list
;; MUST NOT ALTER REGISTERS
:emit_out
	PUSH_RAX                                    ; Protect RAX
	PUSH_RBX                                    ; Protect RBX
	LOAD64_RBX_from_ABS32 &output_list          ; Using output_list
	CALLI32 %emit                               ; emit it
	STORE64_from_RAX_into_ABS32 &output_list    ; update it
	POP_RBX                                     ; Restore RBX
	POP_RAX                                     ; Restore RAX
	RET


;; emit function
;; Receives char *s in RAX and struct token_list* head in RBX
;; Returns struct token_list* T in RAX
:emit
	PUSH_RCX                                    ; Protect RCX
	COPY_RAX_to_RCX                             ; put S out of the way
	LOADI32_EAX %40                             ; sizeof(struct token_list)
	CALLI32 %malloc                             ; get T
	STORE64_RBX_into_Address_RAX                ; t->next = head;
	STORE64_RCX_into_Address_RAX_Immediate8 !16 ; t->s = s;
	POP_RCX                                     ; Restore RCX
	RET


;; escape_lookup function
;; Receives char* c in RAX
;; Returns integer value of char in RAX
;; Aborts hard if unknown escape is received
;; Uses RCX to hold char* C
:escape_lookup
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	COPY_RAX_to_RCX                             ; Put char* C in safe place
	LOAD8_AL_from_Address_RCX                   ; Load c[0]
	ZERO_EXTEND_AL                              ; make it useful
	CMP_RAX_Immediate8 !92                      ; If '\\' != c[0]
	JNE8 !escape_lookup_done                    ; Be done

	COPY_RCX_to_RBX                             ; Prepare for walk
	ADDI8_to_RBX !1                             ; increment
	LOAD8_BL_from_Address_RBX                   ; load c[1]
	ZERO_EXTEND_BL                              ; make it useful

	CMP_RBX_Immediate8 !120                     ; Check if \x??
	JE8 !escape_lookup_hex                      ; Deal with hex

	;; Deal with \? escapes
	LOADI32_EAX %10                             ; Guess "\n"
	CMP_RBX_Immediate8 !110                     ; If n
	JE8 !escape_lookup_done                     ; Be done

	LOADI32_EAX %9                              ; Guess "\t"
	CMP_RBX_Immediate8 !116                     ; If t
	JE8 !escape_lookup_done                     ; Be done

	COPY_RBX_to_RAX                             ; "\\", "'" and '\"' all encode as themselves
	CMP_RBX_Immediate8 !92                      ; If "\\"
	JE8 !escape_lookup_done                     ; Be done
	CMP_RBX_Immediate8 !39                      ; IF "'"
	JE8 !escape_lookup_done                     ; Be done
	CMP_RBX_Immediate8 !34                      ; IF '\"'
	JE8 !escape_lookup_done                     ; Be done

	LOADI32_EAX %13                             ; Guess "\r"
	CMP_RBX_Immediate8 !114                     ; IF r
	JE8 !escape_lookup_done                     ; Be done

	;; Looks like we have no clue what we are doing
	;; Aborting hard
	LOADI32_R14 %2                              ; write to standard error
	LOADI32_RAX &escape_lookup_string_0         ; Using "Unknown escape received: "
	CALLI32 %File_Print                         ; Print it
	COPY_RCX_to_RAX                             ; Using C
	CALLI32 %File_Print                         ; Print it
	LOADI32_RAX &escape_lookup_string_1         ; Using " Unable to process\n"
	CALLI32 %File_Print                         ; Print it
	JMP32 %Exit_Failure                         ; Abort Hard

:escape_lookup_done
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET

:escape_lookup_hex
	;; Give up on C and just assume they know what they are doing
	ADDI8_to_RCX !2                             ; increment
	LOAD8_AL_from_Address_RCX                   ; c[2]
	ZERO_EXTEND_AL                              ; make it useful
	ADDI8_to_RCX !1                             ; increment
	CALLI32 %char2hex                           ; Get the hex value
	SHIFT_LEFT_RAX_Immediate8 !4                ; c << 4
	LOAD8_BL_from_Address_RCX                   ; c[3]
	ZERO_EXTEND_BL                              ; make it useful
	SWAP_RAX_RBX                                ; protect c << 4
	CALLI32 %char2hex                           ; Get the hex value
	ADD_RBX_to_RAX                              ; hex(c[2]) << 4 + hex(c[3])
	JMP8 !escape_lookup_done                    ; Be done

:escape_lookup_string_0
"Unknown escape received: "

:escape_lookup_string_1
" Unable to process
"


;; char2hex function
;; Receives char in RAX
;; Returns hex or aborts hard
:char2hex
	SUBI8_RAX !48                               ; Try 0-9
	CMP_RAX_Immediate8 !10                      ; Otherwise fun times
	JL8 !char2hex_done                          ; Be done

	;; Deal with A-F
	ANDI32_RAX %0xDF                            ; Unset High bit turning a-f into A-F
	SUBI8_RAX !7                                ; Shift down into position
	CMP_RAX_Immediate8 !10                      ; Everything below A is bad
	JL8 !char2hex_fail                          ; And should fail
	CMP_RAX_Immediate8 !16                      ; Make sure we are below F
	JL8 !char2hex_done                          ; If so be done

:char2hex_fail
	;; Time to fail hard
	LOADI32_R14 %2                              ; write to standard error
	LOADI32_RAX &char2hex_string_0              ; Using "Tried to print non-hex number\n"
	CALLI32 %File_Print                         ; Print it
	JMP32 %Exit_Failure                         ; Abort Hard

:char2hex_done
	RET

:char2hex_string_0
"Tried to print non-hex number
"


;; parse_string function
;; Receives char* string in RAX
;; Returns cleaned up string
;; Protects char* string in RBX
:parse_string
	PUSH_RBX                                    ; Protect RBX
	COPY_RAX_to_RBX                             ; Protect char* string
	CALLI32 %weird                              ; Determine if we have a weird string
	CMP_RAX_Immediate8 !0                       ; If weird
	JE8 !parse_string_weird                     ; Deal with it

	;; Dealing with regular string
	COPY_RBX_to_RAX                             ; Passing Char* string
	CALLI32 %collect_regular_string             ; Collect it
	JMP8 !parse_string_done                     ; Be done

:parse_string_weird
	COPY_RBX_to_RAX                             ; Passing Char* string
	CALLI32 %collect_weird_string               ; Collect it

:parse_string_done
	POP_RBX                                     ; Restore RBX
	RET


;; weird function
;; Receives char* string in RAX
;; Returns true(0) or false(1) in RAX
;; Uses RCX to hold char* string
:weird
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	COPY_RAX_to_RCX                             ; Place string in safe place
	ADDI8_to_RCX !1                             ; increment past the '\"'
:weird_reset
	LOAD8_AL_from_Address_RCX                   ; Load a char
	ZERO_EXTEND_AL                              ; Make it useful
	CMP_RAX_Immediate8 !0                       ; IF NULL == C
	JE8 !weird_false                            ; Nothing weird found

	CMP_RAX_Immediate8 !92                      ; IF '\\'
	JNE8 !weird_escaped                         ; Deal with escaping

	;; Deal with escape
	COPY_RCX_to_RAX                             ; We are passing the string
	CALLI32 %escape_lookup                      ; to look it up

	ADDI8_to_RCX !1                             ; string = string + 1
	LOAD8_BL_from_Address_RCX                   ; get string[1]
	ZERO_EXTEND_BL                              ; make it useful
	CMP_RBX_Immediate8 !120                     ; IF 'x' == string[1]
	JNE8 !weird_escaped                         ; otherwise skip the gap

	ADDI8_to_RCX !2                             ; string = string + 2

:weird_escaped
	PUSH_RAX                                    ; Protect C in case we need it
	LOADI32_RBX &weird_string_0                 ; Use "\t\n !#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
	CALLI32 %In_Set                             ; To find if weird
	CMP_RAX_Immediate8 !1                       ; IF TRUE
	POP_RAX                                     ; Restore C
	JNE8 !weird_true                            ; Then not weird

	ADDI8_to_RCX !1                             ; string = string + 1

	;; Last chance for weird
	LOADI32_RBX &weird_string_1                 ; Use "\t\n\r "
	CALLI32 %In_Set                             ; Check for special case
	CMP_RAX_Immediate8 !1                       ; IF TRUE
	JNE8 !weird_reset                           ; Otherwise not in the special case

	;; Deal with possible special case
	LOAD8_AL_from_Address_RCX                   ; Load string[1]
	ZERO_EXTEND_AL                              ; Make it useful
	CMP_RAX_Immediate8 !58                      ; IF string[1] == ":"
	JE8 !weird_true                             ; Then we hit the special case
	JMP8 !weird_reset                           ; Keep trying

:weird_done
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET

:weird_true
	LOADI32_EAX %0                              ; Return true
	JMP8 !weird_done                            ; Be done

:weird_false
	LOADI32_EAX %1                              ; Return false
	JMP8 !weird_done                            ; Be done

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

:weird_string_1
'09 0a 0d 20 00'


;; collect_regular_string function
;; Receives char* string in RAX
;; Malloc and creates new string to return in RAX
;; Uses RCX for return string and RDX for passed string
:collect_regular_string
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	PUSH_RDX                                    ; Protect RDX
	COPY_RAX_to_RDX                             ; Protect our passed string
	LOADI32_EAX %256                            ; We need 256bytes of storage
	CALLI32 %malloc                             ; Get our new pointer
	COPY_RAX_to_RCX                             ; put it in place
	PUSH_RAX                                    ; protect until done
:collect_regular_string_reset
	LOAD8_AL_from_Address_RDX                   ; string[0]
	ZERO_EXTEND_AL                              ; Make it useful
	CMP_RAX_Immediate8 !0                       ; See if we hit the end
	JE8 !collect_regular_string_done            ; And be done

	CMP_RAX_Immediate8 !92                      ; IF string[0] == '\\'
	JE8 !collect_regular_string_escaped         ; Deal with that mess

	;; deal with boring char
	STORE8_AL_into_Address_RCX                  ; hold_string[index] = string[0]
	ADDI8_to_RCX !1                             ; Increment it
	ADDI8_to_RDX !1                             ; Increment it
	JMP8 !collect_regular_string_reset          ; And keep going

:collect_regular_string_escaped
	COPY_RDX_to_RAX                             ; Using string
	CALLI32 %escape_lookup                      ; Get the char
	STORE8_AL_into_Address_RCX                  ; hold_string[index] = escape_lookup(string)
	ADDI8_to_RDX !1                             ; Increment it
	ADDI8_to_RCX !1                             ; Increment it
	LOAD8_AL_from_Address_RDX                   ; string[0]
	ZERO_EXTEND_AL                              ; Make it useful
	ADDI8_to_RDX !1                             ; Increment it
	CMP_RAX_Immediate8 !120                     ; IF 'x' == string[1]
	JNE8 !collect_regular_string_reset          ; Otherwise keep going

	ADDI8_to_RDX !2                             ; Increment it
	JMP8 !collect_regular_string_reset          ; Keep going

:collect_regular_string_done
	LOADI32_EAX %34                             ; Using '\"'
	STORE8_AL_into_Address_RCX                  ; hold_string[index] = '\"'
	ADDI8_to_RCX !1                             ; Increment it
	LOADI32_EAX %10                             ; Using "\n"
	STORE8_AL_into_Address_RCX                  ; hold_string[index] = '\n'
	POP_RAX                                     ; Return our new string
	POP_RDX                                     ; Restore RDX
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET


;; collect_weird_string function
;; Receives char* string in RAX
;; Mallocs and returns char* hold in RAX
;; Uses RCX for char* hold and RDX for char* string
:collect_weird_string
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	PUSH_RDX                                    ; Protect RDX
	COPY_RAX_to_RDX                             ; Protect our passed string
	LOADI32_EAX %512                            ; We need 512bytes of storage
	CALLI32 %malloc                             ; Get our new pointer
	COPY_RAX_to_RCX                             ; put it in place
	PUSH_RAX                                    ; protect until done

	LOADI32_EAX %39                             ; Using "'"
	STORE8_AL_into_Address_RCX                  ; hold_string[index] = "'"
	ADDI8_to_RCX !1                             ; Increment it
	ADDI8_to_RDX !1                             ; Increment it
:collect_weird_string_reset
	LOAD8_AL_from_Address_RDX                   ; Read a byte
	ZERO_EXTEND_AL                              ; Make it useful
	CMP_RAX_Immediate8 !0                       ; IF NULL == string[0]
	JE8 !collect_weird_string_done              ; Be done

	LOADI32_EAX %32                             ; Using ' '
	STORE8_AL_into_Address_RCX                  ; hold_string[index] = ' '
	ADDI8_to_RCX !1                             ; Increment it

	COPY_RDX_to_RAX                             ; Using string
	CALLI32 %escape_lookup                      ; Get the char
	CALLI32 %hex8                               ; Update RCX

	LOAD8_AL_from_Address_RDX                   ; Read a byte
	ZERO_EXTEND_AL                              ; Make it useful
	ADDI8_to_RDX !1                             ; Increment it
	CMP_RAX_Immediate8 !92                      ; IF string[0] == '\\'
	JNE8 !collect_weird_string_reset            ; Otherwise keep going

	LOAD8_AL_from_Address_RDX                   ; Read a byte
	ZERO_EXTEND_AL                              ; Make it useful
	ADDI8_to_RDX !1                             ; Increment it
	CMP_RAX_Immediate8 !120                     ; IF 'x' == string[1]
	JNE8 !collect_weird_string_reset            ; Otherwise keep going

	ADDI8_to_RDX !2                             ; Increment it
	JMP8 !collect_weird_string_reset            ; Keep going

:collect_weird_string_done
	LOADI32_EAX %32                             ; Using ' '
	STORE8_AL_into_Address_RCX                  ; hold_string[index] = ' '
	ADDI8_to_RCX !1                             ; Increment it
	LOADI32_EAX %48                             ; Using '0'
	STORE8_AL_into_Address_RCX                  ; hold_string[index] = '0'
	ADDI8_to_RCX !1                             ; Increment it
	STORE8_AL_into_Address_RCX                  ; hold_string[index] = '0'
	ADDI8_to_RCX !1                             ; Increment it
	LOADI32_EAX %39                             ; Using "'"
	STORE8_AL_into_Address_RCX                  ; hold_string[index] = "'"
	ADDI8_to_RCX !1                             ; Increment it
	LOADI32_EAX %10                             ; Using "\n"
	STORE8_AL_into_Address_RCX                  ; hold_string[index] = '\n'
	POP_RAX                                     ; Return our new string
	POP_RDX                                     ; Restore RDX
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET


;; HEX to ascii routine
;; Receives INT in RAX and CHAR* in RCX
;; Stores ascii of INT in CHAR*
;; Returns only modifying RAX and RCX
:hex8
	PUSH_RAX                                    ; Protect bottom nibble
	SHIFT_RIGHT_RAX_Immediate8 !4               ; do high nibble first
	CALLI32 %hex4                               ; Store it
	POP_RAX                                     ; do low nibble
:hex4
	ANDI32_RAX %0xF                             ; isolate nibble
	ADDI8_to_RAX !48                            ; convert to ascii
	CMP_AL_Immediate8 !57                       ; valid digit?
	JBE8 !hex1                                  ; yes
	ADDI8_to_RAX !7                             ; use alpha range
:hex1
	STORE8_AL_into_Address_RCX                  ; store result
	ADDI8_to_RCX !1                             ; next position
	RET


;; type_name function
;; Receives nothing
;; Returns type_size in RAX
;; Uses RCX for STRUCT TYPE* RET
:type_name
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	LOAD64_RBX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RBX_from_Address_RBX_Immediate8 !16 ; global_token->S
	LOADI32_RAX &struct                         ; Using "struct"
	CALLI32 %match                              ; IF global_token->S == "struct"
	COPY_RAX_to_RCX                             ; Protect structure
	CMP_RAX_Immediate8 !0                       ; need to skip over "struct"
	JNE8 !type_name_native                      ; otherwise keep going

	;; Deal with possible STRUCTs
	LOAD64_RBX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RBX_from_Address_RBX            ; global_token->next
	STORE64_from_RBX_into_ABS32 &global_token   ; global_token = global_token->next
	LOAD64_into_RAX_from_Address_RBX_Immediate8 !16 ; global_token->S
	LOAD64_RBX_from_ABS32 &global_types         ; get all known types
	CALLI32 %lookup_type                        ; Find type if possible
	COPY_RAX_to_RCX                             ; Set ret

	CMP_RAX_Immediate8 !0                       ; IF NULL == ret
	JNE8 !type_name_common                      ; We have to create struct

	;; Create a struct
	CALLI32 %create_struct                      ; Create a new struct
	LOADI32_RCX %0                              ; We wish to return NULL
	JMP32 %type_name_done                       ; be done

:type_name_native
	;; Deal only with native types
	COPY_RBX_to_RAX                             ; Put global_token->S in the right place
	LOAD64_RBX_from_ABS32 &global_types         ; get all known types
	CALLI32 %lookup_type                        ; Find the type if possible
	COPY_RAX_to_RCX                             ; Set ret

	CMP_RAX_Immediate8 !0                       ; IF NULL == ret
	JNE8 !type_name_common                      ; We need to abort hard

	;; Aborting hard
	LOADI32_R14 %2                              ; write to standard error
	LOADI32_RAX &type_name_string_0             ; Print header
	CALLI32 %File_Print                         ; Print it

	LOAD64_RAX_from_ABS32 &global_token         ; Using global token
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; global_token->S
	CALLI32 %File_Print                         ; Print it

	LOADI32_RAX &type_name_string_1             ; Print footer
	CALLI32 %File_Print                         ; Print it

;	CALLI32 %line_error                         ; Give details
	JMP32 %Exit_Failure                         ; Abort

:type_name_common
	LOAD64_RBX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RBX_from_Address_RBX            ; global_token->next
	STORE64_from_RBX_into_ABS32 &global_token   ; global_token = global_token->next

:type_name_iter
	LOAD64_into_RAX_from_Address_RBX_Immediate8 !16 ; global_token->S
	LOAD8_AL_from_Address_RAX                   ; global_token->S[0]
	ZERO_EXTEND_AL                              ; make it useful
	CMP_RAX_Immediate8 !42                      ; IF global_token->S[0] == '*'
	JNE8 !type_name_done                        ; recurse

	;; Deal with char**
	LOAD64_into_RCX_from_Address_RCX_Immediate8 !24 ; ret = ret->indirect
	LOAD64_RBX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RBX_from_Address_RBX            ; global_token->next
	STORE64_from_RBX_into_ABS32 &global_token   ; global_token = global_token->next
	JMP8 !type_name_iter                        ; keep looping

:type_name_done
	COPY_RCX_to_RAX                             ; put ret in the right place
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET

:type_name_string_0
"Unknown type "

:type_name_string_1
"
"


;; lookup_type function
;; Receives char* s in RAX and struct type* start in RBX
;; Returns struct type* in RAX
;; Uses RBX for S and RCX for I
:lookup_type
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	COPY_RBX_to_RCX                             ; I = Start
	COPY_RAX_to_RBX                             ; Put S in place
:lookup_type_iter
	CMP_RCX_Immediate8 !0                       ; Check if I == NULL
	JE8 !lookup_type_done                       ; return NULL

	LOAD64_into_RAX_from_Address_RCX_Immediate8 !48 ; I->NAME
	CALLI32 %match                              ; Check if matching
	CMP_RAX_Immediate8 !0                       ; IF I->NAME == S
	JE8 !lookup_type_done                       ; return it

	LOAD64_into_RCX_from_Address_RCX            ; Otherwise I = I->NEXT
	JMP8 !lookup_type_iter                      ; And keep looping

:lookup_type_done
	COPY_RCX_to_RAX                             ; return either I or NULL
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET


;; create_struct function
;; Receives nothing
;; Returns nothing
;; Uses global_token to malloc a struct's definition
;; Uses RCX for int OFFSET, RDX for struct type* head, RBP for struct type* I,
;; RDI for member_size (Which is passed) and RSI for LAST
;; RAX and RBX are used for scratch
:create_struct
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	PUSH_RDX                                    ; Protect RDX
	PUSH_RBP                                    ; Protect RBP
	PUSH_RDI                                    ; Protect RDI
	PUSH_RSI                                    ; Protect RSI
	LOADI32_RCX %0                              ; OFFSET = 0
	LOADI32_EDI %0                              ; member_size = 0

	LOADI32_EAX %56                             ; sizeof(struct type)
	CALLI32 %malloc                             ; malloc(sizeof(struct type))
	COPY_RAX_to_RDX                             ; Set HEAD

	LOADI32_EAX %56                             ; sizeof(struct type)
	CALLI32 %malloc                             ; malloc(sizeof(struct type))
	COPY_RAX_to_RBP                             ; Set I

	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; global_token->S
	STORE64_RAX_into_Address_RDX_Immediate8 !48 ; HEAD->NAME = global_token->S
	STORE64_RAX_into_Address_RBP_Immediate8 !48 ; I->NAME = global_token->S

	STORE64_RBP_into_Address_RDX_Immediate8 !24 ; HEAD->INDIRECT = I
	STORE64_RDX_into_Address_RBP_Immediate8 !24 ; I->INDIRECT = HEAD

	LOAD64_RAX_from_ABS32 &global_types         ; Using global_types
	STORE64_RAX_into_Address_RDX                ; HEAD->NEXT = global_types
	STORE64_from_RDX_into_ABS32 &global_types   ; global_types = HEAD

	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX            ; global_token->NEXT
	STORE64_from_RAX_into_ABS32 &global_token   ; global_token = global_token->NEXT

	LOADI32_EAX %8                              ; Using register size
	STORE64_RAX_into_Address_RBP_Immediate8 !8  ; I->SIZE = register size

	LOADI32_RAX &create_struct_string_0         ; Using "ERROR in create_struct\n Missing {\n"
	LOADI32_RBX &open_curly_brace               ; Using "{"
	CALLI32 %require_match                      ; Make sure we have it

	LOADI32_RSI %0                              ; LAST = NULL

:create_struct_iter
	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; global_token->S
	LOAD8_AL_from_Address_RAX                   ; global_token->S[0]
	ZERO_EXTEND_AL                              ; Make it useful
	CMP_RAX_Immediate8 !125                     ; IF global_token->S[0] == "}"
	JE32 %create_struct_done                    ; be done

	;; Looks like we are adding members
	;; Lets see if it is a union
	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; global_token->S
	LOADI32_RBX &union                          ; Using "union"
	CALLI32 %match                              ; IF match(global_token->s, "union")
	CMP_RAX_Immediate8 !0                       ; Deal with union
	JNE8 !create_struct_single                  ; Otherwise deal with singles

	;; Deal with union
	COPY_RSI_to_RAX                             ; Put last in right place
	COPY_RCX_to_RBX                             ; put offset in right place
	CALLI32 %build_union                        ; ASSEMBLE
	COPY_RAX_to_RSI                             ; last = build_union(last, offset)
	ADD_RDI_to_RCX                              ; offset = offset + member_size

	LOADI32_RAX &create_struct_string_1         ; Using "ERROR in create_struct\n Missing ;\n"
	LOADI32_RBX &semicolon                      ; Using ";"
	CALLI32 %require_match                      ; Make sure we have it
	JMP8 !create_struct_iter                    ; keep going

:create_struct_single
	;; deal with singles
	COPY_RSI_to_RAX                             ; Put last in right place
	COPY_RCX_to_RBX                             ; put offset in right place
	CALLI32 %build_member                       ; ASSEMBLE
	COPY_RAX_to_RSI                             ; last = build_union(last, offset)
	ADD_RDI_to_RCX                              ; offset = offset + member_size

	LOADI32_RAX &create_struct_string_1         ; Using "ERROR in create_struct\n Missing ;\n"
	LOADI32_RBX &semicolon                      ; Using ";"
	CALLI32 %require_match                      ; Make sure we have it
	JMP32 %create_struct_iter                   ; keep going

:create_struct_done
	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX            ; global_token->NEXT
	STORE64_from_RAX_into_ABS32 &global_token   ; global_token = global_token->NEXT

	LOADI32_RAX &create_struct_string_1         ; Using "ERROR in create_struct\n Missing ;\n"
	LOADI32_RBX &semicolon                      ; Using ";"
	CALLI32 %require_match                      ; Make sure we have it

	STORE64_RCX_into_Address_RDX_Immediate8 !8  ; HEAD->SIZE = OFFSET
	STORE64_RSI_into_Address_RDX_Immediate8 !32 ; HEAD->MEMBERS = LAST
	STORE64_RSI_into_Address_RBP_Immediate8 !32 ; I->MEMBERS = LAST

	POP_RSI                                     ; Restore RSI
	POP_RDI                                     ; Restore RDI
	POP_RBP                                     ; Restore RBP
	POP_RDX                                     ; Restore RDX
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET

:create_struct_string_0
"ERROR in create_struct
 Missing {
"

:create_struct_string_1
"ERROR in create_struct
 Missing ;
"


;; lookup_member function
;; Receives struct type* parent in RAX and char* name in RBX
;; Returns struct type* I in RAX
;; Uses char* NAME in RBX, RCX for struct type* I and RDX to hold parent for errors
;; Aborts hard if not found
:lookup_member
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	PUSH_RDX                                    ; Protect RDX
	COPY_RAX_to_RDX                             ; Protect Parent
	LOAD64_into_RCX_from_Address_RAX_Immediate8 !32 ; struct type* I = parent->MEMBERS
:lookup_member_iter
	CMP_RCX_Immediate8 !0                       ; IF I == NULL
	JE8 !lookup_member_fail                     ; Abort HARD

	LOAD64_into_RAX_from_Address_RCX_Immediate8 !48 ; Using I->NAME
	CALLI32 %match                              ; IF I->NAME == NAME
	CMP_RAX_Immediate8 !0                       ; Then we have found the member
	COPY_RCX_to_RAX                             ; Prepare for return
	LOAD64_into_RCX_from_Address_RCX_Immediate8 !32 ; Prepare for loop I = I->MEMBERS
	JNE8 !lookup_member_iter                    ; Looks like we are looping

	;; I is already in RAX
	POP_RDX                                     ; Restore RDX
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET

:lookup_member_fail
	LOADI32_R14 %2                              ; write to standard error
	LOADI32_RAX &lookup_member_string_0         ; Using "ERROR in lookup_member "
	CALLI32 %File_Print                         ; print it

	LOAD64_into_RAX_from_Address_RDX_Immediate8 !48 ; PARENT->NAME
	CALLI32 %File_Print                         ; print it

	LOADI32_RAX &arrow_string                   ; Using "->"
	CALLI32 %File_Print                         ; print it

	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; global_token->S
	CALLI32 %File_Print                         ; print it

	LOADI32_RAX &lookup_member_string_1         ; Using " does not exist\n"
	CALLI32 %File_Print                         ; print it

;	CALLI32 %line_error                         ; Write useful debug info

	LOADI32_RAX &lookup_member_string_2         ; Using "\n"
	CALLI32 %File_Print                         ; print it
	JMP32 %Exit_Failure                         ; Abort Hard

:lookup_member_string_0
"ERROR in lookup_member "

:lookup_member_string_1
" does not exist
"

:lookup_member_string_2
"
"


;; build_member function
;; Receives struct type* last in RAX, int offset in RBX and global member_size in RDI
;; Updates member_size in RDI and returns struct type* I in RAX
;; Uses RCX for struct type* member_type and RDX for struct type* I
:build_member
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	PUSH_RDX                                    ; Protect RDX
	COPY_RAX_to_RDX                             ; Put last out of the way
	LOADI32_EAX %56                             ; Allocate type
	CALLI32 %malloc                             ; Get I
	STORE64_RDX_into_Address_RAX_Immediate8 !32 ; I->MEMBERS = LAST
	STORE64_RBX_into_Address_RAX_Immediate8 !16 ; I->OFFSET = OFFSET
	COPY_RAX_to_RDX                             ; Put I in place

	CALLI32 %type_name                          ; Get member_type
	COPY_RAX_to_RCX                             ; Put in place
	STORE64_RCX_into_Address_RDX_Immediate8 !40 ; I->TYPE = MEMBER_TYPE
	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RBX_from_Address_RAX_Immediate8 !16 ; global_token->S
	STORE64_RBX_into_Address_RDX_Immediate8 !48 ; I->NAME = global_token->S
	LOAD64_into_RAX_from_Address_RAX            ; global_token->NEXT
	STORE64_from_RAX_into_ABS32 &global_token   ; global_token = global_token->NEXT

	;; Check if we have an array
	LOAD64_into_RBX_from_Address_RAX_Immediate8 !16 ; global_token->S
	LOADI32_RAX &open_bracket                   ; Using "["
	CALLI32 %match                              ; IF global_token->S == "["
	CMP_RAX_Immediate8 !0                       ; Then we have to deal with arrays in our structs
	JE8 !build_member_array                     ; So deal with that pain

	;; Deal with non-array case
	LOAD64_into_RAX_from_Address_RCX_Immediate8 !8  ; member_type->SIZE
	STORE64_RAX_into_Address_RDX_Immediate8 !8  ; I->SIZE = member_type->SIZE
	JMP8 !build_member_done                     ; Be done

:build_member_array
	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX            ; global_token->NEXT
	STORE64_from_RAX_into_ABS32 &global_token   ; global_token = global_token->NEXT

	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; global_token->S
	CALLI32 %numerate_string                    ; convert number
	LOAD64_into_RBX_from_Address_RCX_Immediate8 !40 ; member_type->TYPE
	LOAD64_into_RBX_from_Address_RBX_Immediate8 !8  ; member_type->TYPE->SIZE
	IMUL_RBX_RAX                                ; member_type->type->size * numerate_string(global_token->s)
	STORE64_RAX_into_Address_RDX_Immediate8 !8  ; I->SIZE = member_type->type->size * numerate_string(global_token->s)

	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX            ; global_token->NEXT
	STORE64_from_RAX_into_ABS32 &global_token   ; global_token = global_token->NEXT

	LOADI32_RAX &build_member_string_0          ; Using "Struct only supports [num] form\n"
	LOADI32_RBX &close_bracket                  ; Using "]"
	CALLI32 %require_match                      ; Make sure we have it

:build_member_done
	LOAD64_into_RDI_from_Address_RDX_Immediate8 !8  ; MEMBER_SIZE = I->SIZE
	STORE64_RCX_into_Address_RDX_Immediate8 !40 ; I->TYPE = MEMBER_TYPE
	COPY_RDX_to_RAX                             ; Return I

	POP_RDX                                     ; Restore RDX
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET

:build_member_string_0
"Struct only supports [num] form
"


;; build_union function
;; Receives struct type* last in RAX, int offset in RBX and global member_size in RDI
;; Updates member_size in RDI and returns struct type* LAST in RAX
;; Uses RCX for struct type* last, RDX for int offset, RSI for int size and RDI for int member_size
:build_union
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	PUSH_RDX                                    ; Protect RDX
	PUSH_RSI                                    ; Protect RSI
	COPY_RAX_to_RCX                             ; Put LAST in right spot
	COPY_RBX_to_RDX                             ; Put OFFSET in right spot
	LOADI32_RSI %0                              ; SIZE = 0

	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX            ; global_token->NEXT
	STORE64_from_RAX_into_ABS32 &global_token   ; global_token = global_token->NEXT

	LOADI32_RAX &build_union_string_0           ; Using "ERROR in build_union\nMissing {\n"
	LOADI32_RBX &open_curly_brace               ; Using "{"
	CALLI32 %require_match                      ; Make sure we have it

:build_union_iter
	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; global_token->S
	LOAD8_AL_from_Address_RAX                   ; global_token->S[0]
	ZERO_EXTEND_AL                              ; make it useful
	CMP_RAX_Immediate8 !125                     ; IF global_token->S[0] == "}"
	JE8 !build_union_done                       ; Be done

	;; Collect union member
	COPY_RCX_to_RAX                             ; Passing LAST
	COPY_RDX_to_RBX                             ; Passing offset
	CALLI32 %build_member                       ; build_member(last, offset)
	COPY_RAX_to_RCX                             ; last = build_member(last, offset)

	CMP_RDI_to_RSI                              ; IF member_size > size
	JG8 !build_union_size                       ; Then update size

	;; deal with member_size > size
	COPY_RDI_to_RSI                             ; SIZE = MEMBER_SIZE

:build_union_size
	LOADI32_RAX &build_union_string_1           ; Using "ERROR in build_union\nMissing ;\n"
	LOADI32_RBX &semicolon                      ; Using ";"
	CALLI32 %require_match                      ; Make sure we have it
	JMP8 !build_union_iter                      ; Keep going

:build_union_done
	COPY_RSI_to_RDI                             ; MEMBER_SIZE = SIZE

	LOAD64_RAX_from_ABS32 &global_token         ; Using global_token
	LOAD64_into_RAX_from_Address_RAX            ; global_token->NEXT
	STORE64_from_RAX_into_ABS32 &global_token   ; global_token = global_token->NEXT

	COPY_RCX_to_RAX                             ; Return last

	POP_RSI                                     ; Restore RSI
	POP_RDX                                     ; Restore RDX
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET

:build_union_string_0
"ERROR in build_union
Missing {
"

:build_union_string_1
"ERROR in build_union
Missing ;
"


;; sym_declare function
;; Receives char *s in RAX, struct type* t in RBX, and struct token_list* list in RCX
;; Returns struct token_list* in RAX
;; Uses RAX for A
:sym_declare
	PUSH_RDX                                    ; Protect RDX
	COPY_RAX_to_RDX                             ; Get char *S safely out of the way
	LOADI32_EAX %40                             ; Using sizeof(struct token_list)
	CALLI32 %malloc                             ; Get pointer to A
	STORE64_RCX_into_Address_RAX                ; A->NEXT = LIST
	STORE64_RDX_into_Address_RAX_Immediate8 !16 ; A->S = S
	STORE64_RBX_into_Address_RAX_Immediate8 !24 ; A->TYPE = T
	POP_RDX                                     ; Restore RDX
	RET


;; match function
;; Receives CHAR* in RAX and CHAR* in RBX
;; Returns 0 (TRUE) or 1 (FALSE) in RAX
:match
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	PUSH_RDX                                    ; Protect RDX
	COPY_RAX_to_RCX                             ; S1 in place
	COPY_RBX_to_RDX                             ; S2 in place
:match_Loop
	LOAD8_AL_from_Address_RCX                   ; S1[0]
	ZERO_EXTEND_AL                              ; Make it useful
	LOAD8_BL_from_Address_RDX                   ; S2[0]
	ZERO_EXTEND_BL                              ; Make it useful
	CMP_RBX_to_RAX                              ; See if they match
	JNE8 !match_False                           ; If not

	ADDI8_to_RCX !1                             ; S1 = S1 + 1
	ADDI8_to_RDX !1                             ; S2 = S2 + 1
	CMP_RAX_Immediate8 !0                       ; If reached end of string
	JE8 !match_Done                             ; Perfect match
	JMP8 !match_Loop                            ; Otherwise keep looping

:match_False
	LOADI32_EAX %1                              ; Return false
:match_Done
	POP_RDX                                     ; Restore RDX
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET


;; numerate_number function
;; Receives an INT A in RAX
;; Returns char* result in RAX
;; Allocates 16 bytes of memory
;; Behaves badly when given a negative number too large
;; Uses RAX for temp, RBX for DIVISOR, RDX for mod/0, RSI for result[i] and RBP for A
:numerate_number
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	PUSH_RDX                                    ; Protect RDX
	PUSH_RSI                                    ; Protect RSI
	PUSH_RBP                                    ; Protect RBP
	COPY_RAX_to_RBP                             ; Protect A

	LOADI32_EAX %16                             ; 16bytes
	CALLI32 %malloc                             ; Get our pointer
	PUSH_RAX                                    ; Protect our pointer
	COPY_RAX_to_RSI                             ; put pointer in right place
	LOADI32_EBX %0x3B9ACA00                     ; Set divisor to largest positive number that fits in 32bits

	CMP_RBP_Immediate8 !0                       ; Deal with 0 case
	JE8 !numerate_number_ZERO                   ; If it is
	JG8 !numerate_number_positive               ; If it is positive

	;; Deal with negative case
	LOADI32_EAX %45                             ; Using "-"
	STORE8_AL_into_Address_RSI                  ; Write it
	ADDI8_to_RSI !1                             ; increment
	IMULI8_RBP !-1                              ; A = A * -1

:numerate_number_positive
	LOADI32_EDX %0                              ; Set top to 0
	COPY_RBP_to_RAX                             ; Using A as bottom
	IDIV_RBX                                    ; rdx:rax % rbx -> rdx + rdx:rax / rbx -> rax [Even if we don't want it]
	CMP_RAX_Immediate8 !0                       ; IF 0 == (a / divisor)
	JNE8 !numerate_number_iter                  ; Clean up those leading Zeros

	LOADI32_EDX %0                              ; Set top to 0
	COPY_RBX_to_RAX                             ; Using Divisor for bottom
	LOADI32_EBX %10                             ; Make this shit work because idiv 10 doesn't work
	IDIV_RBX                                    ; rdx:rax % 10 -> rdx + rdx:rax / 10 -> rax [Even if we don't want it]
	COPY_RAX_to_RBX                             ; Update divisor
	JMP8 !numerate_number_positive              ; Keep collecting

:numerate_number_iter
	CMP_RBX_Immediate8 !0                       ; IF DIVISOR < 0
	JLE8 !numerate_number_done                  ; Be done

	LOADI32_EDX %0                              ; Set top to 0
	COPY_RBP_to_RAX                             ; Using A as bottom
	IDIV_RBX                                    ; rdx:rax % rbx -> rdx + rdx:rax / rbx -> rax [Even if we don't want it]
	ADDI8_to_RAX !48                            ; ((a / divisor) + 48)
	STORE8_AL_into_Address_RSI                  ; Write it
	COPY_RDX_to_RBP                             ; a = a % divisor

	LOADI32_EDX %0                              ; Set top to 0
	COPY_RBX_to_RAX                             ; Using Divisor for bottom
	LOADI32_EBX %10                             ; Make this shit work because idiv 10 doesn't work
	IDIV_RBX                                    ; rdx:rax % 10 -> rdx + rdx:rax / 10 -> rax [Even if we don't want it]
	COPY_RAX_to_RBX                             ; Update divisor

	ADDI8_to_RSI !1                             ; increment
	JMP8 !numerate_number_iter                  ; Keep going

:numerate_number_done
	POP_RAX                                     ; Restore our result
	POP_RBP                                     ; Restore RBP
	POP_RSI                                     ; Restore RSI
	POP_RDX                                     ; Restore RDX
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET

:numerate_number_ZERO
	LOADI32_EAX %48                             ; Using '0'
	STORE8_AL_into_Address_RSI                  ; Write it
	ADDI8_to_RSI !1                             ; increment
	JMP8 !numerate_number_done                  ; Be done


;; numerate_string function
;; Receives CHAR* in RAX
;; Returns value of CHAR* in RAX
;; Uses RAX for VALUE, RBX for S, RCX for CH and RSI for NEGATIVE?
:numerate_string
	PUSH_RBX                                    ; Protect RBX
	PUSH_RCX                                    ; Protect RCX
	PUSH_RDX                                    ; Protect RDX
	PUSH_RSI                                    ; Protect RSI
	COPY_RAX_to_RBX                             ; put S in correct place
	LOADI32_EAX %0                              ; Initialize to Zero
:numerate_string_loop
	LOAD8_CL_from_Address_RBX_Immediate8 !1     ; S[1]
	ZERO_EXTEND_CL                              ; make it useful
	CMP_RCX_Immediate8 !120                     ; IF 'x' == S[1]
	JE8 !numerate_hex                           ; Deal with hex input

	;; Assume decimal input
	LOADI32_RSI %0                              ; Assume no negation
	LOAD8_CL_from_Address_RBX                   ; S[0]
	ZERO_EXTEND_CL                              ; make it useful
	CMP_RCX_Immediate8 !45                      ; IF '-' == S[0]
	JNE8 !numerate_decimal                      ; Skip negation

	LOADI32_RSI %1                              ; Set FLAG
	ADDI8_to_RBX !1                             ; S = S + 1

:numerate_decimal
	LOAD8_CL_from_Address_RBX                   ; S[0]
	ZERO_EXTEND_CL                              ; make it useful
	CMP_RCX_Immediate8 !0                       ; IF NULL == S[0]
	JE8 !numerate_decimal_done                  ; We are done

	IMULI8_RAX !10                              ; VALUE = VALUE * 10
	SUBI8_RCX !48                               ; CH = CH - '0'
	CMP_RCX_Immediate8 !9                       ; Check for illegal
	JG8 !numerate_string_fail                   ; If CH > '9'
	CMP_RCX_Immediate8 !0                       ; Check for illegal
	JL8 !numerate_string_fail                   ; IF CH < 0
	ADD_RCX_to_RAX                              ; VALUE = VALUE + CH
	ADDI8_to_RBX !1                             ; S = S + 1
	JMP8 !numerate_decimal                      ; Keep looping

:numerate_decimal_done
	CMP_RSI_Immediate8 !1                       ; Check if need to negate
	JNE8 !numerate_string_done                  ; Nope

	IMULI8_RAX !-1                              ; VALUE = VALUE * -1
	JMP8 !numerate_string_done                  ; Done

:numerate_hex
	ADDI8_to_RBX !2                             ; S = S + 2
:numerate_hex_loop
	LOAD8_CL_from_Address_RBX                   ; S[0]
	ZERO_EXTEND_CL                              ; make it useful
	CMP_RCX_Immediate8 !0                       ; IF NULL == S[0]
	JE8 !numerate_string_done                   ; We are done

	SHIFT_LEFT_RAX_Immediate8 !4                ; VALUE = VALUE << 4
	SUBI8_RCX !48                               ; CH = CH - '0'
	CMP_RCX_Immediate8 !10                      ; IF 10 >= CH
	JL8 !numerate_hex_digit                     ; NO
	SUBI8_RCX !7                                ; Push A-F into range
:numerate_hex_digit
	CMP_RCX_Immediate8 !15                      ; Check for illegal
	JG8 !numerate_string_fail                   ; If CH > 'F'
	CMP_RCX_Immediate8 !0                       ; Check for illegal
	JL8 !numerate_string_fail                   ; IF CH < 0
	ADD_RCX_to_RAX                              ; VALUE = VALUE + CH
	ADDI8_to_RBX !1                             ; S = S + 1
	JMP8 !numerate_hex_loop                     ; Keep looping

:numerate_string_fail
	LOADI32_EAX %0                              ; return ZERO

:numerate_string_done
	POP_RSI                                     ; Restore RSI
	POP_RDX                                     ; Restore RDX
	POP_RCX                                     ; Restore RCX
	POP_RBX                                     ; Restore RBX
	RET


;; Exit_Failure function
;; Receives nothing
;; And aborts hard
;; Does NOT return
:Exit_Failure
	LOADI32_EDI %1                              ; All is wrong
	LOADI32_EAX %0x3C                           ; put the exit syscall number in eax
	SYSCALL                                     ; Call it a bad day

;; 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
"%"

:newline
"\n"

:underline
"_"


: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_void_name
"void"

: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_int_name
"int"

: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_name
"char"

: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_indirect_name
"char*"

: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_char_double_indirect_name
"char**"

: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_file_name
"FILE"

: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_function_name
"FUNCTION"

: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_unsigned_name
"unsigned"

: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_long_name
"long"

;; debug_list function
;; Receives struct token_list* in RAX
;; Prints contents of list and exits
;; Does NOT return
:debug_list
	COPY_RAX_to_R12                             ; Protect the list pointer
	LOADI32_R14 %2                              ; write to standard error

:debug_list_iter
	;; Header
	LOADI32_RAX &debug_list_string0             ; Using our first string
	CALLI32 %File_Print                         ; Print it
	COPY_R12_to_RAX                             ; Use address of pointer
	CALLI32 %numerate_number                    ; Convert it into string
	CALLI32 %File_Print                         ; Print it

	;; NEXT
	LOADI32_RAX &debug_list_string1             ; Using our second string
	CALLI32 %File_Print                         ; Print it
	LOAD64_into_RAX_from_Address_R12            ; Use address of pointer
	CALLI32 %numerate_number                    ; Convert it into string
	CALLI32 %File_Print                         ; Print it

	;; PREV
	LOADI32_RAX &debug_list_string2             ; Using our third string
	CALLI32 %File_Print                         ; Print it
	LOAD64_into_RAX_from_Address_R12_Immediate8 !8  ; Use address of pointer
	CALLI32 %numerate_number                    ; Convert it into string
	CALLI32 %File_Print                         ; Print it

	;; S
	LOADI32_RAX &debug_list_string3             ; Using our fourth string
	CALLI32 %File_Print                         ; Print it
	LOAD64_into_RAX_from_Address_R12_Immediate8 !16 ; Use address of pointer
	CALLI32 %numerate_number                    ; Convert it into string
	CALLI32 %File_Print                         ; Print it

	;; S Contents
	LOADI32_RAX &debug_list_string4             ; Using our fifth string
	CALLI32 %File_Print                         ; Print it
	LOAD64_into_RAX_from_Address_R12_Immediate8 !16 ; Use address of string
	CMP_RAX_Immediate8 !0                       ; IF NULL Pointer
	JNE8 !debug_list_null                       ; otherwise display
	LOADI32_RAX &debug_list_string_null         ; Give meaningful message instead
:debug_list_null
	CALLI32 %File_Print                         ; Print it

	;; TYPE
	LOADI32_RAX &debug_list_string5             ; Using our sixth string
	CALLI32 %File_Print                         ; Print it
	LOAD64_into_RAX_from_Address_R12_Immediate8 !24 ; Use address of pointer
	CALLI32 %numerate_number                    ; Convert it into string
	CALLI32 %File_Print                         ; Print it

	;; ARGS/DEPTH
	LOADI32_RAX &debug_list_string6             ; Using our seventh string
	CALLI32 %File_Print                         ; Print it
	LOAD64_into_RAX_from_Address_R12_Immediate8 !32 ; Use address of pointer
	CALLI32 %numerate_number                    ; Convert it into string
	CALLI32 %File_Print                         ; Print it

	LOADI32_EAX %10                             ; Add "\n"
	CALLI32 %fputc                              ; print it
	CALLI32 %fputc                              ; print it

	LOAD64_into_R12_from_Address_R12            ; TOKEN = TOKEN->NEXT
	CMP_R12_Immediate8 !0                       ; Check if NULL
	JNE32 %debug_list_iter                      ; iterate otherwise

	LOADI32_EDI %666                            ; All is HELL
	LOADI32_EAX %0x3C                           ; put the exit syscall number in eax
	SYSCALL                                     ; Call it a bad day


: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>::<"

:Address_of
	NULL
:C
	NULL
:Token
	NULL
:break_frame
	NULL
:break_target_func
	NULL
:break_target_head
	NULL
:break_target_num
	NULL
:current_count
	NULL
:current_target
	NULL
:function
	NULL
:global_constant_list
	NULL
:global_function_list
	NULL
:global_symbol_list
	NULL
:global_token
	NULL
:global_types
	# Needed to zero pad pointer to 64 bits
	&prim_types %0
:globals_list
	NULL
:output_list
	NULL
:string_index
	NULL
:strings_list
	NULL

#:ELF_data
:ELF_end
