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

	section .text
	global _start

	;; 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
	mov rsi, 0                  ; prepare read_only
	mov rax, 2                  ; the syscall number for open()
	syscall                     ; Now open that damn file
	mov r15, rax                ; Preserve the file pointer we were given

	pop rdi                     ; Get the actual output name
	mov rsi, 577                ; Prepare file as O_WRONLY|O_CREAT|O_TRUNC
	mov rdx, 384                ; Prepare file as RW for owner only (600 in octal)
	mov rax, 2                  ; the syscall number for open()
	syscall                     ; Now open that damn file
	cmp rax, 0                  ; Check for missing output
	jg _start_out               ; Have real input
	mov rax, 1                  ; Use stdout

_start_out:
	mov r14, rax                ; Preserve the file pointer we were given

	mov rax, 12                 ; the Syscall # for SYS_BRK
	mov rdi, 0                  ; Get current brk
	syscall                     ; Let the kernel do the work
	mov r13, rax                ; Set our malloc pointer
	mov rax, 0                  ; HEAD = NULL
	call read_all_tokens        ; Read all tokens
	call Reverse_List           ; Reverse order
;	call debug_list             ; Try to figure out what is wrong
	mov [global_token], rax     ; Set global_token
	call program                ; Convert into program
	mov rax, header_string1     ; Our header string
	call File_Print             ; Print it
	mov rax, [output_list]      ; Our output_list
	call recursive_output       ; Print core program
;	mov rax, header_string2     ; Our Enable debug
;	call File_Print             ; Print it
	mov rax, header_string3     ; Our second label
	call File_Print             ; Print it
	mov rax, [globals_list]     ; Our globals
	call recursive_output       ; Get them
	mov rax, header_string4     ; Our final header
	call File_Print             ; Print it
	mov rax, [strings_list]     ; Our strings
	call recursive_output       ; Get them
	mov rax, header_string5     ; Our final header
	call File_Print             ; Print it

Done:
	; program completed Successfully
	mov rdi, 0                  ; All is well
	mov rax, 0x3c               ; put the exit syscall number in eax
	syscall                     ; Call it a good day

header_string1: db 10, "# Core program", 10, 0
header_string2: db 10, ":ELF_data", 10, 0
header_string3: db 10, "# Program global variables", 10, 0
header_string4: db 10, "# Program strings", 10, 0
header_string5: db 10, ":ELF_end", 10, 0


;; 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:
	mov [Token], rax
	call fgetc
read_all_tokens_loop:
	cmp rax, -4                 ; Check for EOF
	je read_all_tokens_done     ; Stop if found
	call get_token              ; Read all tokens
	jmp read_all_tokens_loop    ; Loop
read_all_tokens_done:
	mov rax, [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

	mov [C], rax                ; Set C

	mov rax, 40                 ; Malloc CURRENT
	call malloc                 ; Get Pointer
	mov rdx, rax                ; Set CURRENT

	mov rax, 256                ; Malloc the string
	call malloc                 ; Get pointer to S
	mov rcx, rax                ; Set S
	mov [rdx+16], rcx           ; CURRENT->S = S
reset:
	mov [string_index], rcx     ; S[0]
	mov rax, [C]                ; Using C

	call clear_white_space      ; Clear WhiteSpace
	mov [C], rax                ; Set C

	cmp rax, -4                 ; Check for EOF
	je get_token_abort          ; if EOF abort

	cmp rax, 35                 ; Check for '#'
	jne get_token_alpha         ; Nope

	;; Deal with # line comments
	call purge_macro            ; Let it handle it
	mov [C], rax                ; Set C
	jmp reset                   ; Try again

get_token_alpha:
	mov rax, [C]                ; Send C
	mov rbx, alphas             ; Get alphanumerics
	call In_Set                 ; See if in set
	cmp rax, 1                  ; IF TRUE
	jne get_token_symbol        ; Otherwise

	;; Store keywords
	mov rax, [C]                ; Send C
	call preserve_keyword       ; Store
	mov [C], rax                ; Set C
	jmp get_token_done          ; Be done with this token

get_token_symbol:
	mov rax, [C]                ; Send C
	mov rbx, symbols            ; Get symbols
	call In_Set                 ; See if in set
	cmp rax, 1                  ; IF TRUE
	jne get_token_strings       ; Otherwise

	;; Store symbols
	mov rax, [C]                ; Send C
	call preserve_symbol        ; Store
	mov [C], rax                ; Set C
	jmp get_token_done          ; Be done with this token

get_token_strings:
	mov rax, [C]                ; Send C
	mov rbx, strings            ; Get strings
	call In_Set                 ; See if in set
	cmp rax, 1                  ; IF TRUE
	jne get_token_comment       ; Otherwise

	;; Store String
	mov rax, [C]                ; Send C
	call consume_word           ; Store
	mov [C], rax                ; Set C
	jmp get_token_done          ; Be done with this token

get_token_comment:
	mov rax, [C]                ; Send C
	cmp rax, 47                 ; IF '/' == C
	jne get_token_else          ; Otherwise

	call consume_byte           ; Hope it just is '/'
	mov [C], rax                ; Set C

	cmp rax, 42                 ; IF '*' we have '/*'
	jne get_token_comment_line  ; Check for '//'

	;; Deal with /* block comments */
	call fgetc                  ; get next C
	mov [C], rax                ; Set C
get_token_comment_block_outer:
	mov rax, [C]                ; Using C
	cmp rax, 47                 ; IF '/' != C
	je get_token_comment_block_done ; be done

get_token_comment_block_inner:
	mov rax, [C]                ; Using C
	cmp rax, 42                 ; IF '*' != C
	je get_token_comment_block_iter ; jump over

	;; Deal with inner loop
	call fgetc                  ; get next C
	mov [C], rax                ; Set C
	jmp get_token_comment_block_inner ; keep going

get_token_comment_block_iter:
	call fgetc                  ; get next C
	mov [C], rax                ; Set C
	jmp get_token_comment_block_outer

get_token_comment_block_done:
	call fgetc                  ; get next C
	mov [C], rax                ; Set C
	jmp reset                   ; throw away, try again

get_token_comment_line:
	cmp rax, 47                 ; IF '/' we have //
	jne get_token_done          ; keep if just '/'

	;; Deal with // line comment
	call fgetc                  ; drop to match
	mov [C], rax                ; Set C
	jmp reset                   ; throw away, try again

get_token_else:
	mov rax, [C]                ; Send C
	call consume_byte
	mov [C], rax                ; Set C

get_token_done:
	mov rax, [Token]            ; TOKEN
	mov [rdx+8], rax            ; CURRENT->PREV = TOKEN
	mov [rdx], rax              ; CURRENT->NEXT = TOKEN
	mov [Token], rdx            ; TOKEN = CURRENT

get_token_abort:
	pop rdx                     ; Restore RDX
	pop rcx                     ; Restore RCX
	pop rbx                     ; Restore RBX
	mov rax, [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:
	mov rdi, r13                ; Using the current pointer
	add rdi, rax                ; Request the number of desired bytes
	mov rax, 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
	mov rax, r13                ; Return pointer
	mov r13, rdi                ; 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, 32                 ; Check for ' '
	je clear_white_space_wipe   ; wipe it out

	cmp rax, 10                 ; Check for '\n'
	je clear_white_space_wipe   ; wipe it output

	cmp rax, 9                  ; Check for '\t'
	jne clear_white_space_done  ; looks like non-whitespace

clear_white_space_wipe:
	call fgetc                  ; Read a new byte
	cmp rax, -4                 ; Check for EOF
	je clear_white_space_done   ; Short circuit
	jmp 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:
	mov cl, [rbx]               ; Read char
	movzx rcx, cl               ; Zero extend it

	cmp rax, rcx                ; See if they match
	je In_Set_True              ; return true

	cmp rcx, 0                  ; Check for NULL
	je In_Set_False             ; return false

	add rbx, 1                  ; s = s + 1
	jmp In_Set_loop             ; Keep looping

In_Set_True:
	mov rax, 1                  ; Set True
	pop rcx                     ; Restore RCX
	pop rbx                     ; Restore RBX
	ret

In_Set_False:
	mov rax, 0                  ; Set FALSE
	pop rcx                     ; Restore RCX
	pop rbx                     ; Restore RBX
	ret

alphas: db "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz", 0
symbols: db "<=>|&!-", 0
strings: db 34, 39, 0


;; purge_macro function
;; Receives CH in RAX
;; Reads chars until Line feed is read
;; returns line feed
purge_macro:
	call fgetc                  ; read next char
	cmp rax, 10                 ; Check for '\n'
	jne 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
	mov rcx, rax                ; Setup C
	mov rbx, alphas             ; Concerning ourselves with "abc.."
preserve_keyword_loop:
	call In_Set                 ; Check if alphanumerics
	cmp rax, 1                  ; IF TRUE
	jne preserve_keyword_label  ; Otherwise check for label

	mov rax, rcx                ; Pass C
	call consume_byte           ; consume that byte
	mov rcx, rax                ; Update C
	jmp preserve_keyword_loop   ; keep looping

preserve_keyword_label:
	mov rax, rcx                ; Fix return
	cmp rax, 58                 ; Check for ':'
	jne preserve_keyword_done   ; be done

	;; Fix our goto label
	call fixup_label            ; Fix the label
	mov rax, 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
	mov rcx, rax                ; Setup C
	mov rbx, symbols            ; Concerning ourselves with "<=>.."
preserve_symbol_loop:
	call In_Set                 ; Check if symbol
	cmp rax, 1                  ; IF TRUE
	jne preserve_symbol_done    ; Otherwise be done

	mov rax, rcx                ; Pass C
	call consume_byte           ; consume that byte
	mov rcx, rax                ; Update C
	jmp preserve_symbol_loop    ; keep looping

preserve_symbol_done:
	mov rax, rcx                ; 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
	mov rbx, rax                ; FREQ = C
	mov rcx, 0                  ; ESCAPE = FALSE
consume_word_loop:
	cmp rcx, 0                  ; IF !ESCAPE
	jne consume_word_escape     ; Enable escape

	cmp rax, 92                 ; if '\\'
	jne consume_word_iter       ; keep state

	mov rcx, 1                  ; ESCAPE = TRUE
	jmp consume_word_iter       ; keep going

consume_word_escape:
	mov rcx, 0                  ; ESCAPE = FALSE

consume_word_iter:
	call consume_byte           ; read next char

	cmp rcx, 0                  ; IF ESCAPE
	jne consume_word_loop       ; keep looping

	cmp rax, rbx                ; IF C != FREQ
	jne consume_word_loop       ; keep going

	call 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
	mov rbx, [string_index]     ; S[0]
	mov [rbx], al               ; S[0] = C
	add rbx, 1                  ; S = S + 1
	mov [string_index], rbx     ; Update S
	call 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
	mov rax, 58                 ; HOLD = ':'
	mov rcx, [rdx+16]           ; HOLD_STRING[0]
fixup_label_loop:
	mov rbx, rax                ; PREV = HOLD
	mov al, [rcx]               ; HOLD = HOLD_STRING[I]
	movzx rax, al               ; make useful
	mov [rcx], bl               ; HOLD_STRING[I] = PREV
	add rcx, 1                  ; I = I + 1
	cmp rax, 0                  ; IF NULL == HOLD
	jne 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:
	mov rax, -4                 ; Put EOF in rax
	push rax                    ; Assume bad (If nothing read, value will remain EOF)
	lea rsi, [rsp]              ; Get stack address
	mov rdi, r15                ; Where are we reading from
	mov rax, 0                  ; the syscall number for read
	push rdx                    ; Protect RDX
	mov rdx,  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, -4                 ; Check for EOF
	je fgetc_done               ; Return as is
	movzx rax, 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
	mov rbx, rax                ; Set HEAD
	mov rax, 0                  ; ROOT = NULL
Reverse_List_Loop:
	cmp rbx, 0                  ; WHILE HEAD != NULL
	je Reverse_List_Done        ; Stop otherwise

	mov rcx, [rbx]              ; NEXT = HEAD->NEXT
	mov [rbx], rax              ; HEAD->NEXT = ROOT
	mov rax, rbx                ; ROOT = HEAD
	mov rbx, rcx                ; HEAD = NEXT
	jmp 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, 0                  ; Check for NULL
	je recursive_output_done    ; Skip the work
	mov rbx, rax                ; I = Head

	mov rax, [rbx]              ; Iterate to next Token
	call recursive_output       ; Recurse

	mov rax, [rbx + 16]         ; Using S
	call 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
	mov rbx, rax                ; Protect S
	cmp rax, 0                  ; Protect against nulls
	je File_Print_Done          ; Simply don't try to print them
File_Print_Loop:
	mov al, [rbx]               ; Read byte
	movzx rax, al               ; zero extend
	cmp rax, 0                  ; Check for NULL
	je File_Print_Done          ; Stop at NULL

	call fputc                  ; write it
	add rbx, 1                  ; S = S + 1
	jmp 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, [rsp]              ; Get stack address
	mov rdi, r14                ; Write to target file
	mov rax, 1                  ; the syscall number for write
	push rdx                    ; Protect RDX
	mov rdx, 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:
	mov rax, [global_token]     ; Using global_token
	cmp rax, 0                  ; Check if NULL
	je program_done             ; Be done if null

	mov rbx, [rax+16]           ; GLOBAL_TOKEN->S
	mov rax, constant           ; "CONSTANT"
	call match                  ; IF GLOBAL_TOKEN->S == "CONSTANT"
	cmp rax, 0                  ; If true
	jne program_else            ; Looks like not a constant

	;; Deal with minimal constant case
	mov rax, [global_token]     ; Using global_token
	mov rax, [rax]              ; global_token->next
	mov [global_token], rax     ; global_token = global_token->next

	mov rax, [rax+16]           ; global_token->S
	mov rbx, 0                  ; NULL
	mov rcx, [global_constant_list] ; global_constant_list
	call sym_declare            ; Declare that constant
	mov [global_constant_list], rax ; global_constant_list = sym_declare(global_token->s, NULL, global_constant_list);

	mov rbx, [global_token]     ; Using global_token
	mov rbx, [rbx]              ; global_token->next
	mov [rax+32], rbx           ; global_constant_list->arguments = global_token->next

	mov rbx, [rbx]              ; global_token->next->next
	mov [global_token], rbx     ; global_token = global_token->next->next;
	jmp new_type                ; go around again

program_else:
	call type_name              ; Figure out the type_size
	cmp rax, 0                  ; IF NULL == type_size
	je new_type                 ; it was a new type

	;; Add to global symbol table
	mov rbx, rax                ; put type_size in the right spot
	mov rax, [global_token]     ; Using global token
	mov rax, [rax+16]           ; global_token->S
	mov rcx, [global_symbol_list] ; Using global_symbol_list
	call sym_declare            ; Declare symbol
	mov [global_symbol_list], rax ; global_symbol_list = sym_declare(global_token->s, type_size, global_symbol_list);
	mov rbx, [global_token]     ; Using global token
	mov rbx, [rbx]              ; global_token->next
	mov [global_token], rbx     ; global_token = global_token->next

	mov rbx, [global_token]     ; Using global token
	mov rbx, [rbx+16]           ; global_token->S
	mov rax, semicolon          ; ";"
	call match                  ; if(match(";", global_token->s))
	cmp rax, 0                  ; If true
	jne program_function        ; looks like not a match

	;; Deal with the global variable
	mov rbx, [globals_list]     ; Using globals_list
	mov rax, program_string_0   ; ":GLOBAL_"
	call emit                   ; Emit it
	mov rbx, rax                ; update globals_list

	mov rax, [global_token]     ; Using global token
	mov rax, [rax+8]            ; global token->prev
	mov rax, [rax+16]           ; global token->prev->s
	call emit                   ; Emit it

	mov rbx, rax                ; update globals_list
	mov rax, program_string_1   ; "\nNULL\n"
	call emit                   ; Emit it
	mov [globals_list], rax     ; update globals_list

	mov rax, [global_token]     ; Using global token
	mov rax, [rax]              ; global_token->next
	mov [global_token], rax     ; global_token = global_token->next
	jmp new_type                ; go around again

program_function:
	mov rbx, [global_token]     ; Using global token
	mov rbx, [rbx+16]           ; global_token->S
	mov rax, open_paren         ; "("
	call match                  ; if(match(";", global_token->s))
	cmp rax, 0                  ; If true
	jne program_error           ; Otherwise deal with error case

	;; Deal with function definition
	call declare_function       ; Lets get the parsing rolling
	jmp 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: db ":GLOBAL_", 0
program_string_1: db 10, "NULL", 10, 0


;; 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
	mov rax, 0                  ; Using NULL
	mov [current_count], rax    ; current_count = 0

	mov rax, [global_token]     ; Using global token
	mov rax, [rax+8]            ; global token->prev
	mov rax, [rax+16]           ; global token->prev->s
	mov rbx, 0                  ; NULL
	mov rcx, [global_function_list] ; global_function_list
	call sym_declare            ; sym_declare(global_token->prev->s, NULL, global_function_list);
	mov [function], rax         ; function = sym_declare(global_token->prev->s, NULL, global_function_list);
	mov [global_function_list], rax ; global_function_list = function

	call collect_arguments      ; collect all of the function arguments

	mov rax, [global_token]     ; Using global token
	mov rax, [rax+16]           ; global token->s
	mov rbx, semicolon          ; ";"
	call match                  ; IF global token->s == ";"
	cmp rax, 0                  ; If true
	jne declare_function_full   ; It was a prototype

	;; Deal with prototypes
	mov rax, [global_token]     ; Using global token
	mov rax, [rax]              ; global token->next
	mov [global_token], rax     ; global token = global token->next
	jmp declare_function_done   ; Move on

declare_function_full:
	;; Deal with full function definitions
	mov rax, declare_function_string_0 ; "# Defining function "
	call emit_out               ; emit it

	mov rax, [function]         ; function
	mov rax, [rax+16]           ; function->s
	call emit_out               ; emit it

	mov rax, declare_function_string_1 ; "\n:FUNCTION_"
	call emit_out               ; emit it

	mov rax, [function]         ; function
	mov rax, [rax+16]           ; function->s
	call emit_out               ; emit it

	mov rax, declare_function_string_3 ; "\n"
	call emit_out               ; emit it

	call statement              ; Recursively get the function pieces

	mov rax, [output_list]      ; output
	mov rax, [rax+16]           ; output->s
	mov rbx, declare_function_string_2 ; "RETURN\n"
	call match                  ; IF output->s == "RETURN\n"
	cmp rax, 0                  ; If true we can skip adding it
	je declare_function_done    ; otherwise we need to add it

	;; Add the return to the end of a function lacking a return;
	mov rax, declare_function_string_2 ; "RETURN\n"
	call emit_out               ; emit it

declare_function_done:
	pop rcx                     ; Restore RCX
	pop rbx                     ; Restore RBX
	ret

declare_function_string_0: db 35, " Defining function ", 0
declare_function_string_1: db 10, ":FUNCTION_", 0
declare_function_string_2: db "RETURN", 10, 0
declare_function_string_3: db 10, 0


;; 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
	mov rax, [global_token]     ; Using global_token
	mov rax, [rax]              ; global_token->next
	mov [global_token], rax     ; global_token = global_token->next
collect_arguments_loop:
	mov rbx, [global_token]     ; Using global_token
	mov rbx, [rbx+16]           ; global_token->S
	mov rax, close_paren        ; ")"
	call match                  ; IF global_token->S == ")"
	cmp rax, 0                  ; we reached the end
	je collect_arguments_done   ; be done

	;; deal with the case of there are arguments
	call type_name              ; Get the type
	mov rcx, rax                ; put type_size safely out of the way

	mov rbx, [global_token]     ; Using global_token
	mov rbx, [rbx+16]           ; global_token->S
	mov rax, close_paren        ; ")"
	call match                  ; IF global_token->S == ")"
	cmp rax, 0                  ; is a foo(int, char,void) case
	je collect_arguments_common ; deal with commas

	;; Trying second else
	mov rbx, [global_token]     ; Using global_token
	mov rbx, [rbx+16]           ; global_token->S
	mov rax, comma              ; ","
	call match                  ; IF global_token->S == ","
	cmp rax, 0                  ; then deal with the common
	je collect_arguments_common ; case of commas between arguments

	;; deal with foo(int a, char b)
	mov rax, [global_token]     ; Using global_token
	mov rax, [rax+16]           ; global_token->S
	mov rbx, rcx                ; put type_size in the right place
	mov rcx, [function]         ; Using function
	mov rcx, [rcx+32]           ; function->args
	call sym_declare            ; sym_declare(global_token->s, type_size, function->arguments);
	mov rcx, rax                ; put a in a safe place

	mov rax, [function]         ; Using function
	mov rax, [rax+32]           ; function->args
	cmp rax, 0                  ; IF function->args == NULL
	jne collect_arguments_another ; otherwise it isn't the first

	;; Deal with the case of first argument in the function
	mov rax, -8                 ; -8
	mov [rcx+32], rax           ; a->depth = -8
	jmp collect_arguments_next  ; get to next

collect_arguments_another:
	;; deal with the case of non-first arguments
	mov rax, [function]         ; Using function
	mov rax, [rax+32]           ; function->args
	mov rax, [rax+32]           ; function->args->depth
	sub rax, 8                  ; function->args->depth - 8
	mov [rcx+32], rax           ; a->depth = function->args->depth - 8

collect_arguments_next:
	mov rax, [global_token]     ; Using global_token
	mov rax, [rax]              ; global_token->next
	mov [global_token], rax     ; global_token = global_token->next

	mov rax, [function]         ; Using function
	mov [rax+32], rcx           ; function->args = a

collect_arguments_common:
	mov rbx, [global_token]     ; Using global_token
	mov rbx, [rbx+16]           ; global_token->S
	mov rax, comma              ; ","
	call match                  ; IF global_token->S == ","
	cmp rax, 0                  ; then deal with the comma
	jne collect_arguments_loop  ; otherwise loop

	;; keep foo(bar(), 1) expressions working
	mov rax, [global_token]     ; Using global_token
	mov rax, [rax]              ; global_token->next
	mov [global_token], rax     ; global_token = global_token->next
	jmp collect_arguments_loop  ; keep going

collect_arguments_done:
	mov rax, [global_token]     ; Using global_token
	mov rax, [rax]              ; global_token->next
	mov [global_token], rax     ; 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

	mov rbx, [global_token]     ; Using global_token
	mov rbx, [rbx+16]           ; global_token->S
	mov rax, open_curly_brace   ; "{"
	call match                  ; IF global_token->S == "{"
	jne statement_label         ; otherwise try label

	;; deal with { statement }
	call recursive_statement    ; Statements inside of statements for days
	jmp statement_done          ; Be done

statement_label:
	mov al, [rbx]               ; global_token->S[0]
	movzx rax, al               ; make it useful
	cmp rax, 58                 ; IF global_token->S == ':'
	jne statement_local         ; otherwise try locals

	;; deal with labels
	mov rax, rbx                ; put global_token->S in the right spot
	call emit_out               ; emit it

	mov rax, statement_string_0 ; Using "\t#C goto label\n"
	call emit_out               ; emit it

	mov rax, [global_token]     ; Using global_token
	mov rax, [rax]              ; global_token->next
	mov [global_token], rax     ; global_token = global_token->next
	jmp statement_done          ; be done

statement_local:
	mov rax, rbx                ; put global_token->S in the right place
	mov rbx, prim_types         ; pointer to primative types
	call lookup_type            ; See if found
	cmp rax, 0                  ; IF NULL == lookup_type(global_token->S, prim_types)
	jne statement_local_success ; Sweet a new local

	;; Second chance
	mov rbx, [global_token]     ; Using global_token
	mov rbx, [rbx+16]           ; global_token->S
	mov rax, struct             ; "struct"
	call match                  ; IF global_token->S == "struct"
	cmp rax, 0                  ; then we are a local
	jne statement_if            ; otherwise try IF

statement_local_success:
	call collect_local          ; Grab those locals
	jmp statement_done          ; be done

statement_if:
	mov rax, if_string          ; Using "if"
	call match                  ; IF global_token->S == "if"
	cmp rax, 0                  ; then we have an if statement
	jne statement_do            ; otherwise try DO

	;; Deal with IF statement
	call process_if             ; DO IT
	jmp statement_done          ; be done

statement_do:
	mov rax, do_string          ; Using "do
	call match                  ; IF global_token->S == "do"
	cmp rax, 0                  ; then we have a do statement
	jne statement_while         ; otherwise try WHILE

	;; Deal with DO statement
	call process_do             ; DO IT
	jmp statement_done          ; be done

statement_while:
	mov rax, while_string       ; Using "while"
	call match                  ; IF global_token->S == "while"
	cmp rax, 0                  ; then we have a while statement
	jne statement_for           ; otherwise try FOR

	;; Deal with WHILE statement
	call process_while          ; DO IT
	jmp statement_done          ; be done

statement_for:
	mov rax, for_string         ; Using "for"
	call match                  ; IF global_token->S == "for"
	cmp rax, 0                  ; then we have a for statement
	jne statement_asm           ; otherwise try ASM

	;; Deal with FOR statement
	call process_for            ; DO IT
	jmp statement_done          ; be done

statement_asm:
	mov rax, asm_string         ; Using "asm"
	call match                  ; IF global_token->S == "asm"
	cmp rax, 0                  ; then we have an asm statement
	jne statement_goto          ; otherwise try GOTO

	;; Deal with ASM statement
	call process_asm            ; Hit it
	jmp statement_done          ; be done

statement_goto:
	mov rax, goto_string        ; Using "goto"
	call match                  ; IF global_token->S == "goto"
	cmp rax, 0                  ; then we have a goto statement
	jne statement_return        ; Otherwise try RETURN

	;; Deal with GOTO statement
	mov rax, statement_string_1 ; Using "JUMP %"
	call emit_out               ; emit it

	mov rax, [global_token]     ; Using global_token
	mov rax, [rax]              ; global_token->next
	mov [global_token], rax     ; global_token = global_token->next

	mov rax, [rax+16]           ; global_token->S
	call emit_out               ; emit it

	mov rax, statement_string_2 ; Using "\n"
	call emit_out               ; emit it

	mov rax, [global_token]     ; Using global_token
	mov rax, [rax]              ; global_token->next
	mov [global_token], rax     ; global_token = global_token->next

	mov rax, statement_string_4 ; Using "ERROR in statement\nMissing ;\n"
	mov rbx, semicolon          ; Using ";"
	call require_match          ; Make sure it has the required
	jmp statement_done          ; Be done

statement_return:
	mov rax, return_string      ; Using "return"
	call match                  ; IF global_token->S == "return"
	cmp rax, 0                  ; then we have a return statement
	jne statement_break         ; Otherwise try BREAK

	;; Deal with RETURN Statement
	call return_result          ; Return anything they want
	jmp statement_done          ; be done

statement_break:
	mov rax, break_string       ; Using "break"
	call match                  ; IF global_token->S == "break"
	cmp rax, 0                  ; then we have a break statement
	jne statement_continue      ; Otherwise try CONTINUE

	;; Deal with BREAK statement
	call process_break          ; Lets do some damage
	jmp statement_done          ; be done

statement_continue:
	mov rax, continue_string    ; Using "continue"
	call match                  ; IF global_token->S == "continue"
	cmp rax, 0                  ; then we have a continue statement
	jne statement_else          ; Otherwise we are punting to an expression

	;; Deal with CONTINUE statement
	mov rax, [global_token]     ; Using global_token
	mov rax, [rax]              ; global_token->next
	mov [global_token], rax     ; global_token = global_token->next

	mov rax, statement_string_3 ; Using "\n#continue statement\n"
	call emit_out               ; emit it

	mov rax, statement_string_4 ; Using "ERROR in statement\nMissing ;\n"
	mov rbx, semicolon          ; Using ";"
	call require_match          ; Don't forget the ";"
	jmp statement_done          ; Be done

statement_else:
	call expression             ; Collect expression
	mov rax, statement_string_4 ; Using "ERROR in statement\nMissing ;\n"
	mov rbx, semicolon          ; Using ";"
	call require_match          ; make sure we have it

statement_done:
	pop rcx                     ; Restore RCX
	pop rbx                     ; Restore RBX
	ret

statement_string_0: db 9, "#C goto label", 10, 0
statement_string_1: db "JUMP %", 0
statement_string_2: db 10, 0
statement_string_3: db 10, "#continue statement", 10, 0
statement_string_4: db "ERROR in statement", 10, "Missing ;", 10, 0


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

	mov rax, [global_token]     ; Using global_token
	mov rax, [rax]              ; global_token->next
	mov [global_token], rax     ; global_token = global_token->next

	mov rcx, [function]         ; Using function
	mov rcx, [rcx+8]            ; frame = function->locals

recursive_statement_loop:
	mov rbx, [global_token]     ; Using global_token
	mov rbx, [rbx+16]           ; global_token->S
	mov rax, close_curly_brace  ; Using "}"
	call match                  ; IF global_token->S == "}"
	cmp rax, 0                  ; Then we are done recursing
	je recursive_statement_cleanup ; and then we clean up

	;; Deal with the recursive calls
	call statement              ; Deal with another statement
	jmp recursive_statement_loop ; loop some more

recursive_statement_cleanup:
	mov rax, [global_token]     ; Using global_token
	mov rax, [rax]              ; global_token->next
	mov [global_token], rax     ; global_token = global_token->next

	mov rax, recursive_statement_string_0 ; Using "RETURN\n"
	mov rbx, [output_list]      ; Using output
	mov rbx, [rbx+16]           ; output->S
	call match                  ; IF output->S == "RETURN\n"
	cmp rax, 0                  ; Then we can skip the clean up
	je recursive_statement_done ; and be done

	;; Deal with cleanup
	mov rbx, [function]         ; Using function
	mov rbx, [rbx+8]            ; i = function->locals
	mov rax, recursive_statement_string_1 ; Using "POP_RBX\t# _recursive_statement_locals\n"

recursive_statement_locals:
	cmp rbx, rcx                ; IF frame != i
	je recursive_statement_done ; Otherwise be done

	;; Lets emit
	call emit_out               ; emit it
	mov rbx, [rbx]              ; i = i->next
	jmp recursive_statement_locals ; keep going

recursive_statement_done:
	mov rax, [function]         ; Using function
	mov [rax+8], rcx            ; function->locals = frame
	pop rcx                     ; Restore RCX
	pop rbx                     ; Restore RBX
	ret

recursive_statement_string_0: db "RETURN", 10, 0
recursive_statement_string_1: db "POP_RBX", 9, "# _recursive_statement_locals", 10, 0


;; 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
	mov rax, [global_token]     ; Using global_token
	mov rax, [rax]              ; global_token->next
	mov [global_token], rax     ; global_token = global_token->next

	mov rax, [rax+16]           ; global_token->S
	mov al, [rax]               ; global_token->S[0]
	movzx rax, al               ; make it useful
	cmp rax, 59                 ; If global_token->S[0] == ';'
	je return_result_cleanup    ; Go straight to cleanup

	call expression             ; get the expression we are returning

return_result_cleanup:
	mov rax, return_result_string_0 ; Using "ERROR in return_result\nMISSING ;\n"
	mov rbx, semicolon          ; Using ";"
	call require_match          ; Make sure we have it

	mov rbx, [function]         ; Using function
	mov rbx, [rbx+8]            ; function->locals
	mov rax, return_result_string_1 ; Using "POP_RBX\t# _return_result_locals\n"
return_result_locals:
	cmp rbx, 0                  ; IF NULL == i
	je return_result_done       ; Be done

	call emit_out               ; Emit out pop
	mov rbx, [rbx]              ; i = i->NEXT
	jmp return_result_locals    ; Keep going

return_result_done:
	mov rax, return_result_string_2 ; Using "RETURN\n"
	call emit_out               ; Emit it
	pop rcx                     ; Restore RCX
	pop rbx                     ; Restore RBX
	ret

return_result_string_0: db "ERROR in return_result", 10, "MISSING ;", 10, 0
return_result_string_1: db "POP_RBX", 9, "# _return_result_locals", 10, 0
return_result_string_2: db "RETURN", 10, 0


;; 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
	call type_name              ; Get the local's type

	mov rbx, rax                ; Put struct type* type_size in the right place
	mov rax, [global_token]     ; Using global_token
	mov rax, [rax+16]           ; global_token->S
	mov rcx, [function]         ; Using function
	mov rcx, [rcx+8]            ; function->locals
	call sym_declare            ; Declare it
	mov rcx, rax                ; put it away safely

	;; Try for main
	mov rax, main_string        ; Using "main"
	mov rbx, [function]         ; Using function
	mov rbx, [rbx+16]           ; function->S
	call match                  ; IF match("main", function->s)
	cmp rax, 0                  ; possible
	jne collect_local_fresh     ; try to see if fresh function

	;; Ok we are in main, now to see if main is fresh
	mov rax, [function]         ; Using function
	mov rax, [rax+8]            ; function->locals
	cmp rax, 0                  ; IF NULL == function->locals
	jne collect_local_fresh     ; try to see if fresh function

	;; Sweet we are in a fresh main
	mov rax, -40                ; We start at -40
	mov [rcx+32], rax           ; a->DEPTH = -40
	jmp collect_local_common    ; Go to the commons

collect_local_fresh:
	mov rax, [function]         ; Using function
	mov rax, [rax+32]           ; function->args
	cmp rax, 0                  ; IF NULL == function->args
	jne collect_local_first     ; Otherwise see if first

	mov rax, [function]         ; Using function
	mov rax, [rax+8]            ; function->locals
	cmp rax, 0                  ; IF NULL == function->locals
	jne collect_local_first     ; Otherwise try first

	;; Sweet we are in a fresh function
	mov rax, -16                ; We start at -16
	mov [rcx+32], rax           ; a->DEPTH = -16
	jmp collect_local_common    ; Go to the commons

collect_local_first:
	mov rax, [function]         ; Using function
	mov rax, [rax+8]            ; function->locals
	cmp rax, 0                  ; IF NULL == function->locals
	jne collect_local_else      ; Looks like we are just another local

	;; Ok we are the first local
	mov rax, [function]         ; Using function
	mov rax, [rax+32]           ; function->args
	mov rax, [rax+32]           ; function->args->depth
	sub rax, 16                 ; function->arguments->depth - 16
	mov [rcx+32], rax           ; a->DEPTH = function->arguments->depth - 16
	jmp collect_local_common    ; Go to the commons

collect_local_else:
	;; Always the last to know
	mov rax, [function]         ; Using function
	mov rax, [rax+8]            ; function->locals
	mov rax, [rax+32]           ; function->locals->depth
	sub rax, 8                  ; function->locals->depth - 8
	mov [rcx+32], rax           ; a->DEPTH = function->locals->depth - 8

collect_local_common:
	mov rax, [function]         ; Using function

	mov [rax+8], rcx            ; function->locals = a
	mov rcx, [rcx+16]           ; a->S

	mov rax, collect_local_string_0 ; Using "# Defining local "
	call emit_out               ; emit it

	mov rax, [global_token]     ; Using global_token
	mov rax, [rax+16]           ; global_token->S
	call emit_out               ; emit it

	mov rax, collect_local_string_1 ; Using "\n"
	call emit_out               ; emit it

	mov rax, [global_token]     ; Using global_token
	mov rax, [rax]              ; global_token->NEXT
	mov [global_token], rax     ; global_token = global_token->NEXT

	mov rbx, [rax+16]           ; global_token->S
	mov rax, equal              ; Using "="
	call match                  ; IF match("=", global_token->s)
	cmp rax, 0                  ; Deal with assignment
	jne collect_local_done      ; Otherwise finish it

	;; Deal with assignment
	mov rax, [global_token]     ; Using global_token
	mov rax, [rax]              ; global_token->NEXT
	mov [global_token], rax     ; global_token = global_token->NEXT

	call expression             ; Recurse

collect_local_done:
	mov rax, collect_local_string_2 ; Using "ERROR in collect_local\nMissing ;\n"
	mov rbx, semicolon          ; Using ";"
	call require_match          ; Make sure we have it

	mov rax, collect_local_string_3 ; Using "PUSH_RAX\t#"
	call emit_out               ; emit it

	mov rax, rcx                ; put A->S where it belongs
	call emit_out               ; emit it

	mov rax, collect_local_string_1 ; Using "\n"
	call emit_out               ; emit it

	pop rcx                     ; Restore RCX
	pop rbx                     ; Restore RBX
	ret

collect_local_string_0: db "# Defining local ", 0
collect_local_string_1: db 10, 0
collect_local_string_2: db "ERROR in collect_local", 10, "Missing ;", 10, 0
collect_local_string_3: db "PUSH_RAX", 9, "#", 0


;; 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
	mov rax, [global_token]     ; Using global_token
	mov rax, [rax]              ; global_token->NEXT
	mov [global_token], rax     ; global_token = global_token->NEXT

	mov rax, process_asm_string_0 ; Using "ERROR in process_asm\nMISSING (\n"
	mov rbx, open_paren         ; Using "("
	call require_match          ; Make sure we have it

	mov rbx, [global_token]     ; Using global_token
process_asm_iter:
	mov rax, [rbx+16]           ; global_token->S
	mov al, [rax]               ; global_token->S[0]
	movzx rax, al               ; Make it useful
	cmp rax, 34                 ; IF global_token->S[0] == '"'
	jne process_asm_done        ; Otherwise be done

	mov rax, [rbx+16]           ; global_token->S
	add rax, 1                  ; global_token->S + 1
	call emit_out               ; Emit it

	mov rax, process_asm_string_1 ; Using "\n"
	call emit_out               ; Emit it

	mov rbx, [rbx]              ; global_token->NEXT
	mov [global_token], rbx     ; global_token = global_token->NEXT
	jmp process_asm_iter        ; keep going

process_asm_done:
	mov rax, process_asm_string_2 ; Using "ERROR in process_asm\nMISSING )\n"
	mov rbx, close_paren        ; Using ")"
	call require_match          ; Make sure we have it

	mov rax, process_asm_string_3 ; Using "ERROR in process_asm\nMISSING ;\n"
	mov rbx, semicolon          ; Using ";"
	call require_match          ; Make sure we have it

	pop rbx                     ; Restore RBX
	ret

process_asm_string_0: db "ERROR in process_asm", 10, "MISSING (", 10, 0
process_asm_string_1: db 10, 0
process_asm_string_2: db "ERROR in process_asm", 10, "MISSING )", 10, 0
process_asm_string_3: db "ERROR in process_asm", 10, "MISSING ;", 10, 0


;; 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
	mov rax, [current_count]    ; Using current count
	mov rbx, rax                ; Preparing for update
	add rbx, 1                  ; current_count + 1
	mov [current_count], rbx    ; current_count = current_count + 1
	call numerate_number        ; convert to string
	mov rcx, rax                ; put NUMBER_STRING in place

	mov rax, process_if_string_0 ; Using "# IF_"
	call emit_out               ; Emit it

	mov rax, [function]         ; Using function
	mov rax, [rax+16]           ; function->S
	mov rbx, rcx                ; Passing NUMBER_STRING
	call uniqueID_out           ; uniqueID_out(function->s, number_string)

	mov rax, [global_token]     ; Using global_token
	mov rax, [rax]              ; global_token->NEXT
	mov [global_token], rax     ; global_token = global_token->NEXT

	mov rax, process_if_string_1 ; Using "ERROR in process_if\nMISSING (\n"
	mov rbx, open_paren         ; Using "("
	call require_match          ; Make sure we have it

	call expression             ; Recurse to get the IF(...) part

	mov rax, process_if_string_2 ; Using "TEST\nJUMP_EQ %ELSE_"
	call emit_out               ; Emit it

	mov rax, [function]         ; Using function
	mov rax, [rax+16]           ; function->S
	mov rbx, rcx                ; Passing NUMBER_STRING
	call uniqueID_out           ; uniqueID_out(function->s, number_string)

	mov rax, process_if_string_3 ; Using "ERROR in process_if\nMISSING )\n"
	mov rbx, close_paren        ; Using ")"
	call require_match          ; Make sure we have it

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

	mov rax, process_if_string_4 ; Using "JUMP %_END_IF_"
	call emit_out               ; Emit it

	mov rax, [function]         ; Using function
	mov rax, [rax+16]           ; function->S
	mov rbx, rcx                ; Passing NUMBER_STRING
	call uniqueID_out           ; uniqueID_out(function->s, number_string)

	mov rax, process_if_string_5 ; Using ":ELSE_"
	call emit_out               ; Emit it

	mov rax, [function]         ; Using function
	mov rax, [rax+16]           ; function->S
	call uniqueID_out           ; uniqueID_out(function->s, number_string)

	mov rbx, [global_token]     ; Using global_token
	mov rbx, [rbx+16]           ; global_token->S
	mov rax, else_string        ; Using "else"
	call match                  ; IF global_token->S == "else"
	cmp rax, 0                  ; Then we need to collect the else too
	jne process_if_done         ; Otherwise finish up

	;; deal with else statement
	mov rax, [global_token]     ; Using global_token
	mov rax, [rax]              ; global_token->NEXT
	mov [global_token], rax     ; global_token = global_token->NEXT

	call statement              ; Recurse to get the ELSE {...} part

process_if_done:
	mov rax, process_if_string_6 ; Using ":_END_IF_"
	call emit_out               ; Emit it

	mov rax, [function]         ; Using function
	mov rax, [rax+16]           ; function->S
	mov rbx, rcx                ; Passing NUMBER_STRING
	call uniqueID_out           ; uniqueID_out(function->s, number_string)

	pop rcx                     ; Restore RCX
	pop rbx                     ; Restore RBX
	ret

process_if_string_0: db "# IF_",0
process_if_string_1: db "ERROR in process_if", 10, "MISSING (", 10, 0
process_if_string_2: db "TEST", 10, "JUMP_EQ %ELSE_", 0
process_if_string_3: db "ERROR in process_if", 10, "MISSING )", 10, 0
process_if_string_4: db "JUMP %_END_IF_", 0
process_if_string_5: db ":ELSE_", 0
process_if_string_6: db ":_END_IF_", 0


;; save_break_frame microfunction
;; Overwrites RAX and RBX
;; Saves break frame on stack
;; Returns to caller
save_break_frame:
	pop rbx                     ; Save return Address
	mov rax, [break_frame]      ; Get break_frame
	push rax                    ; Store as nested_locals
	mov rax, [break_target_head] ; Get break_target_head
	push rax                    ; Store as nested_break_head
	mov rax, [break_target_func] ; Get break_target_func
	push rax                    ; Store as nested_break_func
	mov rax, [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
	mov [break_target_num], rax ; Restore break_target_num
	pop rax                     ; Get nested_break_func
	mov [break_target_func], rax ; Restore break_target_func
	pop rax                     ; Get nested_break_head
	mov [break_target_head], rax ; Restore break_target_head
	pop rax                     ; Get nested_locals
	mov [break_frame], rax      ; 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:
	mov [break_target_head], rax ; update break_target_head
	mov [break_target_num], rbx ; update break_target_num
	mov rbx, [function]         ; Using function
	mov rax, [rbx+8]            ; function->LOCALS
	mov [break_frame], rax      ; break_frame = function->LOCALS
	mov rax, [rbx+16]           ; function->S
	mov [break_target_func], rax ; 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
	call save_break_frame       ; Save the frame

	mov rax, [current_count]    ; Using current count
	mov rbx, rax                ; Preparing for update
	add rbx, 1                  ; current_count + 1
	mov [current_count], rbx    ; current_count = current_count + 1
	call numerate_number        ; convert to string
	mov rcx, rax                ; put NUMBER_STRING in place

	mov rax, process_do_string_0 ; Using "DO_END_"
	mov rbx, rcx                ; Passing NUMBER_STRING
	call set_break_frame        ; Set the frame

	mov rax, process_do_string_1 ; Using ":DO_"
	call emit_out               ; Emit it

	mov rax, [function]         ; Using function
	mov rax, [rax+16]           ; function->S
	mov rbx, rcx                ; Passing NUMBER_STRING
	call uniqueID_out           ; uniqueID_out(function->s, number_string)

	mov rax, [global_token]     ; Using global_token
	mov rax, [rax]              ; global_token->NEXT
	mov [global_token], rax     ; global_token = global_token->NEXT

	call statement              ; Do the DO {...} part

	mov rax, process_do_string_2 ; Using "ERROR in process_do\nMISSING while\n"
	mov rbx, while_string       ; Using "while"
	call require_match          ; Make sure we have it

	mov rax, process_do_string_3 ; Using "ERROR in process_do\nMISSING (\n"
	mov rbx, open_paren         ; Using "("
	call require_match          ; Make sure we have it

	call expression             ; Do the WHILE (...) part

	mov rax, process_do_string_4 ; Using "ERROR in process_do\nMISSING )\n"
	mov rbx, close_paren        ; Using ")"
	call require_match          ; Make sure we have it

	mov rax, process_do_string_5 ; Using "ERROR in process_do\nMISSING ;\n"
	mov rbx, semicolon          ; Using ";"
	call require_match          ; Make sure we have it

	mov rax, process_do_string_6 ; Using "TEST\nJUMP_NE %DO_"
	call emit_out               ; Emit it

	mov rax, [function]         ; Using function
	mov rax, [rax+16]           ; function->S
	mov rbx, rcx                ; Passing NUMBER_STRING
	call uniqueID_out           ; uniqueID_out(function->s, number_string)

	mov rax, process_do_string_7 ; Using ":DO_END_"
	call emit_out               ; Emit it

	mov rax, [function]         ; Using function
	mov rax, [rax+16]           ; function->S
	call uniqueID_out           ; uniqueID_out(function->s, number_string)

	call restore_break_frame    ; Restore the old break frame

	pop rcx                     ; Restore RCX
	pop rbx                     ; Restore RBX
	ret

process_do_string_0: db "DO_END_", 0
process_do_string_1: db ":DO_", 0
process_do_string_2: db "ERROR in process_do", 10, "MISSING while", 10, 0
process_do_string_3: db "ERROR in process_do", 10, "MISSING (", 10, 0
process_do_string_4: db "ERROR in process_do", 10, "MISSING )", 10, 0
process_do_string_5: db "ERROR in process_do", 10, "MISSING ;", 10, 0
process_do_string_6: db "TEST", 10, "JUMP_NE %DO_", 0
process_do_string_7: db ":DO_END_", 0


;; 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
	call save_break_frame       ; Save break_frame

	mov rax, [current_count]    ; Using current count
	mov rbx, rax                ; Preparing for update
	add rbx, 1                  ; current_count + 1
	mov [current_count], rbx    ; current_count = current_count + 1
	call numerate_number        ; convert to string
	mov rcx, rax                ; put NUMBER_STRING in place

	mov rax, process_while_string_0 ; Using "END_WHILE_"
	mov rbx, rcx                ; Passing NUMBER_STRING
	call set_break_frame        ; Set it and forget it

	mov rax, process_while_string_1 ; Using ":WHILE_"
	call emit_out               ; Emit it

	mov rax, [function]         ; Using function
	mov rax, [rax+16]           ; function->S
	mov rbx, rcx                ; Passing NUMBER_STRING
	call uniqueID_out           ; uniqueID_out(function->s, number_string)

	mov rax, [global_token]     ; Using global_token
	mov rax, [rax]              ; global_token->NEXT
	mov [global_token], rax     ; global_token = global_token->NEXT

	mov rax, process_while_string_2 ; Using "ERROR in process_while\nMISSING (\n"
	mov rbx, open_paren         ; Using "("
	call require_match          ; Make sure we have it

	call expression             ; Deal with the WHILE (...) part

	mov rax, process_while_string_3 ; Using "TEST\nJUMP_EQ %END_WHILE_"
	call emit_out               ; Emit it

	mov rax, [function]         ; Using function
	mov rax, [rax+16]           ; function->S
	mov rbx, rcx                ; Passing NUMBER_STRING
	call uniqueID_out           ; uniqueID_out(function->s, number_string)

	mov rax, process_while_string_4 ; Using "# THEN_while_"
	call emit_out               ; Emit it

	mov rax, [function]         ; Using function
	mov rax, [rax+16]           ; function->S
	call uniqueID_out           ; uniqueID_out(function->s, number_string)

	mov rax, process_while_string_5 ; Using "ERROR in process_while\nMISSING )\n"
	mov rbx, close_paren        ; Using ")"
	call require_match          ; Make sure we have it

	call statement              ; Deal with the {....} part

	mov rax, process_while_string_6 ; Using "JUMP %WHILE_"
	call emit_out               ; Emit it

	mov rax, [function]         ; Using function
	mov rax, [rax+16]           ; function->S
	mov rbx, rcx                ; Passing NUMBER_STRING
	call uniqueID_out           ; uniqueID_out(function->s, number_string)

	mov rax, process_while_string_7 ; Using ":END_WHILE_"
	call emit_out               ; Emit it

	mov rax, [function]         ; Using function
	mov rax, [rax+16]           ; function->S
	call uniqueID_out           ; uniqueID_out(function->s, number_string)

	call restore_break_frame    ; Restore the old break frame

	pop rcx                     ; Restore RCX
	pop rbx                     ; Restore RBX
	ret

process_while_string_0: db "END_WHILE_", 0
process_while_string_1: db ":WHILE_", 0
process_while_string_2: db "ERROR in process_while", 10, "MISSING (", 10, 0
process_while_string_3: db "TEST", 10, "JUMP_EQ %END_WHILE_", 0
process_while_string_4: db "# THEN_while_", 0
process_while_string_5: db "ERROR in process_while", 10, "MISSING )", 10, 0
process_while_string_6: db "JUMP %WHILE_", 0
process_while_string_7: db ":END_WHILE_", 0


;; 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
	call save_break_frame       ; Save the frame

	mov rax, [current_count]    ; Using current count
	mov rbx, rax                ; Preparing for update
	add rbx, 1                  ; current_count + 1
	mov [current_count], rbx    ; current_count = current_count + 1
	call numerate_number        ; convert to string
	mov rcx, rax                ; put NUMBER_STRING in place

	mov rax, process_for_string_0 ; Using "FOR_END_"
	mov rbx, rcx                ; Passing NUMBER_STRING
	call set_break_frame        ; Set it and forget it

	mov rax, process_for_string_1 ; Using "# FOR_initialization_"
	call emit_out               ; Emit it

	mov rax, [function]         ; Using function
	mov rax, [rax+16]           ; function->S
	mov rbx, rcx                ; Passing NUMBER_STRING
	call uniqueID_out           ; uniqueID_out(function->s, number_string)

	mov rax, [global_token]     ; Using global_token
	mov rax, [rax]              ; global_token->NEXT
	mov [global_token], rax     ; global_token = global_token->NEXT

	mov rax, process_for_string_2 ; Using "ERROR in process_for\nMISSING (\n"
	mov rbx, open_paren         ; Using "("
	call require_match          ; Make Sure we have it

	mov rbx, [global_token]     ; Using global_token
	mov rbx, [rbx+16]           ; global_token->S
	mov rax, semicolon          ; Using ";"
	call match                  ; IF global_token->S == ";"
	cmp rax, 0                  ; Then no initializer
	je process_for_terminator   ; And skip getting the expression

	;; Deal with FOR (...; case
	call expression             ; Get the FOR ( ... ; part

process_for_terminator:
	mov rax, process_for_string_3 ; Using ":FOR_"
	call emit_out               ; Emit it

	mov rax, [function]         ; Using function
	mov rax, [rax+16]           ; function->S
	mov rbx, rcx                ; Passing NUMBER_STRING
	call uniqueID_out           ; uniqueID_out(function->s, number_string)

	mov rax, process_for_string_4 ; Using "ERROR in process_for\nMISSING ;1\n"
	mov rbx, semicolon          ; Using ";"
	call require_match          ; Make sure we have it

	call expression             ; Get the FOR ( ; ... ; Part

	mov rax, process_for_string_5 ; Using "TEST\nJUMP_EQ %FOR_END_"
	call emit_out               ; Emit it

	mov rax, [function]         ; Using function
	mov rax, [rax+16]           ; function->S
	mov rbx, rcx                ; Passing NUMBER_STRING
	call uniqueID_out           ; uniqueID_out(function->s, number_string)

	mov rax, process_for_string_6 ; Using "JUMP %FOR_THEN_"
	call emit_out               ; Emit it

	mov rax, [function]         ; Using function
	mov rax, [rax+16]           ; function->S
	call uniqueID_out           ; uniqueID_out(function->s, number_string)

	mov rax, process_for_string_7 ; Using ":FOR_ITER_"
	call emit_out               ; Emit it

	mov rax, [function]         ; Using function
	mov rax, [rax+16]           ; function->S
	call uniqueID_out           ; uniqueID_out(function->s, number_string)

	mov rax, process_for_string_8 ; Using "ERROR in process_for\nMISSING ;2\n"
	mov rbx, semicolon          ; Using ";"
	call require_match          ; Make sure we have it

	call expression             ; Get the FOR (;;...) part

	mov rax, process_for_string_9 ; Using "JUMP %FOR_"
	call emit_out               ; Emit it

	mov rax, [function]         ; Using function
	mov rax, [rax+16]           ; function->S
	mov rbx, rcx                ; Passing NUMBER_STRING
	call uniqueID_out           ; uniqueID_out(function->s, number_string)

	mov rax, process_for_string_10 ; Using ":FOR_THEN_"
	call emit_out               ; Emit it

	mov rax, [function]         ; Using function
	mov rax, [rax+16]           ; function->S
	call uniqueID_out           ; uniqueID_out(function->s, number_string)

	mov rax, process_for_string_11 ; Using "ERROR in process_for\nMISSING )\n"
	mov rbx, close_paren        ; Using ")"
	call require_match          ; Make sure we have it

	call statement              ; Get FOR (;;) {...} part

	mov rax, process_for_string_12 ; Using "JUMP %FOR_ITER_"
	call emit_out               ; Emit it

	mov rax, [function]         ; Using function
	mov rax, [rax+16]           ; function->S
	mov rbx, rcx                ; Passing NUMBER_STRING
	call uniqueID_out           ; uniqueID_out(function->s, number_string)

	mov rax, process_for_string_13 ; Using ":FOR_END_"
	call emit_out               ; Emit it

	mov rax, [function]         ; Using function
	mov rax, [rax+16]           ; function->S
	call uniqueID_out           ; uniqueID_out(function->s, number_string)

	call restore_break_frame    ; Restore the old break frame

	pop rcx                     ; Restore RCX
	pop rbx                     ; Restore RBX
	ret

process_for_string_0: db "FOR_END_", 0
process_for_string_1: db "# FOR_initialization_", 0
process_for_string_2: db "ERROR in process_for", 10, "MISSING (", 10, 0
process_for_string_3: db ":FOR_", 0
process_for_string_4: db "ERROR in process_for", 10, "MISSING ;1", 10, 0
process_for_string_5: db "TEST", 10, "JUMP_EQ %FOR_END_", 0
process_for_string_6: db "JUMP %FOR_THEN_", 0
process_for_string_7: db ":FOR_ITER_", 0
process_for_string_8: db "ERROR in process_for", 10, "MISSING ;2", 10, 0
process_for_string_9: db "JUMP %FOR_", 0
process_for_string_10: db ":FOR_THEN_", 0
process_for_string_11: db "ERROR in process_for",10, "MISSING )", 10, 0
process_for_string_12: db "JUMP %FOR_ITER_", 0
process_for_string_13: db ":FOR_END_", 0


;; 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
	mov rax, [break_target_head] ; Catch big error
	cmp rax, 0                  ; IF(NULL == break_target_head)
	je process_break_bad        ; I'm sorry Mr White but you have stage-3 lung cancer

	mov rax, [function]         ; Using function
	mov rcx, [rax+8]            ; I = function->LOCALS
	mov rbx, [break_frame]      ; Put break_frame in the right spot
	mov rax, process_break_string_1 ; Using "POP_RBX\t# break_cleanup_locals\n"

process_break_iter:
	cmp rcx, 0                  ; IF (NULL == I)
	je process_break_cleaned    ; We are done

	cmp rcx, rbx                ; IF I != break_frame
	je process_break_cleaned    ; We are done

	call emit_out               ; Emit it
	mov rcx, [rcx]              ; I = I->NEXT
	jmp process_break_iter      ; Keep looping

process_break_cleaned:
	mov rax, [global_token]     ; Using global_token
	mov rax, [rax]              ; global_token->NEXT
	mov [global_token], rax     ; global_token = global_token->NEXT

	mov rax, process_break_string_2 ; Using "JUMP %"
	call emit_out               ; Emit it

	mov rax, [break_target_head] ; Get what we are in
	call emit_out               ; Emit it

	mov rax, [break_target_func] ; Get what function we are in
	call emit_out               ; Emit it

	mov rax, underline          ; Using "_"
	call emit_out               ; Emit it

	mov rax, [break_target_num] ; Get dem digits
	call emit_out               ; Emit it

	mov rax, process_break_string_3 ; Using "\n"
	call emit_out               ; Emit it

	mov rax, process_break_string_4 ; Using "ERROR in break statement\nMissing ;\n"
	mov rbx, semicolon          ; Using ";"
	call require_match          ; Make sure we have it

	pop rcx                     ; Restore RCX
	pop rbx                     ; Restore RBX
	ret


process_break_bad:
	;; Breaking badly
	mov r14, 2                  ; write to standard error
;	call line_error             ; Write useful debug info
	mov rax, rcx                ; put S in the right place
	call File_Print             ; print it

	mov rax, process_break_string_0 ; Ending string
	call File_Print             ; print it
	jmp Exit_Failure            ; Abort Hard

process_break_string_0: db "Not inside of a loop or case statement", 0
process_break_string_1: db "POP_RBX", 9, "# break_cleanup_locals", 10, 0
process_break_string_2: db "JUMP %", 0
process_break_string_3: db 10, 0
process_break_string_4: db "ERROR in break statement",10, "Missing ;", 10, 0


;; 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
	call bitwise_expr           ; Collect bitwise expressions

	mov rbx, [global_token]     ; Using global_token
	mov rbx, [rbx+16]           ; global_token->S
	mov rax, equal              ; "="
	call match                  ; IF global_token->S == "="
	cmp rax, 0                  ; We have to deal with assignment
	jne expression_done         ; Looks like nope

	;; Deal with possible assignment
	mov rcx, expression_string_1 ; Assume "STORE_CHAR\n" by default
	mov rbx, [global_token]     ; Using global_token
	mov rbx, [rbx+8]            ; global_token->PREV
	mov rbx, [rbx+16]           ; global_token->PREV->S
	mov rax, close_bracket      ; Using "]"
	call match                  ; IF global_token->S == "]"
	cmp rax, 0                  ; Then we might have a char
	jne expression_int          ; Otherwise INT

	mov rbx, [current_target]   ; Using current_target
	mov rbx, [rbx+48]           ; current_target->NAME
	mov rax, type_char_indirect_name ; Using "char*"
	call match                  ; Intentional inefficiency because I feel like it
	cmp rax, 0                  ; IF current_target->NAME == "char*"
	jne expression_int          ; Do char anyway

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

expression_int:
	mov rcx, expression_string_0 ; Use "STORE_INTEGER\n"

expression_common:
	mov rax, expression         ; Passing expression
	call common_recursion       ; Recurse
	mov rax, rcx                ; Using Store
	call emit_out               ; Emit it
	mov rax, 0                  ; Using NULL
	mov [current_target], rax   ; current_target = NULL

expression_done:
	pop rcx                     ; Restore RCX
	pop rbx                     ; Restore RBX
	ret

expression_string_0: db "STORE_INTEGER", 10, 0
expression_string_1: db "STORE_CHAR", 10, 0


;; bitwise_expr function
;; Receives nothing
;; Returns nothing
;; Walks global_token list and updates output list
;; Just calls other functions
bitwise_expr:
	call relational_expr        ; Walk up the tree
	call 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

	mov rax, relational_expr    ; Using relational_expr
	mov rbx, bitwise_expr_stub_string_0 ; Using "AND_rax_rbx\n"
	mov rcx, bitwise_and        ; Using "&"
	mov rdx, bitwise_expr_stub  ; And recurse
	call general_recursion      ; Hit it

	mov rax, relational_expr    ; Using relational_expr
	mov rbx, bitwise_expr_stub_string_0 ; Using "AND_rax_rbx\n"
	mov rcx, logical_and        ; Using "&&"
	mov rdx, bitwise_expr_stub  ; And recurse
	call general_recursion      ; Hit it

	mov rax, relational_expr    ; Using relational_expr
	mov rbx, bitwise_expr_stub_string_1 ; Using "OR_rax_rbx\n"
	mov rcx, bitwise_or         ; Using "|"
	mov rdx, bitwise_expr_stub  ; And recurse
	call general_recursion      ; Hit it

	mov rax, relational_expr    ; Using relational_expr
	mov rbx, bitwise_expr_stub_string_1 ; Using "OR_rax_rbx\n"
	mov rcx, logical_or         ; Using "||"
	mov rdx, bitwise_expr_stub  ; And recurse
	call general_recursion      ; Hit it

	mov rax, relational_expr    ; Using relational_expr
	mov rbx, bitwise_expr_stub_string_2 ; Using "XOR_rbx_rax_into_rax\n"
	mov rcx, bitwise_xor        ; Using "^"
	mov rdx, bitwise_expr_stub  ; And recurse
	call general_recursion      ; Hit it

	pop rdx                     ; Restore RDX
	pop rcx                     ; Restore RCX
	pop rbx                     ; Restore RBX
	ret

bitwise_expr_stub_string_0: db "AND_rax_rbx", 10, 0
bitwise_expr_stub_string_1: db "OR_rax_rbx", 10, 0
bitwise_expr_stub_string_2: db "XOR_rbx_rax_into_rax", 10, 0


;; relational_expr function
;; Receives nothing
;; Returns Nothing
;; Walks global_token list and updates output list
;; just calls other function
relational_expr:
	call additive_expr          ; Walk up the tree
	call 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

	mov rax, additive_expr      ; Using additive_expr
	mov rbx, relational_expr_stub_string_0 ; Using "CMP\nSETL\nMOVEZX\n"
	mov rcx, less_than_string   ; Using "<"
	mov rdx, relational_expr_stub ; And recurse
	call general_recursion      ; Hit it

	mov rax, additive_expr      ; Using additive_expr
	mov rbx, relational_expr_stub_string_1 ; Using "CMP\nSETLE\nMOVEZX\n"
	mov rcx, less_than_equal_string ; Using "<="
	mov rdx, relational_expr_stub ; And recurse
	call general_recursion      ; Hit it

	mov rax, additive_expr      ; Using additive_expr
	mov rbx, relational_expr_stub_string_2 ; Using "CMP\nSETGE\nMOVEZX\n"
	mov rcx, greater_than_equal_string ; Using ">="
	mov rdx, relational_expr_stub ; And recurse
	call general_recursion      ; Hit it

	mov rax, additive_expr      ; Using additive_expr
	mov rbx, relational_expr_stub_string_3 ; Using "CMP\nSETG\nMOVEZX\n"
	mov rcx, greater_than_string ; Using ">"
	mov rdx, relational_expr_stub ; And recurse
	call general_recursion      ; Hit it

	mov rax, additive_expr      ; Using additive_expr
	mov rbx, relational_expr_stub_string_4 ; Using "CMP\nSETE\nMOVEZX\n"
	mov rcx, equal_to_string    ; Using "=="
	mov rdx, relational_expr_stub ; And recurse
	call general_recursion      ; Hit it

	mov rax, additive_expr      ; Using additive_expr
	mov rbx, relational_expr_stub_string_5 ; Using "CMP\nSETNE\nMOVEZX\n"
	mov rcx, not_equal_string   ; Using "!="
	mov rdx, relational_expr_stub ; And recurse
	call general_recursion      ; Hit it

	pop rdx                     ; Restore RDX
	pop rcx                     ; Restore RCX
	pop rbx                     ; Restore RBX
	ret

relational_expr_stub_string_0: db "CMP", 10, "SETL", 10, "MOVEZX", 10,  0
relational_expr_stub_string_1: db "CMP", 10, "SETLE", 10, "MOVEZX", 10, 0
relational_expr_stub_string_2: db "CMP", 10, "SETGE", 10, "MOVEZX", 10, 0
relational_expr_stub_string_3: db "CMP", 10, "SETG", 10, "MOVEZX", 10, 0
relational_expr_stub_string_4: db "CMP", 10, "SETE", 10, "MOVEZX", 10, 0
relational_expr_stub_string_5: db "CMP", 10, "SETNE", 10, "MOVEZX", 10, 0


;; additive_expr function
;; Receives nothing
;; Returns Nothing
;; Walks global_token list and updates output list
;; just calls other function
additive_expr:
	call postfix_expr           ; Walk up the tree
	call 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

	mov rax, postfix_expr       ; Using postfix_expr
	mov rbx, additive_expr_stub_string_0 ; Using "ADD_rbx_to_rax\n"
	mov rcx, plus_string        ; Using "+"
	mov rdx, additive_expr_stub ; And recurse
	call general_recursion      ; Hit it

	mov rax, postfix_expr       ; Using postfix_expr
	mov rbx, additive_expr_stub_string_1 ; Using "SUBTRACT_rax_from_rbx_into_rbx\nMOVE_rbx_to_rax\n"
	mov rcx, minus_string       ; Using "-"
	mov rdx, additive_expr_stub ; And recurse
	call general_recursion      ; Hit it

	mov rax, postfix_expr       ; Using postfix_expr
	mov rbx, additive_expr_stub_string_2 ; Using "MULTIPLY_rax_by_rbx_into_rax\n"
	mov rcx, multiply_string    ; Using "*"
	mov rdx, additive_expr_stub ; And recurse
	call general_recursion      ; Hit it

	mov rax, postfix_expr       ; Using postfix_expr
	mov rbx, additive_expr_stub_string_3 ; Using "XCHG_rax_rbx\nLOAD_IMMEDIATE_rdx %0\nDIVIDE_rax_by_rbx_into_rax\n"
	mov rcx, divide_string      ; Using "/"
	mov rdx, additive_expr_stub ; And recurse
	call general_recursion      ; Hit it

	mov rax, postfix_expr       ; Using postfix_expr
	mov 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"
	mov rcx, modulus_string     ; Using "%"
	mov rdx, additive_expr_stub ; And recurse
	call general_recursion      ; Hit it

	mov rax, postfix_expr       ; Using postfix_expr
	mov rbx, additive_expr_stub_string_5 ; Using "COPY_rax_to_rcx\nCOPY_rbx_to_rax\nSAL_rax_cl\n"
	mov rcx, left_shift_string  ; Using "<<"
	mov rdx, additive_expr_stub ; And recurse
	call general_recursion      ; Hit it

	mov rax, postfix_expr       ; Using postfix_expr
	mov rbx, additive_expr_stub_string_6 ; Using "COPY_rax_to_rcx\nCOPY_rbx_to_rax\nSAR_rax_cl\n"
	mov rcx, right_shift_string ; Using ">>"
	mov rdx, additive_expr_stub ; And recurse
	call general_recursion      ; Hit it

	pop rdx                     ; Restore RDX
	pop rcx                     ; Restore RCX
	pop rbx                     ; Restore RBX
	ret

additive_expr_stub_string_0: db "ADD_rbx_to_rax", 10, 0
additive_expr_stub_string_1: db "SUBTRACT_rax_from_rbx_into_rbx", 10, "MOVE_rbx_to_rax", 10, 0
additive_expr_stub_string_2: db "MULTIPLY_rax_by_rbx_into_rax", 10, 0
additive_expr_stub_string_3: db "XCHG_rax_rbx", 10, "LOAD_IMMEDIATE_rdx %0", 10, "DIVIDE_rax_by_rbx_into_rax", 10, 0
additive_expr_stub_string_4: db "XCHG_rax_rbx", 10, "LOAD_IMMEDIATE_rdx %0", 10, "MODULUS_rax_from_rbx_into_rbx", 10, "MOVE_rdx_to_rax", 10, 0
additive_expr_stub_string_5: db "COPY_rax_to_rcx", 10, "COPY_rbx_to_rax", 10, "SAL_rax_cl", 10, 0
additive_expr_stub_string_6: db "COPY_rax_to_rcx", 10, "COPY_rbx_to_rax", 10, "SAR_rax_cl", 10, 0


;; postfix_expr function
;; Receives nothing
;; Returns Nothing
;; Walks global_token list and updates output list
;; just calls other function
postfix_expr:
	call primary_expr           ; Walk up the tree
	call 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
	mov rbx, [global_token]     ; Using global_token
	mov rbx, [rbx+16]           ; global_token->S
	mov rax, open_bracket       ; Using "["
	call match                  ; IF global_token->S == "["
	cmp rax, 0                  ; then we have an array
	jne postfix_expr_stub_arrow ; Otherwise try arrow

	;; Deal with array
	call postfix_expr_array     ; Get it
	call postfix_expr_stub      ; Recurse

postfix_expr_stub_arrow:
	mov rax, arrow_string       ; Using "->"
	call match                  ; IF global_token->S == "->"
	cmp rax, 0                  ; Then we need to deal with struct offsets
	jne postfix_expr_stub_done  ; Otherwise be done

	;; Deal with arrow
	call postfix_expr_arrow     ; Get it
	call 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
	mov rax, [global_token]     ; Using global_token
	mov rax, [rax]              ; global_token->NEXT
	mov [global_token], rax     ; global_token = global_token->NEXT

	mov rax, unary_expr_sizeof_string_0 ; Using "ERROR in unary_expr\nMissing (\n"
	mov rbx, open_paren         ; Using "("
	call require_match          ; Make sure we have it

	call type_name              ; Get the type
	mov rcx, [rax+8]            ; Set A->TYPE

	mov rax, unary_expr_sizeof_string_1 ; Using "ERROR in unary_expr\nMissing )\n"
	mov rbx, close_paren        ; Using ")"
	call require_match          ; Make sure we have it

	mov rax, unary_expr_sizeof_string_2 ; Using "LOAD_IMMEDIATE_rax %"
	call emit_out               ; Emit it

	mov rax, rcx                ; Put A->SIZE in the right place
	call numerate_number        ; Turn into string
	call emit_out               ; Emit it

	mov rax, unary_expr_sizeof_string_3 ; Using "\n"
	call emit_out               ; Emit it

	pop rcx                     ; Restore RCX
	pop rbx                     ; Restore RBX
	ret

unary_expr_sizeof_string_0: db "ERROR in unary_expr", 10, "Missing (", 10, 0
unary_expr_sizeof_string_1: db "ERROR in unary_expr", 10, "Missing )", 10, 0
unary_expr_sizeof_string_2: db "LOAD_IMMEDIATE_rax %", 0
unary_expr_sizeof_string_3: db 10, 0


;; 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
	mov rax, [current_target]   ; ARRAY = current_target
	push rax                    ; Protect it

	mov rax, expression         ; Using expression
	call common_recursion       ; Recurse

	pop rbx                     ; Restore array
	mov [current_target], rbx   ; current_target = ARRAY

	mov rcx, postfix_expr_array_string_0 ; ASSIGN = "LOAD_INTEGER\n"

	mov rax, type_char_indirect_name ; Using "char*"
	mov rbx, [rbx+48]           ; current_target->NAME
	call match                  ; IF current_target->NAME == "char*"
	cmp rax, 0                  ; load a byte
	jne postfix_expr_array_large ; Otherwise adjust

	;; Deal with loading byte
	mov rcx, postfix_expr_array_string_1 ; ASSIGN = "LOAD_BYTE\n"
	jmp postfix_expr_array_common ; Do the next bit

postfix_expr_array_large:
	;; deal with arrays made of things other than chars
	mov rax, postfix_expr_array_string_2 ; Using "SAL_rax_Immediate8 !"
	call emit_out               ; Emit it

	mov rax, [current_target]   ; Using current_target
	mov rax, [rax+24]           ; current_target->INDIRECT
	mov rax, [rax+8]            ; current_target->INDIRECT->SIZE
	call ceil_log2              ; ceil_log2(current_target->indirect->size)
	call numerate_number        ; numerate_number(ceil_log2(current_target->indirect->size))
	call emit_out               ; Emit it

	mov rax, postfix_expr_array_string_3 ; Using "\n"
	call emit_out               ; Emit it

postfix_expr_array_common:
	mov rax, postfix_expr_array_string_4 ; Using "ADD_rbx_to_rax\n"
	call emit_out               ; Emit it

	mov rax, postfix_expr_array_string_5 ; Using "ERROR in postfix_expr\nMissing ]\n"
	mov rbx, close_bracket      ; Using "]"
	call require_match          ; Make sure we have it

	mov rbx, [global_token]     ; Using global_token
	mov rbx, [rbx+16]           ; global_token->S
	mov rax, equal              ; Using "="
	call match                  ; IF global_token->S == "="
	cmp rax, 0                  ; We need to preserve address
	jne postfix_expr_array_done ; Otherwise be done

	;; Clearing out assign
	mov rcx, postfix_expr_array_string_6 ; ASSIGN = ""

postfix_expr_array_done:
	mov rax, rcx                ; Using ASSIGN
	call emit_out               ; Emit it

	pop rcx                     ; Restore RCX
	pop rbx                     ; Restore RBX
	ret

postfix_expr_array_string_0: db "LOAD_INTEGER", 10, 0
postfix_expr_array_string_1: db "LOAD_BYTE", 10, 0
postfix_expr_array_string_2: db "SAL_rax_Immediate8 !", 0
postfix_expr_array_string_3: db 10, 0
postfix_expr_array_string_4: db "ADD_rbx_to_rax", 10, 0
postfix_expr_array_string_5: db "ERROR in postfix_expr", 10, "Missing ]", 10, 0
postfix_expr_array_string_6: db 0


;; 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
	mov rcx, 0                  ; RESULT = 0

	mov rbx, rax                ; put A in right place
	sub rax, 1                  ; (A - 1)
	and rax, rbx                ; A & (A - 1)
	cmp rax, 0                  ; IF 0 == (A & (A - 1))
	jne ceil_log2_iter          ; Starting from -1

	mov rcx, -1                 ; RESULT = -1

ceil_log2_iter:
	cmp rbx, 0                  ; IF A > 0
	jle ceil_log2_done          ; Otherwise be done

	add rcx, 1                  ; RESULT = RESULT + 1
	shr rbx, 1                  ; A = A >> 1
	jmp ceil_log2_iter          ; Keep looping

ceil_log2_done:
	mov rax, rcx                ; 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
	mov rax, postfix_expr_arrow_string_0 ; Using "# looking up offset\n"
	call emit_out               ; Emit it

	mov rax, [global_token]     ; Using global_token
	mov rax, [rax]              ; global_token->NEXT
	mov [global_token], rax     ; global_token = global_token->NEXT

	mov rbx, [rax+16]           ; Using global_token->S
	mov rax, [current_target]   ; Using current_target
	call lookup_member          ; lookup_member(current_target, global_token->s)
	mov rbx, rax                ; struct type* I = lookup_member(current_target, global_token->s)

	mov rax, [rax+40]           ; I->TYPE
	mov [current_target], rax   ; current_target = I->TYPE

	mov rax, [global_token]     ; Using global_token
	mov rax, [rax]              ; global_token->NEXT
	mov [global_token], rax     ; global_token = global_token->NEXT

	mov rax, [rbx+16]           ; I->OFFSET
	cmp rax, 0                  ; IF 0 != I->OFFSET
	je postfix_expr_arrow_first ; Then we don't need to do an offset

	;; Deal with needing an offset
	mov rax, postfix_expr_arrow_string_1 ; Using "# -> offset calculation\nLOAD_IMMEDIATE_rbx %"
	call emit_out               ; Emit it

	mov rax, [rbx+16]           ; I->OFFSET
	call numerate_number        ; Convert to string
	call emit_out               ; Emit it

	mov rax, postfix_expr_arrow_string_2 ; Using "\nADD_rbx_to_rax\n"
	call emit_out               ; Emit it

postfix_expr_arrow_first:
	mov rax, [rbx+8]            ; I->SIZE
	cmp rax, 4                  ; IF I->SIZE >= 4
	jl postfix_expr_arrow_done  ; Otherwise be done

	;; Last chance for load
	mov rax, [global_token]     ; Using global_token
	mov rbx, [rax+16]           ; global_token->S
	mov rax, equal              ; Using "="
	call match                  ; IF global_token->S == "="
	cmp rax, 0                  ; Then we have assignment and should not load
	je postfix_expr_arrow_done  ; Be done

	;; Deal with load case
	mov rax, postfix_expr_arrow_string_3 ; Using "LOAD_INTEGER\n"
	call emit_out               ; Emit it

postfix_expr_arrow_done:
	pop rbx                     ; Restore RBX
	ret

postfix_expr_arrow_string_0: db "# looking up offset", 10, 0
postfix_expr_arrow_string_1: db "# -> offset calculation", 10, "LOAD_IMMEDIATE_rbx %", 0
postfix_expr_arrow_string_2: db 10, "ADD_rbx_to_rax", 10, 0
postfix_expr_arrow_string_3: db "LOAD_INTEGER", 10, 0


;; primary_expr function
;; Receives nothing
;; Returns nothing
primary_expr:
	push rbx                    ; Protect RBX

	mov rax, [global_token]     ; Using global_token
	mov rbx, [rax+16]           ; global_token->S
	mov rax, sizeof_string      ; Using "sizeof"
	call match                  ; See if match
	cmp rax, 0                  ; IF match
	jne primary_expr_neg        ; Otherwise try negatives

	;; Deal with sizeof
	call unary_expr_sizeof      ; Lets do this
	jmp primary_expr_done       ; Be done

primary_expr_neg:
	mov rax, [global_token]     ; Using global_token
	mov rax, [rax+16]           ; global_token->S
	mov al, [rax]               ; global_token->S[0]
	movzx rax, al               ; Make it useful
	cmp rax, 45                 ; IF global_token->S[0] == "-"
	jne primary_expr_not        ; Otherwise try logical NOT

	;; Deal with negative numbers
	mov rax, primary_expr_string_0 ; Using "LOAD_IMMEDIATE_rax %0\n"
	call emit_out               ; Emit it

	mov rax, postfix_expr       ; Passing postfix_expr
	call common_recursion       ; Get what it is notting

	mov rax, primary_expr_string_1 ; Using "SUBTRACT_rax_from_rbx_into_rbx\nMOVE_rbx_to_rax\n"
	call emit_out               ; Emit it
	jmp primary_expr_done       ; Be done

primary_expr_not:
	mov rax, [global_token]     ; Using global_token
	mov rax, [rax+16]           ; global_token->S
	mov al, [rax]               ; global_token->S[0]
	movzx rax, al               ; Make it useful
	cmp rax, 33                 ; IF global_token->S[0] == "!"
	jne primary_expr_bin        ; Otherwise try '~'

	;; Deal with logical not
	mov rax, primary_expr_string_2 ; Using "LOAD_IMMEDIATE_rax %1\n"
	call emit_out               ; Emit it

	mov rax, postfix_expr       ; Passing postfix_expr
	call common_recursion       ; Get what it is notting

	mov rax, primary_expr_string_3 ; Using "XOR_rbx_rax_into_rax\n"
	call emit_out               ; Emit it
	jmp primary_expr_done       ; Be done

primary_expr_bin:
	mov rax, [global_token]     ; Using global_token
	mov rax, [rax+16]           ; global_token->S
	mov al, [rax]               ; global_token->S[0]
	movzx rax, al               ; Make it useful
	cmp rax, 126                ; IF global_token->S[0] == "~"
	jne primary_expr_paren      ; Otherwise try paren

	;; Deal with binary NOT
	mov rax, postfix_expr       ; Passing postfix_expr
	call common_recursion       ; Get what it is notting
	mov rax, primary_expr_string_4 ; Using "NOT_rax\n"
	call emit_out               ; Emit it
	jmp primary_expr_done       ; Be done

primary_expr_paren:
	mov rax, [global_token]     ; Using global_token
	mov rax, [rax+16]           ; global_token->S
	mov al, [rax]               ; global_token->S[0]
	movzx rax, al               ; Make it useful
	cmp rax, 40                 ; IF global_token->S[0] == "("
	jne primary_expr_ch         ; Otherwise try char

	;; deal with nesting
	mov rax, [global_token]     ; Using global_token
	mov rax, [rax]              ; global_token->NEXT
	mov [global_token], rax     ; global_token = global_token->NEXT
	call expression             ; Lets recurse
	mov rax, primary_expr_string_5 ; Using "Error in Primary expression\nDidn't get )\n"
	mov rbx, close_paren        ; Using ")"
	call require_match          ; Make sure we have it
	jmp primary_expr_done       ; Be done

primary_expr_ch:
	mov rax, [global_token]     ; Using global_token
	mov rax, [rax+16]           ; global_token->S
	mov al, [rax]               ; global_token->S[0]
	movzx rax, al               ; Make it useful
	cmp rax, 39                 ; Using "'"
	jne primary_expr_str        ; Otherwise try string

	;; Deal with chars
	call primary_expr_char      ; Handle that char
	jmp primary_expr_done       ; Be done

primary_expr_str:
	mov rax, [global_token]     ; Using global_token
	mov rax, [rax+16]           ; global_token->S
	mov al, [rax]               ; global_token->S[0]
	movzx rax, al               ; Make it useful
	cmp rax, 34                 ; Using '"'
	jne primary_expr_var        ; Otherwise try a variable

	;; Deal with strings
	call primary_expr_string    ; Handle that string
	jmp primary_expr_done       ; Be done

primary_expr_var:
	mov rax, [global_token]     ; Using global_token
	mov rax, [rax+16]           ; global_token->S
	mov al, [rax]               ; global_token->S[0]
	movzx rax, al               ; Make it useful
	mov rbx, primary_expr_string_6 ; Using "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"
	call In_Set                 ; See if we have a match
	cmp rax, 1                  ; IF match
	jne primary_expr_num        ; otherwise try number

	;; Deal with variables
	call primary_expr_variable  ; Deal with variable
	jmp primary_expr_done       ; Be done

primary_expr_num:
	mov rax, [global_token]     ; Using global_token
	mov rax, [rax+16]           ; global_token->S
	mov al, [rax]               ; global_token->S[0]
	movzx rax, al               ; Make it useful
	mov rbx, primary_expr_string_7 ; Using "0123456789"
	call In_Set                 ; See if we have a match
	cmp rax, 1                  ; IF match
	jne primary_expr_fail       ; otherwise we failed hard

	;; Deal with numbers
	call primary_expr_number    ; Collect the number
	jmp primary_expr_done       ; Be done

primary_expr_fail:
	;; looks like we hit bad input
	;; abort before it gets bad
	call primary_expr_failure   ; No match means failure
primary_expr_done:
	pop rbx                     ; Restore RBX
	ret

primary_expr_string_0: db "LOAD_IMMEDIATE_rax %0", 10, 0
primary_expr_string_1: db "SUBTRACT_rax_from_rbx_into_rbx", 10, "MOVE_rbx_to_rax", 10, 0
primary_expr_string_2: db "LOAD_IMMEDIATE_rax %1", 10, 0
primary_expr_string_3: db "XOR_rbx_rax_into_rax", 10, 0
primary_expr_string_4: db "NOT_rax", 10, 0
primary_expr_string_5: db "Error in Primary expression", 10, "Didn't get )", 10, 0
primary_expr_string_6: db "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_", 0
primary_expr_string_7:db "0123456789", 0


;; 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
	mov rax, [global_token]     ; Using global_token
	mov rcx, [rax+16]           ; S = global_token->S
	mov rax, [rax]              ; global_token->NEXT
	mov [global_token], rax     ; global_token = global_token->NEXT

	mov rax, rcx                ; Using S
	mov rbx, [global_constant_list] ; Using global_constant_list
	call sym_lookup             ; sym_lookup(s, global_constant_list)
	cmp rax, 0                  ; IF NULL == sym_lookup(s, global_constant_list)
	je primary_expr_variable_local ; Try locals next

	;; Deal with constant load
	mov rbx, [rax+32]              ; a->ARGS
	mov rax, primary_expr_variable_string_2 ; Using "LOAD_IMMEDIATE_rax %"
	call emit_out               ; Emit it

	mov rax, [rbx+16]           ; a->ARGS->S
	call emit_out               ; Emit it

	mov rax, primary_expr_variable_string_1 ; Using "\n"
	call emit_out               ; Emit it
	jmp primary_expr_variable_done ; Be done

primary_expr_variable_local:
	mov rax, rcx                ; Using S
	mov rbx, [function]         ; Using function
	mov rbx, [rbx+8]            ; function->locals
	call sym_lookup             ; sym_lookup(s, function->locals)
	cmp rax, 0                  ; IF NULL == sym_lookup(s, function->locals)
	je primary_expr_variable_arguments ; try arguments next

	;; Deal with local load
	call variable_load          ; Collect it
	jmp primary_expr_variable_done ; Be done

primary_expr_variable_arguments:
	mov rax, rcx                ; Using S
	mov rbx, [function]         ; Using function
	mov rbx, [rbx+32]           ; function->args
	call sym_lookup             ; sym_lookup(s, function->args)
	cmp rax, 0                  ; IF NULL == sym_lookup(s, function->args)
	je primary_expr_variable_function ; try functions next

	;; Deal with argument load
	call variable_load          ; Collect it
	jmp primary_expr_variable_done ; Be done

primary_expr_variable_function:
	mov rax, rcx                ; Using S
	mov rbx, [global_function_list] ; Using global_function_list
	call sym_lookup             ; sym_lookup(s, global_function_list)
	cmp rax, 0                  ; IF NULL == sym_lookup(s, global_function_list)
	je primary_expr_variable_global ; try globals next

	;; Deal with functions
	call function_load          ; Deal with the function
	jmp primary_expr_variable_done ; Be done

primary_expr_variable_global:
	mov rax, rcx                ; Using S
	mov rbx, [global_symbol_list] ; Using global_symbol_list
	call sym_lookup             ; sym_lookup(s, global_symbol_list)
	cmp rax, 0                  ; IF NULL == sym_lookup(s, global_symbol_list)
	je primary_expr_variable_error ; Give up

	;; Deal with globals
	call global_load               ; Collect that global
	jmp primary_expr_variable_done ; Be done

primary_expr_variable_error:
	mov r14, 2                  ; write to standard error
;	call line_error             ; Write useful debug info
	mov rax, rcx                ; put S in the right place
	call File_Print             ; print it

	mov rax, primary_expr_variable_string_0 ; Ending string
	call File_Print             ; print it
	jmp Exit_Failure            ; Abort Hard

primary_expr_variable_done:
	pop rcx                     ; Restore RCX
	pop rbx                     ; Restore RBX
	ret

primary_expr_variable_string_0: db " is not a defined symbol", 10, 0
primary_expr_variable_string_1: db 10, 0
primary_expr_variable_string_2: db "LOAD_IMMEDIATE_rax %", 0


;; 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
	mov rcx, rax                ; Put S in place
	mov rdx, rbx                ; Put BOOL in place
	mov rsi, 0                  ; PASSED = 0

	mov rax, function_call_string_0 ; Using "ERROR in process_expression_list\nNo ( was found\n"
	mov rbx, open_paren         ; Using "("
	call require_match          ; Make sure we have it

	mov rax, function_call_string_1 ; Using "PUSH_RDI\t# Prevent overwriting in recursion\n"
	call emit_out               ; Emit it

	mov rax, function_call_string_2 ; Using "PUSH_RBP\t# Protect the old base pointer\n"
	call emit_out               ; Emit it

	mov rax, function_call_string_3 ; Using "COPY_RSP_to_RDI\t# Copy new base pointer\n"
	call emit_out               ; Emit it

	mov rax, [global_token]     ; Using global_token
	mov rax, [rax+16]           ; global_token->S
	mov al, [rax]               ; global_token->S[0]
	movzx rax, al               ; Make it useful
	cmp rax, 41                 ; IF global_token->S[0] == ")"
	je function_call_gen_done   ; Then no arguments to send

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

	mov rax, function_call_string_4 ; Using "PUSH_RAX\t#_process_expression1\n"
	call emit_out               ; Emit it
	mov rsi, 1                  ; PASSED = 1

function_call_gen_iter:
	mov rax, [global_token]     ; Using global_token
	mov rax, [rax+16]           ; global_token->S
	mov al, [rax]               ; global_token->S[0]
	movzx rax, al               ; Make it useful
	cmp rax, 44                 ; IF global_token->S[0] == ","
	jne function_call_gen_done  ; Otherwise we are done

	mov rax, [global_token]     ; Using global_token
	mov rax, [rax]              ; global_token->NEXT
	mov [global_token], rax     ; global_token = global_token->NEXT

	call expression             ; Collect the argument

	mov rax, function_call_string_5 ; Using "PUSH_RAX\t#_process_expression2\n"
	call emit_out               ; Emit it
	add rsi, 1                  ; PASSED = PASSED + 1
	jmp function_call_gen_iter  ; Keep trying

function_call_gen_done:
	;; All is collected
	mov rax, function_call_string_6 ; Using "ERROR in process_expression_list\nNo ) was found\n"
	mov rbx, close_paren         ; Using ")"
	call require_match          ; Make sure we have it

	cmp rdx, 0                  ; IF(BOOL == TRUE)
	jne function_call_static    ; Otherwise it is a static call

	;; Deal with a passed function pointer
	mov rax, function_call_string_7 ; Using "LOAD_BASE_ADDRESS_rax %"
	call emit_out               ; Emit it

	mov rax, rcx                ; Using S
	call emit_out               ; Emit it

	mov rax, function_call_string_8 ; Using "\nLOAD_INTEGER\n"
	call emit_out               ; Emit it

	mov rax, function_call_string_9 ; Using "COPY_rdi_to_rbp\n"
	call emit_out               ; Emit it

	mov rax, function_call_string_10 ; Using "CALL_rax\n"
	call emit_out               ; Emit it

	mov rax, function_call_string_13 ; Using "POP_RBX\t# _process_expression_locals\n"
	jmp function_call_cleanup   ; Clean up

function_call_static:
	;; Deal with fixed function name
	mov rax, function_call_string_9 ; Using "COPY_rdi_to_rbp\n"
	call emit_out               ; Emit it

	mov rax, function_call_string_11 ; Using "CALL_IMMEDIATE %FUNCTION_"
	call emit_out               ; Emit it

	mov rax, rcx                ; Using S
	call emit_out               ; Emit it

	mov rax, function_call_string_12 ; Using "\n"
	call emit_out               ; Emit it

	mov rax, function_call_string_13 ; Using "POP_RBX\t# _process_expression_locals\n"

function_call_cleanup:
	cmp rsi, 0                  ; IF PASSED > 0
	jle function_call_done      ; Otherwise be done

	;; The desired string is already in RAX
	call emit_out               ; Emit it

	sub rsi, 1                  ; PASSED = PASSED - 1
	jmp function_call_cleanup   ; Keep going

function_call_done:
	mov rax, function_call_string_14 ; Using "POP_rbp\t# Restore old base pointer\n"
	call emit_out               ; Emit it

	mov rax, function_call_string_15 ; Using "POP_rdi\t# Prevent overwrite\n"
	call 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: db "ERROR in process_expression_list", 10, "No ( was found", 10, 0
function_call_string_1: db "PUSH_RDI", 9, "# Prevent overwriting in recursion", 10, 0
function_call_string_2: db "PUSH_RBP", 9, "# Protect the old base pointer", 10, 0
function_call_string_3: db "COPY_RSP_to_RDI", 9, "# Copy new base pointer", 10, 0
function_call_string_4: db "PUSH_RAX", 9, "#_process_expression1", 10, 0
function_call_string_5: db "PUSH_RAX", 9, "#_process_expression2", 10, 0
function_call_string_6: db "ERROR in process_expression_list", 10, "No ) was found", 10, 0
function_call_string_7: db "LOAD_BASE_ADDRESS_rax %", 0
function_call_string_8: db 10, "LOAD_INTEGER", 10, 0
function_call_string_9: db "COPY_rdi_to_rbp", 10, 0
function_call_string_10: db "CALL_rax", 10, 0
function_call_string_11: db "CALL_IMMEDIATE %FUNCTION_", 0
function_call_string_12: db 10, 0
function_call_string_13: db "POP_RBX", 9, "# _process_expression_locals", 10, 0
function_call_string_14: db "POP_RBP", 9, "# Restore old base pointer", 10, 0
function_call_string_15: db "POP_RDI", 9, "# Prevent overwrite", 10, 0


;; 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
	mov rcx, rax                ; Protect A

	mov rbx, [global_token]     ; Using global_token
	mov rbx, [rbx+16]           ; global_token->S
	mov rax, open_paren         ; Using "("
	call match                  ; IF global_token->S == "("
	cmp rax, 0                  ; Then it might be a function
	jne variable_load_regular   ; Otherwise it is regular

	mov rbx, [rcx+24]           ; A->TYPE
	mov rbx, [rbx+48]           ; A->TYPE->NAME
	mov rax, type_function_name ; Using "FUNCTION"
	call match                  ; IF A->TYPE->NAME == "FUNCTION"
	cmp rax, 0                  ; Then it must be a function
	jne variable_load_regular   ; otherwise just another regular

	;; deal with function
	mov rax, [rcx+32]           ; A->DEPTH
	call numerate_number        ; Convert to string
	mov rbx, 0                  ; pass 0 for true
	call function_call          ; Create the function call
	jmp variable_load_done      ; Be done

variable_load_regular:
	mov rax, [rcx+24]           ; A->TYPE
	mov [current_target], rax   ; current_target = A->TYPE

	mov rax, variable_load_string_0 ; Using "LOAD_BASE_ADDRESS_rax %"
	call emit_out               ; Emit it

	mov rax, [rcx+32]           ; A->DEPTH
	call numerate_number        ; Convert to string
	call emit_out               ; Emit it

	mov rax, variable_load_string_1 ; Using "\n"
	call emit_out               ; Emit it

	;; Check for special case of assignment
	mov rbx, [global_token]     ; Using global_token
	mov rbx, [rbx+16]           ; global_token->S
	mov rax, equal              ; Using "="
	call match                  ; IF global_token->S == "="
	cmp rax, 0                  ; Then we skip loading
	je variable_load_done       ; And be done

	;; Deal with common case
	mov rax, variable_load_string_2 ; Using "LOAD_INTEGER\n"
	call emit_out               ; Emit it

variable_load_done:
	pop rcx                     ; Restore RCX
	pop rbx                     ; Restore RBX
	ret

variable_load_string_0: db "LOAD_BASE_ADDRESS_rax %", 0
variable_load_string_1: db 10, 0
variable_load_string_2: db "LOAD_INTEGER", 10, 0


;; 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
	mov rax, [rax+16]           ; A->S
	mov rcx, rax                ; Protect A->S
	mov rbx, [global_token]     ; Using global_token
	mov rbx, [rbx+16]           ; global_token->S
	mov rax, open_paren         ; Using "("
	call match                  ; IF global_token->S == "("
	cmp rax, 0                  ; The we need to do a function call
	jne function_load_regular   ; Otherwise just load it's address

	;; Deal with function call
	mov rax, rcx                ; Using A->S
	mov rbx, 1                  ; Using FALSE
	call function_call          ; Deal with it
	jmp function_load_done      ; Be done

function_load_regular:
	mov rax, function_load_string_0 ; Using "LOAD_IMMEDIATE_rax &FUNCTION_"
	call emit_out               ; Emit it

	mov rax, rcx                ; Using A->S
	call emit_out               ; Emit it

	mov rax, function_load_string_1 ; Using "\n"
	call emit_out               ; Emit it

function_load_done:
	pop rcx                     ; Restore RCX
	pop rbx                     ; Restore RBX
	ret

function_load_string_0: db "LOAD_IMMEDIATE_rax &FUNCTION_", 0
function_load_string_1: db 10, 0


;; global_load function
;; Receives struct token_list* A in RAX
;; Returns nothing
;; Uses RBX to hold A->S
global_load:
	push rbx                    ; Protect RBX
	mov rbx, rax                ; Set as A
	mov rbx, [rbx+16]           ; Set as A->S

	mov rax, [rax+24]           ; A->TYPE
	mov [current_target], rax   ; current_target = A->TYPE

	mov rax, global_load_string_0 ; Using "LOAD_IMMEDIATE_rax &GLOBAL_"
	call emit_out               ; Emit it

	mov rax, rbx                ; Using A->S
	call emit_out               ; Emit it

	mov rax, global_load_string_1 ; Using "\n"
	call emit_out               ; Emit it

	mov rbx, [global_token]     ; Using global_token
	mov rbx, [rbx+16]           ; global_token->S
	mov rax, equal              ; "="
	call match                  ; IF global_token->S == "="
	cmp rax, 0                  ; We need to skip for assignment
	je global_load_done         ; and be done

	;; Otherwise we are loading the contents
	mov rax, global_load_string_2 ; Using "LOAD_INTEGER\n"
	call emit_out               ; Emit it

global_load_done:
	pop rbx                     ; Restore RBX
	ret

global_load_string_0: db "LOAD_IMMEDIATE_rax &GLOBAL_", 0
global_load_string_1: db 10, 0
global_load_string_2: db "LOAD_INTEGER", 10, 0


;; 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
	mov rcx, rbx                ; I = symbol_list
	mov rbx, rax                ; Put S in the right place
sym_lookup_iter:
	cmp rcx, 0                  ; IF NULL == I
	je sym_lookup_done          ; We failed to find match

	mov rax, [rcx+16]           ; Using I->S
	call match                  ; IF I->S == S
	cmp rax, 0                  ; then be done
	je sym_lookup_done          ; Failed

	mov rcx, [rcx]              ; I = I->NEXT
	jmp sym_lookup_iter         ; otherwise keep looping

sym_lookup_done:
	mov rax, rcx                ; 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:
	mov rax, primary_expr_number_string_0 ; Using "LOAD_IMMEDIATE_rax %"
	call emit_out               ; Emit it

	mov rax, [global_token]     ; Using global_token
	mov rax, [rax+16]           ; global_token->S
	call emit_out               ; Emit it

	mov rax, primary_expr_number_string_1 ; Using "\n"
	call emit_out               ; Emit it

	mov rax, [global_token]     ; Using global_token
	mov rax, [rax]              ; global_token->NEXT
	mov [global_token], rax     ; global_token = global_token->NEXT
	ret

primary_expr_number_string_0: db "LOAD_IMMEDIATE_rax %", 0
primary_expr_number_string_1: db 10, 0


;; 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
	mov rbx, [current_count]    ; Using current_count
	mov rax, rbx                ; And putting it in the right place
	call numerate_number        ; Get the string
	mov rcx, rax                ; protect number_string

	add rbx, 1                  ; current_count + 1
	mov [current_count], rbx    ; current_count = current_count + 1

	mov rax, primary_expr_string_string_0 ; Using "LOAD_IMMEDIATE_rax &STRING_"
	call emit_out               ; Emit it

	mov rax, [function]         ; Using function
	mov rax, [rax+16]           ; function->S
	mov rbx, rcx                ; Put number_string in the right place
	call uniqueID_out           ; Make it unique

	;; Generate the target
	mov rax, primary_expr_string_string_1 ; Using ":STRING_"
	mov rbx, [strings_list]     ; Using strings_list
	call emit                   ; Emit it
	mov rbx, rax                ; put new strings_list in place

	mov rax, [function]         ; Using function
	mov rax, [rax+16]           ; function->S
	call uniqueID               ; Make it unique
	mov rbx, rax                ; put new strings_list in place

	;; Parse the string
	mov rax, [global_token]     ; Using global token
	mov rax, [rax+16]           ; global_token->S
	call parse_string           ; convert to useful form
	call emit                   ; Emit it
	mov [strings_list], rax     ; Update Strings _list

	mov rax, [global_token]     ; Using global token
	mov rax, [rax]              ; global_token->NEXT
	mov [global_token], rax     ; global_token = global_token->NEXT

	pop rcx                     ; Restore RCX
	pop rbx                     ; Restore RBX
	ret

primary_expr_string_string_0: db "LOAD_IMMEDIATE_rax &STRING_", 0
primary_expr_string_string_1: db ":STRING_", 0


;; 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
	mov rax, primary_expr_char_string_0 ; Using "LOAD_IMMEDIATE_rax %"
	call emit_out               ; Emit it

	mov rax, [global_token]     ; Using global token
	mov rax, [rax+16]           ; global_token->S
	add rax, 1                  ; global_token->S + 1
	call escape_lookup          ; Get the char
	call numerate_number        ; Convert to string
	call emit_out               ; emit it

	mov rax, primary_expr_char_string_1 ; Using "\n"
	call emit_out               ; Emit it

	mov rax, [global_token]     ; Using global token
	mov rax, [rax]              ; global_token->NEXT
	mov [global_token], rax     ; global_token = global_token->NEXT
	pop rcx                     ; Restore RCX
	pop rbx                     ; Restore RBX
	ret

primary_expr_char_string_0: db "LOAD_IMMEDIATE_rax %", 0
primary_expr_char_string_1: db 10, 0


;; primary_expr_failure function
;; Receives nothing
;; Does not return but aborts hard
;; Complains about the bad input
primary_expr_failure:
;	call line_error             ; Get line of issue
	mov r14, 2                  ; write to standard error
	mov rax, primary_expr_failure_string_0 ; Using "Received "
	call File_Print             ; Print it

	mov rax, [global_token]     ; Using global_token
	mov rax, [rax+16]           ; global_token->S
	call File_Print             ; Print it

	mov rax, primary_expr_failure_string_1 ; Using " in primary_expr\n"
	call File_Print             ; Print it
	jmp Exit_Failure            ; Abort Hard

primary_expr_failure_string_0: db "Received ", 0
primary_expr_failure_string_1: db " in primary_expr", 10, 0


;; 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
	mov rbp, rax                ; Protect F
	mov rax, rcx                ; Put name in the right place
	mov rcx, rbx                ; Protect S

	mov rbx, [global_token]     ; Using global_token
	mov rbx, [rbx+16]           ; global_token->S
	call match                  ; IF match(name, global_token->s)
	cmp rax, 0                  ; If true we do
	jne general_recursion_done  ; Otherwise skip it

	;; Deal with the recursion
	mov rax, rbp                ; Put F in the right place
	call common_recursion       ; Recurse

	mov rax, rcx                ; Put S in the right place
	call emit_out               ; Emit it

	mov rax, rdx                ; 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, 0                  ; IF NULL == B
	je promote_type_done        ; Just return A

	mov rcx, rax                ; Put A in place
	mov rdx, rbx                ; Put B in place
	mov rax, rbx                ; IF NULL == A
	cmp rcx, 0                  ; Then we just return B
	je promote_type_done        ; Be done

	;; Looks like we need to walk the list
	mov rcx, [rcx+48]           ; A->NAME
	mov rdx, [rdx+48]           ; B->NAME
	mov rax, [global_types]     ; I = global_types
promote_type_iter:
	cmp rax, 0                  ; IF NULL == I
	je promote_type_done        ; Just be done

	mov rbx, [rax+48]           ; I->NAME
	cmp rbx, rcx                ; IF(A->NAME == I->NAME)
	je promote_type_done        ; Be done

	cmp rbx, rdx                ; IF(B->NAME == I->NAME)
	je promote_type_done        ; Be done

	mov rbx, [rax+24]           ; I->INDIRECT
	mov rbx, [rbx+48]           ; I->INDIRECT->NAME

	cmp rbx, rcx                ; IF(A->NAME == I->INDIRECT->NAME)
	je promote_type_done        ; Be done

	cmp rbx, rdx                ; IF(B->NAME == I->INDIRECT->NAME)
	je promote_type_done        ; Be done

	mov rax, [rax]              ; I = I->NEXT
	jmp 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
	mov rbx, rax                ; Put FUNCTION F safely out of the way
	mov rax, common_recursion_string_0 ; Using "PUSH_RAX\t#_common_recursion\n"
	call emit_out               ; Emit it

	mov rax, [global_token]     ; Using global_token
	mov rax, [rax]              ; global_token->NEXT
	mov [global_token], rax     ; global_token = global_token->NEXT

	mov rax, rbx                ; Prepare for function call
	mov rbx, [current_target]   ; Get last type
	call rax                    ; F();
	mov rax, [current_target]   ; Get current_target
	call promote_type           ; get the right type
	mov [current_target], rax   ; Set new current_target

	mov rax, common_recursion_string_1 ; Using "POP_RBX\t# _common_recursion\n"
	call emit_out               ; Emit it
	pop rbx                     ; Restore RBX
	ret

common_recursion_string_0: db "PUSH_RAX", 9, "#_common_recursion", 10, 0
common_recursion_string_1: db "POP_RBX", 9, "# _common_recursion", 10, 0


;; 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
	mov rcx, rax                ; put the message somewhere safe
	mov rax, [global_token]     ; Using global_token
	mov rax, [rax+16]           ; global_token->S
	call match                  ; IF required == global_token->S
	cmp rax, 0                  ; we are fine
	je require_match_good       ; otherwise pain

	;; Deal with bad times
;	call line_error             ; Tell user what went wrong
	mov r14, 2                  ; write to standard error
	mov rax, rcx                ; using our message
	call File_Print             ; Print it
	jmp Exit_Failure            ; Abort HARD

require_match_good:
	mov rax, [global_token]     ; Using global_token
	mov rax, [rax]              ; global_token->next
	mov [global_token], rax     ; 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
	call emit                   ; emit(s, l)
	mov rbx, rax                ; Put L in correct place
	mov rax, underline          ; Using "_"
	call emit                   ; emit("_", l)
	mov rbx, rax                ; Put L in correct place
	mov rax, rcx                ; Put num in correct place
	call emit                   ; emit(num, l)
	mov rbx, rax                ; Put L in correct place
	mov rax, uniqueID_string_0  ; Using "\n"
	call emit                   ; emit("\n", l)
	pop rcx                     ; Restore RCX
	pop rbx                     ; Restore RBX
	ret

uniqueID_string_0: db 10, 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
	mov rcx, rbx                ; Put num in right spot
	mov rbx, [output_list]      ; Using output_list
	call uniqueID               ; Get updated list
	mov [output_list], rax      ; 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
	mov rbx, [output_list]      ; Using output_list
	call emit                   ; emit it
	mov [output_list], rax      ; 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
	mov rcx, rax                ; put S out of the way
	mov rax, 40                 ; sizeof(struct token_list)
	call malloc                 ; get T
	mov [rax], rbx              ; t->next = head;
	mov [rax+16], rcx           ; 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
	mov rcx, rax                ; Put char* C in safe place
	mov al, [rcx]               ; Load c[0]
	movzx rax, al               ; make it useful
	cmp rax, 92                 ; If '\\' != c[0]
	jne escape_lookup_done      ; Be done

	mov rbx, rcx                ; Prepare for walk
	add rbx, 1                  ; increment
	mov bl, [rbx]               ; load c[1]
	movzx rbx, bl               ; make it useful

	cmp rbx, 120                ; Check if \x??
	je escape_lookup_hex        ; Deal with hex

	;; Deal with \? escapes
	mov rax, 10                 ; Guess "\n"
	cmp rbx, 110                ; If n
	je escape_lookup_done       ; Be done

	mov rax, 9                  ; Guess "\t"
	cmp rbx, 116                ; If t
	je escape_lookup_done       ; Be done

	mov rax, rbx                ; "\\", "'" and '"' all encode as themselves
	cmp rbx, 92                 ; If "\\"
	je escape_lookup_done       ; Be done
	cmp rbx, 39                 ; IF "'"
	je escape_lookup_done       ; Be done
	cmp rbx, 34                 ; IF '"'
	je escape_lookup_done       ; Be done

	mov rax, 13                 ; Guess "\r"
	cmp rbx, 114                ; IF r
	je escape_lookup_done       ; Be done

	;; Looks like we have no clue what we are doing
	;; Aborting hard
	mov r14, 2                  ; write to standard error
	mov rax, escape_lookup_string_0 ; Using "Unknown escape received: "
	call File_Print             ; Print it
	mov rax, rcx                ; Using C
	call File_Print             ; Print it
	mov rax, escape_lookup_string_1 ; Using " Unable to process\n"
	call File_Print             ; Print it
	jmp 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
	add rcx, 2                  ; increment
	mov al, [rcx]               ; c[2]
	movzx rax, al               ; make it useful
	add rcx, 1                  ; increment
	call char2hex               ; Get the hex value
	sal rax, 4                  ; c << 4
	mov bl, [rcx]               ; c[3]
	movzx rbx, bl               ; make it useful
	xchg rax, rbx               ; protect c << 4
	call char2hex               ; Get the hex value
	add rax, rbx                ; hex(c[2]) << 4 + hex(c[3])
	jmp escape_lookup_done      ; Be done

escape_lookup_string_0: db "Unknown escape received: ", 0
escape_lookup_string_1: db " Unable to process", 10, 0


;; char2hex function
;; Receives char in RAX
;; Returns hex or aborts hard
char2hex:
	sub rax, 48                 ; Try 0-9
	cmp rax, 10                 ; Otherwise fun times
	jl char2hex_done            ; Be done

	;; Deal with A-F
	and rax, 0xDF               ; Unset High bit turning a-f into A-F
	sub rax, 7                  ; Shift down into position
	cmp rax, 10                 ; Everything below A is bad
	jl char2hex_fail            ; And should fail
	cmp rax, 16                 ; Make sure we are below F
	jl char2hex_done            ; If so be done

char2hex_fail:
	;; Time to fail hard
	mov r14, 2                  ; write to standard error
	mov rax, char2hex_string_0  ; Using "Tried to print non-hex number\n"
	call File_Print             ; Print it
	jmp Exit_Failure            ; Abort Hard

char2hex_done:
	ret

char2hex_string_0: db "Tried to print non-hex number", 10, 0


;; parse_string function
;; Receives char* string in RAX
;; Returns cleaned up string
;; Protects char* string in RBX
parse_string:
	push rbx                    ; Protect RBX
	mov rbx, rax                ; Protect char* string
	call weird                  ; Determine if we have a weird string
	cmp rax, 0                  ; If weird
	je parse_string_weird       ; Deal with it

	;; Dealing with regular string
	mov rax, rbx                ; Passing Char* string
	call collect_regular_string ; Collect it
	jmp parse_string_done       ; Be done

parse_string_weird:
	mov rax, rbx                ; Passing Char* string
	call 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
	mov rcx, rax                ; Place string in safe place
	add rcx, 1                  ; increment past the "
weird_reset:
	mov al, [rcx]               ; Load a char
	movzx rax, al               ; Make it useful
	cmp rax, 0                  ; IF NULL == C
	je weird_false              ; Nothing weird found

	cmp rax, 92                 ; IF '\\'
	jne weird_escaped           ; Deal with escaping

	;; Deal with escape
	mov rax, rcx                ; We are passing the string
	call escape_lookup          ; to look it up

	add rcx, 1                  ; string = string + 1
	mov bl, [rcx]               ; get string[1]
	movzx rbx, bl               ; make it useful
	cmp rbx, 120                ; IF 'x' == string[1]
	jne weird_escaped           ; otherwise skip the gap

	add rcx, 2                  ; string = string + 2

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

	add rcx, 1                  ; string = string + 1

	;; Last chance for weird
	mov rbx, weird_string_1     ; Use "\t\n\r "
	call In_Set                 ; Check for special case
	cmp rax, 1                  ; IF TRUE
	jne weird_reset             ; Otherwise not in the special case

	;; Deal with possible special case
	mov al, [rcx]               ; Load string[1]
	movzx rax, al               ; Make it useful
	cmp rax, 58                 ; IF string[1] == ":"
	je weird_true               ; Then we hit the special case
	jmp weird_reset             ; Keep trying

weird_done:
	pop rcx                     ; Restore RCX
	pop rbx                     ; Restore RBX
	ret

weird_true:
	mov rax, 0                  ; Return true
	jmp weird_done              ; Be done

weird_false:
	mov rax, 1                  ; Return false
	jmp weird_done              ; Be done

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


;; 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
	mov rdx, rax                ; Protect our passed string
	mov rax, 256                ; We need 256bytes of storage
	call malloc                 ; Get our new pointer
	mov rcx, rax                ; put it in place
	push rax                    ; protect until done
collect_regular_string_reset:
	mov al, [rdx]               ; string[0]
	movzx rax, al               ; Make it useful
	cmp rax, 0                  ; See if we hit the end
	je collect_regular_string_done ; And be done

	cmp rax, 92                 ; IF string[0] == '\\'
	je collect_regular_string_escaped ; Deal with that mess

	;; deal with boring char
	mov [rcx], al               ; hold_string[index] = string[0]
	add rcx, 1                  ; Increment it
	add rdx, 1                  ; Increment it
	jmp collect_regular_string_reset ; And keep going

collect_regular_string_escaped:
	mov rax, rdx                ; Using string
	call escape_lookup          ; Get the char
	mov [rcx], al               ; hold_string[index] = escape_lookup(string)
	add rdx, 1                  ; Increment it
	add rcx, 1                  ; Increment it
	mov al, [rdx]               ; string[0]
	movzx rax, al               ; Make it useful
	add rdx, 1                  ; Increment it
	cmp rax, 120                ; IF 'x' == string[1]
	jne collect_regular_string_reset ; Otherwise keep going

	add rdx, 2                  ; Increment it
	jmp collect_regular_string_reset ; Keep going

collect_regular_string_done:
	mov rax, 34                 ; Using '"'
	mov [rcx], al               ; hold_string[index] = '"'
	add rcx, 1                  ; Increment it
	mov rax, 10                 ; Using "\n"
	mov [rcx], al               ; 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
	mov rdx, rax                ; Protect our passed string
	mov rax, 512                ; We need 512bytes of storage
	call malloc                 ; Get our new pointer
	mov rcx, rax                ; put it in place
	push rax                    ; protect until done

	mov rax, 39                 ; Using "'"
	mov [rcx], al               ; hold_string[index] = "'"
	add rcx, 1                  ; Increment it
	add rdx, 1                  ; Increment it
collect_weird_string_reset:
	mov al, [rdx]               ; Read a byte
	movzx rax, al               ; Make it useful
	cmp rax, 0                  ; IF NULL == string[0]
	je collect_weird_string_done ; Be done

	mov rax, 32                 ; Using ' '
	mov [rcx], al               ; hold_string[index] = ' '
	add rcx, 1                  ; Increment it

	mov rax, rdx                ; Using string
	call escape_lookup          ; Get the char
	call hex8                   ; Update RCX

	mov al, [rdx]               ; Read a byte
	movzx rax, al               ; Make it useful
	add rdx, 1                  ; Increment it
	cmp rax, 92                 ; IF string[0] == '\\'
	jne collect_weird_string_reset ; Otherwise keep going

	mov al, [rdx]               ; Read a byte
	movzx rax, al               ; Make it useful
	add rdx, 1                  ; Increment it
	cmp rax, 120                ; IF 'x' == string[1]
	jne collect_weird_string_reset ; Otherwise keep going

	add rdx, 2                  ; Increment it
	jmp collect_weird_string_reset ; Keep going

collect_weird_string_done:
	mov rax, 32                 ; Using ' '
	mov [rcx], al               ; hold_string[index] = ' '
	add rcx, 1                  ; Increment it
	mov rax, 48                 ; Using '0'
	mov [rcx], al               ; hold_string[index] = '0'
	add rcx, 1                  ; Increment it
	mov [rcx], al               ; hold_string[index] = '0'
	add rcx, 1                  ; Increment it
	mov rax, 39                 ; Using "'"
	mov [rcx], al               ; hold_string[index] = "'"
	add rcx, 1                  ; Increment it
	mov rax, 10                 ; Using "\n"
	mov [rcx], al               ; 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
	shr rax, 4                  ; do high nibble first
	call hex4                   ; Store it
	pop rax                     ; do low nibble
hex4:
	and rax, 0xf                ; isolate nibble
	add al,'0'                  ; convert to ascii
	cmp al,'9'                  ; valid digit?
	jbe hex1                    ; yes
	add al,7                    ; use alpha range
hex1:
	mov [ecx], al               ; store result
	add ecx, 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
	mov rbx, [global_token]     ; Using global_token
	mov rbx, [rbx+16]           ; global_token->S
	mov rax, struct             ; Using "struct"
	call match                  ; IF global_token->S == "struct"
	mov rcx, rax                ; Protect structure
	cmp rax, 0                  ; need to skip over "struct"
	jne type_name_native        ; otherwise keep going

	;; Deal with possible STRUCTs
	mov rbx, [global_token]     ; Using global_token
	mov rbx, [rbx]              ; global_token->next
	mov [global_token], rbx     ; global_token = global_token->next
	mov rax, [rbx+16]           ; global_token->S
	mov rbx, [global_types]     ; get all known types
	call lookup_type            ; Find type if possible
	mov rcx, rax                ; Set ret

	cmp rax, 0                  ; IF NULL == ret
	jne type_name_common        ; We have to create struct

	;; Create a struct
	call create_struct          ; Create a new struct
	mov rcx, 0                  ; We wish to return NULL
	jmp type_name_done          ; be done

type_name_native:
	;; Deal only with native types
	mov rax, rbx                ; Put global_token->S in the right place
	mov rbx, [global_types]     ; get all known types
	call lookup_type            ; Find the type if possible
	mov rcx, rax                ; Set ret

	cmp rax, 0                  ; IF NULL == ret
	jne type_name_common        ; We need to abort hard

	;; Aborting hard
	mov r14, 2                  ; write to standard error
	mov rax, type_name_string_0 ; Print header
	call File_Print             ; Print it

	mov rax, [global_token]     ; Using global token
	mov rax, [rax+16]           ; global_token->S
	call File_Print             ; Print it

	mov rax, type_name_string_1 ; Print footer
	call File_Print             ; Print it

;	call line_error             ; Give details
	jmp Exit_Failure           ; Abort

type_name_common:
	mov rbx, [global_token]     ; Using global_token
	mov rbx, [rbx]              ; global_token->next
	mov [global_token], rbx     ; global_token = global_token->next

type_name_iter:
	mov rax, [rbx+16]           ; global_token->S
	mov al, [rax]               ; global_token->S[0]
	movzx rax, al               ; make it useful
	cmp rax, 42                 ; IF global_token->S[0] == '*'
	jne type_name_done          ; recurse

	;; Deal with char**
	mov rcx, [rcx+24]           ; ret = ret->indirect
	mov rbx, [global_token]     ; Using global_token
	mov rbx, [rbx]              ; global_token->next
	mov [global_token], rbx     ; global_token = global_token->next
	jmp type_name_iter          ; keep looping

type_name_done:
	mov rax, rcx                ; put ret in the right place
	pop rcx                     ; Restore RCX
	pop rbx                     ; Restore RBX
	ret

type_name_string_0: db "Unknown type ", 0
type_name_string_1: db 10, 0


;; 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
	mov rcx, rbx                ; I = Start
	mov rbx, rax                ; Put S in place
lookup_type_iter:
	cmp rcx, 0                  ; Check if I == NULL
	je lookup_type_done         ; return NULL

	mov rax, [rcx+48]           ; I->NAME
	call match                  ; Check if matching
	cmp rax, 0                  ; IF I->NAME == S
	je lookup_type_done         ; return it

	mov rcx, [rcx]              ; Otherwise I = I->NEXT
	jmp lookup_type_iter        ; And keep looping

lookup_type_done:
	mov rax, rcx                ; 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
	mov rcx, 0                  ; OFFSET = 0
	mov rdi, 0                  ; member_size = 0

	mov rax, 56                 ; sizeof(struct type)
	call malloc                 ; malloc(sizeof(struct type))
	mov rdx, rax                ; Set HEAD

	mov rax, 56                 ; sizeof(struct type)
	call malloc                 ; malloc(sizeof(struct type))
	mov rbp, rax                ; Set I

	mov rax, [global_token]     ; Using global_token
	mov rax, [rax+16]           ; global_token->S
	mov [rdx+48], rax           ; HEAD->NAME = global_token->S
	mov [rbp+48], rax           ; I->NAME = global_token->S

	mov [rdx+24], rbp           ; HEAD->INDIRECT = I
	mov [rbp+24], rdx           ; I->INDIRECT = HEAD

	mov rax, [global_types]     ; Using global_types
	mov [rdx], rax              ; HEAD->NEXT = global_types
	mov [global_types], rdx     ; global_types = HEAD

	mov rax, [global_token]     ; Using global_token
	mov rax, [rax]              ; global_token->NEXT
	mov [global_token], rax     ; global_token = global_token->NEXT

	mov rax, 8                  ; Using register size
	mov [rbp+8], rax            ; I->SIZE = register size

	mov rax, create_struct_string_0 ; Using "ERROR in create_struct\n Missing {\n"
	mov rbx, open_curly_brace   ; Using "{"
	call require_match          ; Make sure we have it

	mov rsi, 0                  ; LAST = NULL

create_struct_iter:
	mov rax, [global_token]     ; Using global_token
	mov rax, [rax+16]           ; global_token->S
	mov al, [rax]               ; global_token->S[0]
	movzx rax, al               ; Make it useful
	cmp rax, 125                ; IF global_token->S[0] == "}"
	je create_struct_done       ; be done

	;; Looks like we are adding members
	;; Lets see if it is a union
	mov rax, [global_token]     ; Using global_token
	mov rax, [rax+16]           ; global_token->S
	mov rbx, union              ; Using "union"
	call match                  ; IF match(global_token->s, "union")
	cmp rax, 0                  ; Deal with union
	jne create_struct_single    ; Otherwise deal with singles

	;; Deal with union
	mov rax, rsi                ; Put last in right place
	mov rbx, rcx                ; put offset in right place
	call build_union            ; ASSEMBLE
	mov rsi, rax                ; last = build_union(last, offset)
	add rcx, rdi                ; offset = offset + member_size

	mov rax, create_struct_string_1 ; Using "ERROR in create_struct\n Missing ;\n"
	mov rbx, semicolon          ; Using ";"
	call require_match          ; Make sure we have it
	jmp create_struct_iter      ; keep going

create_struct_single:
	;; deal with singles
	mov rax, rsi                ; Put last in right place
	mov rbx, rcx                ; put offset in right place
	call build_member           ; ASSEMBLE
	mov rsi, rax                ; last = build_union(last, offset)
	add rcx, rdi                ; offset = offset + member_size

	mov rax, create_struct_string_1 ; Using "ERROR in create_struct\n Missing ;\n"
	mov rbx, semicolon          ; Using ";"
	call require_match          ; Make sure we have it
	jmp create_struct_iter      ; keep going

create_struct_done:
	mov rax, [global_token]     ; Using global_token
	mov rax, [rax]              ; global_token->NEXT
	mov [global_token], rax     ; global_token = global_token->NEXT

	mov rax, create_struct_string_1 ; Using "ERROR in create_struct\n Missing ;\n"
	mov rbx, semicolon          ; Using ";"
	call require_match          ; Make sure we have it

	mov [rdx+8], rcx            ; HEAD->SIZE = OFFSET
	mov [rdx+32], rsi           ; HEAD->MEMBERS = LAST
	mov [rbp+32], rsi           ; 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: db "ERROR in create_struct", 10, " Missing {", 10, 0
create_struct_string_1: db "ERROR in create_struct", 10, " Missing ;", 10, 0


;; 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
	mov rdx, rax                ; Protect Parent
	mov rcx, [rax+32]           ; struct type* I = parent->MEMBERS
lookup_member_iter:
	cmp rcx, 0                  ; IF I == NULL
	je lookup_member_fail       ; Abort HARD

	mov rax, [rcx+48]           ; Using I->NAME
	call match                  ; IF I->NAME == NAME
	cmp rax, 0                  ; Then we have found the member
	mov rax, rcx                ; Prepare for return
	mov rcx, [rcx+32]           ; Prepare for loop I = I->MEMBERS
	jne 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:
	mov r14, 2                  ; write to standard error
	mov rax, lookup_member_string_0 ; Using "ERROR in lookup_member "
	call File_Print             ; print it

	mov rax, [rdx+48]           ; PARENT->NAME
	call File_Print             ; print it

	mov rax, arrow_string       ; Using "->"
	call File_Print             ; print it

	mov rax, [global_token]     ; Using global_token
	mov rax, [rax+16]           ; global_token->S
	call File_Print             ; print it

	mov rax, lookup_member_string_1 ; Using " does not exist\n"
	call File_Print             ; print it

;	call line_error             ; Write useful debug info

	mov rax, lookup_member_string_2 ; Using "\n"
	call File_Print             ; print it
	jmp Exit_Failure            ; Abort Hard

lookup_member_string_0: db "ERROR in lookup_member ", 0
lookup_member_string_1: db " does not exist", 10, 0
lookup_member_string_2: db 10, 0


;; 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
	mov rdx, rax                ; Put last out of the way
	mov rax, 56                 ; Allocate type
	call malloc                 ; Get I
	mov [rax+32], rdx           ; I->MEMBERS = LAST
	mov [rax+16], rbx           ; I->OFFSET = OFFSET
	mov rdx, rax                ; Put I in place

	call type_name              ; Get member_type
	mov rcx, rax                ; Put in place
	mov [rdx+40], rcx           ; I->TYPE = MEMBER_TYPE
	mov rax, [global_token]     ; Using global_token
	mov rbx, [rax+16]           ; global_token->S
	mov [rdx+48], rbx           ; I->NAME = global_token->S
	mov rax, [rax]              ; global_token->NEXT
	mov [global_token], rax     ; global_token = global_token->NEXT

	;; Check if we have an array
	mov rbx, [rax+16]           ; global_token->S
	mov rax, open_bracket       ; Using "["
	call match                  ; IF global_token->S == "["
	cmp rax, 0                  ; Then we have to deal with arrays in our structs
	je build_member_array       ; So deal with that pain

	;; Deal with non-array case
	mov rax, [rcx+8]            ; member_type->SIZE
	mov [rdx+8], rax            ; I->SIZE = member_type->SIZE
	jmp build_member_done       ; Be done

build_member_array:
	mov rax, [global_token]     ; Using global_token
	mov rax, [rax]              ; global_token->NEXT
	mov [global_token], rax     ; global_token = global_token->NEXT

	mov rax, [rax+16]           ; global_token->S
	call numerate_string        ; convert number
	mov rbx, [rcx+40]           ; member_type->TYPE
	mov rbx, [rbx+8]            ; member_type->TYPE->SIZE
	imul rax, rbx               ; member_type->type->size * numerate_string(global_token->s)
	mov [rdx+8], rax            ; I->SIZE = member_type->type->size * numerate_string(global_token->s)

	mov rax, [global_token]     ; Using global_token
	mov rax, [rax]              ; global_token->NEXT
	mov [global_token], rax     ; global_token = global_token->NEXT

	mov rax, build_member_string_0 ; Using "Struct only supports [num] form\n"
	mov rbx, close_bracket      ; Using "]"
	call require_match          ; Make sure we have it

build_member_done:
	mov rdi, [rdx+8]            ; MEMBER_SIZE = I->SIZE
	mov [rdx+40], rcx           ; I->TYPE = MEMBER_TYPE
	mov rax, rdx                ; Return I

	pop rdx                     ; Restore RDX
	pop rcx                     ; Restore RCX
	pop rbx                     ; Restore RBX
	ret

build_member_string_0: db "Struct only supports [num] form", 10, 0


;; 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
	mov rcx, rax                ; Put LAST in right spot
	mov rdx, rbx                ; Put OFFSET in right spot
	mov rsi, 0                  ; SIZE = 0

	mov rax, [global_token]     ; Using global_token
	mov rax, [rax]              ; global_token->NEXT
	mov [global_token], rax     ; global_token = global_token->NEXT

	mov rax, build_union_string_0 ; Using "ERROR in build_union\nMissing {\n"
	mov rbx, open_curly_brace   ; Using "{"
	call require_match          ; Make sure we have it

build_union_iter:
	mov rax, [global_token]     ; Using global_token
	mov rax, [rax+16]           ; global_token->S
	mov al, [rax]               ; global_token->S[0]
	movzx rax, al               ; make it useful
	cmp rax, 125                ; IF global_token->S[0] == "}"
	je build_union_done         ; Be done

	;; Collect union member
	mov rax, rcx                ; Passing LAST
	mov rbx, rdx                ; Passing offset
	call build_member           ; build_member(last, offset)
	mov rcx, rax                ; last = build_member(last, offset)

	cmp rsi, rdi                ; IF member_size > size
	jg build_union_size         ; Then update size

	;; deal with member_size > size
	mov rsi, rdi                ; SIZE = MEMBER_SIZE

build_union_size:
	mov rax, build_union_string_1 ; Using "ERROR in build_union\nMissing ;\n"
	mov rbx, semicolon          ; Using ";"
	call require_match          ; Make sure we have it
	jmp build_union_iter        ; Keep going

build_union_done:
	mov rdi, rsi                ; MEMBER_SIZE = SIZE

	mov rax, [global_token]     ; Using global_token
	mov rax, [rax]              ; global_token->NEXT
	mov [global_token], rax     ; global_token = global_token->NEXT

	mov rax, rcx                ; Return last

	pop rsi                     ; Restore RSI
	pop rdx                     ; Restore RDX
	pop rcx                     ; Restore RCX
	pop rbx                     ; Restore RBX
	ret

build_union_string_0: db "ERROR in build_union", 10, "Missing {", 10, 0
build_union_string_1: db "ERROR in build_union", 10, "Missing ;", 10, 0


;; 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
	mov rdx, rax                ; Get char *S safely out of the way
	mov rax, 40                 ; Using sizeof(struct token_list)
	call malloc                 ; Get pointer to A
	mov [rax], rcx              ; A->NEXT = LIST
	mov [rax+16], rdx           ; A->S = S
	mov [rax+24], rbx           ; 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
	mov rcx, rax                ; S1 in place
	mov rdx, rbx                ; S2 in place
match_Loop:
	mov al, [rcx]               ; S1[0]
	movzx rax, al               ; Make it useful
	mov bl, [rdx]               ; S2[0]
	movzx rbx, bl               ; Make it useful
	cmp rax, rbx                ; See if they match
	jne match_False             ; If not

	add rcx, 1                  ; S1 = S1 + 1
	add rdx, 1                  ; S2 = S2 + 1
	cmp rax, 0                  ; If reached end of string
	je match_Done               ; Perfect match
	jmp match_Loop              ; Otherwise keep looping

match_False:
	mov rax, 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
	mov rbp, rax                ; Protect A

	mov rax, 16                 ; 16bytes
	call malloc                 ; Get our pointer
	push rax                    ; Protect our pointer
	mov rsi, rax                ; put pointer in right place
	mov rbx, 0x3B9ACA00         ; Set divisor to largest positive number that fits in 32bits

	cmp rbp, 0                  ; Deal with 0 case
	je numerate_number_ZERO     ; If it is
	jg numerate_number_positive ; If it is positive

	;; Deal with negative case
	mov rax, 45                 ; Using "-"
	mov [rsi], al               ; Write it
	add rsi, 1                  ; increment
	imul rbp, -1                ; A = A * -1

numerate_number_positive:
	mov rdx, 0                  ; Set top to 0
	mov rax, rbp                ; Using A as bottom
	idiv rbx                    ; rdx:rax % rbx -> rdx + rdx:rax / rbx -> rax [Even if we don't want it]
	cmp rax, 0                  ; IF 0 == (a / divisor)
	jne numerate_number_iter    ; Clean up those leading Zeros

	mov rdx, 0                  ; Set top to 0
	mov rax, rbx                ; Using Divisor for bottom
	mov rbx, 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]
	mov rbx, rax                ; Update divisor
	jmp numerate_number_positive ; Keep collecting

numerate_number_iter:
	cmp rbx, 0                  ; IF DIVISOR < 0
	jle numerate_number_done    ; Be done

	mov rdx, 0                  ; Set top to 0
	mov rax, rbp                ; Using A as bottom
	idiv rbx                    ; rdx:rax % rbx -> rdx + rdx:rax / rbx -> rax [Even if we don't want it]
	add rax, 48                 ; ((a / divisor) + 48)
	mov [rsi], al               ; Write it
	mov rbp, rdx                ; a = a % divisor

	mov rdx, 0                  ; Set top to 0
	mov rax, rbx                ; Using Divisor for bottom
	mov rbx, 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]
	mov rbx, rax                ; Update divisor

	add rsi, 1                  ; increment
	jmp 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:
	mov rax, 48                 ; Using '0'
	mov [rsi], al               ; Write it
	add rsi, 1                  ; increment
	jmp 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
	mov rbx, rax                ; put S in correct place
	mov rax, 0                  ; Initialize to Zero
numerate_string_loop:
	mov cl, [rbx+1]             ; S[1]
	movzx rcx, cl               ; make it useful
	cmp rcx, 120                ; IF 'x' == S[1]
	je numerate_hex             ; Deal with hex input

	;; Assume decimal input
	mov rsi, 0                  ; Assume no negation
	mov cl, [rbx]               ; S[0]
	movzx rcx, cl               ; make it useful
	cmp rcx, 45                 ; IF '-' == S[0]
	jne numerate_decimal        ; Skip negation

	mov rsi, 1                  ; Set FLAG
	add rbx, 1                  ; S = S + 1

numerate_decimal:
	mov cl, [rbx]               ; S[0]
	movzx rcx, cl               ; make it useful
	cmp rcx, 0                  ; IF NULL == S[0]
	je numerate_decimal_done    ; We are done

	imul rax, 10                ; VALUE = VALUE * 10
	sub rcx, 48                 ; CH = CH - '0'
	cmp rcx, 9                  ; Check for illegal
	jg numerate_string_fail     ; If CH > '9'
	cmp rcx, 0                  ; Check for illegal
	jl numerate_string_fail     ; IF CH < 0
	add rax, rcx                ; VALUE = VALUE + CH
	add rbx, 1                  ; S = S + 1
	jmp numerate_decimal        ; Keep looping

numerate_decimal_done:
	cmp rsi, 1                  ; Check if need to negate
	jne numerate_string_done    ; Nope

	imul rax, -1                ; VALUE = VALUE * -1
	jmp numerate_string_done    ; Done

numerate_hex:
	add rbx, 2                  ; S = S + 2
numerate_hex_loop:
	mov cl, [rbx]               ; S[0]
	movzx rcx, cl               ; make it useful
	cmp rcx, 0                  ; IF NULL == S[0]
	je numerate_string_done     ; We are done

	shl rax, 4                  ; VALUE = VALUE << 4
	sub rcx, 48                 ; CH = CH - '0'
	cmp rcx, 10                 ; IF 10 >= CH
	jl numerate_hex_digit       ; NO
	sub rcx, 7                  ; Push A-F into range
numerate_hex_digit:
	cmp rcx, 15                 ; Check for illegal
	jg numerate_string_fail     ; If CH > 'F'
	cmp rcx, 0                  ; Check for illegal
	jl numerate_string_fail     ; IF CH < 0
	add rax, rcx                ; VALUE = VALUE + CH
	add rbx, 1                  ; S = S + 1
	jmp numerate_hex_loop       ; Keep looping

numerate_string_fail:
	mov rax, 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:
	mov rdi, 1                  ; All is wrong
	mov rax, 0x3c               ; put the exit syscall number in eax
	syscall                     ; Call it a bad day

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


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


prim_types:
type_void:
	dq type_int                 ; NEXT
	dq 8                        ; SIZE
	dq 0                        ; OFFSET
	dq type_void                ; INDIRECT
	dq 0                        ; MEMBERS
	dq type_void                ; TYPE
	dq type_void_name           ; NAME
type_void_name: db "void", 0

type_int:
	dq type_char                ; NEXT
	dq 8                        ; SIZE
	dq 0                        ; OFFSET
	dq type_int                 ; INDIRECT
	dq 0                        ; MEMBERS
	dq type_int                 ; TYPE
	dq type_int_name            ; NAME
type_int_name: db "int", 0

type_char:
	dq type_file                ; NEXT
	dq 1                        ; SIZE
	dq 0                        ; OFFSET
	dq type_char_indirect       ; INDIRECT
	dq 0                        ; MEMBERS
	dq type_char                ; TYPE
	dq type_char_name           ; NAME
type_char_name: db "char", 0

type_char_indirect:
	dq type_file                ; NEXT
	dq 8                        ; SIZE
	dq 0                        ; OFFSET
	dq type_char_double_indirect ; INDIRECT
	dq 0                        ; MEMBERS
	dq type_char_indirect       ; TYPE
	dq type_char_indirect_name  ; NAME
type_char_indirect_name: db "char*", 0

type_char_double_indirect:
	dq type_file                ; NEXT
	dq 8                        ; SIZE
	dq 0                        ; OFFSET
	dq type_char_double_indirect ; INDIRECT
	dq 0                        ; MEMBERS
	dq type_char_indirect       ; TYPE
	dq type_char_double_indirect_name ; NAME
type_char_double_indirect_name: db "char**", 0

type_file:
	dq type_function            ; NEXT
	dq 8                        ; SIZE
	dq 0                        ; OFFSET
	dq type_file                ; INDIRECT
	dq 0                        ; MEMBERS
	dq type_file                ; TYPE
	dq type_file_name           ; NAME
type_file_name: db "FILE", 0

type_function:
	dq type_unsigned            ; NEXT
	dq 8                        ; SIZE
	dq 0                        ; OFFSET
	dq type_function            ; INDIRECT
	dq 0                        ; MEMBERS
	dq type_function            ; TYPE
	dq type_function_name       ; NAME
type_function_name: db "FUNCTION", 0

type_unsigned:
	dq type_long                ; NEXT
	dq 8                        ; SIZE
	dq 0                        ; OFFSET
	dq type_unsigned            ; INDIRECT
	dq 0                        ; MEMBERS
	dq type_unsigned            ; TYPE
	dq type_unsigned_name       ; NAME
type_unsigned_name: db "unsigned", 0

type_long:
	dq 0                        ; NEXT
	dq 8                        ; SIZE
	dq 0                        ; OFFSET
	dq type_long                ; INDIRECT
	dq 0                        ; MEMBERS
	dq type_long                ; TYPE
	dq type_long_name           ; NAME
type_long_name: db "long", 0


;; debug_list function
;; Receives struct token_list* in RAX
;; Prints contents of list and exits
;; Does NOT return
debug_list:
	mov r12, rax                ; Protect the list pointer
	mov r14, 2                  ; write to standard error

debug_list_iter:
	;; Header
	mov rax, debug_list_string0 ; Using our first string
	call File_Print             ; Print it
	mov rax, r12                ; Use address of pointer
	call numerate_number        ; Convert it into string
	call File_Print             ; Print it

	;; NEXT
	mov rax, debug_list_string1 ; Using our second string
	call File_Print             ; Print it
	mov rax, [r12]              ; Use address of pointer
	call numerate_number        ; Convert it into string
	call File_Print             ; Print it

	;; PREV
	mov rax, debug_list_string2 ; Using our third string
	call File_Print             ; Print it
	mov rax, [r12+8]            ; Use address of pointer
	call numerate_number        ; Convert it into string
	call File_Print             ; Print it

	;; S
	mov rax, debug_list_string3 ; Using our fourth string
	call File_Print             ; Print it
	mov rax, [r12+16]              ; Use address of pointer
	call numerate_number        ; Convert it into string
	call File_Print             ; Print it

	;; S Contents
	mov rax, debug_list_string4 ; Using our fifth string
	call File_Print             ; Print it
	mov rax, [r12+16]           ; Use address of string
	cmp rax, 0                  ; IF NULL Pointer
	jne debug_list_null         ; otherwise display
	mov rax, debug_list_string_null ; Give meaningful message instead
debug_list_null:
	call File_Print             ; Print it

	;; TYPE
	mov rax, debug_list_string5 ; Using our sixth string
	call File_Print             ; Print it
	mov rax, [r12+24]           ; Use address of pointer
	call numerate_number        ; Convert it into string
	call File_Print             ; Print it

	;; ARGS/DEPTH
	mov rax, debug_list_string6 ; Using our seventh string
	call File_Print             ; Print it
	mov rax, [r12+32]           ; Use address of pointer
	call numerate_number        ; Convert it into string
	call File_Print             ; Print it

	mov rax, 10                 ; Add "\n"
	call fputc                  ; print it
	call fputc                  ; print it

	mov r12, [r12]              ; TOKEN = TOKEN->NEXT
	cmp r12, 0                  ; Check if NULL
	jne debug_list_iter         ; iterate otherwise

	mov rdi, 666                ; All is HELL
	mov rax, 0x3c               ; put the exit syscall number in eax
	syscall                     ; Call it a bad day

debug_list_string0: db "Token_list node at address: ", 0
debug_list_string1: db 10, "NEXT address: ", 0
debug_list_string2: db 10, "PREV address: ", 0
debug_list_string3: db 10, "S address: ", 0
debug_list_string4: db 10, "The contents of S are: ", 0
debug_list_string5: db 10, "TYPE address: ", 0
debug_list_string6: db 10, "ARGUMENTS address: ", 0
debug_list_string_null: db ">::<NULL>::<", 0


section .data
Address_of: dq 0
C: dq 0
Token: dq 0
break_frame: dq 0
break_target_func: dq 0
break_target_head: dq 0
break_target_num: dq 0
current_count: dq 0
current_target: dq 0
function: dq 0
global_constant_list: dq 0
global_function_list: dq 0
global_symbol_list: dq 0
global_token: dq 0
global_types: dq prim_types
globals_list: dq 0
output_list: dq 0
string_index: dq 0
strings_list: dq 0
