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

DEFINE NULL64 0000000000000000
DEFINE NULL8 00

DEFINE PUSH_X0  e00f1ff8
DEFINE PUSH_X1  e10f1ff8
DEFINE PUSH_X2  e20f1ff8
DEFINE PUSH_X3  e30f1ff8
DEFINE PUSH_X4  e40f1ff8
DEFINE PUSH_X5  e50f1ff8
DEFINE PUSH_X6  e60f1ff8
DEFINE PUSH_X13 ed0f1ff8
DEFINE PUSH_LR  fe0f1ff8

DEFINE POP_X0 e00741f8
DEFINE POP_X1 e10741f8
DEFINE POP_X2 e20741f8
DEFINE POP_X3 e30741f8
DEFINE POP_X4 e40741f8
DEFINE POP_X5 e50741f8
DEFINE POP_X6 e60741f8
DEFINE POP_LR fe0741f8

DEFINE BLR_X0  00003fd6
DEFINE BLR_X16 00023fd6
DEFINE BR_X16 00021fd6
DEFINE RETURN c0035fd6

DEFINE CBNZ_X0_PAST_BR a00000b5

DEFINE CBZ_X7_PAST_INST 470000b4

DEFINE CMP_X0_TO_0   1f0000f1
DEFINE CMP_X0_TO_1   1f0400f1
DEFINE CMP_X0_TO_2   1f0800f1
DEFINE CMP_X0_TO_4   1f1000f1
DEFINE CMP_X0_TO_9   1f2400f1
DEFINE CMP_X0_TO_10  1f2800f1
DEFINE CMP_X0_TO_13  1f3400f1
DEFINE CMP_X0_TO_16  1f4000f1
DEFINE CMP_X0_TO_32  1f8000f1
DEFINE CMP_X0_TO_33  1f8400f1
DEFINE CMP_X0_TO_34  1f8800f1
DEFINE CMP_X0_TO_35  1f8c00f1
DEFINE CMP_X0_TO_36  1f9000f1
DEFINE CMP_X0_TO_37  1f9400f1
DEFINE CMP_X0_TO_38  1f9800f1
DEFINE CMP_X0_TO_39  1f9c00f1
DEFINE CMP_X0_TO_40  1fa000f1
DEFINE CMP_X0_TO_41  1fa400f1
DEFINE CMP_X0_TO_42  1fa800f1
DEFINE CMP_X0_TO_44  1fb000f1
DEFINE CMP_X0_TO_45  1fb400f1
DEFINE CMP_X0_TO_47  1fbc00f1
DEFINE CMP_X0_TO_48  1fc000f1
DEFINE CMP_X0_TO_57  1fe400f1
DEFINE CMP_X0_TO_58  1fe800f1
DEFINE CMP_X0_TO_59  1fec00f1
DEFINE CMP_X0_TO_62  1ff800f1
DEFINE CMP_X0_TO_64  1f0001f1
DEFINE CMP_X0_TO_65  1f0401f1
DEFINE CMP_X0_TO_71  1f1c01f1
DEFINE CMP_X0_TO_92  1f7001f1
DEFINE CMP_X0_TO_94  1f7801f1
DEFINE CMP_X0_TO_97  1f8401f1
DEFINE CMP_X0_TO_103 1f9c01f1
DEFINE CMP_X0_TO_120 1fe001f1
DEFINE CMP_X0_TO_125 1ff401f1
DEFINE CMP_X0_TO_126 1ff801f1
DEFINE CMP_X0_TO_MINUS_4 1f1000b1
DEFINE CMP_X1_TO_0   3f0000f1
DEFINE CMP_X1_TO_1   3f0400f1
DEFINE CMP_X1_TO_34  3f8800f1
DEFINE CMP_X1_TO_39  3f9c00f1
DEFINE CMP_X1_TO_62  3ff800f1
DEFINE CMP_X1_TO_92  3f7001f1
DEFINE CMP_X1_TO_110 3fb801f1
DEFINE CMP_X1_TO_114 3fc801f1
DEFINE CMP_X1_TO_116 3fd001f1
DEFINE CMP_X1_TO_120 3fe001f1
DEFINE CMP_X2_TO_0   5f0000f1
DEFINE CMP_X2_TO_9   5f2400f1
DEFINE CMP_X2_TO_10  5f2800f1
DEFINE CMP_X2_TO_15  5f3c00f1
DEFINE CMP_X2_TO_37  5f9400f1
DEFINE CMP_X2_TO_45  5fb400f1
DEFINE CMP_X2_TO_48  5fc000f1
DEFINE CMP_X2_TO_64  5f0001f1
DEFINE CMP_X2_TO_120 5fe001f1
DEFINE CMP_X3_TO_0   7f0000f1
DEFINE CMP_X4_TO_0   9f0000f1
DEFINE CMP_X4_TO_1   9f0400f1
DEFINE CMP_X5_TO_0   bf0000f1
DEFINE CMP_X5_TO_1   bf0400f1
DEFINE CMP_X12_TO_0  9f0100f1
DEFINE CMP_X15_TO_0  ff0100f1

DEFINE CMP_X0_X1  1f0001eb
DEFINE CMP_X0_X2  1f0002eb
DEFINE CMP_X1_X2  3f0002eb
DEFINE CMP_X1_X3  3f0003eb
DEFINE CMP_X2_X1  5f0001eb
DEFINE CMP_X5_X6  bf0006eb

DEFINE SET_X0_FROM_X1 e00301aa
DEFINE SET_X0_FROM_X2 e00302aa
DEFINE SET_X0_FROM_X3 e00303aa
DEFINE SET_X0_FROM_X4 e00304aa
DEFINE SET_X0_FROM_X5 e00305aa
DEFINE SET_X0_FROM_X9 e00309aa
DEFINE SET_X0_FROM_X10 e0030aaa
DEFINE SET_X0_FROM_X12 e0030caa
DEFINE SET_X0_FROM_X13 e0030daa
DEFINE SET_X0_FROM_X14 e0030eaa
DEFINE SET_X0_FROM_X15 e0030faa
DEFINE SET_X1_FROM_X0 e10300aa
DEFINE SET_X1_FROM_X2 e10302aa
DEFINE SET_X1_FROM_X3 e10303aa
DEFINE SET_X1_FROM_X12 e1030caa
DEFINE SET_X1_FROM_X13 e1030daa
DEFINE SET_X1_FROM_SP e1030091
DEFINE SET_X2_FROM_X0 e20300aa
DEFINE SET_X2_FROM_X1 e20301aa
DEFINE SET_X2_FROM_X6 e20306aa
DEFINE SET_X3_FROM_X0 e30300aa
DEFINE SET_X3_FROM_X1 e30301aa
DEFINE SET_X4_FROM_X0 e40300aa
DEFINE SET_X4_FROM_X3 e40303aa
DEFINE SET_X5_FROM_X0 e50300aa
DEFINE SET_X5_FROM_X6 e50306aa
DEFINE SET_X5_FROM_X11 e5030baa
DEFINE SET_X6_FROM_X5 e60305aa
DEFINE SET_X6_FROM_X13 e6030daa
DEFINE SET_X9_FROM_X0 e90300aa
DEFINE SET_X10_FROM_X0 ea0300aa
DEFINE SET_X11_FROM_X0 eb0300aa
DEFINE SET_X12_FROM_X0 ec0300aa
DEFINE SET_X13_FROM_X0 ed0300aa
DEFINE SET_X13_FROM_X3 ed0303aa
DEFINE SET_X12_FROM_X1 ec0301aa
DEFINE SET_X14_FROM_X0 ee0300aa
DEFINE SET_X15_FROM_X0 ef0300aa

DEFINE SET_X0_TO_0 000080d2
DEFINE SET_X0_TO_1 200080d2
DEFINE SET_X0_TO_2 400080d2
DEFINE SET_X0_TO_3 600080d2
DEFINE SET_X0_TO_5 a00080d2
DEFINE SET_X0_TO_8 000180d2
DEFINE SET_X0_TO_9 200180d2
DEFINE SET_X0_TO_10 400180d2
DEFINE SET_X0_TO_13 a00180d2
DEFINE SET_X0_TO_16 000280d2
DEFINE SET_X0_TO_32 000480d2
DEFINE SET_X0_TO_34 400480d2
DEFINE SET_X0_TO_39 e00480d2
DEFINE SET_X0_TO_40 000580d2
DEFINE SET_X0_TO_45 a00580d2
DEFINE SET_X0_TO_48 000680d2
DEFINE SET_X0_TO_56 000780d2
DEFINE SET_X0_TO_58 400780d2
DEFINE SET_X0_TO_256 002080d2
DEFINE SET_X0_TO_512 004080d2
DEFINE SET_X0_TO_666 405380d2
DEFINE SET_X0_TO_MINUS_1 00008092
DEFINE SET_X0_TO_MINUS_4 60008092
DEFINE SET_X1_TO_0 010080d2
DEFINE SET_X1_TO_1 210080d2
DEFINE SET_X1_TO_10 410180d2
DEFINE SET_X1_TO_0xCA00 014099d2
DEFINE SET_X2_TO_MINUS_1 02008092
DEFINE SET_X2_TO_0 020080d2
DEFINE SET_X2_TO_1 220080d2
DEFINE SET_X2_TO_2 420080d2
DEFINE SET_X2_TO_3 620080d2
DEFINE SET_X2_TO_4 820080d2
DEFINE SET_X2_TO_577 224880d2
DEFINE SET_X3_TO_0 030080d2
DEFINE SET_X3_TO_384 033080d2
DEFINE SET_X3_TO_448 033880d2
DEFINE SET_X4_TO_0 040080d2
DEFINE SET_X4_TO_1 240080d2
DEFINE SET_X5_TO_0 050080d2
DEFINE SET_X5_TO_1 250080d2
DEFINE SET_X6_TO_0 060080d2
DEFINE SET_X7_TO_0 070080d2
DEFINE SET_X9_TO_10 490180d2
DEFINE SET_X9_TO_223 e91b80d2
DEFINE SET_X11_TO_0 0b0080d2
DEFINE SET_X13_TO_0 0d0080d2
DEFINE SET_X13_TO_0x00600000 0d0ca0d2
DEFINE SET_X14_TO_0 0e0080d2
DEFINE SET_X14_TO_2 4e0080d2
DEFINE SET_X15_TO_0 0f0080d2
DEFINE SET_X15_TO_MINUS_1 0f008092
DEFINE SET_X0_TO_FCNTL_H_AT_FDCWD 600c8092

DEFINE SET16_X1_0x3B9A 4173a7f2

DEFINE SKIP_INST_EQ 40000054
DEFINE SKIP_INST_GE 4a000054
DEFINE SKIP_INST_LT 4b000054
DEFINE SKIP_INST_NE 41000054
DEFINE SKIP_INST_LE 4d000054
DEFINE SKIP_INST_GT 4c000054

DEFINE SKIP_32_DATA 02000014

DEFINE LOAD_W0_AHEAD 40000098
DEFINE LOAD_W1_AHEAD 41000018
DEFINE LOAD_W2_AHEAD 42000018
DEFINE LOAD_W3_AHEAD 43000018
DEFINE LOAD_W4_AHEAD 44000018
DEFINE LOAD_W9_AHEAD 49000018
DEFINE LOAD_W16_AHEAD 50000018

DEFINE LDR_X0_[X0]           000040f9
DEFINE LDR_X0_[X0,8]         000440f9
DEFINE LDR_X0_[X0,16]        000840f9
DEFINE LDR_X0_[X0,24]        000c40f9
DEFINE LDR_X0_[X0,32]        001040f9
DEFINE LDR_X0_[X0,40]        001440f9
DEFINE LDR_X0_[X1]           200040f9
DEFINE LDR_X0_[X1,8]         200440f9
DEFINE LDR_X0_[X1,16]         200840f9
DEFINE LDR_X0_[X1,24]         200c40f9
DEFINE LDR_X0_[X2]           400040f9
DEFINE LDR_X0_[X2,8]         400440f9
DEFINE LDR_X0_[X2,16]        400840f9
DEFINE LDR_X0_[X2,24]        400c40f9
DEFINE LDR_X0_[X2,32]        401040f9
DEFINE LDR_X0_[X2,48]        401840f9
DEFINE LDR_X0_[X3,8]         600440f9
DEFINE LDR_X0_[X3,16]        600840f9
DEFINE LDR_X0_[X3,24]        600c40f9
DEFINE LDR_X0_[X3,48]        601840f9
DEFINE LDR_X0_[X5,8]         a00440f9
DEFINE LDR_X0_[X9]           200140f9
DEFINE LDR_X0_[X12]          800140f9
DEFINE LDR_X0_[X12,8]        800540f9
DEFINE LDR_X0_[X12,16]       800940f9
DEFINE LDR_X0_[X12,24]       800d40f9
DEFINE LDR_X0_[X12,32]       801140f9
DEFINE LDR_X1_[X0,8]         010440f9
DEFINE LDR_X1_[X0,16]        010840f9
DEFINE LDR_X1_[X0,24]        010c40f9
DEFINE LDR_X1_[X0,32]        011040f9
DEFINE LDR_X1_[X0,48]        011840f9
DEFINE LDR_X1_[X1]           210040f9
DEFINE LDR_X1_[X1,8]         210440f9
DEFINE LDR_X1_[X1,16]        210840f9
DEFINE LDR_X1_[X1,32]        211040f9
DEFINE LDR_X1_[X1,48]        211840f9
DEFINE LDR_X1_[X2,16]        410840f9
DEFINE LDR_X1_[X2,24]        410c40f9
DEFINE LDR_X1_[X2,40]        411440f9
DEFINE LDR_X1_[X9]           210140f9
DEFINE LDR_X1_[SP,16]        e10b40f9
DEFINE LDR_X1_[SP,24]        e10f40f9
DEFINE LDR_X2_[X0,8]         020440f9
DEFINE LDR_X2_[X0,16]        020840f9
DEFINE LDR_X2_[X0,24]        020c40f9
DEFINE LDR_X2_[X0,32]        021040f9
DEFINE LDR_X2_[X1]           220040f9
DEFINE LDR_X2_[X2]           420040f9
DEFINE LDR_X2_[X2,8]         420440f9
DEFINE LDR_X2_[X2,16]        420840f9
DEFINE LDR_X2_[X2,24]        420c40f9
DEFINE LDR_X2_[X2,32]        421040f9
DEFINE LDR_X2_[X2,48]        421840f9
DEFINE LDR_X2_[X3,16]        620840f9
DEFINE LDR_X2_[X9]           220140f9
DEFINE LDR_X3_[X3]           630040f9
DEFINE LDR_X3_[X3,48]        631840f9
DEFINE LDR_X3_[X5,16]        a30840f9
DEFINE LDR_X6_[X3,8]         660440f9
DEFINE LDR_X12_[X12]         8c0140f9

DEFINE LDR_BYTE_W0_[X0]      00004039
DEFINE LDR_BYTE_W0_[X1]      20004039
DEFINE LDR_BYTE_W0_[X2]      40004039
DEFINE LDR_BYTE_W0_[X3]      60004039
DEFINE LDR_BYTE_W0_[X1,X2]   20686238
DEFINE LDR_BYTE_W0_[X1]_1    20144038
DEFINE LDR_BYTE_W0_[X2]_1    40144038
DEFINE LDR_BYTE_W0_[X3]_1    60144038
DEFINE LDR_BYTE_W1_[X1]      21004039
DEFINE LDR_BYTE_W1_[X2]      41004039
DEFINE LDR_BYTE_W1_[X3]      61004039
DEFINE LDR_BYTE_W1_[X0]_1    01144038
DEFINE LDR_BYTE_W1_[X3]_1    61144038
DEFINE LDR_BYTE_W1_[X4]_1    81144038
DEFINE LDR_BYTE_W2_[X1]_1    22144038
DEFINE LDR_BYTE_W2_[X0]      02004039
DEFINE LDR_BYTE_W2_[X1]      22004039
DEFINE LDR_BYTE_W3_[X4]      83004039

DEFINE LDR_BYTE_W2_[X1,1]    22044039

DEFINE STR_BYTE_W0_[X1]      20000039
DEFINE STR_BYTE_W0_[X2]      40000039
DEFINE STR_BYTE_W0_[X5]      a0000039
DEFINE STR_BYTE_W0_[X1]_1    20140038
DEFINE STR_BYTE_W1_[X2]      41000039
DEFINE STR_BYTE_W2_[X5]      a2000039
DEFINE STR_BYTE_W2_[X3]_1    62140038
DEFINE STR_BYTE_W3_[X1]_1    23140038
DEFINE STR_BYTE_W0_[X4,1]_WB 801c0038
DEFINE STR_X0_[X1]           200000f9
DEFINE STR_X0_[X2]           400000f9
DEFINE STR_X0_[X2,8]         400400f9
DEFINE STR_X0_[X2,16]        400800f9
DEFINE STR_X0_[X2,24]        400c00f9
DEFINE STR_X0_[X2,32]        401000f9
DEFINE STR_X0_[X3]           600000f9
DEFINE STR_X0_[X3,8]         600400f9
DEFINE STR_X0_[X3,16]        600800f9
DEFINE STR_X0_[X3,24]        600c00f9
DEFINE STR_X0_[X3,48]        601800f9
DEFINE STR_X0_[X4,8]         800400f9
DEFINE STR_X0_[X4,48]        801800f9
DEFINE STR_X0_[X9]           200100f9
DEFINE STR_X1_[X0]           010000f9
DEFINE STR_X1_[X0,16]        010800f9
DEFINE STR_X1_[X0,24]        010c00f9
DEFINE STR_X1_[X0,32]        011000f9
DEFINE STR_X1_[X2,24]        410c00f9
DEFINE STR_X1_[X3,48]        611800f9
DEFINE STR_X1_[X9]           210100f9
DEFINE STR_X2_[X0]           020000f9
DEFINE STR_X2_[X0,8]         020400f9
DEFINE STR_X2_[X0,16]        020800f9
DEFINE STR_X2_[X0,32]        021000f9
DEFINE STR_X2_[X3,8]         620400f9
DEFINE STR_X2_[X3,16]        620800f9
DEFINE STR_X2_[X3,24]        620c00f9
DEFINE STR_X2_[X3,40]        621400f9
DEFINE STR_X2_[X9]           220100f9
DEFINE STR_X3_[X0,16]        030800f9
DEFINE STR_X3_[X0,32]        031000f9
DEFINE STR_X3_[X4,24]        830c00f9
DEFINE STR_X3_[X9]           230100f9
DEFINE STR_X4_[X3,24]        640c00f9
DEFINE STR_X5_[X3,32]        651000f9
DEFINE STR_X5_[X4,32]        851000f9
DEFINE STR_X11_[X0]          0b0000f9
DEFINE STR_X12_[X0,16]       0c0800f9
DEFINE STR_X13_[X0,8]        0d0400f9
DEFINE STR_X13_[X0]          0d0000f9
DEFINE STR_X13_[X3]          6d0000f9

DEFINE DEREF_X0 000040f9
DEFINE DEREF_X5 a50040f9

DEFINE ADD_X0_X0_X14_LSL_4 00100e8b
DEFINE ADD_X0_X1_X0_LSL_3 200c008b
DEFINE ADD_X12_X12_24 8c610091

DEFINE ADD_X0_X0_X1  0000018b
DEFINE ADD_X0_X0_X2  0000028b
DEFINE ADD_X0_X0_X12 00000c8b
DEFINE ADD_X0_X0_X13 00000d8b
DEFINE ADD_X2_X2_X6  4200068b

DEFINE AND_X0_X0_X1 0000018a
DEFINE AND_X0_X0_X9 0000098a


DEFINE ADD_X0_X0_1    00040091
DEFINE ADD_X0_X0_7    001c0091
DEFINE ADD_X0_X0_8    00200091
DEFINE ADD_X0_X0_48   00c00091
DEFINE ADD_X0_X0_12MB 00007091
DEFINE ADD_X1_X1_1   21040091
DEFINE ADD_X1_X1_2   21080091
DEFINE ADD_X2_X2_1   42040091
DEFINE ADD_X2_X2_2   42080091
DEFINE ADD_X2_X2_48  42c00091
DEFINE ADD_X3_X3_1   63040091
DEFINE ADD_X3_X3_2   63080091
DEFINE ADD_X5_X5_1   a5040091
DEFINE ADD_X13_X13_1 ad050091
DEFINE ADD_X13_2     ad090091
DEFINE ADD_X13_3     ad0d0091

DEFINE SUB_X0_X0_X2 000002cb
DEFINE SUB_X0_X0_X13 00000dcb

DEFINE SUB_X0_X0_1  000400d1
DEFINE SUB_X0_X0_7  001c00d1
DEFINE SUB_X0_X0_48 00c000d1
DEFINE SUB_X0_X0_55 00dc00d1
DEFINE SUB_X0_X0_87 005c01d1
DEFINE SUB_X2_X2_7  421c00d1
DEFINE SUB_X2_X2_48 42c000d1
DEFINE SUB_X5_X5_1 a50400d1


DEFINE AND_X0_X0_0xF  000c4092
DEFINE AND_X0_X0_0xFF 001c4092

DEFINE ASR_X0_X0_2 00fc4293
DEFINE ASR_X1_X1_1 21fc4193
DEFINE LSR_X0_X0_2 00fc42d3
DEFINE LSR_X0_X0_4 00fc44d3
DEFINE LSR_X0_X0_8 00fc48d3
DEFINE LSR_X0_X0_16 00fc50d3
DEFINE LSR_X0_X0_32 00fc60d3
DEFINE LSL_X0_X0_3 00f07dd3
DEFINE LSL_X0_X0_4 00ec7cd3

DEFINE MUL_X0_X0_X1     007c019b
DEFINE MUL_X0_X0_X9     007c099b
DEFINE MSUB_X3_X2_X1_X0 4380019b

DEFINE NEG_X0_X0 e00300cb
DEFINE NEG_X4_X4 e40304cb

DEFINE UDIV_X0_X0_X1 0008c19a
DEFINE UDIV_X2_X0_X1 0208c19a


DEFINE NOT_X7_X7 e70327aa
DEFINE NOT_X15_X15 ef032faa

DEFINE SET_X8_TO_SYS_BRK c81a80d2
DEFINE SET_X8_TO_SYS_EXIT a80b80d2
DEFINE SET_X8_TO_SYS_LSEEK c80780d2
DEFINE SET_X8_TO_SYS_OPENAT 080780d2
DEFINE SET_X8_TO_SYS_READ e80780d2
DEFINE SET_X8_TO_SYS_WRITE 080880d2
DEFINE SYSCALL 010000d4

DEFINE FCALL 94
DEFINE RCALL 97
DEFINE FBRANCH 14
DEFINE RBRANCH 17

    # Register usage:
    # X0, X5, X6 => Temps
    # X13 => MALLOC
    # X14 => Output_file
    # X15 => 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
    LDR_X1_[SP,16]                      # Get the actual input name
    SET_X0_TO_FCNTL_H_AT_FDCWD          # AT_FDCWD, relative to current working directory
    SET_X2_TO_0                         # prepare read_only
    SET_X8_TO_SYS_OPENAT                # The syscall number for openat(), aarch64 has no open()
    SYSCALL                             # Now open that damn file
    SET_X15_FROM_X0                     # Preserve the file pointer we were given

    LDR_X1_[SP,24]                      # Get the output name
    SET_X0_TO_FCNTL_H_AT_FDCWD          # AT_FDCWD, relative to current working directory
    SET_X2_TO_577                       # Prepare file as O_WRONLY|O_CREAT|O_TRUNC
    SET_X3_TO_384                       # Prepare file as RWX for owner only (600 in octal)
    SET_X8_TO_SYS_OPENAT                # The syscall number for openat(), aarch64 has no open()
    SYSCALL                             # Open file!
    CMP_X0_TO_0                         # Check for missing output
    SKIP_INST_LE                        # Have real output
    ^~_start_out FBRANCH
    SET_X0_TO_1                         # Use stdout

:_start_out
    SET_X14_FROM_X0                     # Preserve the file pointer we were given

    SET_X8_TO_SYS_BRK                   # the Syscall # for SYS_BRK
    SET_X0_TO_0                         # Get current brk
    SYSCALL                             # Let the kernel do the work
    SET_X13_FROM_X0                     # Set our malloc pointer
    SET_X0_TO_0                         # HEAD = NULL
    ^~read_all_tokens FCALL             # Read all tokens
    ^~Reverse_List FCALL                # Reverse order
#   ^~debug_list FCALL                  # Try to figure out what is wrong
    LOAD_W9_AHEAD                       # Set global_token
    SKIP_32_DATA
    &global_token
    STR_X0_[X9]
    ^~program FCALL                     # Convert into program
    LOAD_W0_AHEAD                       # Our header string
    SKIP_32_DATA
    &header_string1
    ^~File_Print FCALL                  # Print it
    LOAD_W9_AHEAD                       # Our output_list
    SKIP_32_DATA
    &output_list
    LDR_X0_[X9]
    ^~recursive_output FCALL            # Print core program
#   LOAD_W0_AHEAD                       # Our Enable debug
#   SKIP_32_DATA
#   &header_string2
#   File_Print CALL                     # Print it
    LOAD_W0_AHEAD                       # Our second label
    SKIP_32_DATA
    &header_string3
    ^~File_Print FCALL                  # Print it
    LOAD_W9_AHEAD                       # Our globals
    SKIP_32_DATA
    &globals_list
    LDR_X0_[X9]
    ^~recursive_output FCALL            # Get them
    LOAD_W0_AHEAD                       # Our final header
    SKIP_32_DATA
    &header_string4
    ^~File_Print FCALL                  # Print it
    LOAD_W9_AHEAD                       # Our strings
    SKIP_32_DATA
    &strings_list
    LDR_X0_[X9]
    ^~recursive_output FCALL            # Get them
    LOAD_W0_AHEAD                       # Our final header
    SKIP_32_DATA
    &header_string5
    ^~File_Print FCALL                  # Print it

:Done
    # program completed Successfully
    SET_X0_TO_0                         # All is well
    SET_X8_TO_SYS_EXIT                  # put the exit syscall number in eax
    SYSCALL                             # Call it a good day

# read_all_tokens function
# Receives FILE* in X15 and Token_List* in X0
# Tokenizes all input and returns updated list in X0
# Returns TOKEN in X0
# Uses X0 for C
:read_all_tokens
    PUSH_LR
    LOAD_W9_AHEAD
    SKIP_32_DATA
    &Token
    STR_X0_[X9]
    ^~fgetc FCALL
:read_all_tokens_loop
    CMP_X0_TO_MINUS_4                   # Check for EOF
    SKIP_INST_NE                        # Stop if found
    ^~read_all_tokens_done FBRANCH
    ^~get_token FCALL                   # Read all tokens
    ^~read_all_tokens_loop RBRANCH      # Loop
:read_all_tokens_done
    LOAD_W9_AHEAD
    SKIP_32_DATA
    &Token
    LDR_X0_[X9]
    POP_LR
    RETURN


# get_token function
# Receives INT in X0 and FILE* in X15
# Makes a list of TOKEN_LIST
# C and STRING_INDEX are stored in memory, X2 is used for S and X3 is used for current
# Returns C in X0
:get_token
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    PUSH_X3                             # Protect X3

    LOAD_W9_AHEAD                       # Set C
    SKIP_32_DATA
    &C
    STR_X0_[X9]

    SET_X0_TO_40                        # Malloc CURRENT
    ^~malloc FCALL                      # Get Pointer
    SET_X3_FROM_X0                      # Set CURRENT

    SET_X0_TO_256                       # Malloc the string
    ^~malloc FCALL                      # Get pointer to S
    SET_X2_FROM_X0                      # Set S
    STR_X2_[X3,16]                      # CURRENT->S = S
:reset
    LOAD_W9_AHEAD                       # S[0]
    SKIP_32_DATA
    &string_index
    STR_X2_[X9]
    LOAD_W9_AHEAD                       # Using C
    SKIP_32_DATA
    &C
    LDR_X0_[X9]

    ^~clear_white_space FCALL           # Clear WhiteSpace
    LOAD_W9_AHEAD                       # Set C
    SKIP_32_DATA
    &C
    STR_X0_[X9]

    CMP_X0_TO_MINUS_4                   # Check for EOF
    SKIP_INST_NE                        # if EOF abort
    ^~get_token_abort FBRANCH

    CMP_X0_TO_35                        # Check for '#'
    SKIP_INST_EQ                        # Nope
    ^~get_token_alpha FBRANCH

    # Deal with # line comments
    ^~purge_macro FCALL                 # Let it handle it
    LOAD_W9_AHEAD                       # Set C
    SKIP_32_DATA
    &C
    STR_X0_[X9]
    ^~reset RBRANCH                     # Try again

:get_token_alpha
    LOAD_W9_AHEAD                       # Send C
    SKIP_32_DATA
    &C
    LDR_X0_[X9]
    LOAD_W1_AHEAD                       # Get alphanumerics
    SKIP_32_DATA
    &alphas
    ^~In_Set FCALL                      # See if in set
    CMP_X0_TO_1                         # IF TRUE
    SKIP_INST_EQ                        # Otherwise
    ^~get_token_symbol FBRANCH

    # Store keywords
    LOAD_W9_AHEAD                       # Send C
    SKIP_32_DATA
    &C
    LDR_X0_[X9]
    ^~preserve_keyword FCALL            # Store
    LOAD_W9_AHEAD                       # Set C
    SKIP_32_DATA
    &C
    STR_X0_[X9]
    ^~get_token_done FBRANCH            # Be done with this token

:get_token_symbol
    LOAD_W9_AHEAD                       # Send C
    SKIP_32_DATA
    &C
    LDR_X0_[X9]
    LOAD_W1_AHEAD                       # Get symbols
    SKIP_32_DATA
    &symbols
    ^~In_Set FCALL                      # See if in set
    CMP_X0_TO_1                         # IF TRUE
    SKIP_INST_EQ                        # Otherwise
    ^~get_token_strings FBRANCH

    # Store symbols
    LOAD_W9_AHEAD                       # Send C
    SKIP_32_DATA
    &C
    LDR_X0_[X9]
    ^~preserve_symbol FCALL             # Store
    LOAD_W9_AHEAD                       # Set C
    SKIP_32_DATA
    &C
    STR_X0_[X9]
    ^~get_token_done FBRANCH            # Be done with this token

:get_token_strings
    LOAD_W9_AHEAD                       # Send C
    SKIP_32_DATA
    &C
    LDR_X0_[X9]
    LOAD_W1_AHEAD                       # Get strings
    SKIP_32_DATA
    &strings
    ^~In_Set FCALL                      # See if in set
    CMP_X0_TO_1                         # IF TRUE
    SKIP_INST_EQ                        # Otherwise
    ^~get_token_comment FBRANCH

    # Store String
    LOAD_W9_AHEAD                       # Send C
    SKIP_32_DATA
    &C
    LDR_X0_[X9]
    ^~consume_word FCALL                # Store
    LOAD_W9_AHEAD                       # Set C
    SKIP_32_DATA
    &C
    STR_X0_[X9]
    ^~get_token_done FBRANCH            # Be done with this token

:get_token_comment
    LOAD_W9_AHEAD                       # Send C
    SKIP_32_DATA
    &C
    LDR_X0_[X9]
    CMP_X0_TO_47                        # IF '/' == C
    SKIP_INST_EQ                        # Otherwise
    ^~get_token_else FBRANCH

    ^~consume_byte FCALL                # Hope it just is '/'
    LOAD_W9_AHEAD                       # Set C
    SKIP_32_DATA
    &C
    STR_X0_[X9]

    CMP_X0_TO_42                        # IF '*' we have '/*'
    SKIP_INST_EQ                        # Check for '#'
    ^~get_token_comment_line FBRANCH

    # Deal with /* block comments */
    ^~fgetc FCALL                       # get next C
    LOAD_W9_AHEAD                       # Set C
    SKIP_32_DATA
    &C
    STR_X0_[X9]
:get_token_comment_block_outer
    LOAD_W9_AHEAD                       # Using C
    SKIP_32_DATA
    &C
    LDR_X0_[X9]
    CMP_X0_TO_47                        # IF '/' != C
    SKIP_INST_NE                        # be done
    ^~get_token_comment_block_done FBRANCH

:get_token_comment_block_inner
    LOAD_W9_AHEAD                       # Using C
    SKIP_32_DATA
    &C
    LDR_X0_[X9]
    CMP_X0_TO_42                        # IF '*' != C
    SKIP_INST_NE                        # jump over
    ^~get_token_comment_block_iter FBRANCH

    # Deal with inner loop
    ^~fgetc FCALL                       # get next C
    LOAD_W9_AHEAD                       # Set C
    SKIP_32_DATA
    &C
    STR_X0_[X9]
    ^~get_token_comment_block_inner RBRANCH # keep going

:get_token_comment_block_iter
    ^~fgetc FCALL                       # get next C
    LOAD_W9_AHEAD                       # Set C
    SKIP_32_DATA
    &C
    STR_X0_[X9]
    ^~get_token_comment_block_outer RBRANCH

:get_token_comment_block_done
    ^~fgetc FCALL                       # get next C
    LOAD_W9_AHEAD                       # Set C
    SKIP_32_DATA
    &C
    STR_X0_[X9]
    ^~reset RBRANCH                     # throw away, try again

:get_token_comment_line
    CMP_X0_TO_47                        # IF '/' we have #
    SKIP_INST_EQ                        # keep if just '/'
    ^~get_token_done FBRANCH

    # Deal with # line comment
    ^~fgetc FCALL                       # drop to match
    LOAD_W9_AHEAD                       # Set C
    SKIP_32_DATA
    &C
    STR_X0_[X9]
    ^~reset RBRANCH                     # throw away, try again

:get_token_else
    LOAD_W9_AHEAD                       # Send C
    SKIP_32_DATA
    &C
    LDR_X0_[X9]
    ^~consume_byte FCALL
    LOAD_W9_AHEAD                       # Set C
    SKIP_32_DATA
    &C
    STR_X0_[X9]

:get_token_done
    LOAD_W9_AHEAD                       # TOKEN
    SKIP_32_DATA
    &Token
    LDR_X0_[X9]
    STR_X0_[X3,8]                       # CURRENT->PREV = TOKEN
    STR_X0_[X3]                         # CURRENT->NEXT = TOKEN
    LOAD_W9_AHEAD                       # TOKEN = CURRENT
    SKIP_32_DATA
    &Token
    STR_X3_[X9]

:get_token_abort
    POP_X3                              # Restore X3
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    LOAD_W9_AHEAD                       # Return C
    SKIP_32_DATA
    &C
    LDR_X0_[X9]
    POP_LR
    RETURN


# Malloc isn't actually required if the program being built fits in the initial memory
# However, it doesn't take much to add it.
# Requires X13 to be initialized and X0 to have the number of desired bytes
:malloc
    PUSH_X13                            # save the old pointer
    ADD_X0_X0_X13                       # Request the number of desired bytes
    SET_X8_TO_SYS_BRK                   # the Syscall # for SYS_BRK
    SYSCALL

    SET_X13_FROM_X0
    POP_X0                              # return the old pointer
    RETURN


# clear_white_space function
# Receives INT C in X0 and FILE* in X15
# Returns first non-whitespace char in X0
:clear_white_space
    PUSH_LR
:clear_white_space_loop
    CMP_X0_TO_32                        # Check for ' '
    SKIP_INST_NE                        # wipe it out
    ^~clear_white_space_wipe FBRANCH

    CMP_X0_TO_10                        # Check for '\n'
    SKIP_INST_NE                        # wipe it output
    ^~clear_white_space_wipe FBRANCH

    CMP_X0_TO_9                         # Check for '\t'
    SKIP_INST_EQ                        # looks like non-whitespace
    ^~clear_white_space_done FBRANCH

:clear_white_space_wipe
    ^~fgetc FCALL                       # Read a new byte
    CMP_X0_TO_MINUS_4                   # Check for EOF
    SKIP_INST_NE                        # Short circuit
    ^~clear_white_space_done FBRANCH
    ^~clear_white_space_loop RBRANCH    # iterate

:clear_white_space_done
    POP_LR
    RETURN


# In_Set function
# Receives Char C in X0 and CHAR* in X1
# Returns 1 if true, zero if false in X0
:In_Set
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
:In_Set_loop
    LDR_BYTE_W2_[X1]                    # Read char

    CMP_X0_X2                           # See if they match
    SKIP_INST_NE                        # return true
    ^~In_Set_True FBRANCH

    CMP_X2_TO_0                         # Check for NULL
    SKIP_INST_NE                        # return false
    ^~In_Set_False FBRANCH

    ADD_X1_X1_1                         # s = s + 1
    ^~In_Set_loop RBRANCH               # Keep looping

:In_Set_True
    SET_X0_TO_1                         # Set True
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    RETURN

:In_Set_False
    SET_X0_TO_0                         # Set FALSE
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    RETURN

# purge_macro function
# Receives CH in X0
# Reads chars until Line feed is read
# returns line feed
:purge_macro
    PUSH_LR
:purge_macro_loop
    ^~fgetc FCALL                       # read next char
    CMP_X0_TO_10                        # Check for '\n'
    SKIP_INST_EQ                        # Keep going
    ^~purge_macro_loop RBRANCH
    POP_LR
    RETURN


# preserve_keyword function
# Receives INT C in X0
# collects all chars in keyword
# Returns C in X0
# Uses X2 for INT C
:preserve_keyword
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    SET_X2_FROM_X0                      # Setup C
    LOAD_W1_AHEAD                       # Concerning ourselves with "abc.."
    SKIP_32_DATA
    &alphas
:preserve_keyword_loop
    ^~In_Set RCALL                      # Check if alphanumerics
    CMP_X0_TO_1                         # IF TRUE
    SKIP_INST_EQ                        # Otherwise check for label
    ^~preserve_keyword_label FBRANCH

    SET_X0_FROM_X2                      # Pass C
    ^~consume_byte FCALL                # consume that byte
    SET_X2_FROM_X0                      # Update C
    ^~preserve_keyword_loop RBRANCH     # keep looping

:preserve_keyword_label
    SET_X0_FROM_X2                      # Fix return
    CMP_X0_TO_58                        # Check for ':'
    SKIP_INST_EQ                        # be done
    ^~preserve_keyword_done FBRANCH

    # Fix our goto label
    ^~fixup_label FCALL                 # Fix the label
    SET_X0_TO_32                        # Return Whitespace

:preserve_keyword_done
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN


# preserve_symbol function
# Receives INT C in X0
# collects all chars in symbol
# Returns C in X0
# Uses X2 for INT C
:preserve_symbol
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    SET_X2_FROM_X0                      # Setup C
    LOAD_W1_AHEAD                       # Concerning ourselves with "<=>.."
    SKIP_32_DATA
    &symbols
:preserve_symbol_loop
    ^~In_Set RCALL                      # Check if symbol
    CMP_X0_TO_1                         # IF TRUE
    SKIP_INST_EQ                        # Otherwise be done
    ^~preserve_symbol_done FBRANCH

    SET_X0_FROM_X2                      # Pass C
    ^~consume_byte FCALL                # consume that byte
    SET_X2_FROM_X0                      # Update C
    ^~preserve_symbol_loop RBRANCH      # keep looping

:preserve_symbol_done
    SET_X0_FROM_X2                      # Fix return
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN


# consume_word function
# receives INT C in X0
# returns INT C in X0
# Uses X0 for C, X1 for FREQ and X2 for ESCAPE
:consume_word
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    SET_X1_FROM_X0                      # FREQ = C
    SET_X2_TO_0                         # ESCAPE = FALSE
:consume_word_loop
    CMP_X2_TO_0                         # IF !ESCAPE
    SKIP_INST_EQ                        # Enable escape
    ^~consume_word_escape FBRANCH

    CMP_X0_TO_92                        # if '\\'
    SKIP_INST_EQ                        # keep state
    ^~consume_word_iter FBRANCH

    SET_X2_TO_1                         # ESCAPE = TRUE
    ^~consume_word_iter FBRANCH         # keep going

:consume_word_escape
    SET_X2_TO_0                         # ESCAPE = FALSE

:consume_word_iter
    ^~consume_byte FCALL                # read next char

    CMP_X2_TO_0                         # IF ESCAPE
    SKIP_INST_EQ                        # keep looping
    ^~consume_word_loop RBRANCH

    CMP_X0_X1                           # IF C != FREQ
    SKIP_INST_EQ                        # keep going
    ^~consume_word_loop RBRANCH

    ^~fgetc FCALL                       # return next char
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN


# consume_byte function
# Receives INT C in X0
# Inserts C into string S, updates String S
# Returns Next char in X0
:consume_byte
    PUSH_LR
    PUSH_X1                             # Protect X1
    LOAD_W9_AHEAD                       # S[0]
    SKIP_32_DATA
    &string_index
    LDR_X1_[X9]
    STR_BYTE_W0_[X1]                    # S[0] = C
    ADD_X1_X1_1                         # S = S + 1
    LOAD_W9_AHEAD                       # Update S
    SKIP_32_DATA
    &string_index
    STR_X1_[X9]
    ^~fgetc FCALL
    POP_X1                              # Restore X1
    POP_LR
    RETURN


# fixup_label function
# Receives S in X2
# prepends ':' to string and returns registers unchanged
# Uses X0 for HOLD, X1 for PREV and X2 for S[0]
:fixup_label
    PUSH_X0                             # Protect X0
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    SET_X0_TO_58                        # HOLD = ':'
    LDR_X2_[X3,16]                      # HOLD_STRING[0]
:fixup_label_loop
    SET_X1_FROM_X0                      # PREV = HOLD
    LDR_BYTE_W0_[X2]                    # HOLD = HOLD_STRING[I]
    STR_BYTE_W1_[X2]                    # HOLD_STRING[I] = PREV
    ADD_X2_X2_1                         # I = I + 1
    CMP_X0_TO_0                         # IF NULL == HOLD
    SKIP_INST_EQ                        # Keep looping
    ^~fixup_label_loop RBRANCH

    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_X0                              # Restore X0
    RETURN


# fgetc function
# Receives FILE* in X15
# Returns -4 (EOF) or char in X0
:fgetc
    PUSH_X1
    PUSH_X2
    SET_X0_TO_MINUS_4                   # Put EOF in x0
    PUSH_X0                             # Assume bad (If nothing read, value will remain EOF)
    SET_X1_FROM_SP                      # Get stack addresss
    SET_X0_FROM_X15                     # Where are we reading from
    SET_X8_TO_SYS_READ                  # the syscall number for read
    SET_X2_TO_1                         # set the size of chars we want

    SYSCALL                             # call the Kernel

    POP_X0                              # Get either char or EOF
    CMP_X0_TO_MINUS_4                   # Check for EOF
    SKIP_INST_NE                        # Return as is
    ^~fgetc_done FBRANCH
    AND_X0_X0_0xFF                      # Make it useful
:fgetc_done
    POP_X2
    POP_X1
    RETURN


# Reverse_List function
# Receives List in X0
# Returns the list reversed in X0
:Reverse_List
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    SET_X1_FROM_X0                      # Set HEAD
    SET_X0_TO_0                         # ROOT = NULL
:Reverse_List_Loop
    CMP_X1_TO_0                         # WHILE HEAD != NULL
    SKIP_INST_NE                        # Stop otherwise
    ^~Reverse_List_Done FBRANCH

    LDR_X2_[X1]                         # NEXT = HEAD->NEXT
    STR_X0_[X1]                         # HEAD->NEXT = ROOT
    SET_X0_FROM_X1                      # ROOT = HEAD
    SET_X1_FROM_X2                      # HEAD = NEXT
    ^~Reverse_List_Loop RBRANCH         # Keep Going

:Reverse_List_Done
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    RETURN


# recursive_output function
# Receives list in X0
# walks the list and prints the I->S for all nodes backwards
# Uses X1 for I
:recursive_output
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    CMP_X0_TO_0                         # Check for NULL
    SKIP_INST_NE                        # Skip the work
    ^~recursive_output_done FBRANCH
    SET_X1_FROM_X0                      # I = Head

    LDR_X0_[X1]                         # Iterate to next Token
    ^~recursive_output RCALL            # Recurse

    LDR_X0_[X1,16]                      # Using S
    ^~File_Print FCALL                  # Print it

:recursive_output_done
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN


# File_Print function
# Receives CHAR* in X0
# calls fputc for every non-null char
:File_Print
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    SET_X1_FROM_X0                      # Protect S
    CMP_X0_TO_0                         # Protect against nulls
    SKIP_INST_NE                        # Simply don't try to print them
    ^~File_Print_Done FBRANCH
:File_Print_Loop
    LDR_BYTE_W0_[X1]                    # Read byte
    CMP_X0_TO_0                         # Check for NULL
    SKIP_INST_NE                        # Stop at NULL
    ^~File_Print_Done FBRANCH

    ^~fputc FCALL                       # write it
    ADD_X1_X1_1                         # S = S + 1
    ^~File_Print_Loop RBRANCH           # Keep going

:File_Print_Done
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN


# fputc function
# receives CHAR in X0 and FILE* in X14
# writes char and returns
:fputc
    PUSH_X1
    PUSH_X2

    PUSH_X0                             # We are writing x0
    SET_X1_FROM_SP                      # Get stack address
    SET_X0_FROM_X14                     # Write to target file
    SET_X8_TO_SYS_WRITE                 # the syscall number for write
    SET_X2_TO_1                         # set the size of chars we want

    SYSCALL                             # call the Kernel

    POP_X0                              # Restore stack
    POP_X2                              # Restore X2
    POP_X1
    RETURN


# program function
# receives nothing, returns nothing
# Uses X0 for type_size
:program
    # The binary initialized the globals to null, so we can skip those steps
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2

:new_type
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    CMP_X0_TO_0                         # Check if NULL
    SKIP_INST_NE                        # Be done if null
    ^~program_done FBRANCH

    LDR_X1_[X0,16]                      # GLOBAL_TOKEN->S
    LOAD_W0_AHEAD                       # "CONSTANT"
    SKIP_32_DATA
    &constant
    ^~match FCALL                       # IF GLOBAL_TOKEN->S == "CONSTANT"
    CMP_X0_TO_0                         # If true
    SKIP_INST_EQ                        # Looks like not a constant
    ^~program_else FBRANCH

    # Deal with minimal constant case
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0]                         # global_token->next
    LOAD_W9_AHEAD                       # global_token = global_token->next
    SKIP_32_DATA
    &global_token
    STR_X0_[X9]

    LDR_X0_[X0,16]                      # global_token->S
    SET_X1_TO_0                         # NULL
    LOAD_W9_AHEAD                       # global_constant_list
    SKIP_32_DATA
    &global_constant_list
    LDR_X2_[X9]
    ^~sym_declare FCALL                 # Declare that constant
    LOAD_W9_AHEAD                       # global_constant_list = sym_declare(global_token->s, NULL, global_constant_list)#
    SKIP_32_DATA
    &global_constant_list
    STR_X0_[X9]

    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X1_[X9]
    LDR_X1_[X1]                         # global_token->next
    STR_X1_[X0,32]                      # global_constant_list->arguments = global_token->next

    LDR_X1_[X1]                         # global_token->next->next
    LOAD_W9_AHEAD                       # global_token = global_token->next->next#
    SKIP_32_DATA
    &global_token
    STR_X1_[X9]
    ^~new_type RBRANCH                  # go around again

:program_else
    ^~type_name FCALL                   # Figure out the type_size
    CMP_X0_TO_0                         # IF NULL == type_size
    SKIP_INST_NE                        # it was a new type
    ^~new_type RBRANCH

    # Add to global symbol table
    SET_X1_FROM_X0                      # put type_size in the right spot
    LOAD_W9_AHEAD                       # Using global token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # global_token->S
    LOAD_W9_AHEAD                       # Using global_symbol_list
    SKIP_32_DATA
    &global_symbol_list
    LDR_X2_[X9]
    ^~sym_declare FCALL                 # Declare symbol
    LOAD_W9_AHEAD                       # global_symbol_list = sym_declare(global_token->s, type_size, global_symbol_list)#
    SKIP_32_DATA
    &global_symbol_list
    STR_X0_[X9]
    LOAD_W9_AHEAD                       # Using global token
    SKIP_32_DATA
    &global_token
    LDR_X1_[X9]
    LDR_X1_[X1]                         # global_token->next
    LOAD_W9_AHEAD                       # global_token = global_token->next
    SKIP_32_DATA
    &global_token
    STR_X1_[X9]

    LOAD_W9_AHEAD                       # Using global token
    SKIP_32_DATA
    &global_token
    LDR_X1_[X9]
    LDR_X1_[X1,16]                      # global_token->S
    LOAD_W0_AHEAD                       # ";"
    SKIP_32_DATA
    &semicolon
    ^~match FCALL                       # if(match(";", global_token->s))
    CMP_X0_TO_0                         # If true
    SKIP_INST_EQ                        # looks like not a match
    ^~program_function FBRANCH

    # Deal with the global variable
    LOAD_W9_AHEAD                       # Using globals_list
    SKIP_32_DATA
    &globals_list
    LDR_X1_[X9]
    LOAD_W0_AHEAD                       # ":GLOBAL_"
    SKIP_32_DATA
    &program_string_0
    ^~emit FCALL                        # Emit it
    SET_X1_FROM_X0                      # update globals_list

    LOAD_W9_AHEAD                       # Using global token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0,8]                       # global token->prev
    LDR_X0_[X0,16]                      # global token->prev->s
    ^~emit FCALL                        # Emit it

    SET_X1_FROM_X0                      # update globals_list
    LOAD_W0_AHEAD                       # "\nNULL\n"
    SKIP_32_DATA
    &program_string_1
    ^~emit FCALL                        # Emit it
    LOAD_W9_AHEAD                       # update globals_list
    SKIP_32_DATA
    &globals_list
    STR_X0_[X9]

    LOAD_W9_AHEAD                       # Using global token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0]                         # global_token->next
    LOAD_W9_AHEAD                       # global_token = global_token->next
    SKIP_32_DATA
    &global_token
    STR_X0_[X9]
    ^~new_type RBRANCH                  # go around again

:program_function
    LOAD_W9_AHEAD                       # Using global token
    SKIP_32_DATA
    &global_token
    LDR_X1_[X9]
    LDR_X1_[X1,16]                      # global_token->S
    LOAD_W0_AHEAD                       # "("
    SKIP_32_DATA
    &open_paren
    ^~match FCALL                       # if(match("#", global_token->s))
    CMP_X0_TO_0                         # If true
    SKIP_INST_EQ                        # Otherwise deal with error case
    ^~program_error FBRANCH

    # Deal with function definition
    ^~declare_function FCALL            # Lets get the parsing rolling
    ^~new_type RBRANCH                  # Keep looping through functions

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

:program_done
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN

# declare_function function
# Receives nothing and returns nothing
# Sets current function and adds it to the global function list
:declare_function
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    SET_X0_TO_0                         # Using NULL
    LOAD_W9_AHEAD                       # current_count = 0
    SKIP_32_DATA
    &current_count
    STR_X0_[X9]

    LOAD_W9_AHEAD                       # Using global token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0,8]                       # global token->prev
    LDR_X0_[X0,16]                      # global token->prev->s
    SET_X1_TO_0                         # NULL
    LOAD_W9_AHEAD                       # global_function_list
    SKIP_32_DATA
    &global_function_list
    LDR_X2_[X9]
    ^~sym_declare FCALL                 # sym_declare(global_token->prev->s, NULL, global_function_list)#
    LOAD_W9_AHEAD                       # function = sym_declare(global_token->prev->s, NULL, global_function_list)#
    SKIP_32_DATA
    &function
    STR_X0_[X9]
    LOAD_W9_AHEAD                       # global_function_list = function
    SKIP_32_DATA
    &global_function_list
    STR_X0_[X9]

    ^~collect_arguments FCALL           # collect all of the function arguments

    LOAD_W9_AHEAD                       # Using global token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # global token->s
    LOAD_W1_AHEAD                       # ";"
    SKIP_32_DATA
    &semicolon
    ^~match FCALL                       # IF global token->s == ";"
    CMP_X0_TO_0                         # If true
    SKIP_INST_EQ                        # It was a prototype
    ^~declare_function_full FBRANCH

    # Deal with prototypes
    LOAD_W9_AHEAD                       # Using global token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0]                         # global token->next
    LOAD_W9_AHEAD                       # global token = global token->next
    SKIP_32_DATA
    &global_token
    STR_X0_[X9]
    ^~declare_function_done FBRANCH     # Move on

:declare_function_full
    # Deal with full function definitions
    LOAD_W0_AHEAD                       # "# Defining function "
    SKIP_32_DATA
    &declare_function_string_0
    ^~emit_out FCALL                    # emit it

    LOAD_W9_AHEAD                       # function
    SKIP_32_DATA
    &function
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # function->s
    ^~emit_out FCALL                    # emit it

    LOAD_W0_AHEAD                       # "\n:FUNCTION_"
    SKIP_32_DATA
    &declare_function_string_1
    ^~emit_out FCALL                    # emit it

    LOAD_W9_AHEAD                       # function
    SKIP_32_DATA
    &function
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # function->s
    ^~emit_out FCALL                    # emit it

    LOAD_W0_AHEAD                       # "\n"
    SKIP_32_DATA
    &declare_function_string_3
    ^~emit_out FCALL                    # emit it

    ^~statement FCALL                   # Recursively get the function pieces

    LOAD_W9_AHEAD                       # output
    SKIP_32_DATA
    &output_list
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # output->s
    LOAD_W1_AHEAD                       # "RETURN\n"
    SKIP_32_DATA
    &declare_function_string_2
    ^~match FCALL                       # IF output->s == "RETURN\n"
    CMP_X0_TO_0                         # If true we can skip adding it
    SKIP_INST_NE                        # otherwise we need to add it
    ^~declare_function_done FBRANCH

    # Add the return to the end of a function lacking a return#
    LOAD_W0_AHEAD                       # "RETURN\n"
    SKIP_32_DATA
    &declare_function_string_2
    ^~emit_out FCALL                    # emit it

:declare_function_done
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN

# collect_arguments function
# Receives nothing
# Returns Nothing
# Adds arguments to the function definition
# holds struct type* type_size in X2, then replace with struct token_list* a in X2 when type_size is used
:collect_arguments
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0]                         # global_token->next
    LOAD_W9_AHEAD                       # global_token = global_token->next
    SKIP_32_DATA
    &global_token
    STR_X0_[X9]
:collect_arguments_loop
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X1_[X9]
    LDR_X1_[X1,16]                      # global_token->S
    LOAD_W0_AHEAD                       # ")"
    SKIP_32_DATA
    &close_paren
    ^~match FCALL                       # IF global_token->S == ")"
    CMP_X0_TO_0                         # we reached the end
    SKIP_INST_NE                        # be done
    ^~collect_arguments_done FBRANCH

    # deal with the case of there are arguments
    ^~type_name FCALL                   # Get the type
    SET_X2_FROM_X0                      # put type_size safely out of the way

    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X1_[X9]
    LDR_X1_[X1,16]                      # global_token->S
    LOAD_W0_AHEAD                       # ")"
    SKIP_32_DATA
    &close_paren
    ^~match FCALL                       # IF global_token->S == ")"
    CMP_X0_TO_0                         # is a foo(int, char,void) case
    SKIP_INST_NE                        # deal with commas
    ^~collect_arguments_common FBRANCH

    # Trying second else
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X1_[X9]
    LDR_X1_[X1,16]                      # global_token->S
    LOAD_W0_AHEAD                       # ","
    SKIP_32_DATA
    &comma
    ^~match FCALL                       # IF global_token->S == ","
    CMP_X0_TO_0                         # then deal with the common
    SKIP_INST_NE                        # case of commas between arguments
    ^~collect_arguments_common FBRANCH

    # deal with foo(int a, char b)
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # global_token->S
    SET_X1_FROM_X2                      # put type_size in the right place
    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X2_[X9]
    LDR_X2_[X2,32]                      # function->args
    ^~sym_declare FCALL                 # sym_declare(global_token->s, type_size, function->arguments)#
    SET_X2_FROM_X0                      # put a in a safe place

    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X0_[X9]
    LDR_X0_[X0,32]                      # function->args
    CMP_X0_TO_0                         # IF function->args == NULL
    SKIP_INST_EQ                        # otherwise it isn't the first
    ^~collect_arguments_another FBRANCH

    # Deal with the case of first argument in the function
    SET_X0_TO_8                         # 8
    STR_X0_[X2,32]                      # a->depth = 8
    ^~collect_arguments_next FBRANCH    # get to next

:collect_arguments_another
    # deal with the case of non-first arguments
    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X0_[X9]
    LDR_X0_[X0,32]                      # function->args
    LDR_X0_[X0,32]                      # function->args->depth
    ADD_X0_X0_8                         # function->args->depth + 8
    STR_X0_[X2,32]                      # a->depth = function->args->depth + 8

:collect_arguments_next
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0]                         # global_token->next
    LOAD_W9_AHEAD                       # global_token = global_token->next
    SKIP_32_DATA
    &global_token
    STR_X0_[X9]

    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X0_[X9]
    STR_X2_[X0,32]                      # function->args = a

:collect_arguments_common
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X1_[X9]
    LDR_X1_[X1,16]                      # global_token->S
    LOAD_W0_AHEAD                       # ","
    SKIP_32_DATA
    &comma
    ^~match FCALL                       # IF global_token->S == ","
    CMP_X0_TO_0                         # then deal with the comma
    SKIP_INST_EQ                        # otherwise loop
    ^~collect_arguments_loop RBRANCH

    # keep foo(bar(), 1) expressions working
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0]                         # global_token->next
    LOAD_W9_AHEAD                       # global_token = global_token->next
    SKIP_32_DATA
    &global_token
    STR_X0_[X9]
    ^~collect_arguments_loop RBRANCH    # keep going

:collect_arguments_done
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0]                         # global_token->next
    LOAD_W9_AHEAD                       # global_token = global_token->next
    SKIP_32_DATA
    &global_token
    STR_X0_[X9]
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN


# statement function
# Receives nothing
# Returns nothing
# Walks down global_token recursively to collect the contents of the function
:statement
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2

    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X1_[X9]
    LDR_X1_[X1,16]                      # global_token->S
    LOAD_W0_AHEAD                       # "{"
    SKIP_32_DATA
    &open_curly_brace
    ^~match FCALL                       # IF global_token->S == "{"
    SKIP_INST_EQ                        # otherwise try label
    ^~statement_label FBRANCH

    # deal with { statement }
    ^~recursive_statement FCALL         # Statements inside of statements for days
    ^~statement_done FBRANCH            # Be done

:statement_label
    LDR_BYTE_W0_[X1]                    # global_token->S[0]
    CMP_X0_TO_58                        # IF global_token->S == ':'
    SKIP_INST_EQ                        # otherwise try locals
    ^~statement_local FBRANCH

    # deal with labels
    SET_X0_FROM_X1                      # put global_token->S in the right spot
    ^~emit_out FCALL                    # emit it

    LOAD_W0_AHEAD                       # Using "\t#C goto label\n"
    SKIP_32_DATA
    &statement_string_0
    ^~emit_out FCALL                    # emit it

    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0]                         # global_token->next
    LOAD_W9_AHEAD                       # global_token = global_token->next
    SKIP_32_DATA
    &global_token
    STR_X0_[X9]
    ^~statement_done FBRANCH            # be done

:statement_local
    SET_X0_FROM_X1                      # put global_token->S in the right place
    LOAD_W1_AHEAD                       # pointer to primative types
    SKIP_32_DATA
    &prim_types
    ^~lookup_type FCALL                 # See if found
    CMP_X0_TO_0                         # IF NULL == lookup_type(global_token->S, prim_types)
    SKIP_INST_EQ                        # Sweet a new local
    ^~statement_local_success FBRANCH

    # Second chance
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X1_[X9]
    LDR_X1_[X1,16]                      # global_token->S
    LOAD_W0_AHEAD                       # "struct"
    SKIP_32_DATA
    &struct
    ^~match FCALL                       # IF global_token->S == "struct"
    CMP_X0_TO_0                         # then we are a local
    SKIP_INST_EQ                        # otherwise try IF
    ^~statement_if FBRANCH

:statement_local_success
    ^~collect_local FCALL               # Grab those locals
    ^~statement_done FBRANCH            # be done

:statement_if
    LOAD_W0_AHEAD                       # Using "if"
    SKIP_32_DATA
    &if_string
    ^~match FCALL                       # IF global_token->S == "if"
    CMP_X0_TO_0                         # then we have an if statement
    SKIP_INST_EQ                        # otherwise try DO
    ^~statement_do FBRANCH

    # Deal with IF statement
    ^~process_if FCALL                  # DO IT
    ^~statement_done FBRANCH            # be done

:statement_do
    LOAD_W0_AHEAD                       # Using "do"
    SKIP_32_DATA
    &do_string
    ^~match FCALL                       # IF global_token->S == "do"
    CMP_X0_TO_0                         # then we have a do statement
    SKIP_INST_EQ                        # otherwise try WHILE
    ^~statement_while FBRANCH

    # Deal with DO statement
    ^~process_do FCALL                  # DO IT
    ^~statement_done FBRANCH            # be done

:statement_while
    LOAD_W0_AHEAD                       # Using "while"
    SKIP_32_DATA
    &while_string
    ^~match FCALL                       # IF global_token->S == "while"
    CMP_X0_TO_0                         # then we have a while statement
    SKIP_INST_EQ                        # otherwise try FOR
    ^~statement_for FBRANCH

    # Deal with WHILE statement
    ^~process_while FCALL               # DO IT
    ^~statement_done FBRANCH            # be done

:statement_for
    LOAD_W0_AHEAD                       # Using "for"
    SKIP_32_DATA
    &for_string
    ^~match FCALL                       # IF global_token->S == "for"
    CMP_X0_TO_0                         # then we have a for statement
    SKIP_INST_EQ                        # otherwise try ASM
    ^~statement_asm FBRANCH

    # Deal with FOR statement
    ^~process_for FCALL                 # DO IT
    ^~statement_done FBRANCH            # be done

:statement_asm
    LOAD_W0_AHEAD                       # Using "asm"
    SKIP_32_DATA
    &asm_string
    ^~match FCALL                       # IF global_token->S == "asm"
    CMP_X0_TO_0                         # then we have an asm statement
    SKIP_INST_EQ                        # otherwise try GOTO
    ^~statement_goto FBRANCH

    # Deal with ASM statement
    ^~process_asm FCALL                 # Hit it
    ^~statement_done FBRANCH            # be done

:statement_goto
    LOAD_W0_AHEAD                       # Using "goto"
    SKIP_32_DATA
    &goto_string
    ^~match FCALL                       # IF global_token->S == "goto"
    CMP_X0_TO_0                         # then we have a goto statement
    SKIP_INST_EQ                        # Otherwise try RETURN
    ^~statement_return FBRANCH

    # Deal with GOTO statement
    LOAD_W0_AHEAD                       # Using "LOAD_W16_AHEAD\nSKIP_32_DATA\n&"
    SKIP_32_DATA
    &statement_string_1a
    ^~emit_out FCALL                    # emit it

    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0]                         # global_token->next
    LOAD_W9_AHEAD                       # global_token = global_token->next
    SKIP_32_DATA
    &global_token
    STR_X0_[X9]

    LDR_X0_[X0,16]                      # global_token->S
    ^~emit_out FCALL                    # emit it

    LOAD_W0_AHEAD                       # Using "\nBR_X16"
    SKIP_32_DATA
    &statement_string_1b
    ^~emit_out FCALL

    LOAD_W0_AHEAD                       # Using "\n"
    SKIP_32_DATA
    &statement_string_2
    ^~emit_out FCALL                    # emit it

    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0]                         # global_token->next
    LOAD_W9_AHEAD                       # global_token = global_token->next
    SKIP_32_DATA
    &global_token
    STR_X0_[X9]

    LOAD_W0_AHEAD                       # Using "ERROR in statement\nMissing #\n"
    SKIP_32_DATA
    &statement_string_4
    LOAD_W1_AHEAD                       # Using ";"
    SKIP_32_DATA
    &semicolon
    ^~require_match FCALL               # Make sure it has the required
    ^~statement_done FBRANCH            # Be done

:statement_return
    LOAD_W0_AHEAD                       # Using "return"
    SKIP_32_DATA
    &return_string
    ^~match FCALL                       # IF global_token->S == "return"
    CMP_X0_TO_0                         # then we have a return statement
    SKIP_INST_EQ                        # Otherwise try BREAK
    ^~statement_break FBRANCH

    # Deal with RETURN Statement
    ^~return_result FCALL               # Return anything they want
    ^~statement_done FBRANCH            # be done

:statement_break
    LOAD_W0_AHEAD                       # Using "break"
    SKIP_32_DATA
    &break_string
    ^~match FCALL                       # IF global_token->S == "break"
    CMP_X0_TO_0                         # then we have a break statement
    SKIP_INST_EQ                        # Otherwise try CONTINUE
    ^~statement_continue FBRANCH

    # Deal with BREAK statement
    ^~process_break FCALL               # Lets do some damage
    ^~statement_done FBRANCH            # be done

:statement_continue
    LOAD_W0_AHEAD                       # Using "continue"
    SKIP_32_DATA
    &continue_string
    ^~match FCALL                       # IF global_token->S == "continue"
    CMP_X0_TO_0                         # then we have a continue statement
    SKIP_INST_EQ                        # Otherwise we are punting to an expression
    ^~statement_else FBRANCH

    # Deal with CONTINUE statement
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0]                         # global_token->next
    LOAD_W9_AHEAD                       # global_token = global_token->next
    SKIP_32_DATA
    &global_token
    STR_X0_[X9]

    LOAD_W0_AHEAD                       # Using "\n#continue statement\n"
    SKIP_32_DATA
    &statement_string_3
    ^~emit_out FCALL                    # emit it

    LOAD_W0_AHEAD                       # Using "ERROR in statement\nMissing ;\n"
    SKIP_32_DATA
    &statement_string_4
    LOAD_W1_AHEAD                       # Using ";"
    SKIP_32_DATA
    &semicolon
    ^~require_match FCALL               # Don't forget the ";"
    ^~statement_done FBRANCH            # Be done

:statement_else
    ^~expression FCALL                  # Collect expression
    LOAD_W0_AHEAD                       # Using "ERROR in statement\nMissing ;\n"
    SKIP_32_DATA
    &statement_string_4
    LOAD_W1_AHEAD                       # Using ";"
    SKIP_32_DATA
    &semicolon
    ^~require_match FCALL               # make sure we have it

:statement_done
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN

# recursive_statement function
# Receives nothing
# Returns nothing
# Walks the global_token list to build the contents of statements
# Uses struct token_list* frame in X2
:recursive_statement
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2

    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0]                         # global_token->next
    LOAD_W9_AHEAD                       # global_token = global_token->next
    SKIP_32_DATA
    &global_token
    STR_X0_[X9]

    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X2_[X9]
    LDR_X2_[X2,8]                       # frame = function->locals

:recursive_statement_loop
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X1_[X9]
    LDR_X1_[X1,16]                      # global_token->S
    LOAD_W0_AHEAD                       # Using "}"
    SKIP_32_DATA
    &close_curly_brace
    ^~match FCALL                       # IF global_token->S == "}"
    CMP_X0_TO_0                         # Then we are done recursing
    SKIP_INST_NE                        # and then we clean up
    ^~recursive_statement_cleanup FBRANCH

    # Deal with the recursive calls
    ^~statement RCALL                   # Deal with another statement
    ^~recursive_statement_loop RBRANCH  # loop some more

:recursive_statement_cleanup
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0]                         # global_token->next
    LOAD_W9_AHEAD                       # global_token = global_token->next
    SKIP_32_DATA
    &global_token
    STR_X0_[X9]

    LOAD_W0_AHEAD                       # Using "RETURN\n"
    SKIP_32_DATA
    &recursive_statement_string_0
    LOAD_W9_AHEAD                       # Using output
    SKIP_32_DATA
    &output_list
    LDR_X1_[X9]
    LDR_X1_[X1,16]                      # output->S
    ^~match FCALL                       # IF output->S == "RETURN\n"
    CMP_X0_TO_0                         # Then we can skip the clean up
    SKIP_INST_NE                        # and be done
    ^~recursive_statement_done FBRANCH

    # Deal with cleanup
    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X1_[X9]
    LDR_X1_[X1,8]                       # i = function->locals
    LOAD_W0_AHEAD                       # Using "POP_X1\t# _recursive_statement_locals\n"
    SKIP_32_DATA
    &recursive_statement_string_1

:recursive_statement_locals
    CMP_X1_X2                           # IF frame != i
    SKIP_INST_NE                        # Otherwise be done
    ^~recursive_statement_done FBRANCH

    # Lets emit
    ^~emit_out FCALL                    # emit it
    LDR_X1_[X1]                         # i = i->next
    ^~recursive_statement_locals RBRANCH # keep going

:recursive_statement_done
    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X0_[X9]
    STR_X2_[X0,8]                       # function->locals = frame
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN

# return_result function
# Receives nothing
# Returns nothing
# Cleans up function and generates return
# Also handles returning expressions
:return_result
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0]                         # global_token->next
    LOAD_W9_AHEAD                       # global_token = global_token->next
    SKIP_32_DATA
    &global_token
    STR_X0_[X9]

    LDR_X0_[X0,16]                      # global_token->S
    LDR_BYTE_W0_[X0]                    # global_token->S[0]
    CMP_X0_TO_59                        # If global_token->S[0] == ';'
    SKIP_INST_NE                        # Go straight to cleanup
    ^~return_result_cleanup FBRANCH

    ^~expression FCALL                  # get the expression we are returning

:return_result_cleanup
    LOAD_W0_AHEAD                       # Using "ERROR in return_result\nMISSING ;\n"
    SKIP_32_DATA
    &return_result_string_0
    LOAD_W1_AHEAD                       # Using ";"
    SKIP_32_DATA
    &semicolon
    ^~require_match FCALL               # Make sure we have it

    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X1_[X9]
    LDR_X1_[X1,8]                       # function->locals
    LOAD_W0_AHEAD                       # Using "POP_X1\t# _return_result_locals\n"
    SKIP_32_DATA
    &return_result_string_1
:return_result_locals
    CMP_X1_TO_0                         # IF NULL == i
    SKIP_INST_NE                        # Be done
    ^~return_result_done FBRANCH

    ^~emit_out FCALL                    # Emit out pop
    LDR_X1_[X1]                         # i = i->NEXT
    ^~return_result_locals RBRANCH      # Keep going

:return_result_done
    LOAD_W0_AHEAD                       # Using "RETURN\n"
    SKIP_32_DATA
    &return_result_string_2
    ^~emit_out FCALL                    # Emit it
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN

# collect_local function
# Receives nothing
# Returns nothing
# Walks global_token list to create function locals
# Uses X2 for struct token_list* A
:collect_local
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    ^~type_name FCALL                   # Get the local's type

    SET_X1_FROM_X0                      # Put struct type* type_size in the right place
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # global_token->S
    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X2_[X9]
    LDR_X2_[X2,8]                       # function->locals
    ^~sym_declare FCALL                 # Declare it
    SET_X2_FROM_X0                      # put it away safely

    # Try for main
    LOAD_W0_AHEAD                       # Using "main"
    SKIP_32_DATA
    &main_string
    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X1_[X9]
    LDR_X1_[X1,16]                      # function->S
    ^~match FCALL                       # IF match("main", function->s)
    CMP_X0_TO_0                         # possible
    SKIP_INST_EQ                        # try to see if fresh function
    ^~collect_local_fresh FBRANCH

    # Ok we are in main, now to see if main is fresh
    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X0_[X9]
    LDR_X0_[X0,8]                       # function->locals
    CMP_X0_TO_0                         # IF NULL == function->locals
    SKIP_INST_EQ                        # try to see if fresh function
    ^~collect_local_fresh FBRANCH

    # Sweet we are in a fresh main
    SET_X0_TO_32                        # We start at 32
    STR_X0_[X2,32]                      # a->DEPTH = 32
    ^~collect_local_common FBRANCH      # Go to the commons

:collect_local_fresh
    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X0_[X9]
    LDR_X0_[X0,32]                      # function->args
    CMP_X0_TO_0                         # IF NULL == function->args
    SKIP_INST_EQ                        # Otherwise see if first
    ^~collect_local_first FBRANCH

    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X0_[X9]
    LDR_X0_[X0,8]                       # function->locals
    CMP_X0_TO_0                         # IF NULL == function->locals
    SKIP_INST_EQ                        # Otherwise try first
    ^~collect_local_first FBRANCH

    # Sweet we are in a fresh function
    SET_X0_TO_8                         # We start at 8
    STR_X0_[X2,32]                      # a->DEPTH = 8
    ^~collect_local_common FBRANCH      # Go to the commons

:collect_local_first
    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X0_[X9]
    LDR_X0_[X0,8]                       # function->locals
    CMP_X0_TO_0                         # IF NULL == function->locals
    SKIP_INST_EQ                        # Looks like we are just another local
    ^~collect_local_else FBRANCH

    # Ok we are the first local
    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X0_[X9]
    LDR_X0_[X0,32]                      # function->args
    LDR_X0_[X0,32]                      # function->args->depth
    ADD_X0_X0_8                         # function->arguments->depth + 8
    STR_X0_[X2,32]                      # a->DEPTH = function->arguments->depth + 8
    ^~collect_local_common FBRANCH      # Go to the commons

:collect_local_else
    # Always the last to know
    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X0_[X9]
    LDR_X0_[X0,8]                       # function->locals
    LDR_X0_[X0,32]                      # function->locals->depth
    ADD_X0_X0_8                         # function->locals->depth + 8
    STR_X0_[X2,32]                      # a->DEPTH = function->locals->depth + 8

:collect_local_common
    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X0_[X9]

    STR_X2_[X0,8]                       # function->locals = a
    LDR_X2_[X2,16]                      # a->S

    LOAD_W0_AHEAD                       # Using "# Defining local "
    SKIP_32_DATA
    &collect_local_string_0
    ^~emit_out FCALL                    # emit it

    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # global_token->S
    ^~emit_out FCALL                    # emit it

    LOAD_W0_AHEAD                       # Using "\n"
    SKIP_32_DATA
    &collect_local_string_1
    ^~emit_out FCALL                    # emit it

    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0]                         # global_token->NEXT
    LOAD_W9_AHEAD                       # global_token = global_token->NEXT
    SKIP_32_DATA
    &global_token
    STR_X0_[X9]

    LDR_X1_[X0,16]                      # global_token->S
    LOAD_W0_AHEAD                       # Using "="
    SKIP_32_DATA
    &equal
    ^~match FCALL                       # IF match("=", global_token->s)
    CMP_X0_TO_0                         # Deal with assignment
    SKIP_INST_EQ                        # Otherwise finish it
    ^~collect_local_done FBRANCH

    # Deal with assignment
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0]                         # global_token->NEXT
    LOAD_W9_AHEAD                       # global_token = global_token->NEXT
    SKIP_32_DATA
    &global_token
    STR_X0_[X9]

    ^~expression FCALL                  # Recurse

:collect_local_done
    LOAD_W0_AHEAD                       # Using "ERROR in collect_local\nMissing ;\n"
    SKIP_32_DATA
    &collect_local_string_2
    LOAD_W1_AHEAD                       # Using ";"
    SKIP_32_DATA
    &semicolon
    ^~require_match FCALL               # Make sure we have it

    LOAD_W0_AHEAD                       # Using "PUSH_X0\t#"
    SKIP_32_DATA
    &collect_local_string_3
    ^~emit_out FCALL                    # emit it

    SET_X0_FROM_X2                      # put A->S where it belongs
    ^~emit_out FCALL                    # emit it

    LOAD_W0_AHEAD                       # Using "\n"
    SKIP_32_DATA
    &collect_local_string_1
    ^~emit_out FCALL                    # emit it

    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN

# process_asm function
# Receives nothing
# Returns nothing
# Simply inlines the asm statements
# Uses X1 for global_token temp storage
:process_asm
    PUSH_LR
    PUSH_X1                             # Protect X1
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0]                         # global_token->NEXT
    LOAD_W9_AHEAD                       # global_token = global_token->NEXT
    SKIP_32_DATA
    &global_token
    STR_X0_[X9]

    LOAD_W0_AHEAD                       # Using "ERROR in process_asm\nMISSING (\n"
    SKIP_32_DATA
    &process_asm_string_0
    LOAD_W1_AHEAD                       # Using "("
    SKIP_32_DATA
    &open_paren
    ^~require_match FCALL               # Make sure we have it

    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X1_[X9]
:process_asm_iter
    LDR_X0_[X1,16]                      # global_token->S
    LDR_BYTE_W0_[X0]                    # global_token->S[0]
    CMP_X0_TO_34                        # IF global_token->S[0] == '\"'
    SKIP_INST_EQ                        # Otherwise be done
    ^~process_asm_done FBRANCH

    LDR_X0_[X1,16]                      # global_token->S
    ADD_X0_X0_1                         # global_token->S + 1
    ^~emit_out FCALL                    # Emit it

    LOAD_W0_AHEAD                       # Using "\n"
    SKIP_32_DATA
    &process_asm_string_1
    ^~emit_out FCALL                    # Emit it

    LDR_X1_[X1]                         # global_token->NEXT
    LOAD_W9_AHEAD                       # global_token = global_token->NEXT
    SKIP_32_DATA
    &global_token
    STR_X1_[X9]
    ^~process_asm_iter RBRANCH          # keep going

:process_asm_done
    LOAD_W0_AHEAD                       # Using "ERROR in process_asm\nMISSING )\n"
    SKIP_32_DATA
    &process_asm_string_2
    LOAD_W1_AHEAD                       # Using ")"
    SKIP_32_DATA
    &close_paren
    ^~require_match FCALL               # Make sure we have it

    LOAD_W0_AHEAD                       # Using "ERROR in process_asm\nMISSING ;\n"
    SKIP_32_DATA
    &process_asm_string_3
    LOAD_W1_AHEAD                       # Using ";"
    SKIP_32_DATA
    &semicolon
    ^~require_match FCALL               # Make sure we have it

    POP_X1                              # Restore X1
    POP_LR
    RETURN

# process_if function
# Receives nothing
# Returns Nothing
# Increments current_count recurses into expression + statement
# Uses X2 for char* NUMBER_STRING
:process_if
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    LOAD_W9_AHEAD                       # Using current count
    SKIP_32_DATA
    &current_count
    LDR_X0_[X9]
    SET_X1_FROM_X0                      # Preparing for update
    ADD_X1_X1_1                         # current_count + 1
    LOAD_W9_AHEAD                       # current_count = current_count + 1
    SKIP_32_DATA
    &current_count
    STR_X1_[X9]
    ^~numerate_number FCALL             # convert to string
    SET_X2_FROM_X0                      # put NUMBER_STRING in place

    LOAD_W0_AHEAD                       # Using "# IF_"
    SKIP_32_DATA
    &process_if_string_0
    ^~emit_out FCALL                    # Emit it

    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # function->S
    SET_X1_FROM_X2                      # Passing NUMBER_STRING
    ^~uniqueID_out FCALL                # uniqueID_out(function->s, number_string)

    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0]                         # global_token->NEXT
    LOAD_W9_AHEAD                       # global_token = global_token->NEXT
    SKIP_32_DATA
    &global_token
    STR_X0_[X9]

    LOAD_W0_AHEAD                       # Using "ERROR in process_if\nMISSING (\n"
    SKIP_32_DATA
    &process_if_string_1
    LOAD_W1_AHEAD                       # Using "("
    SKIP_32_DATA
    &open_paren
    ^~require_match FCALL               # Make sure we have it

    ^~expression FCALL                  # Recurse to get the IF(...) part

    LOAD_W0_AHEAD                       # Using "CBNZ_X0_PAST_BR\nLOAD_W16_AHEAD\nSKIP_32_DATA\n&ELSE_"
    SKIP_32_DATA
    &process_if_string_2a
    ^~emit_out FCALL                    # Emit it

    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # function->S
    SET_X1_FROM_X2                      # Passing NUMBER_STRING
    ^~uniqueID_out FCALL                # uniqueID_out(function->s, number_string)

    LOAD_W0_AHEAD                       # Using "\nBR_X16\n"
    SKIP_32_DATA
    &process_if_string_2b
    ^~emit_out FCALL                    # Emit it

    LOAD_W0_AHEAD                       # Using "ERROR in process_if\nMISSING )\n"
    SKIP_32_DATA
    &process_if_string_3
    LOAD_W1_AHEAD                       # Using ")"
    SKIP_32_DATA
    &close_paren
    ^~require_match FCALL               # Make sure we have it

    ^~statement RCALL                   # Recursive to get the IF(){...} part

    LOAD_W0_AHEAD                       # Using "LOAD_W16_AHEAD\nSKIP_32_DATA\n&_END_IF_"
    SKIP_32_DATA
    &process_if_string_4
    ^~emit_out FCALL                    # Emit it

    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # function->S
    SET_X1_FROM_X2                      # Passing NUMBER_STRING
    ^~uniqueID_out FCALL                # uniqueID_out(function->s, number_string)

    LOAD_W0_AHEAD                       # Using "\nBR_X16\n:ELSE_"
    SKIP_32_DATA
    &process_if_string_5
    ^~emit_out FCALL                    # Emit it

    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # function->S
    ^~uniqueID_out FCALL                # uniqueID_out(function->s, number_string)

    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X1_[X9]
    LDR_X1_[X1,16]                      # global_token->S
    LOAD_W0_AHEAD                       # Using "else"
    SKIP_32_DATA
    &else_string
    ^~match FCALL                       # IF global_token->S == "else"
    CMP_X0_TO_0                         # Then we need to collect the else too
    SKIP_INST_EQ                        # Otherwise finish up
    ^~process_if_done FBRANCH

    # deal with else statement
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0]                         # global_token->NEXT
    LOAD_W9_AHEAD                       # global_token = global_token->NEXT
    SKIP_32_DATA
    &global_token
    STR_X0_[X9]

    ^~statement RCALL                   # Recurse to get the ELSE {...} part

:process_if_done
    LOAD_W0_AHEAD                       # Using ":_END_IF_"
    SKIP_32_DATA
    &process_if_string_6
    ^~emit_out FCALL                    # Emit it

    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # function->S
    SET_X1_FROM_X2                      # Passing NUMBER_STRING
    ^~uniqueID_out FCALL                # uniqueID_out(function->s, number_string)

    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN

# save_break_frame microfunction
# Overwrites X0 and X1
# Saves break frame on stack
# Returns to caller
:save_break_frame
    POP_X1                              # Save return Address
    LOAD_W9_AHEAD                       # Get break_frame
    SKIP_32_DATA
    &break_frame
    LDR_X0_[X9]
    PUSH_X0                             # Store as nested_locals
    LOAD_W9_AHEAD                       # Get break_target_head
    SKIP_32_DATA
    &break_target_head
    LDR_X0_[X9]
    PUSH_X0                             # Store as nested_break_head
    LOAD_W9_AHEAD                       # Get break_target_func
    SKIP_32_DATA
    &break_target_func
    LDR_X0_[X9]
    PUSH_X0                             # Store as nested_break_func
    LOAD_W9_AHEAD                       # Get break_target_num
    SKIP_32_DATA
    &break_target_num
    LDR_X0_[X9]
    PUSH_X0                             # Store as nested_break_num
    PUSH_X1                             # Put return back in place
    RETURN                              # Return to caller


# restore_break_frame microfunction
# Overwrites X0 and X1
# Restores break frame from stack
# Returns to caller
:restore_break_frame
    POP_X1                              # Save return Address
    POP_X0                              # Get nested_break_num
    LOAD_W9_AHEAD                       # Restore break_target_num
    SKIP_32_DATA
    &break_target_num
    STR_X0_[X9]
    POP_X0                              # Get nested_break_func
    LOAD_W9_AHEAD                       # Restore break_target_func
    SKIP_32_DATA
    &break_target_func
    STR_X0_[X9]
    POP_X0                              # Get nested_break_head
    LOAD_W9_AHEAD                       # Restore break_target_head
    SKIP_32_DATA
    &break_target_head
    STR_X0_[X9]
    POP_X0                              # Get nested_locals
    LOAD_W9_AHEAD                       # Restore break_frame
    SKIP_32_DATA
    &break_frame
    STR_X0_[X9]
    PUSH_X1                             # Put return back in place
    RETURN                              # Return to caller


# set_break_frame microfunction
# Receives char* head in X0 and char* num in X1
# Overwrites X0 and X1
# Returns to calling function
:set_break_frame
    LOAD_W9_AHEAD                       # update break_target_head
    SKIP_32_DATA
    &break_target_head
    STR_X0_[X9]
    LOAD_W9_AHEAD                       # update break_target_num
    SKIP_32_DATA
    &break_target_num
    STR_X1_[X9]
    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X1_[X9]
    LDR_X0_[X1,8]                       # function->LOCALS
    LOAD_W9_AHEAD                       # break_frame = function->LOCALS
    SKIP_32_DATA
    &break_frame
    STR_X0_[X9]
    LDR_X0_[X1,16]                      # function->S
    LOAD_W9_AHEAD                       # break_target_func = function->S
    SKIP_32_DATA
    &break_target_func
    STR_X0_[X9]
    RETURN                              # Return to sender


# process_do function
# Receives Nothing
# Returns Nothing
# Increments current_count and leverages save/restore_break_frame pieces
# Uses X2 for char* NUMBER_STRING
:process_do
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    ^~save_break_frame RCALL            # Save the frame

    LOAD_W9_AHEAD                       # Using current count
    SKIP_32_DATA
    &current_count
    LDR_X0_[X9]
    SET_X1_FROM_X0                      # Preparing for update
    ADD_X1_X1_1                         # current_count + 1
    LOAD_W9_AHEAD                       # current_count = current_count + 1
    SKIP_32_DATA
    &current_count
    STR_X1_[X9]
    ^~numerate_number FCALL             # convert to string
    SET_X2_FROM_X0                      # put NUMBER_STRING in place

    LOAD_W0_AHEAD                       # Using "DO_END_"
    SKIP_32_DATA
    &process_do_string_0
    SET_X1_FROM_X2                      # Passing NUMBER_STRING
    ^~set_break_frame RCALL             # Set the frame

    LOAD_W0_AHEAD                       # Using ":DO_"
    SKIP_32_DATA
    &process_do_string_1
    ^~emit_out FCALL                    # Emit it

    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # function->S
    SET_X1_FROM_X2                      # Passing NUMBER_STRING
    ^~uniqueID_out FCALL                # uniqueID_out(function->s, number_string)

    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0]                         # global_token->NEXT
    LOAD_W9_AHEAD                       # global_token = global_token->NEXT
    SKIP_32_DATA
    &global_token
    STR_X0_[X9]

    ^~statement RCALL                   # Do the DO {...} part

    LOAD_W0_AHEAD                       # Using "ERROR in process_do\nMISSING while\n"
    SKIP_32_DATA
    &process_do_string_2
    LOAD_W1_AHEAD                       # Using "while"
    SKIP_32_DATA
    &while_string
    ^~require_match FCALL               # Make sure we have it

    LOAD_W0_AHEAD                       # Using "ERROR in process_do\nMISSING (\n"
    SKIP_32_DATA
    &process_do_string_3
    LOAD_W1_AHEAD                       # Using "("
    SKIP_32_DATA
    &open_paren
    ^~require_match FCALL               # Make sure we have it

    ^~expression FCALL                  # Do the WHILE (...) part

    LOAD_W0_AHEAD                       # Using "ERROR in process_do\nMISSING )\n"
    SKIP_32_DATA
    &process_do_string_4
    LOAD_W1_AHEAD                       # Using ")"
    SKIP_32_DATA
    &close_paren
    ^~require_match FCALL               # Make sure we have it

    LOAD_W0_AHEAD                       # Using "ERROR in process_do\nMISSING ;\n"
    SKIP_32_DATA
    &process_do_string_5
    LOAD_W1_AHEAD                       # Using ";"
    SKIP_32_DATA
    &semicolon
    ^~require_match FCALL               # Make sure we have it

    LOAD_W0_AHEAD                       # Using "CBZ_X0_PAST_BR\nLOAD_W16_AHEAD\nSKIP_32_DATA\n&DO_"
    SKIP_32_DATA
    &process_do_string_6a
    ^~emit_out FCALL                    # Emit it

    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # function->S
    SET_X1_FROM_X2                      # Passing NUMBER_STRING
    ^~uniqueID_out FCALL                # uniqueID_out(function->s, number_string)

    LOAD_W0_AHEAD                       # Using "\nBR_X16\n"
    SKIP_32_DATA
    &process_do_string_6b
    ^~emit_out FCALL                    # Emit it

    LOAD_W0_AHEAD                       # Using ":DO_END_"
    SKIP_32_DATA
    &process_do_string_7
    ^~emit_out FCALL                    # Emit it

    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # function->S
    ^~uniqueID_out FCALL                # uniqueID_out(function->s, number_string)

    ^~restore_break_frame RCALL         # Restore the old break frame

    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN

# process_while function
# Receives nothing
# Returns nothing
# Increments current_count and leverages save/restore_break_frame pieces
# Uses X2 for char* NUMBER_STRING
:process_while
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    ^~save_break_frame RCALL            # Save break_frame

    LOAD_W9_AHEAD                       # Using current count
    SKIP_32_DATA
    &current_count
    LDR_X0_[X9]
    SET_X1_FROM_X0                      # Preparing for update
    ADD_X1_X1_1                         # current_count + 1
    LOAD_W9_AHEAD                       # current_count = current_count + 1
    SKIP_32_DATA
    &current_count
    STR_X1_[X9]
    ^~numerate_number FCALL             # convert to string
    SET_X2_FROM_X0                      # put NUMBER_STRING in place

    LOAD_W0_AHEAD                       # Using "END_WHILE_"
    SKIP_32_DATA
    &process_while_string_0
    SET_X1_FROM_X2                      # Passing NUMBER_STRING
    ^~set_break_frame RCALL             # Set it and forget it

    LOAD_W0_AHEAD                       # Using ":WHILE_"
    SKIP_32_DATA
    &process_while_string_1
    ^~emit_out FCALL                    # Emit it

    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # function->S
    SET_X1_FROM_X2                      # Passing NUMBER_STRING
    ^~uniqueID_out FCALL                # uniqueID_out(function->s, number_string)

    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0]                         # global_token->NEXT
    LOAD_W9_AHEAD                       # global_token = global_token->NEXT
    SKIP_32_DATA
    &global_token
    STR_X0_[X9]

    LOAD_W0_AHEAD                       # Using "ERROR in process_while\nMISSING (\n"
    SKIP_32_DATA
    &process_while_string_2
    LOAD_W1_AHEAD                       # Using "("
    SKIP_32_DATA
    &open_paren
    ^~require_match FCALL               # Make sure we have it

    ^~expression FCALL                  # Deal with the WHILE (...) part

    LOAD_W0_AHEAD                       # Using "CBNZ_X0_PAST_BR\nLOAD_W16_AHEAD\nSKIP_32_DATA\n&END_WHILE_"
    SKIP_32_DATA
    &process_while_string_3a
    ^~emit_out FCALL                    # Emit it

    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # function->S
    SET_X1_FROM_X2                      # Passing NUMBER_STRING
    ^~uniqueID_out FCALL                # uniqueID_out(function->s, number_string)

    LOAD_W0_AHEAD                       # Using "\nBR_X16\n"
    SKIP_32_DATA
    &process_while_string_3b
    ^~emit_out FCALL

    LOAD_W0_AHEAD                       # Using "# THEN_while_"
    SKIP_32_DATA
    &process_while_string_4
    ^~emit_out FCALL                    # Emit it

    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # function->S
    ^~uniqueID_out FCALL                # uniqueID_out(function->s, number_string)

    LOAD_W0_AHEAD                       # Using "ERROR in process_while\nMISSING )\n"
    SKIP_32_DATA
    &process_while_string_5
    LOAD_W1_AHEAD                       # Using ")"
    SKIP_32_DATA
    &close_paren
    ^~require_match FCALL               # Make sure we have it

    ^~statement RCALL                   # Deal with the {....} part

    LOAD_W0_AHEAD                       # Using "LOAD_W16_AHEAD\nSKIP_32_DATA\n&WHILE_"
    SKIP_32_DATA
    &process_while_string_6a
    ^~emit_out FCALL                    # Emit it

    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # function->S
    SET_X1_FROM_X2                      # Passing NUMBER_STRING
    ^~uniqueID_out FCALL                # uniqueID_out(function->s, number_string)

    LOAD_W0_AHEAD                       # Using "\nBR_X16\n"
    SKIP_32_DATA
    &process_while_string_6b
    ^~emit_out FCALL

    LOAD_W0_AHEAD                       # Using ":END_WHILE_"
    SKIP_32_DATA
    &process_while_string_7
    ^~emit_out FCALL                    # Emit it

    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # function->S
    ^~uniqueID_out FCALL                # uniqueID_out(function->s, number_string)

    ^~restore_break_frame RCALL         # Restore the old break frame

    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN


# process_for function
# Receives Nothing
# Returns Nothing
# Increments current_count and leverages save/restore_break_frame pieces
# Uses X2 for char* NUMBER_STRING
:process_for
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    ^~save_break_frame RCALL            # Save the frame

    LOAD_W9_AHEAD                       # Using current count
    SKIP_32_DATA
    &current_count
    LDR_X0_[X9]
    SET_X1_FROM_X0                      # Preparing for update
    ADD_X1_X1_1                         # current_count + 1
    LOAD_W9_AHEAD                       # current_count = current_count + 1
    SKIP_32_DATA
    &current_count
    STR_X1_[X9]
    ^~numerate_number FCALL             # convert to string
    SET_X2_FROM_X0                      # put NUMBER_STRING in place

    LOAD_W0_AHEAD                       # Using "FOR_END_"
    SKIP_32_DATA
    &process_for_string_0
    SET_X1_FROM_X2                      # Passing NUMBER_STRING
    ^~set_break_frame RCALL             # Set it and forget it

    LOAD_W0_AHEAD                       # Using "# FOR_initialization_"
    SKIP_32_DATA
    &process_for_string_1
    ^~emit_out FCALL                    # Emit it

    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # function->S
    SET_X1_FROM_X2                      # Passing NUMBER_STRING
    ^~uniqueID_out FCALL                # uniqueID_out(function->s, number_string)

    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0]                         # global_token->NEXT
    LOAD_W9_AHEAD                       # global_token = global_token->NEXT
    SKIP_32_DATA
    &global_token
    STR_X0_[X9]

    LOAD_W0_AHEAD                       # Using "ERROR in process_for\nMISSING (\n"
    SKIP_32_DATA
    &process_for_string_2
    LOAD_W1_AHEAD                       # Using "("
    SKIP_32_DATA
    &open_paren
    ^~require_match FCALL               # Make Sure we have it

    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X1_[X9]
    LDR_X1_[X1,16]                      # global_token->S
    LOAD_W0_AHEAD                       # Using ";"
    SKIP_32_DATA
    &semicolon
    ^~match FCALL                       # IF global_token->S == ";"
    CMP_X0_TO_0                         # Then no initializer
    SKIP_INST_NE                        # And skip getting the expression
    ^~process_for_terminator FBRANCH

    # Deal with FOR (...; case
    ^~expression FCALL                  # Get the FOR ( ... ; part

:process_for_terminator
    LOAD_W0_AHEAD                       # Using ":FOR_"
    SKIP_32_DATA
    &process_for_string_3
    ^~emit_out FCALL                    # Emit it

    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # function->S
    SET_X1_FROM_X2                      # Passing NUMBER_STRING
    ^~uniqueID_out FCALL                # uniqueID_out(function->s, number_string)

    LOAD_W0_AHEAD                       # Using "ERROR in process_for\nMISSING ;1\n"
    SKIP_32_DATA
    &process_for_string_4
    LOAD_W1_AHEAD                       # Using ";"
    SKIP_32_DATA
    &semicolon
    ^~require_match FCALL               # Make sure we have it

    ^~expression FCALL                  # Get the FOR ( ; ... ; Part

    LOAD_W0_AHEAD                       # Using "CBNZ_X0_PAST_BR\nLOAD_W16_AHEAD\nSKIP_32_DATA\n&FOR_END_"
    SKIP_32_DATA
    &process_for_string_5a
    ^~emit_out FCALL                    # Emit it

    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # function->S
    SET_X1_FROM_X2                      # Passing NUMBER_STRING
    ^~uniqueID_out FCALL                # uniqueID_out(function->s, number_string)

    LOAD_W0_AHEAD                       # Using "\nBR_X16\n"
    SKIP_32_DATA
    &process_for_string_5b
    ^~emit_out FCALL                    # Emit it

    LOAD_W0_AHEAD                       # Using "LOAD_W16_AHEAD\nSKIP_32_DATA\n&FOR_THEN_"
    SKIP_32_DATA
    &process_for_string_6a
    ^~emit_out FCALL                    # Emit it

    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # function->S
    ^~uniqueID_out FCALL                # uniqueID_out(function->s, number_string)

    LOAD_W0_AHEAD                       # Using "\nBR_X16\n"
    SKIP_32_DATA
    &process_for_string_6b
    ^~emit_out FCALL                    # Emit it

    LOAD_W0_AHEAD                       # Using ":FOR_ITER_"
    SKIP_32_DATA
    &process_for_string_7
    ^~emit_out FCALL                    # Emit it

    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # function->S
    ^~uniqueID_out FCALL                # uniqueID_out(function->s, number_string)

    LOAD_W0_AHEAD                       # Using "ERROR in process_for\nMISSING ;2\n"
    SKIP_32_DATA
    &process_for_string_8
    LOAD_W1_AHEAD                       # Using ";"
    SKIP_32_DATA
    &semicolon
    ^~require_match FCALL               # Make sure we have it

    ^~expression FCALL                  # Get the FOR (;;...) part

    LOAD_W0_AHEAD                       # Using "LOAD_W16_AHEAD\nSKIP_32_DATA\n&FOR_"
    SKIP_32_DATA
    &process_for_string_9a
    ^~emit_out FCALL                    # Emit it

    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # function->S
    SET_X1_FROM_X2                      # Passing NUMBER_STRING
    ^~uniqueID_out FCALL                # uniqueID_out(function->s, number_string)

    LOAD_W0_AHEAD                       # Using "\nBR_X16\n"
    SKIP_32_DATA
    &process_for_string_9b
    ^~emit_out FCALL

    LOAD_W0_AHEAD                       # Using ":FOR_THEN_"
    SKIP_32_DATA
    &process_for_string_10
    ^~emit_out FCALL                    # Emit it

    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # function->S
    ^~uniqueID_out FCALL                # uniqueID_out(function->s, number_string)

    LOAD_W0_AHEAD                       # Using "ERROR in process_for\nMISSING )\n"
    SKIP_32_DATA
    &process_for_string_11
    LOAD_W1_AHEAD                       # Using ")"
    SKIP_32_DATA
    &close_paren
    ^~require_match FCALL               # Make sure we have it

    ^~statement RCALL                   # Get FOR (#) {...} part

    LOAD_W0_AHEAD                       # Using "LOAD_W16_AHEAD\nSKIP_32_DATA\n&FOR_ITER_"
    SKIP_32_DATA
    &process_for_string_12a
    ^~emit_out FCALL                    # Emit it

    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # function->S
    SET_X1_FROM_X2                      # Passing NUMBER_STRING
    ^~uniqueID_out FCALL                # uniqueID_out(function->s, number_string)

    LOAD_W0_AHEAD                       # Using "\nBR_X16\n"
    SKIP_32_DATA
    &process_for_string_12b
    ^~emit_out FCALL

    LOAD_W0_AHEAD                       # Using ":FOR_END_"
    SKIP_32_DATA
    &process_for_string_13
    ^~emit_out FCALL                    # Emit it

    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # function->S
    ^~uniqueID_out FCALL                # uniqueID_out(function->s, number_string)

    ^~restore_break_frame RCALL         # Restore the old break frame

    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN

# process_break function
# Receives nothing
# Returns nothing
# Handles the break out of loops case
# Uses X1 for struct token_list* break_frame and X2 for struct token_list* I
:process_break
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    LOAD_W9_AHEAD                       # Catch big error
    SKIP_32_DATA
    &break_target_head
    LDR_X0_[X9]
    CMP_X0_TO_0                         # IF(NULL == break_target_head)
    SKIP_INST_NE                        # I'm sorry Mr White but you have stage-3 lung cancer
    ^~process_break_bad FBRANCH

    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X0_[X9]
    LDR_X2_[X0,8]                       # I = function->LOCALS
    LOAD_W9_AHEAD                       # Put break_frame in the right spot
    SKIP_32_DATA
    &break_frame
    LDR_X1_[X9]
    LOAD_W0_AHEAD                       # Using "POP_X1\t# break_cleanup_locals\n"
    SKIP_32_DATA
    &process_break_string_1

:process_break_iter
    CMP_X2_TO_0                         # IF (NULL == I)
    SKIP_INST_NE                        # We are done
    ^~process_break_cleaned FBRANCH

    CMP_X2_X1                           # IF I != break_frame
    SKIP_INST_NE                        # We are done
    ^~process_break_cleaned FBRANCH

    ^~emit_out FCALL                    # Emit it
    LDR_X2_[X2]                         # I = I->NEXT
    ^~process_break_iter RBRANCH        # Keep looping

:process_break_cleaned
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0]                         # global_token->NEXT
    LOAD_W9_AHEAD                       # global_token = global_token->NEXT
    SKIP_32_DATA
    &global_token
    STR_X0_[X9]

    LOAD_W0_AHEAD                       # Using "LOAD_W16_AHEAD\nSKIP_32_DATA\n&"
    SKIP_32_DATA
    &process_break_string_2a
    ^~emit_out FCALL                    # Emit it

    LOAD_W9_AHEAD                       # Get what we are in
    SKIP_32_DATA
    &break_target_head
    LDR_X0_[X9]
    ^~emit_out FCALL                    # Emit it

    LOAD_W9_AHEAD                       # Get what function we are in
    SKIP_32_DATA
    &break_target_func
    LDR_X0_[X9]
    ^~emit_out FCALL                    # Emit it

    LOAD_W0_AHEAD                       # Using "_"
    SKIP_32_DATA
    &underline
    ^~emit_out FCALL                    # Emit it

    LOAD_W9_AHEAD                       # Get dem digits
    SKIP_32_DATA
    &break_target_num
    LDR_X0_[X9]
    ^~emit_out FCALL                    # Emit it

    LOAD_W0_AHEAD                       # Using "\nBR_X16"
    SKIP_32_DATA
    &process_break_string_2b
    ^~emit_out FCALL

    LOAD_W0_AHEAD                       # Using "\n"
    SKIP_32_DATA
    &process_break_string_3
    ^~emit_out FCALL                    # Emit it

    LOAD_W0_AHEAD                       # Using "ERROR in break statement\nMissing ;\n"
    SKIP_32_DATA
    &process_break_string_4
    LOAD_W1_AHEAD                       # Using ";"
    SKIP_32_DATA
    &semicolon
    ^~require_match FCALL               # Make sure we have it

    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN


:process_break_bad
    # Breaking badly
    SET_X14_TO_2                        # write to standard error
#   call line_error                     # Write useful debug info
    SET_X0_FROM_X2                      # put S in the right place
    ^~File_Print RCALL                  # print it

    LOAD_W0_AHEAD                       # Ending string
    SKIP_32_DATA
    &process_break_string_0
    ^~File_Print RCALL                  # print it
    ^~Exit_Failure FBRANCH              # Abort Hard

# expression function
# Receives Nothing
# Returns Nothing
# Walks global_token and updates output_list
# Uses X0 and X1 for match and X2 for char* store
:expression
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    ^~bitwise_expr FCALL                # Collect bitwise expressions

    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X1_[X9]
    LDR_X1_[X1,16]                      # global_token->S
    LOAD_W0_AHEAD                       # "="
    SKIP_32_DATA
    &equal
    ^~match FCALL                       # IF global_token->S == "="
    CMP_X0_TO_0                         # We have to deal with assignment
    SKIP_INST_EQ                        # Looks like nope
    ^~expression_done FBRANCH

    # Deal with possible assignment
    LOAD_W2_AHEAD                       # Assume "STR_BYTE_W0_[X1]\n" by default
    SKIP_32_DATA
    &expression_string_1
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X1_[X9]
    LDR_X1_[X1,8]                       # global_token->PREV
    LDR_X1_[X1,16]                      # global_token->PREV->S
    LOAD_W0_AHEAD                       # Using "]"
    SKIP_32_DATA
    &close_bracket
    ^~match FCALL                       # IF global_token->S == "]"
    CMP_X0_TO_0                         # Then we might have a char
    SKIP_INST_EQ                        # Otherwise INT
    ^~expression_int FBRANCH

    LOAD_W9_AHEAD                       # Using current_target
    SKIP_32_DATA
    &current_target
    LDR_X1_[X9]
    LDR_X1_[X1,48]                      # current_target->NAME
    LOAD_W0_AHEAD                       # Using "char*"
    SKIP_32_DATA
    &type_char_indirect_name
    ^~match FCALL                       # Intentional inefficiency because I feel like it
    CMP_X0_TO_0                         # IF current_target->NAME == "char*"
    SKIP_INST_EQ                        # Do char anyway
    ^~expression_int FBRANCH

    ^~expression_common FBRANCH         # Looks like we have to use "STR_BYTE_W0_[X1]\n"

:expression_int
    LOAD_W2_AHEAD                       # Use "STR_X0_[X1]\n"
    SKIP_32_DATA
    &expression_string_0

:expression_common
    LOAD_W0_AHEAD                       # Passing expression
    SKIP_32_DATA
    &expression
    ^~common_recursion FCALL            # Recurse
    SET_X0_FROM_X2                      # Using Store
    ^~emit_out FCALL                    # Emit it
    SET_X0_TO_0                         # Using NULL
    LOAD_W9_AHEAD                       # current_target = NULL
    SKIP_32_DATA
    &current_target
    STR_X0_[X9]

:expression_done
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN

# bitwise_expr function
# Receives nothing
# Returns nothing
# Walks global_token list and updates output list
# Just calls other functions
:bitwise_expr
    PUSH_LR
    ^~relational_expr FCALL             # Walk up the tree
    ^~bitwise_expr_stub FCALL           # Let general recursion do the work
    POP_LR
    RETURN


# bitwise_expr_stub function
# Receives nothing
# Returns Nothing
# Just calls general_recursion a bunch
# Uses X0, X1, X2 and X3 for passing constants to general recursion
:bitwise_expr_stub
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    PUSH_X3                             # Protect X3

    LOAD_W0_AHEAD                       # Using relational_expr
    SKIP_32_DATA
    &relational_expr
    LOAD_W1_AHEAD                       # Using "AND_X0_X1_X0\n"
    SKIP_32_DATA
    &bitwise_expr_stub_string_0
    LOAD_W2_AHEAD                       # Using "&"
    SKIP_32_DATA
    &bitwise_and
    LOAD_W3_AHEAD                       # And recurse
    SKIP_32_DATA
    &bitwise_expr_stub
    ^~general_recursion FCALL           # Hit it

    LOAD_W0_AHEAD                       # Using relational_expr
    SKIP_32_DATA
    &relational_expr
    LOAD_W1_AHEAD                       # Using "AND_X0_X1_X0\n"
    SKIP_32_DATA
    &bitwise_expr_stub_string_0
    LOAD_W2_AHEAD                       # Using "&&"
    SKIP_32_DATA
    &logical_and
    LOAD_W3_AHEAD                       # And recurse
    SKIP_32_DATA
    &bitwise_expr_stub
    ^~general_recursion FCALL           # Hit it

    LOAD_W0_AHEAD                       # Using relational_expr
    SKIP_32_DATA
    &relational_expr
    LOAD_W1_AHEAD                       # Using "OR_X0_X1_X0\n"
    SKIP_32_DATA
    &bitwise_expr_stub_string_1
    LOAD_W2_AHEAD                       # Using "|"
    SKIP_32_DATA
    &bitwise_or
    LOAD_W3_AHEAD                       # And recurse
    SKIP_32_DATA
    &bitwise_expr_stub
    ^~general_recursion FCALL           # Hit it

    LOAD_W0_AHEAD                       # Using relational_expr
    SKIP_32_DATA
    &relational_expr
    LOAD_W1_AHEAD                       # Using "OR_X0_X1_X0\n"
    SKIP_32_DATA
    &bitwise_expr_stub_string_1
    LOAD_W2_AHEAD                       # Using "||"
    SKIP_32_DATA
    &logical_or
    LOAD_W3_AHEAD                       # And recurse
    SKIP_32_DATA
    &bitwise_expr_stub
    ^~general_recursion FCALL           # Hit it

    LOAD_W0_AHEAD                       # Using relational_expr
    SKIP_32_DATA
    &relational_expr
    LOAD_W1_AHEAD                       # Using "XOR_X0_X1_X0\n"
    SKIP_32_DATA
    &bitwise_expr_stub_string_2
    LOAD_W2_AHEAD                       # Using "^"
    SKIP_32_DATA
    &bitwise_xor
    LOAD_W3_AHEAD                       # And recurse
    SKIP_32_DATA
    &bitwise_expr_stub
    ^~general_recursion FCALL           # Hit it

    POP_X3                              # Restore X3
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN

# relational_expr function
# Receives nothing
# Returns Nothing
# Walks global_token list and updates output list
# just calls other function
:relational_expr
    PUSH_LR
    ^~additive_expr FCALL               # Walk up the tree
    ^~relational_expr_stub FCALL        # Recurse
    POP_LR
    RETURN


# relational_expr_stub function
# Receives nothing
# Returns Nothing
# Just calls general_recursion a bunch
# Uses X0, X1, X2 and X3 for passing constants to general recursion
:relational_expr_stub
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    PUSH_X3                             # Protect X3

    LOAD_W0_AHEAD                       # Using additive_expr
    SKIP_32_DATA
    &additive_expr
    LOAD_W1_AHEAD                       # Using "CMP_X1_X0\nSET_X0_TO_1\nSKIP_INST_LT\nSET_X0_TO_0\n"
    SKIP_32_DATA
    &relational_expr_stub_string_0
    LOAD_W2_AHEAD                       # Using "<"
    SKIP_32_DATA
    &less_than_string
    LOAD_W3_AHEAD                       # And recurse
    SKIP_32_DATA
    &relational_expr_stub
    ^~general_recursion FCALL           # Hit it

    LOAD_W0_AHEAD                       # Using additive_expr
    SKIP_32_DATA
    &additive_expr
    LOAD_W1_AHEAD                       # Using "CMP_X1_X0\nSET_X0_TO_1\nSKIP_INST_LE\nSET_X0_TO_0\n"
    SKIP_32_DATA
    &relational_expr_stub_string_1
    LOAD_W2_AHEAD                       # Using "<="
    SKIP_32_DATA
    &less_than_equal_string
    LOAD_W3_AHEAD                       # And recurse
    SKIP_32_DATA
    &relational_expr_stub
    ^~general_recursion FCALL           # Hit it

    LOAD_W0_AHEAD                       # Using additive_expr
    SKIP_32_DATA
    &additive_expr
    LOAD_W1_AHEAD                       # Using "CMP_X1_X0\nSET_X0_TO_1\nSKIP_INST_GE\nSET_X0_TO_0\n"
    SKIP_32_DATA
    &relational_expr_stub_string_2
    LOAD_W2_AHEAD                       # Using ">="
    SKIP_32_DATA
    &greater_than_equal_string
    LOAD_W3_AHEAD                       # And recurse
    SKIP_32_DATA
    &relational_expr_stub
    ^~general_recursion FCALL           # Hit it

    LOAD_W0_AHEAD                       # Using additive_expr
    SKIP_32_DATA
    &additive_expr
    LOAD_W1_AHEAD                       # Using "CMP_X1_X0\nSET_X0_TO_1\nSKIP_INST_GT\nSET_X0_TO_0\n"
    SKIP_32_DATA
    &relational_expr_stub_string_3
    LOAD_W2_AHEAD                       # Using ">"
    SKIP_32_DATA
    &greater_than_string
    LOAD_W3_AHEAD                       # And recurse
    SKIP_32_DATA
    &relational_expr_stub
    ^~general_recursion FCALL           # Hit it

    LOAD_W0_AHEAD                       # Using additive_expr
    SKIP_32_DATA
    &additive_expr
    LOAD_W1_AHEAD                       # Using "CMP_X1_X0\nSET_X0_TO_1\nSKIP_INST_EQ\nSET_X0_TO_0\n"
    SKIP_32_DATA
    &relational_expr_stub_string_4
    LOAD_W2_AHEAD                       # Using "=="
    SKIP_32_DATA
    &equal_to_string
    LOAD_W3_AHEAD                       # And recurse
    SKIP_32_DATA
    &relational_expr_stub
    ^~general_recursion FCALL           # Hit it

    LOAD_W0_AHEAD                       # Using additive_expr
    SKIP_32_DATA
    &additive_expr
    LOAD_W1_AHEAD                       # Using "CMP_X1_X0\nSET_X0_TO_1\nSKIP_INST_NE\nSET_X0_TO_0\n"
    SKIP_32_DATA
    &relational_expr_stub_string_5
    LOAD_W2_AHEAD                       # Using "!="
    SKIP_32_DATA
    &not_equal_string
    LOAD_W3_AHEAD                       # And recurse
    SKIP_32_DATA
    &relational_expr_stub
    ^~general_recursion FCALL           # Hit it

    POP_X3                              # Restore X3
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN

# additive_expr function
# Receives nothing
# Returns Nothing
# Walks global_token list and updates output list
# just calls other function
:additive_expr
    PUSH_LR
    ^~postfix_expr FCALL                # Walk up the tree
    ^~additive_expr_stub FCALL          # Recurse
    POP_LR
    RETURN


# additive_expr_stub function
# Receives nothing
# Returns Nothing
# Just calls general_recursion a bunch
# Uses X0, X1, X2 and X3 for passing constants to general recursion
:additive_expr_stub
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    PUSH_X3                             # Protect X3

    LOAD_W0_AHEAD                       # Using postfix_expr
    SKIP_32_DATA
    &postfix_expr
    LOAD_W1_AHEAD                       # Using "ADD_X0_X1_X0\n"
    SKIP_32_DATA
    &additive_expr_stub_string_0
    LOAD_W2_AHEAD                       # Using "+"
    SKIP_32_DATA
    &plus_string
    LOAD_W3_AHEAD                       # And recurse
    SKIP_32_DATA
    &additive_expr_stub
    ^~general_recursion FCALL           # Hit it

    LOAD_W0_AHEAD                       # Using postfix_expr
    SKIP_32_DATA
    &postfix_expr
    LOAD_W1_AHEAD                       # Using "SUB_X0_X1_X0\n"
    SKIP_32_DATA
    &additive_expr_stub_string_1
    LOAD_W2_AHEAD                       # Using "-"
    SKIP_32_DATA
    &minus_string
    LOAD_W3_AHEAD                       # And recurse
    SKIP_32_DATA
    &additive_expr_stub
    ^~general_recursion FCALL           # Hit it

    LOAD_W0_AHEAD                       # Using postfix_expr
    SKIP_32_DATA
    &postfix_expr
    LOAD_W1_AHEAD                       # Using "MUL_X0_X1_X0\n"
    SKIP_32_DATA
    &additive_expr_stub_string_2
    LOAD_W2_AHEAD                       # Using "*"
    SKIP_32_DATA
    &multiply_string
    LOAD_W3_AHEAD                       # And recurse
    SKIP_32_DATA
    &additive_expr_stub
    ^~general_recursion FCALL           # Hit it

    LOAD_W0_AHEAD                       # Using postfix_expr
    SKIP_32_DATA
    &postfix_expr
    LOAD_W1_AHEAD                       # Using "SDIV_X0_X1_X0\n"
    SKIP_32_DATA
    &additive_expr_stub_string_3
    LOAD_W2_AHEAD                       # Using "/"
    SKIP_32_DATA
    &divide_string
    LOAD_W3_AHEAD                       # And recurse
    SKIP_32_DATA
    &additive_expr_stub
    ^~general_recursion FCALL           # Hit it

    LOAD_W0_AHEAD                       # Using postfix_expr
    SKIP_32_DATA
    &postfix_expr
    LOAD_W1_AHEAD                       # Using "SDIV_X2_X1_X0\nMSUB_X0_X0_X2_X1\n"
    SKIP_32_DATA
    &additive_expr_stub_string_4
    LOAD_W2_AHEAD                       # Using "%"
    SKIP_32_DATA
    &modulus_string
    LOAD_W3_AHEAD                       # And recurse
    SKIP_32_DATA
    &additive_expr_stub
    ^~general_recursion FCALL           # Hit it

    LOAD_W0_AHEAD                       # Using postfix_expr
    SKIP_32_DATA
    &postfix_expr
    LOAD_W1_AHEAD                       # Using "LSHIFT_X0_X1_X0\n"
    SKIP_32_DATA
    &additive_expr_stub_string_5
    LOAD_W2_AHEAD                       # Using "<<"
    SKIP_32_DATA
    &left_shift_string
    LOAD_W3_AHEAD                       # And recurse
    SKIP_32_DATA
    &additive_expr_stub
    ^~general_recursion FCALL           # Hit it

    LOAD_W0_AHEAD                       # Using postfix_expr
    SKIP_32_DATA
    &postfix_expr
    LOAD_W1_AHEAD                       # Using "ARITH_RSHIFT_X0_X1_X0\n"
    SKIP_32_DATA
    &additive_expr_stub_string_6
    LOAD_W2_AHEAD                       # Using ">>"
    SKIP_32_DATA
    &right_shift_string
    LOAD_W3_AHEAD                       # And recurse
    SKIP_32_DATA
    &additive_expr_stub
    ^~general_recursion FCALL           # Hit it

    POP_X3                              # Restore X3
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN

# postfix_expr function
# Receives nothing
# Returns Nothing
# Walks global_token list and updates output list
# just calls other function
:postfix_expr
    PUSH_LR
    ^~primary_expr FCALL                # Walk up the tree
    ^~postfix_expr_stub FCALL           # Recurse
    POP_LR
    RETURN


# postfix_expr_stub function
# Receives nothing
# Returns Nothing
# Checks for "[" and "->" and deals with them otherwise does nothing
# Uses X0, X1, X2 and X3 for passing constants to general recursion
:postfix_expr_stub
    PUSH_LR
    PUSH_X1                             # Protect X1
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X1_[X9]
    LDR_X1_[X1,16]                      # global_token->S
    LOAD_W0_AHEAD                       # Using "["
    SKIP_32_DATA
    &open_bracket
    ^~match FCALL                       # IF global_token->S == "["
    CMP_X0_TO_0                         # then we have an array
    SKIP_INST_EQ                        # Otherwise try arrow
    ^~postfix_expr_stub_arrow FBRANCH

    # Deal with array
    ^~postfix_expr_array FCALL          # Get it
    ^~postfix_expr_stub RCALL           # Recurse

:postfix_expr_stub_arrow
    LOAD_W0_AHEAD                       # Using "->"
    SKIP_32_DATA
    &arrow_string
    ^~match FCALL                       # IF global_token->S == "->"
    CMP_X0_TO_0                         # Then we need to deal with struct offsets
    SKIP_INST_EQ                        # Otherwise be done
    ^~postfix_expr_stub_done FBRANCH

                                        # Deal with arrow
    ^~postfix_expr_arrow FCALL          # Get it
    ^~postfix_expr_stub RCALL           # Recurse

:postfix_expr_stub_done
    POP_X1                              # Restore X1
    POP_LR
    RETURN


# unary_expr_sizeof function
# Receives nothing
# Returns nothing
# Uses X2 for A->SIZE
:unary_expr_sizeof
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0]                         # global_token->NEXT
    LOAD_W9_AHEAD                       # global_token = global_token->NEXT
    SKIP_32_DATA
    &global_token
    STR_X0_[X9]

    LOAD_W0_AHEAD                       # Using "ERROR in unary_expr\nMissing (\n"
    SKIP_32_DATA
    &unary_expr_sizeof_string_0
    LOAD_W1_AHEAD                       # Using "("
    SKIP_32_DATA
    &open_paren
    ^~require_match FCALL               # Make sure we have it

    ^~type_name FCALL                   # Get the type
    LDR_X2_[X0,8]                       # Set A->TYPE

    LOAD_W0_AHEAD                       # Using "ERROR in unary_expr\nMissing )\n"
    SKIP_32_DATA
    &unary_expr_sizeof_string_1
    LOAD_W1_AHEAD                       # Using ")"
    SKIP_32_DATA
    &close_paren
    ^~require_match FCALL               # Make sure we have it

    LOAD_W0_AHEAD                       # Using "LOAD_W0_AHEAD\nSKIP_32_DATA\n%"
    SKIP_32_DATA
    &unary_expr_sizeof_string_2
    ^~emit_out FCALL                    # Emit it

    SET_X0_FROM_X2                      # Put A->SIZE in the right place
    ^~numerate_number FCALL             # Turn into string
    ^~emit_out FCALL                    # Emit it

    LOAD_W0_AHEAD                       # Using "\n"
    SKIP_32_DATA
    &unary_expr_sizeof_string_3
    ^~emit_out FCALL                    # Emit it

    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN

# postfix_expr_array function
# Receives Nothing
# Returns Nothing
# Uses X1 for struct type* ARRAY and X2 for char* ASSIGN
:postfix_expr_array
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    LOAD_W9_AHEAD                       # ARRAY = current_target
    SKIP_32_DATA
    &current_target
    LDR_X0_[X9]
    PUSH_X0                             # Protect it

    LOAD_W0_AHEAD                       # Using expression
    SKIP_32_DATA
    &expression
    ^~common_recursion FCALL            # Recurse

    POP_X1                              # Restore array
    LOAD_W9_AHEAD                       # current_target = ARRAY
    SKIP_32_DATA
    &current_target
    STR_X1_[X9]

    LOAD_W2_AHEAD                       # ASSIGN = "DEREF_X0\n"
    SKIP_32_DATA
    &postfix_expr_array_string_0

    LOAD_W0_AHEAD                       # Using "char*"
    SKIP_32_DATA
    &type_char_indirect_name
    LDR_X1_[X1,48]                      # current_target->NAME
    ^~match FCALL                       # IF current_target->NAME == "char*"
    CMP_X0_TO_0                         # load a byte
    SKIP_INST_EQ                        # Otherwise adjust
    ^~postfix_expr_array_large FBRANCH

    # Deal with loading byte
    LOAD_W2_AHEAD                       # ASSIGN = "DEREF_X0_BYTE\n"
    SKIP_32_DATA
    &postfix_expr_array_string_1
    ^~postfix_expr_array_common FBRANCH # Do the next bit

:postfix_expr_array_large
    # deal with arrays made of things other than chars
    LOAD_W0_AHEAD                       # Using "LOAD_W2_AHEAD\nSKIP_32_DATA\n%"
    SKIP_32_DATA
    &postfix_expr_array_string_2
    ^~emit_out FCALL                    # Emit it

    LOAD_W9_AHEAD                       # Using current_target
    SKIP_32_DATA
    &current_target
    LDR_X0_[X9]
    LDR_X0_[X0,24]                      # current_target->INDIRECT
    LDR_X0_[X0,8]                       # current_target->INDIRECT->SIZE
    ^~ceil_log2 FCALL                   # ceil_log2(current_target->indirect->size)
    ^~numerate_number FCALL             # numerate_number(ceil_log2(current_target->indirect->size))
    ^~emit_out FCALL                    # Emit it

    LOAD_W0_AHEAD                       # Using "\nLSHIFT_X0_X0_X2\n"
    SKIP_32_DATA
    &postfix_expr_array_string_3
    ^~emit_out FCALL                    # Emit it

:postfix_expr_array_common
    LOAD_W0_AHEAD                       # Using "ADD_X0_X1_X0\n"
    SKIP_32_DATA
    &postfix_expr_array_string_4
    ^~emit_out FCALL                    # Emit it

    LOAD_W0_AHEAD                       # Using "ERROR in postfix_expr\nMissing ]\n"
    SKIP_32_DATA
    &postfix_expr_array_string_5
    LOAD_W1_AHEAD                       # Using "]"
    SKIP_32_DATA
    &close_bracket
    ^~require_match FCALL               # Make sure we have it

    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X1_[X9]
    LDR_X1_[X1,16]                      # global_token->S
    LOAD_W0_AHEAD                       # Using "="
    SKIP_32_DATA
    &equal
    ^~match FCALL                       # IF global_token->S == "="
    CMP_X0_TO_0                         # We need to preserve address
    SKIP_INST_EQ                        # Otherwise be done
    ^~postfix_expr_array_done FBRANCH

    # Clearing out assign
    LOAD_W2_AHEAD                       # ASSIGN = ""
    SKIP_32_DATA
    &postfix_expr_array_string_6

:postfix_expr_array_done
    SET_X0_FROM_X2                      # Using ASSIGN
    ^~emit_out FCALL                    # Emit it

    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN

# ceil_log2 function
# Receives int a in X0
# Performs log2 on A and
# Returns result in X0
# Uses X1 for INT A and X2 for INT RESULT
:ceil_log2
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    SET_X2_TO_0                         # RESULT = 0

    SET_X1_FROM_X0                      # put A in right place
    SUB_X0_X0_1                         # (A - 1)
    AND_X0_X0_X1                        # A & (A - 1)
    CMP_X0_TO_0                         # IF 0 == (A & (A - 1))
    SKIP_INST_EQ                        # Starting from -1
    ^~ceil_log2_iter FBRANCH

    SET_X2_TO_MINUS_1                   # RESULT = -1

:ceil_log2_iter
    CMP_X1_TO_0                         # IF A > 0
    SKIP_INST_GT                        # Otherwise be done
    ^~ceil_log2_done FBRANCH

    ADD_X2_X2_1                         # RESULT = RESULT + 1
    ASR_X1_X1_1                         # A = A >> 1
    ^~ceil_log2_iter RBRANCH            # Keep looping

:ceil_log2_done
    SET_X0_FROM_X2                      # Return RESULT
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    RETURN


# postfix_expr_arrow function
# Receives nothing
# Returns nothing
# Emits a bunch and updates current_target
# Uses X1 for struct type* I
:postfix_expr_arrow
    PUSH_LR
    PUSH_X1                             # Protect X1
    LOAD_W0_AHEAD                       # Using "# looking up offset\n"
    SKIP_32_DATA
    &postfix_expr_arrow_string_0
    ^~emit_out FCALL                    # Emit it

    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0]                         # global_token->NEXT
    LOAD_W9_AHEAD                       # global_token = global_token->NEXT
    SKIP_32_DATA
    &global_token
    STR_X0_[X9]

    LDR_X1_[X0,16]                      # Using global_token->S
    LOAD_W9_AHEAD                       # Using current_target
    SKIP_32_DATA
    &current_target
    LDR_X0_[X9]
    ^~lookup_member FCALL               # lookup_member(current_target, global_token->s)
    SET_X1_FROM_X0                      # struct type* I = lookup_member(current_target, global_token->s)

    LDR_X0_[X0,40]                      # I->TYPE
    LOAD_W9_AHEAD                       # current_target = I->TYPE
    SKIP_32_DATA
    &current_target
    STR_X0_[X9]

    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0]                         # global_token->NEXT
    LOAD_W9_AHEAD                       # global_token = global_token->NEXT
    SKIP_32_DATA
    &global_token
    STR_X0_[X9]

    LDR_X0_[X1,16]                      # I->OFFSET
    CMP_X0_TO_0                         # IF 0 != I->OFFSET
    SKIP_INST_NE                        # Then we don't need to do an offset
    ^~postfix_expr_arrow_first FBRANCH

    # Deal with needing an offset
    LOAD_W0_AHEAD                       # Using "# -> offset calculation\nLOAD_W1_AHEAD\nSKIP_32_DATA\n%"
    SKIP_32_DATA
    &postfix_expr_arrow_string_1
    ^~emit_out FCALL                    # Emit it

    LDR_X0_[X1,16]                      # I->OFFSET
    ^~numerate_number FCALL             # Convert to string
    ^~emit_out FCALL                    # Emit it

    LOAD_W0_AHEAD                       # Using "\nADD_X0_X1_X0\n"
    SKIP_32_DATA
    &postfix_expr_arrow_string_2
    ^~emit_out FCALL                    # Emit it

:postfix_expr_arrow_first
    LDR_X0_[X1,8]                       # I->SIZE
    CMP_X0_TO_4                         # IF I->SIZE >= 4
    SKIP_INST_GE                        # Otherwise be done
    ^~postfix_expr_arrow_done FBRANCH

    # Last chance for load
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X1_[X0,16]                      # global_token->S
    LOAD_W0_AHEAD                       # Using "="
    SKIP_32_DATA
    &equal
    ^~match FCALL                       # IF global_token->S == "="
    CMP_X0_TO_0                         # Then we have assignment and should not load
    SKIP_INST_NE                        # Be done
    ^~postfix_expr_arrow_done FBRANCH

    # Deal with load case
    LOAD_W0_AHEAD                       # Using "DEREF_X0\n"
    SKIP_32_DATA
    &postfix_expr_arrow_string_3
    ^~emit_out FCALL                    # Emit it

:postfix_expr_arrow_done
    POP_X1                              # Restore X1
    POP_LR
    RETURN

# primary_expr function
# Receives nothing
# Returns nothing
:primary_expr
    PUSH_LR
    PUSH_X1                             # Protect X1

    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X1_[X0,16]                      # global_token->S
    LOAD_W0_AHEAD                       # Using "sizeof"
    SKIP_32_DATA
    &sizeof_string
    ^~match FCALL                       # See if match
    CMP_X0_TO_0                         # IF match
    SKIP_INST_EQ                        # Otherwise try negatives
    ^~primary_expr_neg FBRANCH

    # Deal with sizeof
    ^~unary_expr_sizeof RCALL           # Lets do this
    ^~primary_expr_done FBRANCH         # Be done

:primary_expr_neg
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # global_token->S
    LDR_BYTE_W0_[X0]                    # global_token->S[0]
    CMP_X0_TO_45                        # IF global_token->S[0] == "-"
    SKIP_INST_EQ                        # Otherwise try logical NOT
    ^~primary_expr_not FBRANCH

    # Deal with negative numbers
    LOAD_W0_AHEAD                       # Using "SET_X0_TO_0\n"
    SKIP_32_DATA
    &primary_expr_string_0
    ^~emit_out FCALL                    # Emit it

    LOAD_W0_AHEAD                       # Passing postfix_expr
    SKIP_32_DATA
    &postfix_expr
    ^~common_recursion FCALL            # Get what it is notting

    LOAD_W0_AHEAD                       # Using "SUB_X0_X1_X0\n"
    SKIP_32_DATA
    &primary_expr_string_1
    ^~emit_out FCALL                    # Emit it
    ^~primary_expr_done FBRANCH         # Be done

:primary_expr_not
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # global_token->S
    LDR_BYTE_W0_[X0]                    # global_token->S[0]
    CMP_X0_TO_33                        # IF global_token->S[0] == "!"
    SKIP_INST_EQ                        # Otherwise try '~'
    ^~primary_expr_bin FBRANCH

    # Deal with logical not
    LOAD_W0_AHEAD                       # Using "SET_X0_TO_1\n"
    SKIP_32_DATA
    &primary_expr_string_2
    ^~emit_out FCALL                    # Emit it

    LOAD_W0_AHEAD                       # Passing postfix_expr
    SKIP_32_DATA
    &postfix_expr
    ^~common_recursion FCALL            # Get what it is notting

    LOAD_W0_AHEAD                       # Using "XOR_X0_X1_X0\n"
    SKIP_32_DATA
    &primary_expr_string_3
    ^~emit_out FCALL                    # Emit it
    ^~primary_expr_done FBRANCH         # Be done

:primary_expr_bin
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # global_token->S
    LDR_BYTE_W0_[X0]                    # global_token->S[0]
    CMP_X0_TO_126                       # IF global_token->S[0] == "~"
    SKIP_INST_EQ                        # Otherwise try paren
    ^~primary_expr_paren FBRANCH

    # Deal with binary NOT
    LOAD_W0_AHEAD                       # Passing postfix_expr
    SKIP_32_DATA
    &postfix_expr
    ^~common_recursion FCALL            # Get what it is notting
    LOAD_W0_AHEAD                       # Using "MVN_X0\n"
    SKIP_32_DATA
    &primary_expr_string_4
    ^~emit_out FCALL                    # Emit it
    ^~primary_expr_done FBRANCH         # Be done

:primary_expr_paren
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # global_token->S
    LDR_BYTE_W0_[X0]                    # global_token->S[0]
    CMP_X0_TO_40                        # IF global_token->S[0] == "("
    SKIP_INST_EQ                        # Otherwise try char
    ^~primary_expr_ch FBRANCH

    # deal with nesting
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0]                         # global_token->NEXT
    LOAD_W9_AHEAD                       # global_token = global_token->NEXT
    SKIP_32_DATA
    &global_token
    STR_X0_[X9]
    ^~expression RCALL                  # Lets recurse
    LOAD_W0_AHEAD                       # Using "Error in Primary expression\nDidn't get )\n"
    SKIP_32_DATA
    &primary_expr_string_5
    LOAD_W1_AHEAD                       # Using ")"
    SKIP_32_DATA
    &close_paren
    ^~require_match FCALL               # Make sure we have it
    ^~primary_expr_done FBRANCH         # Be done

:primary_expr_ch
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # global_token->S
    LDR_BYTE_W0_[X0]                    # global_token->S[0]
    CMP_X0_TO_39                        # Using "'"
    SKIP_INST_EQ                        # Otherwise try string
    ^~primary_expr_str FBRANCH

    # Deal with chars
    ^~primary_expr_char FCALL           # Handle that char
    ^~primary_expr_done FBRANCH         # Be done

:primary_expr_str
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # global_token->S
    LDR_BYTE_W0_[X0]                    # global_token->S[0]
    CMP_X0_TO_34                        # Using '\"'
    SKIP_INST_EQ                        # Otherwise try a variable
    ^~primary_expr_var FBRANCH

    # Deal with strings
    ^~primary_expr_string FCALL         # Handle that string
    ^~primary_expr_done FBRANCH         # Be done

:primary_expr_var
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # global_token->S
    LDR_BYTE_W0_[X0]                    # global_token->S[0]
    LOAD_W1_AHEAD                       # Using "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"
    SKIP_32_DATA
    &primary_expr_string_6
    ^~In_Set RCALL                      # See if we have a match
    CMP_X0_TO_1                         # IF match
    SKIP_INST_EQ                        # otherwise try number
    ^~primary_expr_num FBRANCH

    # Deal with variables
    ^~primary_expr_variable FCALL       # Deal with variable
    ^~primary_expr_done FBRANCH         # Be done

:primary_expr_num
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # global_token->S
    LDR_BYTE_W0_[X0]                    # global_token->S[0]
    LOAD_W1_AHEAD                       # Using "0123456789"
    SKIP_32_DATA
    &primary_expr_string_7
    ^~In_Set RCALL                      # See if we have a match
    CMP_X0_TO_1                         # IF match
    SKIP_INST_EQ                        # otherwise we failed hard
    ^~primary_expr_fail FBRANCH

    # Deal with numbers
    ^~primary_expr_number FCALL         # Collect the number
    ^~primary_expr_done FBRANCH         # Be done

:primary_expr_fail
    # looks like we hit bad input
    # abort before it gets bad
    ^~primary_expr_failure FCALL        # No match means failure
:primary_expr_done
    POP_X1                              # Restore X1
    POP_LR
    RETURN

# primary_expr_variable function
# Receives nothing
# Returns nothing
# Walks global and updates output
# Uses X0 for struct token_list* a and X2 for char* S
:primary_expr_variable
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X2_[X0,16]                      # S = global_token->S
    LDR_X0_[X0]                         # global_token->NEXT
    LOAD_W9_AHEAD                       # global_token = global_token->NEXT
    SKIP_32_DATA
    &global_token
    STR_X0_[X9]

    SET_X0_FROM_X2                      # Using S
    LOAD_W9_AHEAD                       # Using global_constant_list
    SKIP_32_DATA
    &global_constant_list
    LDR_X1_[X9]
    ^~sym_lookup FCALL                  # sym_lookup(s, global_constant_list)
    CMP_X0_TO_0                         # IF NULL == sym_lookup(s, global_constant_list)
    SKIP_INST_NE                        # Try locals next
    ^~primary_expr_variable_local FBRANCH

    # Deal with constant load
    LDR_X1_[X0,32]                      # a->ARGS
    LOAD_W0_AHEAD                       # Using "LOAD_IMMEDIATE_x0 %"
    SKIP_32_DATA
    &primary_expr_variable_string_2
    ^~emit_out FCALL                    # Emit it

    LDR_X0_[X1,16]                      # a->ARGS->S
    ^~emit_out FCALL                    # Emit it

    LOAD_W0_AHEAD                       # Using "\n"
    SKIP_32_DATA
    &primary_expr_variable_string_1
    ^~emit_out FCALL                    # Emit it
    ^~primary_expr_variable_done FBRANCH # Be done

:primary_expr_variable_local
    SET_X0_FROM_X2                      # Using S
    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X1_[X9]
    LDR_X1_[X1,8]                       # function->locals
    ^~sym_lookup FCALL                  # sym_lookup(s, function->locals)
    CMP_X0_TO_0                         # IF NULL == sym_lookup(s, function->locals)
    SKIP_INST_NE                        # try arguments next
    ^~primary_expr_variable_arguments FBRANCH

    # Deal with local load
    ^~variable_load FCALL               # Collect it
    ^~primary_expr_variable_done FBRANCH # Be done

:primary_expr_variable_arguments
    SET_X0_FROM_X2                      # Using S
    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X1_[X9]
    LDR_X1_[X1,32]                      # function->args
    ^~sym_lookup FCALL                  # sym_lookup(s, function->args)
    CMP_X0_TO_0                         # IF NULL == sym_lookup(s, function->args)
    SKIP_INST_NE                        # try functions next
    ^~primary_expr_variable_function FBRANCH

    # Deal with argument load
    ^~variable_load FCALL               # Collect it
    ^~primary_expr_variable_done FBRANCH # Be done

:primary_expr_variable_function
    SET_X0_FROM_X2                      # Using S
    LOAD_W9_AHEAD                       # Using global_function_list
    SKIP_32_DATA
    &global_function_list
    LDR_X1_[X9]
    ^~sym_lookup FCALL                  # sym_lookup(s, global_function_list)
    CMP_X0_TO_0                         # IF NULL == sym_lookup(s, global_function_list)
    SKIP_INST_NE                        # try globals next
    ^~primary_expr_variable_global FBRANCH

    # Deal with functions
    ^~function_load FCALL               # Deal with the function
    ^~primary_expr_variable_done FBRANCH # Be done

:primary_expr_variable_global
    SET_X0_FROM_X2                      # Using S
    LOAD_W9_AHEAD                       # Using global_symbol_list
    SKIP_32_DATA
    &global_symbol_list
    LDR_X1_[X9]
    ^~sym_lookup FCALL                  # sym_lookup(s, global_symbol_list)
    CMP_X0_TO_0                         # IF NULL == sym_lookup(s, global_symbol_list)
    SKIP_INST_NE                        # Give up
    ^~primary_expr_variable_error FBRANCH

    # Deal with globals
    ^~global_load FCALL                 # Collect that global
    ^~primary_expr_variable_done FBRANCH # Be done

:primary_expr_variable_error
    SET_X14_TO_2                        # write to standard error
#   call line_error                     # Write useful debug info
    SET_X0_FROM_X2                      # put S in the right place
    ^~File_Print RCALL                  # print it

    LOAD_W0_AHEAD                       # Ending string
    SKIP_32_DATA
    &primary_expr_variable_string_0
    ^~File_Print RCALL                  # print it
    ^~Exit_Failure FBRANCH              # Abort Hard

:primary_expr_variable_done
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN

# function_call function
# Receives char* S in X0 and int BOOL in X1
# Builds stack frames before and tears them down after function calls
# Uses X2 for char* S, X3 for int BOOL, X5 for PASSED
:function_call
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    PUSH_X3                             # Protect X3
    PUSH_X5                             # Protect X5
    SET_X2_FROM_X0                      # Put S in place
    SET_X3_FROM_X1                      # Put BOOL in place
    SET_X5_TO_0                         # PASSED = 0

    LOAD_W0_AHEAD                       # Using "ERROR in process_expression_list\nNo ( was found\n"
    SKIP_32_DATA
    &function_call_string_0
    LOAD_W1_AHEAD                       # Using "("
    SKIP_32_DATA
    &open_paren
    ^~require_match FCALL               # Make sure we have it

    LOAD_W0_AHEAD                       # Using "PUSH_X16\t# Protect a tmp register we're going to use\n"
    SKIP_32_DATA
    &function_call_string_1
    ^~emit_out FCALL                    # Emit it

    LOAD_W0_AHEAD                       # Using "PUSH_LR\t# Protect the old return pointer (link)\n"
    SKIP_32_DATA
    &function_call_string_2
    ^~emit_out FCALL                    # Emit it

    LOAD_W0_AHEAD                       # Using "PUSH_BP\t# Protect the old base pointer\n"
    SKIP_32_DATA
    &function_call_string_3a
    ^~emit_out FCALL                    # Emit it

    LOAD_W0_AHEAD                       # Using "SET_X16_FROM_SP\t# The base pointer to-be\n"
    SKIP_32_DATA
    &function_call_string_3b
    ^~emit_out FCALL                    # Emit it

    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # global_token->S
    LDR_BYTE_W0_[X0]                    # global_token->S[0]
    CMP_X0_TO_41                        # IF global_token->S[0] == ")"
    SKIP_INST_NE                        # Then no arguments to send
    ^~function_call_gen_done FBRANCH

    # looks like we have arguments to collect
    ^~expression RCALL                  # Collect the argument

    LOAD_W0_AHEAD                       # Using "PUSH_X0\t#_process_expression1\n"
    SKIP_32_DATA
    &function_call_string_4
    ^~emit_out FCALL                    # Emit it
    SET_X5_TO_1                         # PASSED = 1

:function_call_gen_iter
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # global_token->S
    LDR_BYTE_W0_[X0]                    # global_token->S[0]
    CMP_X0_TO_44                        # IF global_token->S[0] == ","
    SKIP_INST_EQ                        # Otherwise we are done
    ^~function_call_gen_done FBRANCH

    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0]                         # global_token->NEXT
    LOAD_W9_AHEAD                       # global_token = global_token->NEXT
    SKIP_32_DATA
    &global_token
    STR_X0_[X9]

    ^~expression RCALL                  # Collect the argument

    LOAD_W0_AHEAD                       # Using "PUSH_X0\t#_process_expression2\n"
    SKIP_32_DATA
    &function_call_string_5
    ^~emit_out FCALL                    # Emit it
    ADD_X5_X5_1                         # PASSED = PASSED + 1
    ^~function_call_gen_iter RBRANCH    # Keep trying

:function_call_gen_done
    # All is collected
    LOAD_W0_AHEAD                       # Using "ERROR in process_expression_list\nNo ) was found\n"
    SKIP_32_DATA
    &function_call_string_6
    LOAD_W1_AHEAD                       # Using ")"
    SKIP_32_DATA
    &close_paren
    ^~require_match FCALL               # Make sure we have it

    CMP_X3_TO_0                         # IF(BOOL == TRUE)
    SKIP_INST_EQ                        # Otherwise it is a static call
    ^~function_call_static FBRANCH

    # Deal with a passed function pointer
    LOAD_W0_AHEAD                       # Using "SET_X0_FROM_BP\n"
    SKIP_32_DATA
    &function_call_string_7a
    ^~emit_out FCALL                    # Emit it

    LOAD_W0_AHEAD                       # Using "LOAD_W1_AHEAD\nSKIP_32_DATA\n%"
    SKIP_32_DATA
    &function_call_string_7b
    ^~emit_out FCALL                    # Emit it

    SET_X0_FROM_X2                      # Using S
    ^~emit_out FCALL                    # Emit it

    LOAD_W0_AHEAD                       # Using "\nSUB_X0_X0_X1\n"
    SKIP_32_DATA
    &function_call_string_8
    ^~emit_out FCALL                    # Emit it

    LOAD_W0_AHEAD                       # Using "DEREF_X0\n"
    SKIP_32_DATA
    &function_call_string_9
    ^~emit_out FCALL                    # Emit it

    LOAD_W0_AHEAD                       # Using "SET_BP_FROM_X16\n"
    SKIP_32_DATA
    &function_call_string_10a
    ^~emit_out FCALL                    # Emit it

    LOAD_W0_AHEAD                       # Using "SET_X16_FROM_X0\n"
    SKIP_32_DATA
    &function_call_string_10b
    ^~emit_out FCALL                    # Emit it

    LOAD_W0_AHEAD                       # Using "BLR_X16\n"
    SKIP_32_DATA
    &function_call_string_10c
    ^~emit_out FCALL                    # Emit it

    LOAD_W0_AHEAD                       # Using "POP_X1\t# _process_expression_locals\n"
    SKIP_32_DATA
    &function_call_string_13
    ^~function_call_cleanup FBRANCH     # Clean up

:function_call_static
    # Deal with fixed function name
    LOAD_W0_AHEAD                       # Using "SET_BP_FROM_X16\n"
    SKIP_32_DATA
    &function_call_string_10a
    ^~emit_out FCALL                    # Emit it

    LOAD_W0_AHEAD                       # Using "LOAD_W16_AHEAD\nSKIP_32_DATA\n&FUNCTION_"
    SKIP_32_DATA
    &function_call_string_11
    ^~emit_out FCALL                    # Emit it

    SET_X0_FROM_X2                      # Using S
    ^~emit_out FCALL                    # Emit it

    LOAD_W0_AHEAD                       # Using "\n"
    SKIP_32_DATA
    &function_call_string_12a
    ^~emit_out FCALL                    # Emit it

    LOAD_W0_AHEAD                       # Using "BLR_X16\n"
    SKIP_32_DATA
    &function_call_string_12b
    ^~emit_out FCALL                    # Emit it

    LOAD_W0_AHEAD                       # Using "POP_X1\t# _process_expression_locals\n"
    SKIP_32_DATA
    &function_call_string_13

:function_call_cleanup
    CMP_X5_TO_0                         # IF PASSED > 0
    SKIP_INST_GT                        # Otherwise be done
    ^~function_call_done FBRANCH

    # The desired string is already in X0
    ^~emit_out FCALL                    # Emit it

    SUB_X5_X5_1                         # PASSED = PASSED - 1
    ^~function_call_cleanup RBRANCH     # Keep going

:function_call_done
    LOAD_W0_AHEAD                       # Using "POP_BP\t# Restore the old base pointer\n"
    SKIP_32_DATA
    &function_call_string_14
    ^~emit_out FCALL                    # Emit it

    LOAD_W0_AHEAD                       # Using "POP_LR\t# Restore the old return pointer (link)\n"
    SKIP_32_DATA
    &function_call_string_15
    ^~emit_out FCALL                    # Emit it

    LOAD_W0_AHEAD                       # Using "POP_X16\t# Restore a register we used as tmp\n"
    SKIP_32_DATA
    &function_call_string_16
    ^~emit_out FCALL                    # Emit it


    POP_X5                              # Restore X5
    POP_X3                              # Restore X3
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN

# variable_load function
# Receives struct token_list* A in X0
# Returns nothing
# Updates output and current_target
# Uses X2 for A
:variable_load
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    SET_X2_FROM_X0                      # Protect A

    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X1_[X9]
    LDR_X1_[X1,16]                      # global_token->S
    LOAD_W0_AHEAD                       # Using "("
    SKIP_32_DATA
    &open_paren
    ^~match FCALL                       # IF global_token->S == "("
    CMP_X0_TO_0                         # Then it might be a function
    SKIP_INST_EQ                        # Otherwise it is regular
    ^~variable_load_regular FBRANCH

    LDR_X1_[X2,24]                      # A->TYPE
    LDR_X1_[X1,48]                      # A->TYPE->NAME
    LOAD_W0_AHEAD                       # Using "FUNCTION"
    SKIP_32_DATA
    &type_function_name
    ^~match FCALL                       # IF A->TYPE->NAME == "FUNCTION"
    CMP_X0_TO_0                         # Then it must be a function
    SKIP_INST_EQ                        # otherwise just another regular
    ^~variable_load_regular FBRANCH

    # deal with function
    LDR_X0_[X2,32]                      # A->DEPTH
    ^~numerate_number FCALL             # Convert to string
    SET_X1_TO_0                         # pass 0 for true
    ^~function_call RCALL               # Create the function call
    ^~variable_load_done FBRANCH        # Be done

:variable_load_regular
    LDR_X0_[X2,24]                      # A->TYPE
    LOAD_W9_AHEAD                       # current_target = A->TYPE
    SKIP_32_DATA
    &current_target
    STR_X0_[X9]

    LOAD_W0_AHEAD                       # Using "SET_X0_FROM_BP\nLOAD_W1_AHEAD\nSKIP_32_DATA\n%"
    SKIP_32_DATA
    &variable_load_string_0
    ^~emit_out FCALL                    # Emit it

    LDR_X0_[X2,32]                      # A->DEPTH
    ^~numerate_number FCALL             # Convert to string
    ^~emit_out FCALL                    # Emit it

    LOAD_W0_AHEAD                       # Using "\nSUB_X0_X0_X1\n"
    SKIP_32_DATA
    &variable_load_string_1
    ^~emit_out FCALL                    # Emit it

    # Check for special case of assignment
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X1_[X9]
    LDR_X1_[X1,16]                      # global_token->S
    LOAD_W0_AHEAD                       # Using "="
    SKIP_32_DATA
    &equal
    ^~match FCALL                       # IF global_token->S == "="
    CMP_X0_TO_0                         # Then we skip loading
    SKIP_INST_NE                        # And be done
    ^~variable_load_done FBRANCH

    # Deal with common case
    LOAD_W0_AHEAD                       # Using "DEREF_X0\n"
    SKIP_32_DATA
    &variable_load_string_2
    ^~emit_out FCALL                    # Emit it

:variable_load_done
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN

# function_load function
# Receives struct token_list* a in X0
# Returns nothing
# Uses X2 to hold A->S
:function_load
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    LDR_X0_[X0,16]                      # A->S
    SET_X2_FROM_X0                      # Protect A->S
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X1_[X9]
    LDR_X1_[X1,16]                      # global_token->S
    LOAD_W0_AHEAD                       # Using "("
    SKIP_32_DATA
    &open_paren
    ^~match FCALL                       # IF global_token->S == "("
    CMP_X0_TO_0                         # The we need to do a function call
    SKIP_INST_EQ                        # Otherwise just load it's address
    ^~function_load_regular FBRANCH

    # Deal with function call
    SET_X0_FROM_X2                      # Using A->S
    SET_X1_TO_1                         # Using FALSE
    ^~function_call RCALL               # Deal with it
    ^~function_load_done FBRANCH        # Be done

:function_load_regular
    LOAD_W0_AHEAD                       # Using "LOAD_W0_AHEAD\nSKIP_32_DATA\n&FUNCTION_"
    SKIP_32_DATA
    &function_load_string_0
    ^~emit_out FCALL                    # Emit it

    SET_X0_FROM_X2                      # Using A->S
    ^~emit_out FCALL                    # Emit it

    LOAD_W0_AHEAD                       # Using "\n"
    SKIP_32_DATA
    &function_load_string_1
    ^~emit_out FCALL                    # Emit it

:function_load_done
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN


# global_load function
# Receives struct token_list* A in X0
# Returns nothing
# Uses X1 to hold A->S
:global_load
    PUSH_LR
    PUSH_X1                             # Protect X1
    SET_X1_FROM_X0                      # Set as A
    LDR_X1_[X1,16]                      # Set as A->S

    LDR_X0_[X0,24]                      # A->TYPE
    LOAD_W9_AHEAD                       # current_target = A->TYPE
    SKIP_32_DATA
    &current_target
    STR_X0_[X9]

    LOAD_W0_AHEAD                       # Using "LOAD_W0_AHEAD\nSKIP_32_DATA\n&GLOBAL_"
    SKIP_32_DATA
    &global_load_string_0
    ^~emit_out FCALL                    # Emit it

    SET_X0_FROM_X1                      # Using A->S
    ^~emit_out FCALL                    # Emit it

    LOAD_W0_AHEAD                       # Using "\n"
    SKIP_32_DATA
    &global_load_string_1
    ^~emit_out FCALL                    # Emit it

    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X1_[X9]
    LDR_X1_[X1,16]                      # global_token->S
    LOAD_W0_AHEAD                       # "="
    SKIP_32_DATA
    &equal
    ^~match FCALL                       # IF global_token->S == "="
    CMP_X0_TO_0                         # We need to skip for assignment
    SKIP_INST_NE                        # and be done
    ^~global_load_done FBRANCH

    # Otherwise we are loading the contents
    LOAD_W0_AHEAD                       # Using "DEREF_X0\n"
    SKIP_32_DATA
    &global_load_string_2
    ^~emit_out FCALL                    # Emit it

:global_load_done
    POP_X1                              # Restore X1
    POP_LR
    RETURN


# sym_lookup function
# Receives char* S in X0 and struct token_list* symbol_list in X1
# Uses I->S in X0, S in X1 and I in X2
# Returns match or NULL
:sym_lookup
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    SET_X2_FROM_X1                      # I = symbol_list
    SET_X1_FROM_X0                      # Put S in the right place
:sym_lookup_iter
    CMP_X2_TO_0                         # IF NULL == I
    SKIP_INST_NE                        # We failed to find match
    ^~sym_lookup_done FBRANCH

    LDR_X0_[X2,16]                      # Using I->S
    ^~match FCALL                       # IF I->S == S
    CMP_X0_TO_0                         # then be done
    SKIP_INST_NE                        # Failed
    ^~sym_lookup_done FBRANCH

    LDR_X2_[X2]                         # I = I->NEXT
    ^~sym_lookup_iter RBRANCH           # otherwise keep looping

:sym_lookup_done
    SET_X0_FROM_X2                      # Return I
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN


# primary_expr_number function
# Receives nothing
# Returns nothing
# Simply uses current global token to update output and then steps to next global_token
:primary_expr_number
    PUSH_LR
    LOAD_W0_AHEAD                       # Using "LOAD_W0_AHEAD\nSKIP_32_DATA\n%"
    SKIP_32_DATA
    &primary_expr_number_string_0
    ^~emit_out FCALL                    # Emit it

    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # global_token->S
    ^~emit_out FCALL                    # Emit it

    LOAD_W0_AHEAD                       # Using "\n"
    SKIP_32_DATA
    &primary_expr_number_string_1
    ^~emit_out FCALL                    # Emit it

    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0]                         # global_token->NEXT
    LOAD_W9_AHEAD                       # global_token = global_token->NEXT
    SKIP_32_DATA
    &global_token
    STR_X0_[X9]
    POP_LR
    RETURN


# primary_expr_string function
# receives nothing
# Returns nothing
# creates entries for string and calls to generate string output
# uses X2 for char* number_string
:primary_expr_string
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    LOAD_W9_AHEAD                       # Using current_count
    SKIP_32_DATA
    &current_count
    LDR_X1_[X9]
    SET_X0_FROM_X1                      # And putting it in the right place
    ^~numerate_number FCALL             # Get the string
    SET_X2_FROM_X0                      # protect number_string

    ADD_X1_X1_1                         # current_count + 1
    LOAD_W9_AHEAD                       # current_count = current_count + 1
    SKIP_32_DATA
    &current_count
    STR_X1_[X9]

    LOAD_W0_AHEAD                       # Using "LOAD_W0_AHEAD\nSKIP_32_DATA\n&STRING_"
    SKIP_32_DATA
    &primary_expr_string_string_0
    ^~emit_out FCALL                    # Emit it

    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # function->S
    SET_X1_FROM_X2                      # Put number_string in the right place
    ^~uniqueID_out FCALL                # Make it unique

    # Generate the target
    LOAD_W0_AHEAD                       # Using ":STRING_"
    SKIP_32_DATA
    &primary_expr_string_string_1
    LOAD_W9_AHEAD                       # Using strings_list
    SKIP_32_DATA
    &strings_list
    LDR_X1_[X9]
    ^~emit FCALL                        # Emit it
    SET_X1_FROM_X0                      # put new strings_list in place

    LOAD_W9_AHEAD                       # Using function
    SKIP_32_DATA
    &function
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # function->S
    ^~uniqueID FCALL                    # Make it unique
    SET_X1_FROM_X0                      # put new strings_list in place

    # Parse the string
    LOAD_W9_AHEAD                       # Using global token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # global_token->S
    ^~parse_string FCALL                # convert to useful form
    ^~emit FCALL                        # Emit it
    LOAD_W9_AHEAD                       # Update Strings _list
    SKIP_32_DATA
    &strings_list
    STR_X0_[X9]

    LOAD_W9_AHEAD                       # Using global token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0]                         # global_token->NEXT
    LOAD_W9_AHEAD                       # global_token = global_token->NEXT
    SKIP_32_DATA
    &global_token
    STR_X0_[X9]

    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN


# primary_expr_char function
# Receives nothing
# Returns nothing
# Updates output_list using global_token
:primary_expr_char
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    LOAD_W0_AHEAD                       # Using "LOAD_W0_AHEAD\nSKIP_32_DATA\n%"
    SKIP_32_DATA
    &primary_expr_char_string_0
    ^~emit_out FCALL                    # Emit it

    LOAD_W9_AHEAD                       # Using global token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # global_token->S
    ADD_X0_X0_1                         # global_token->S + 1
    ^~escape_lookup FCALL               # Get the char
    ^~numerate_number FCALL             # Convert to string
    ^~emit_out FCALL                    # emit it

    LOAD_W0_AHEAD                       # Using "\n"
    SKIP_32_DATA
    &primary_expr_char_string_1
    ^~emit_out FCALL                    # Emit it

    LOAD_W9_AHEAD                       # Using global token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0]                         # global_token->NEXT
    LOAD_W9_AHEAD                       # global_token = global_token->NEXT
    SKIP_32_DATA
    &global_token
    STR_X0_[X9]
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN


# 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
    SET_X14_TO_2                        # write to standard error
    LOAD_W0_AHEAD                       # Using "Received "
    SKIP_32_DATA
    &primary_expr_failure_string_0
    ^~File_Print RCALL                  # Print it

    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # global_token->S
    ^~File_Print RCALL                  # Print it

    LOAD_W0_AHEAD                       # Using " in primary_expr\n"
    SKIP_32_DATA
    &primary_expr_failure_string_1
    ^~File_Print RCALL                  # Print it
    ^~Exit_Failure FBRANCH              # Abort Hard


# general_recursion function
# Receives FUNCTION F in X0, char* S in X1, char* name in X2 and FUNCTION iterate in X3
# Returns nothing
# Uses X2 for char* S, X3 for FUNCTION iterate and X4 for FUNCTION F
# But generally recurses a shitload
:general_recursion
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    PUSH_X3                             # Protect X3
    PUSH_X4                             # Protect X4
    SET_X4_FROM_X0                      # Protect F
    SET_X0_FROM_X2                      # Put name in the right place
    SET_X2_FROM_X1                      # Protect S

    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X1_[X9]
    LDR_X1_[X1,16]                      # global_token->S
    ^~match FCALL                       # IF match(name, global_token->s)
    CMP_X0_TO_0                         # If true we do
    SKIP_INST_EQ                        # Otherwise skip it
    ^~general_recursion_done FBRANCH

    # Deal with the recursion
    SET_X0_FROM_X4                      # Put F in the right place
    ^~common_recursion FCALL            # Recurse

    SET_X0_FROM_X2                      # Put S in the right place
    ^~emit_out FCALL                    # Emit it

    SET_X0_FROM_X3                      # Put iterate in the right place
    BLR_X0                              # Down the rabbit hole

:general_recursion_done
    POP_X4                              # Restore X4
    POP_X3                              # Restore X3
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN


# promote_type function
# Receives struct type* a in X0 and struct type* b in X1
# Returns the most recent type in X0
# Uses X0 for struct type* I, X2 for struct type* A and X3 for struct type* B
:promote_type
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    PUSH_X3                             # Protect X3
    CMP_X1_TO_0                         # IF NULL == B
    SKIP_INST_NE                        # Just return A
    ^~promote_type_done FBRANCH

    SET_X2_FROM_X0                      # Put A in place
    SET_X3_FROM_X1                      # Put B in place
    SET_X0_FROM_X1                      # IF NULL == A
    CMP_X2_TO_0                         # Then we just return B
    SKIP_INST_NE                        # Be done
    ^~promote_type_done FBRANCH

    # Looks like we need to walk the list
    LDR_X2_[X2,48]                      # A->NAME
    LDR_X3_[X3,48]                      # B->NAME
    LOAD_W9_AHEAD                       # I = global_types
    SKIP_32_DATA
    &global_types
    LDR_X0_[X9]
:promote_type_iter
    CMP_X0_TO_0                         # IF NULL == I
    SKIP_INST_NE                        # Just be done
    ^~promote_type_done FBRANCH

    LDR_X1_[X0,48]                      # I->NAME
    CMP_X1_X2                           # IF(A->NAME == I->NAME)
    SKIP_INST_NE                        # Be done
    ^~promote_type_done FBRANCH

    CMP_X1_X3                           # IF(B->NAME == I->NAME)
    SKIP_INST_NE                        # Be done
    ^~promote_type_done FBRANCH

    LDR_X1_[X0,24]                      # I->INDIRECT
    LDR_X1_[X1,48]                      # I->INDIRECT->NAME

    CMP_X1_X2                           # IF(A->NAME == I->INDIRECT->NAME)
    SKIP_INST_NE                        # Be done
    ^~promote_type_done FBRANCH

    CMP_X1_X3                           # IF(B->NAME == I->INDIRECT->NAME)
    SKIP_INST_NE                        # Be done
    ^~promote_type_done FBRANCH

    LDR_X0_[X0]                         # I = I->NEXT
    ^~promote_type_iter RBRANCH         # Keep going

:promote_type_done
    POP_X3                              # Restore X3
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN


# common_recursion function
# Receives FUNCTION F in X0
# Returns Nothing
# Walks global_token list and update output_list
# Updates current_target
# Uses X1 to hold FUNCTION F and struct type* last_type
:common_recursion
    PUSH_LR
    PUSH_X1                             # Protect X1
    SET_X1_FROM_X0                      # Put FUNCTION F safely out of the way
    LOAD_W0_AHEAD                       # Using "PUSH_X0\t#_common_recursion\n"
    SKIP_32_DATA
    &common_recursion_string_0
    ^~emit_out FCALL                    # Emit it

    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0]                         # global_token->NEXT
    LOAD_W9_AHEAD                       # global_token = global_token->NEXT
    SKIP_32_DATA
    &global_token
    STR_X0_[X9]

    SET_X0_FROM_X1                      # Prepare for function call
    LOAD_W9_AHEAD                       # Get last type
    SKIP_32_DATA
    &current_target
    LDR_X1_[X9]
    BLR_X0                              # F();
    LOAD_W9_AHEAD                       # Get current_target
    SKIP_32_DATA
    &current_target
    LDR_X0_[X9]
    ^~promote_type RCALL                # get the right type
    LOAD_W9_AHEAD                       # Set new current_target
    SKIP_32_DATA
    &current_target
    STR_X0_[X9]

    LOAD_W0_AHEAD                       # Using "POP_X1\t# _common_recursion\n"
    SKIP_32_DATA
    &common_recursion_string_1
    ^~emit_out FCALL                    # Emit it
    POP_X1                              # Restore X1
    POP_LR
    RETURN


# require_match function
# Receives char* message in X0 and char* required in X1
# Returns nothing
# Uses X2 to hold message and updates global_token
:require_match
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    SET_X2_FROM_X0                      # put the message somewhere safe
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # global_token->S
    ^~match FCALL                       # IF required == global_token->S
    CMP_X0_TO_0                         # we are fine
    SKIP_INST_NE                        # otherwise pain
    ^~require_match_good FBRANCH

    # Deal with bad times
#   call line_error                     # Tell user what went wrong
    SET_X14_TO_2                        # write to standard error
    SET_X0_FROM_X2                      # using our message
    ^~File_Print RCALL                  # Print it
    ^~Exit_Failure FBRANCH              # Abort HARD

:require_match_good
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0]                         # global_token->next
    LOAD_W9_AHEAD                       # global_token = global_token->next
    SKIP_32_DATA
    &global_token
    STR_X0_[X9]
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN


# uniqueID Function
# Receives char *S in X0, struct token_list* l in X1 and char* num in X2
# Returns updated struct token_list* L in X0
:uniqueID
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    ^~emit FCALL                        # emit(s, l)
    SET_X1_FROM_X0                      # Put L in correct place
    LOAD_W0_AHEAD                       # Using "_"
    SKIP_32_DATA
    &underline
    ^~emit FCALL                        # emit("_", l)
    SET_X1_FROM_X0                      # Put L in correct place
    SET_X0_FROM_X2                      # Put num in correct place
    ^~emit FCALL                        # emit(num, l)
    SET_X1_FROM_X0                      # Put L in correct place
    LOAD_W0_AHEAD                       # Using "\n"
    SKIP_32_DATA
    &uniqueID_string_0
    ^~emit FCALL                        # emit("\n", l)
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN


# uniqueID_out function
# Receives char* S in X0 and char* num in X1
# Returns nothing
:uniqueID_out
    PUSH_LR
    PUSH_X0                             # Protect X0
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    SET_X2_FROM_X1                      # Put num in right spot
    LOAD_W9_AHEAD                       # Using output_list
    SKIP_32_DATA
    &output_list
    LDR_X1_[X9]
    ^~uniqueID RCALL                    # Get updated list
    LOAD_W9_AHEAD                       # output_list = uniqueID(s, output_list, num)
    SKIP_32_DATA
    &output_list
    STR_X0_[X9]
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_X0                              # Restore X0
    POP_LR
    RETURN

# emit_out function
# Receives char* S in X0
# Returns nothing
# Updates output_list
# MUST NOT ALTER REGISTERS
:emit_out
    PUSH_LR
    PUSH_X0                             # Protect X0
    PUSH_X1                             # Protect X1
    LOAD_W9_AHEAD                       # Using output_list
    SKIP_32_DATA
    &output_list
    LDR_X1_[X9]
    ^~emit FCALL                        # emit it
    LOAD_W9_AHEAD                       # update it
    SKIP_32_DATA
    &output_list
    STR_X0_[X9]
    POP_X1                              # Restore X1
    POP_X0                              # Restore X0
    POP_LR
    RETURN


# emit function
# Receives char *s in X0 and struct token_list* head in X1
# Returns struct token_list* T in X0
:emit
    PUSH_LR
    PUSH_X2                             # Protect X2
    SET_X2_FROM_X0                      # put S out of the way
    SET_X0_TO_40                        # sizeof(struct token_list)
    ^~malloc RCALL                      # get T
    STR_X1_[X0]                         # t->next = head;
    STR_X2_[X0,16]                      # t->s = s;
    POP_X2                              # Restore X2
    POP_LR
    RETURN


# escape_lookup function
# Receives char* c in X0
# Returns integer value of char in X0
# Aborts hard if unknown escape is received
# Uses X2 to hold char* C
:escape_lookup
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    SET_X2_FROM_X0                      # Put char* C in safe place
    LDR_BYTE_W0_[X2]                    # Load c[0]
    CMP_X0_TO_92                        # If '\\' != c[0]
    SKIP_INST_EQ                        # Be done
    ^~escape_lookup_done FBRANCH

    SET_X1_FROM_X2                      # Prepare for walk
    ADD_X1_X1_1                         # increment
    LDR_BYTE_W1_[X1]                    # load c[1]

    CMP_X1_TO_120                       # Check if \x??
    SKIP_INST_NE                        # Deal with hex
    ^~escape_lookup_hex FBRANCH

    # Deal with \? escapes
    SET_X0_TO_10                        # Guess "\n"
    CMP_X1_TO_110                       # If n
    SKIP_INST_NE                        # Be done
    ^~escape_lookup_done FBRANCH

    SET_X0_TO_9                         # Guess "\t"
    CMP_X1_TO_116                       # If t
    SKIP_INST_NE                        # Be done
    ^~escape_lookup_done FBRANCH

    SET_X0_FROM_X1                      # "\\", "'" and '\"' all encode as themselves
    CMP_X1_TO_92                        # If "\\"
    SKIP_INST_NE                        # Be done
    ^~escape_lookup_done FBRANCH
    CMP_X1_TO_39                        # IF "'"
    SKIP_INST_NE                        # Be done
    ^~escape_lookup_done FBRANCH
    CMP_X1_TO_34                        # IF '\"'
    SKIP_INST_NE                        # Be done
    ^~escape_lookup_done FBRANCH

    SET_X0_TO_13                        # Guess "\r"
    CMP_X1_TO_114                       # IF r
    SKIP_INST_NE                        # Be done
    ^~escape_lookup_done FBRANCH

    # Looks like we have no clue what we are doing
    # Aborting hard
    SET_X14_TO_2                        # write to standard error
    LOAD_W0_AHEAD                       # Using "Unknown escape received: "
    SKIP_32_DATA
    &escape_lookup_string_0
    ^~File_Print RCALL                  # Print it
    SET_X0_FROM_X2                      # Using C
    ^~File_Print RCALL                  # Print it
    LOAD_W0_AHEAD                       # Using " Unable to process\n"
    SKIP_32_DATA
    &escape_lookup_string_1
    ^~File_Print RCALL                  # Print it
    ^~Exit_Failure FBRANCH              # Abort Hard

:escape_lookup_done
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN

:escape_lookup_hex
    # Give up on C and just assume they know what they are doing
    ADD_X2_X2_2                         # increment
    LDR_BYTE_W0_[X2]                    # c[2]
    ADD_X2_X2_1                         # increment
    ^~char2hex FCALL                    # Get the hex value
    LSL_X0_X0_4                         # c << 4
    SET_X1_FROM_X0                      # protect c << 4
    LDR_BYTE_W0_[X2]                    # c[3]
    ^~char2hex FCALL                    # Get the hex value
    ADD_X0_X0_X1                        # hex(c[2]) << 4 + hex(c[3])
    ^~escape_lookup_done RBRANCH        # Be done


# char2hex function
# Receives char in X0
# Returns hex or aborts hard
:char2hex
    SUB_X0_X0_48                        # Try 0-9
    CMP_X0_TO_10                        # Otherwise fun times
    SKIP_INST_GE                        # Be done
    ^~char2hex_done FBRANCH

    # Deal with A-F
    SET_X9_TO_223
    AND_X0_X0_X9                        # Unset High bit turning a-f into A-F
    SUB_X0_X0_7                         # Shift down into position
    CMP_X0_TO_10                        # Everything below A is bad
    SKIP_INST_GE                        # And should fail
    ^~char2hex_fail FBRANCH
    CMP_X0_TO_16                        # Make sure we are below F
    SKIP_INST_GE                        # If so be done
    ^~char2hex_done FBRANCH

:char2hex_fail
    # Time to fail hard
    SET_X14_TO_2                        # write to standard error
    LOAD_W0_AHEAD                       # Using "Tried to print non-hex number\n"
    SKIP_32_DATA
    &char2hex_string_0
    ^~File_Print RCALL                  # Print it
    ^~Exit_Failure FBRANCH              # Abort Hard

:char2hex_done
    RETURN


# parse_string function
# Receives char* string in X0
# Returns cleaned up string
# Protects char* string in X1
:parse_string
    PUSH_LR
    PUSH_X1                             # Protect X1
    SET_X1_FROM_X0                      # Protect char* string
    ^~weird FCALL                       # Determine if we have a weird string
    CMP_X0_TO_0                         # If weird
    SKIP_INST_NE                        # Deal with it
    ^~parse_string_weird FBRANCH

    # Dealing with regular string
    SET_X0_FROM_X1                      # Passing Char* string
    ^~collect_regular_string FCALL      # Collect it
    ^~parse_string_done FBRANCH         # Be done

:parse_string_weird
    SET_X0_FROM_X1                      # Passing Char* string
    ^~collect_weird_string FCALL        # Collect it

:parse_string_done
    POP_X1                              # Restore X1
    POP_LR
    RETURN


# weird function
# Receives char* string in X0
# Returns true(0) or false(1) in X0
# Uses X2 to hold char* string
:weird
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    SET_X2_FROM_X0                      # Place string in safe place
    ADD_X2_X2_1                         # increment past the '\"'
:weird_reset
    LDR_BYTE_W0_[X2]                    # Load a char
    CMP_X0_TO_0                         # IF NULL == C
    SKIP_INST_NE                        # Nothing weird found
    ^~weird_false FBRANCH

    CMP_X0_TO_92                        # IF '\\'
    SKIP_INST_EQ                        # Deal with escaping
    ^~weird_escaped FBRANCH

    # Deal with escape
    SET_X0_FROM_X2                      # We are passing the string
    ^~escape_lookup RCALL               # to look it up

    ADD_X2_X2_1                         # string = string + 1
    LDR_BYTE_W1_[X2]                    # get string[1]
    CMP_X1_TO_120                       # IF 'x' == string[1]
    SKIP_INST_EQ                        # otherwise skip the gap
    ^~weird_escaped FBRANCH

    ADD_X2_X2_2                         # string = string + 2

:weird_escaped
    PUSH_X0                             # Protect C in case we need it
    LOAD_W1_AHEAD                       # Use "\t\n !#$%&'()*+,-./0123456789:#<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
    SKIP_32_DATA
    &weird_string_0
    ^~In_Set RCALL                      # To find if weird
    CMP_X0_TO_1                         # IF TRUE
    POP_X0                              # Restore C
    SKIP_INST_EQ                        # Then not weird
    ^~weird_true FBRANCH

    ADD_X2_X2_1                         # string = string + 1

    # Last chance for weird
    LOAD_W1_AHEAD                       # Use "\t\n\r "
    SKIP_32_DATA
    &weird_string_1
    ^~In_Set RCALL                      # Check for special case
    CMP_X0_TO_1                         # IF TRUE
    SKIP_INST_EQ                        # Otherwise not in the special case
    ^~weird_reset RBRANCH

    # Deal with possible special case
    LDR_BYTE_W0_[X2]                    # Load string[1]
    CMP_X0_TO_58                        # IF string[1] == ":"
    SKIP_INST_NE                        # Then we hit the special case
    ^~weird_true FBRANCH
    ^~weird_reset RBRANCH               # Keep trying

:weird_done
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN

:weird_true
    SET_X0_TO_0                         # Return true
    ^~weird_done RBRANCH                # Be done

:weird_false
    SET_X0_TO_1                         # Return false
    ^~weird_done RBRANCH                # Be done


# collect_regular_string function
# Receives char* string in X0
# Malloc and creates new string to return in X0
# Uses X2 for return string and X3 for passed string
:collect_regular_string
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    PUSH_X3                             # Protect X3
    SET_X3_FROM_X0                      # Protect our passed string
    SET_X0_TO_256                       # We need 256bytes of storage
    ^~malloc RCALL                      # Get our new pointer
    SET_X2_FROM_X0                      # put it in place
    PUSH_X0                             # protect until done
:collect_regular_string_reset
    LDR_BYTE_W0_[X3]                    # string[0]
    CMP_X0_TO_0                         # See if we hit the end
    SKIP_INST_NE                        # And be done
    ^~collect_regular_string_done FBRANCH

    CMP_X0_TO_92                        # IF string[0] == '\\'
    SKIP_INST_NE                        # Deal with that mess
    ^~collect_regular_string_escaped FBRANCH

    # deal with boring char
    STR_BYTE_W0_[X2]                    # hold_string[index] = string[0]
    ADD_X2_X2_1                         # Increment it
    ADD_X3_X3_1                         # Increment it
    ^~collect_regular_string_reset RBRANCH # And keep going

:collect_regular_string_escaped
    SET_X0_FROM_X3                      # Using string
    ^~escape_lookup RCALL               # Get the char
    STR_BYTE_W0_[X2]                    # hold_string[index] = escape_lookup(string)
    ADD_X3_X3_1                         # Increment it
    ADD_X2_X2_1                         # Increment it
    LDR_BYTE_W0_[X3]                    # string[0]
    ADD_X3_X3_1                         # Increment it
    CMP_X0_TO_120                       # IF 'x' == string[1]
    SKIP_INST_EQ                        # Otherwise keep going
    ^~collect_regular_string_reset RBRANCH

    ADD_X3_X3_2                         # Increment it
    ^~collect_regular_string_reset RBRANCH # Keep going

:collect_regular_string_done
    SET_X0_TO_34                        # Using '\"'
    STR_BYTE_W0_[X2]                    # hold_string[index] = '\"'
    ADD_X2_X2_1                         # Increment it
    SET_X0_TO_10                        # Using "\n"
    STR_BYTE_W0_[X2]                    # hold_string[index] = '\n'
    POP_X0                              # Return our new string
    POP_X3                              # Restore X3
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN


# collect_weird_string function
# Receives char* string in X0
# Mallocs and returns char* hold in X0
# Uses X2 for char* hold and X3 for char* string
:collect_weird_string
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    PUSH_X3                             # Protect X3
    SET_X3_FROM_X0                      # Protect our passed string
    SET_X0_TO_512                       # We need 512bytes of storage
    ^~malloc RCALL                      # Get our new pointer
    SET_X2_FROM_X0                      # put it in place
    PUSH_X0                             # protect until done

    SET_X0_TO_39                        # Using "'"
    STR_BYTE_W0_[X2]                    # hold_string[index] = "'"
    ADD_X2_X2_1                         # Increment it
    ADD_X3_X3_1                         # Increment it
:collect_weird_string_reset
    LDR_BYTE_W0_[X3]                    # Read a byte
    CMP_X0_TO_0                         # IF NULL == string[0]
    SKIP_INST_NE                        # Be done
    ^~collect_weird_string_done FBRANCH

    SET_X0_TO_32                        # Using ' '
    STR_BYTE_W0_[X2]                    # hold_string[index] = ' '
    ADD_X2_X2_1                         # Increment it

    SET_X0_FROM_X3                      # Using string
    ^~escape_lookup RCALL               # Get the char
    ^~hex8 FCALL                        # Update X2

    LDR_BYTE_W0_[X3]                    # Read a byte
    ADD_X3_X3_1                         # Increment it
    CMP_X0_TO_92                        # IF string[0] == '\\'
    SKIP_INST_EQ                        # Otherwise keep going
    ^~collect_weird_string_reset RBRANCH

    LDR_BYTE_W0_[X3]                    # Read a byte
    ADD_X3_X3_1                         # Increment it
    CMP_X0_TO_120                       # IF 'x' == string[1]
    SKIP_INST_EQ                        # Otherwise keep going
    ^~collect_weird_string_reset RBRANCH

    ADD_X3_X3_2                         # Increment it
    ^~collect_weird_string_reset RBRANCH # Keep going

:collect_weird_string_done
    SET_X0_TO_32                        # Using ' '
    STR_BYTE_W0_[X2]                    # hold_string[index] = ' '
    ADD_X2_X2_1                         # Increment it
    SET_X0_TO_48                        # Using '0'
    STR_BYTE_W0_[X2]                    # hold_string[index] = '0'
    ADD_X2_X2_1                         # Increment it
    STR_BYTE_W0_[X2]                    # hold_string[index] = '0'
    ADD_X2_X2_1                         # Increment it
    SET_X0_TO_39                        # Using "'"
    STR_BYTE_W0_[X2]                    # hold_string[index] = "'"
    ADD_X2_X2_1                         # Increment it
    SET_X0_TO_10                        # Using "\n"
    STR_BYTE_W0_[X2]                    # hold_string[index] = '\n'
    POP_X0                              # Return our new string
    POP_X3                              # Restore X3
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN


# HEX to ascii routine
# Receives INT in X0 and CHAR* in X2
# Stores ascii of INT in CHAR*
# Returns only modifying X0 and X2
:hex8
    PUSH_LR
    PUSH_X0                             # Protect bottom nibble
    LSR_X0_X0_4                         # do high nibble first
    ^~hex4 FCALL                        # Store it
    POP_X0                              # do low nibble
    ^~hex4 FCALL
    POP_LR
    RETURN

:hex4
    AND_X0_X0_0xF                       # isolate nibble
    ADD_X0_X0_48                        # convert to ascii
    CMP_X0_TO_57                        # valid digit?
    SKIP_INST_GT                        # yes
    ^~hex1 FBRANCH
    ADD_X0_X0_7                         # use alpha range
:hex1
    STR_BYTE_W0_[X2]                    # store result
    ADD_X2_X2_1                         # next position
    RETURN


# type_name function
# Receives nothing
# Returns type_size in X0
# Uses X2 for STRUCT TYPE* RET
:type_name
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X1_[X9]
    LDR_X1_[X1,16]                      # global_token->S
    LOAD_W0_AHEAD                       # Using "struct"
    SKIP_32_DATA
    &struct
    ^~match FCALL                       # IF global_token->S == "struct"
    SET_X2_FROM_X0                      # Protect structure
    CMP_X0_TO_0                         # need to skip over "struct"
    SKIP_INST_EQ                        # otherwise keep going
    ^~type_name_native FBRANCH

    # Deal with possible STRUCTs
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X1_[X9]
    LDR_X1_[X1]                         # global_token->next
    LOAD_W9_AHEAD                       # global_token = global_token->next
    SKIP_32_DATA
    &global_token
    STR_X1_[X9]
    LDR_X0_[X1,16]                      # global_token->S
    LOAD_W9_AHEAD                       # get all known types
    SKIP_32_DATA
    &global_types
    LDR_X1_[X9]
    ^~lookup_type FCALL                 # Find type if possible
    SET_X2_FROM_X0                      # Set ret

    CMP_X0_TO_0                         # IF NULL == ret
    SKIP_INST_EQ                        # We have to create struct
    ^~type_name_common FBRANCH

    # Create a struct
    ^~create_struct FCALL               # Create a new struct
    SET_X2_TO_0                         # We wish to return NULL
    ^~type_name_done FBRANCH            # be done

:type_name_native
    # Deal only with native types
    SET_X0_FROM_X1                      # Put global_token->S in the right place
    LOAD_W9_AHEAD                       # get all known types
    SKIP_32_DATA
    &global_types
    LDR_X1_[X9]
    ^~lookup_type FCALL                 # Find the type if possible
    SET_X2_FROM_X0                      # Set ret

    CMP_X0_TO_0                         # IF NULL == ret
    SKIP_INST_EQ                        # We need to abort hard
    ^~type_name_common FBRANCH

    # Aborting hard
    SET_X14_TO_2                        # write to standard error
    LOAD_W0_AHEAD                       # Print header
    SKIP_32_DATA
    &type_name_string_0
    ^~File_Print RCALL                  # Print it

    LOAD_W9_AHEAD                       # Using global token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # global_token->S
    ^~File_Print RCALL                  # Print it

    LOAD_W0_AHEAD                       # Print footer
    SKIP_32_DATA
    &type_name_string_1
    ^~File_Print RCALL                  # Print it

#   call line_error                     # Give details
    ^~Exit_Failure FBRANCH              # Abort

:type_name_common
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X1_[X9]
    LDR_X1_[X1]                         # global_token->next
    LOAD_W9_AHEAD                       # global_token = global_token->next
    SKIP_32_DATA
    &global_token
    STR_X1_[X9]

:type_name_iter
    LDR_X0_[X1,16]                      # global_token->S
    LDR_BYTE_W0_[X0]                    # global_token->S[0]
    CMP_X0_TO_42                        # IF global_token->S[0] == '*'
    SKIP_INST_EQ                        # recurse
    ^~type_name_done FBRANCH

    # Deal with char**
    LDR_X2_[X2,24]                      # ret = ret->indirect
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X1_[X9]
    LDR_X1_[X1]                         # global_token->next
    LOAD_W9_AHEAD                       # global_token = global_token->next
    SKIP_32_DATA
    &global_token
    STR_X1_[X9]
    ^~type_name_iter RBRANCH            # keep looping

:type_name_done
    SET_X0_FROM_X2                      # put ret in the right place
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN


# lookup_type function
# Receives char* s in X0 and struct type* start in X1
# Returns struct type* in X0
# Uses X1 for S and X2 for I
:lookup_type
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    SET_X2_FROM_X1                      # I = Start
    SET_X1_FROM_X0                      # Put S in place
:lookup_type_iter
    CMP_X2_TO_0                         # Check if I == NULL
    SKIP_INST_NE                        # return NULL
    ^~lookup_type_done FBRANCH

    LDR_X0_[X2,48]                      # I->NAME
    ^~match FCALL                       # Check if matching
    CMP_X0_TO_0                         # IF I->NAME == S
    SKIP_INST_NE                        # return it
    ^~lookup_type_done FBRANCH

    LDR_X2_[X2]                         # Otherwise I = I->NEXT
    ^~lookup_type_iter RBRANCH          # And keep looping

:lookup_type_done
    SET_X0_FROM_X2                      # return either I or NULL
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN


# create_struct function
# Receives nothing
# Returns nothing
# Uses global_token to malloc a struct's definition
# Uses X2 for int OFFSET, X3 for struct type* head, X4 for struct type* I,
# X6 for member_size (Which is passed) and X5 for LAST
# X0 and X1 are used for scratch
:create_struct
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    PUSH_X3                             # Protect X3
    PUSH_X4                             # Protect X4
    PUSH_X6                             # Protect X6
    PUSH_X5                             # Protect X5
    SET_X2_TO_0                         # OFFSET = 0
    SET_X6_TO_0                         # member_size = 0

    SET_X0_TO_56                        # sizeof(struct type)
    ^~malloc RCALL                      # malloc(sizeof(struct type))
    SET_X3_FROM_X0                      # Set HEAD

    SET_X0_TO_56                        # sizeof(struct type)
    ^~malloc RCALL                      # malloc(sizeof(struct type))
    SET_X4_FROM_X0                      # Set I

    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # global_token->S
    STR_X0_[X3,48]                      # HEAD->NAME = global_token->S
    STR_X0_[X4,48]                      # I->NAME = global_token->S

    STR_X4_[X3,24]                      # HEAD->INDIRECT = I
    STR_X3_[X4,24]                      # I->INDIRECT = HEAD

    LOAD_W9_AHEAD                       # Using global_types
    SKIP_32_DATA
    &global_types
    LDR_X0_[X9]
    STR_X0_[X3]                         # HEAD->NEXT = global_types
    LOAD_W9_AHEAD                       # global_types = HEAD
    SKIP_32_DATA
    &global_types
    STR_X3_[X9]

    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0]                         # global_token->NEXT
    LOAD_W9_AHEAD                       # global_token = global_token->NEXT
    SKIP_32_DATA
    &global_token
    STR_X0_[X9]

    SET_X0_TO_8                         # Using register size
    STR_X0_[X4,8]                       # I->SIZE = register size

    LOAD_W0_AHEAD                       # Using "ERROR in create_struct\n Missing {\n"
    SKIP_32_DATA
    &create_struct_string_0
    LOAD_W1_AHEAD                       # Using "{"
    SKIP_32_DATA
    &open_curly_brace
    ^~require_match RCALL               # Make sure we have it

    SET_X5_TO_0                         # LAST = NULL

:create_struct_iter
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # global_token->S
    LDR_BYTE_W0_[X0]                    # global_token->S[0]
    CMP_X0_TO_125                       # IF global_token->S[0] == "}"
    SKIP_INST_NE                        # be done
    ^~create_struct_done FBRANCH

    # Looks like we are adding members
    # Lets see if it is a union
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # global_token->S
    LOAD_W1_AHEAD                       # Using "union"
    SKIP_32_DATA
    &union
    ^~match FCALL                       # IF match(global_token->s, "union")
    CMP_X0_TO_0                         # Deal with union
    SKIP_INST_EQ                        # Otherwise deal with singles
    ^~create_struct_single FBRANCH

    # Deal with union
    SET_X0_FROM_X5                      # Put last in right place
    SET_X1_FROM_X2                      # put offset in right place
    ^~build_union FCALL                 # ASSEMBLE
    SET_X5_FROM_X0                      # last = build_union(last, offset)
    ADD_X2_X2_X6                        # offset = offset + member_size

    LOAD_W0_AHEAD                       # Using "ERROR in create_struct\n Missing ;\n"
    SKIP_32_DATA
    &create_struct_string_1
    LOAD_W1_AHEAD                       # Using ";"
    SKIP_32_DATA
    &semicolon
    ^~require_match RCALL               # Make sure we have it
    ^~create_struct_iter RBRANCH        # keep going

:create_struct_single
    # deal with singles
    SET_X0_FROM_X5                      # Put last in right place
    SET_X1_FROM_X2                      # put offset in right place
    ^~build_member FCALL                # ASSEMBLE
    SET_X5_FROM_X0                      # last = build_union(last, offset)
    ADD_X2_X2_X6                        # offset = offset + member_size

    LOAD_W0_AHEAD                       # Using "ERROR in create_struct\n Missing ;\n"
    SKIP_32_DATA
    &create_struct_string_1
    LOAD_W1_AHEAD                       # Using ";"
    SKIP_32_DATA
    &semicolon
    ^~require_match RCALL               # Make sure we have it
    ^~create_struct_iter RBRANCH        # keep going

:create_struct_done
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0]                         # global_token->NEXT
    LOAD_W9_AHEAD                       # global_token = global_token->NEXT
    SKIP_32_DATA
    &global_token
    STR_X0_[X9]

    LOAD_W0_AHEAD                       # Using "ERROR in create_struct\n Missing ;\n"
    SKIP_32_DATA
    &create_struct_string_1
    LOAD_W1_AHEAD                       # Using ";"
    SKIP_32_DATA
    &semicolon
    ^~require_match RCALL               # Make sure we have it

    STR_X2_[X3,8]                       # HEAD->SIZE = OFFSET
    STR_X5_[X3,32]                      # HEAD->MEMBERS = LAST
    STR_X5_[X4,32]                      # I->MEMBERS = LAST

    POP_X5                              # Restore X5
    POP_X6                              # Restore X6
    POP_X4                              # Restore X4
    POP_X3                              # Restore X3
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN


# lookup_member function
# Receives struct type* parent in X0 and char* name in X1
# Returns struct type* I in X0
# Uses char* NAME in X1, X2 for struct type* I and X3 to hold parent for errors
# Aborts hard if not found
:lookup_member
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    PUSH_X3                             # Protect X3
    SET_X3_FROM_X0                      # Protect Parent
    LDR_X2_[X0,32]                      # struct type* I = parent->MEMBERS
:lookup_member_iter
    CMP_X2_TO_0                         # IF I == NULL
    SKIP_INST_NE                        # Abort HARD
    ^~lookup_member_fail FBRANCH

    LDR_X0_[X2,48]                      # Using I->NAME
    ^~match FCALL                       # IF I->NAME == NAME
    CMP_X0_TO_0                         # Then we have found the member
    SET_X0_FROM_X2                      # Prepare for return
    LDR_X2_[X2,32]                      # Prepare for loop I = I->MEMBERS
    SKIP_INST_EQ                        # Looks like we are looping
    ^~lookup_member_iter RBRANCH

    # I is already in X0
    POP_X3                              # Restore X3
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN

:lookup_member_fail
    SET_X14_TO_2                        # write to standard error
    LOAD_W0_AHEAD                       # Using "ERROR in lookup_member "
    SKIP_32_DATA
    &lookup_member_string_0
    ^~File_Print RCALL                  # print it

    LDR_X0_[X3,48]                      # PARENT->NAME
    ^~File_Print RCALL                  # print it

    LOAD_W0_AHEAD                       # Using "->"
    SKIP_32_DATA
    &arrow_string
    ^~File_Print RCALL                  # print it

    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # global_token->S
    ^~File_Print RCALL                  # print it

    LOAD_W0_AHEAD                       # Using " does not exist\n"
    SKIP_32_DATA
    &lookup_member_string_1
    ^~File_Print RCALL                  # print it

#   line_error CALL                     # Write useful debug info

    LOAD_W0_AHEAD                       # Using "\n"
    SKIP_32_DATA
    &lookup_member_string_2
    ^~File_Print RCALL                  # print it
    ^~Exit_Failure FBRANCH              # Abort Hard


# build_member function
# Receives struct type* last in X0, int offset in X1 and global member_size in X6
# Updates member_size in X6 and returns struct type* I in X0
# Uses X2 for struct type* member_type and X3 for struct type* I
:build_member
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    PUSH_X3                             # Protect X3
    SET_X3_FROM_X0                      # Put last out of the way
    SET_X0_TO_56                        # Allocate type
    ^~malloc RCALL                      # Get I
    STR_X3_[X0,32]                      # I->MEMBERS = LAST
    STR_X1_[X0,16]                      # I->OFFSET = OFFSET
    SET_X3_FROM_X0                      # Put I in place

    ^~type_name RCALL                   # Get member_type
    SET_X2_FROM_X0                      # Put in place
    STR_X2_[X3,40]                      # I->TYPE = MEMBER_TYPE
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X1_[X0,16]                      # global_token->S
    STR_X1_[X3,48]                      # I->NAME = global_token->S
    LDR_X0_[X0]                         # global_token->NEXT
    LOAD_W9_AHEAD                       # global_token = global_token->NEXT
    SKIP_32_DATA
    &global_token
    STR_X0_[X9]

    # Check if we have an array
    LDR_X1_[X0,16]                      # global_token->S
    LOAD_W0_AHEAD                       # Using "["
    SKIP_32_DATA
    &open_bracket
    ^~match FCALL                       # IF global_token->S == "["
    CMP_X0_TO_0                         # Then we have to deal with arrays in our structs
    SKIP_INST_NE                        # So deal with that pain
    ^~build_member_array FBRANCH

    # Deal with non-array case
    LDR_X0_[X2,8]                       # member_type->SIZE
    STR_X0_[X3,8]                       # I->SIZE = member_type->SIZE
    ^~build_member_done FBRANCH         # Be done

:build_member_array
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0]                         # global_token->NEXT
    LOAD_W9_AHEAD                       # global_token = global_token->NEXT
    SKIP_32_DATA
    &global_token
    STR_X0_[X9]

    LDR_X0_[X0,16]                      # global_token->S
    ^~numerate_string FCALL             # convert number
    LDR_X1_[X2,40]                      # member_type->TYPE
    LDR_X1_[X1,8]                       # member_type->TYPE->SIZE
    MUL_X0_X0_X1                        # member_type->type->size * numerate_string(global_token->s)
    STR_X0_[X3,8]                       # I->SIZE = member_type->type->size * numerate_string(global_token->s)

    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0]                         # global_token->NEXT
    LOAD_W9_AHEAD                       # global_token = global_token->NEXT
    SKIP_32_DATA
    &global_token
    STR_X0_[X9]

    LOAD_W0_AHEAD                       # Using "Struct only supports [num] form\n"
    SKIP_32_DATA
    &build_member_string_0
    LOAD_W1_AHEAD                       # Using "]"
    SKIP_32_DATA
    &close_bracket
    ^~require_match RCALL               # Make sure we have it

:build_member_done
    LDR_X6_[X3,8]                       # MEMBER_SIZE = I->SIZE
    STR_X2_[X3,40]                      # I->TYPE = MEMBER_TYPE
    SET_X0_FROM_X3                      # Return I

    POP_X3                              # Restore X3
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN


# build_union function
# Receives struct type* last in X0, int offset in X1 and global member_size in X6
# Updates member_size in X6 and returns struct type* LAST in X0
# Uses X2 for struct type* last, X3 for int offset, X5 for int size and X6 for int member_size
:build_union
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    PUSH_X3                             # Protect X3
    PUSH_X5                             # Protect X5
    SET_X2_FROM_X0                      # Put LAST in right spot
    SET_X3_FROM_X1                      # Put OFFSET in right spot
    SET_X5_TO_0                         # SIZE = 0

    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0]                         # global_token->NEXT
    LOAD_W9_AHEAD                       # global_token = global_token->NEXT
    SKIP_32_DATA
    &global_token
    STR_X0_[X9]

    LOAD_W0_AHEAD                       # Using "ERROR in build_union\nMissing {\n"
    SKIP_32_DATA
    &build_union_string_0
    LOAD_W1_AHEAD                       # Using "{"
    SKIP_32_DATA
    &open_curly_brace
    ^~require_match RCALL               # Make sure we have it

:build_union_iter
    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0,16]                      # global_token->S
    LDR_BYTE_W0_[X0]                    # global_token->S[0]
    CMP_X0_TO_125                       # IF global_token->S[0] == "}"
    SKIP_INST_NE                        # Be done
    ^~build_union_done FBRANCH

    # Collect union member
    SET_X0_FROM_X2                      # Passing LAST
    SET_X1_FROM_X3                      # Passing offset
    ^~build_member RCALL                # build_member(last, offset)
    SET_X2_FROM_X0                      # last = build_member(last, offset)

    CMP_X5_X6                           # IF member_size > size
    SKIP_INST_LE                        # Then update size
    ^~build_union_size FBRANCH

    # deal with member_size > size
    SET_X5_FROM_X6                      # SIZE = MEMBER_SIZE

:build_union_size
    LOAD_W0_AHEAD                       # Using "ERROR in build_union\nMissing ;\n"
    SKIP_32_DATA
    &build_union_string_1
    LOAD_W1_AHEAD                       # Using ";"
    SKIP_32_DATA
    &semicolon
    ^~require_match RCALL               # Make sure we have it
    ^~build_union_iter RBRANCH          # Keep going

:build_union_done
    SET_X6_FROM_X5                      # MEMBER_SIZE = SIZE

    LOAD_W9_AHEAD                       # Using global_token
    SKIP_32_DATA
    &global_token
    LDR_X0_[X9]
    LDR_X0_[X0]                         # global_token->NEXT
    LOAD_W9_AHEAD                       # global_token = global_token->NEXT
    SKIP_32_DATA
    &global_token
    STR_X0_[X9]

    SET_X0_FROM_X2                      # Return last

    POP_X5                              # Restore X5
    POP_X3                              # Restore X3
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN


# sym_declare function
# Receives char *s in X0, struct type* t in X1, and struct token_list* list in X2
# Returns struct token_list* in X0
# Uses X0 for A
:sym_declare
    PUSH_LR
    PUSH_X3                             # Protect X3
    SET_X3_FROM_X0                      # Get char *S safely out of the way
    SET_X0_TO_40                        # Using sizeof(struct token_list)
    ^~malloc RCALL                      # Get pointer to A
    STR_X2_[X0]                         # A->NEXT = LIST
    STR_X3_[X0,16]                      # A->S = S
    STR_X1_[X0,24]                      # A->TYPE = T
    POP_X3                              # Restore X3
    POP_LR
    RETURN


# match function
# Receives CHAR* in X0 and CHAR* in X1
# Returns 0 (TRUE) or 1 (FALSE) in X0
:match
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    PUSH_X3                             # Protect X3
    SET_X2_FROM_X0                      # S1 in place
    SET_X3_FROM_X1                      # S2 in place
:match_Loop
    LDR_BYTE_W0_[X2]                    # S1[0]
    LDR_BYTE_W1_[X3]                    # S2[0]
    CMP_X0_X1                           # See if they match
    SKIP_INST_EQ                        # If not
    ^~match_False FBRANCH

    ADD_X2_X2_1                         # S1 = S1 + 1
    ADD_X3_X3_1                         # S2 = S2 + 1
    CMP_X0_TO_0                         # If reached end of string
    SKIP_INST_NE                        # Perfect match
    ^~match_Done FBRANCH
    ^~match_Loop RBRANCH                # Otherwise keep looping

:match_False
    SET_X0_TO_1                         # Return false
:match_Done
    POP_X3                              # Restore X3
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    RETURN


# numerate_number function
# Receives an INT A in X0
# Returns char* result in X0
# Allocates 16 bytes of memory
# Behaves badly when given a negative number too large
# Uses X0 for temp, X1 for DIVISOR, X3 for mod/0, X5 for result[i] and X4 for A
:numerate_number
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    PUSH_X3                             # Protect X3
    PUSH_X5                             # Protect X5
    PUSH_X4                             # Protect X4
    SET_X4_FROM_X0                      # Protect A

    SET_X0_TO_16                        # 16bytes
    ^~malloc RCALL                      # Get our pointer
    PUSH_X0                             # Protect our pointer
    SET_X5_FROM_X0                      # put pointer in right place
    SET_X1_TO_0xCA00
    SET16_X1_0x3B9A                     # Set divisor to largest positive number that fits in 32bits

    CMP_X4_TO_0                         # Deal with 0 case
    SKIP_INST_NE                        # If it is
    ^~numerate_number_ZERO FBRANCH
    SKIP_INST_LE                        # If it is positive
    ^~numerate_number_positive FBRANCH

    # Deal with negative case
    SET_X0_TO_45                        # Using "-"
    STR_BYTE_W0_[X5]                    # Write it
    ADD_X5_X5_1                         # increment
    NEG_X4_X4                           # A = A * -1

:numerate_number_positive
    SET_X3_TO_0                         # Set top to 0
    SET_X0_FROM_X4                      # Using A as bottom
    UDIV_X0_X0_X1
    CMP_X0_TO_0                         # IF 0 == (a / divisor)
    SKIP_INST_EQ                        # Clean up those leading Zeros
    ^~numerate_number_iter FBRANCH

    SET_X3_TO_0                         # Set top to 0
    SET_X0_FROM_X1                      # Using Divisor for bottom
    SET_X1_TO_10                        # Make this shit work because idiv 10 doesn't work
    UDIV_X0_X0_X1
    SET_X1_FROM_X0                      # Update divisor
    ^~numerate_number_positive RBRANCH  # Keep collecting

:numerate_number_iter
    CMP_X1_TO_0                         # IF DIVISOR < 0
    SKIP_INST_GT                        # Be done
    ^~numerate_number_done FBRANCH

    SET_X3_TO_0                         # Set top to 0
    SET_X0_FROM_X4                      # Using A as bottom
    UDIV_X2_X0_X1
    MSUB_X3_X2_X1_X0
    ADD_X2_X2_48                        # ((a / divisor) + 48)
    STR_BYTE_W2_[X5]                    # Write it
    SET_X4_FROM_X3                      # a = a % divisor

    SET_X3_TO_0                         # Set top to 0
    SET_X0_FROM_X1                      # Using Divisor for bottom
    SET_X1_TO_10                        # Make this shit work because idiv 10 doesn't work
    UDIV_X0_X0_X1
    SET_X1_FROM_X0                      # Update divisor

    ADD_X5_X5_1                         # increment
    ^~numerate_number_iter RBRANCH      # Keep going

:numerate_number_done
    POP_X0                              # Restore our result
    POP_X4                              # Restore X4
    POP_X5                              # Restore X5
    POP_X3                              # Restore X3
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN

:numerate_number_ZERO
    SET_X0_TO_48                        # Using '0'
    STR_BYTE_W0_[X5]                    # Write it
    ADD_X5_X5_1                         # increment
    ^~numerate_number_done RBRANCH      # Be done


# numerate_string function
# Receives CHAR* in X0
# Returns value of CHAR* in X0
# Uses X0 for VALUE, X1 for S, X2 for CH and X5 for NEGATIVE?
:numerate_string
    PUSH_LR
    PUSH_X1                             # Protect X1
    PUSH_X2                             # Protect X2
    PUSH_X3                             # Protect X3
    PUSH_X5                             # Protect X5
    SET_X1_FROM_X0                      # put S in correct place
    SET_X0_TO_0                         # Initialize to Zero
:numerate_string_loop
    LDR_BYTE_W2_[X1,1]                  # S[1]
    CMP_X2_TO_120                       # IF 'x' == S[1]
    SKIP_INST_NE                        # Deal with hex input
    ^~numerate_hex FBRANCH

    # Assume decimal input
    SET_X5_TO_0                         # Assume no negation
    LDR_BYTE_W2_[X1]                    # S[0]
    CMP_X2_TO_45                        # IF '-' == S[0]
    SKIP_INST_EQ                        # Skip negation
    ^~numerate_decimal FBRANCH

    SET_X5_TO_1                         # Set FLAG
    ADD_X1_X1_1                         # S = S + 1

:numerate_decimal
    LDR_BYTE_W2_[X1]                    # S[0]
    CMP_X2_TO_0                         # IF NULL == S[0]
    SKIP_INST_NE                        # We are done
    ^~numerate_decimal_done FBRANCH

    SET_X9_TO_10
    MUL_X0_X0_X9                        # VALUE = VALUE * 10
    SUB_X2_X2_48                        # CH = CH - '0'
    CMP_X2_TO_9                         # Check for illegal
    SKIP_INST_LE                        # If CH > '9'
    ^~numerate_string_fail FBRANCH
    CMP_X2_TO_0                         # Check for illegal
    SKIP_INST_GE                        # IF CH < 0
    ^~numerate_string_fail FBRANCH
    ADD_X0_X0_X2                        # VALUE = VALUE + CH
    ADD_X1_X1_1                         # S = S + 1
    ^~numerate_decimal RBRANCH          # Keep looping

:numerate_decimal_done
    CMP_X5_TO_1                         # Check if need to negate
    SKIP_INST_EQ                        # Nope
    ^~numerate_string_done FBRANCH

    NEG_X0_X0                           # VALUE = VALUE * -1
    ^~numerate_string_done FBRANCH      # Done

:numerate_hex
    ADD_X1_X1_2                         # S = S + 2
:numerate_hex_loop
    LDR_BYTE_W2_[X1]                    # S[0]
    CMP_X2_TO_0                         # IF NULL == S[0]
    SKIP_INST_NE                        # We are done
    ^~numerate_string_done FBRANCH

    LSL_X0_X0_4                         # VALUE = VALUE << 4
    SUB_X2_X2_48                        # CH = CH - '0'
    CMP_X2_TO_10                        # IF 10 >= CH
    SKIP_INST_GE                        # NO
    ^~numerate_hex_digit FBRANCH
    SUB_X2_X2_7                         # Push A-F into range
:numerate_hex_digit
    CMP_X2_TO_15                        # Check for illegal
    SKIP_INST_LE                        # If CH > 'F'
    ^~numerate_string_fail FBRANCH
    CMP_X2_TO_0                         # Check for illegal
    SKIP_INST_GE                        # IF CH < 0
    ^~numerate_string_fail FBRANCH
    ADD_X0_X0_X2                        # VALUE = VALUE + CH
    ADD_X1_X1_1                         # S = S + 1
    ^~numerate_hex_loop RBRANCH         # Keep looping

:numerate_string_fail
    SET_X0_TO_0                         # return ZERO

:numerate_string_done
    POP_X5                              # Restore X5
    POP_X3                              # Restore X3
    POP_X2                              # Restore X2
    POP_X1                              # Restore X1
    POP_LR
    RETURN


# Exit_Failure function
# Receives nothing
# And aborts hard
# Does NOT return
:Exit_Failure
    SET_X0_TO_1                         # All is wrong
    SET_X8_TO_SYS_EXIT                  # put the exit syscall number in eax
    SYSCALL                             # Call it a good day


# debug_list function
# Receives struct token_list* in X0
# Prints contents of list and exits
# Does NOT return
:debug_list
    SET_X12_FROM_X0                     # Protect the list pointer
    SET_X14_TO_2                        # write to standard error

:debug_list_iter
    # Header
    LOAD_W0_AHEAD                       # Using our first string
    SKIP_32_DATA
    &debug_list_string0
    ^~File_Print RCALL                  # Print it
    SET_X0_FROM_X12                     # Use address of pointer
    ^~numerate_number RCALL             # Convert it into string
    ^~File_Print RCALL                  # Print it

    # NEXT
    LOAD_W0_AHEAD                       # Using our second string
    SKIP_32_DATA
    &debug_list_string1
    ^~File_Print RCALL                  # Print it
    LDR_X0_[X12]                        # Use address of pointer
    ^~numerate_number RCALL             # Convert it into string
    ^~File_Print RCALL                  # Print it

    # PREV
    LOAD_W0_AHEAD                       # Using our third string
    SKIP_32_DATA
    &debug_list_string2
    ^~File_Print RCALL                  # Print it
    LDR_X0_[X12,8]                      # Use address of pointer
    ^~numerate_number RCALL             # Convert it into string
    ^~File_Print RCALL                  # Print it

    # S
    LOAD_W0_AHEAD                       # Using our fourth string
    SKIP_32_DATA
    &debug_list_string3
    ^~File_Print RCALL                  # Print it
    LDR_X0_[X12,16]                     # Use address of pointer
    ^~numerate_number RCALL             # Convert it into string
    ^~File_Print RCALL                  # Print it

    # S Contents
    LOAD_W0_AHEAD                       # Using our fifth string
    SKIP_32_DATA
    &debug_list_string4
    ^~File_Print RCALL                  # Print it
    LDR_X0_[X12,16]                     # Use address of string
    CMP_X0_TO_0                         # IF NULL Pointer
    SKIP_INST_EQ                        # otherwise display
    ^~debug_list_null FBRANCH
    LOAD_W0_AHEAD                       # Give meaningful message instead
    SKIP_32_DATA
    &debug_list_string_null
:debug_list_null
    ^~File_Print RCALL                  # Print it

    # TYPE
    LOAD_W0_AHEAD                       # Using our sixth string
    SKIP_32_DATA
    &debug_list_string5
    ^~File_Print RCALL                  # Print it
    LDR_X0_[X12,24]                     # Use address of pointer
    ^~numerate_number RCALL             # Convert it into string
    ^~File_Print RCALL                  # Print it

    # ARGS/DEPTH
    LOAD_W0_AHEAD                       # Using our seventh string
    SKIP_32_DATA
    &debug_list_string6
    ^~File_Print RCALL                  # Print it
    LDR_X0_[X12,32]                     # Use address of pointer
    ^~numerate_number RCALL             # Convert it into string
    ^~File_Print RCALL                  # Print it

    SET_X0_TO_10                        # Add "\n"
    ^~fputc RCALL                       # print it
    ^~fputc RCALL                       # print it

    LDR_X12_[X12]                       # TOKEN = TOKEN->NEXT
    CMP_X12_TO_0                        # Check if NULL
    SKIP_INST_EQ                        # iterate otherwise
    ^~debug_list_iter RBRANCH

    SET_X0_TO_666                       # All is HELL
    SET_X8_TO_SYS_EXIT                  # put the exit syscall number in eax
    SYSCALL                             # Call it a bad day

:ELF_data

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

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

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

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

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

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

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

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

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


:Address_of NULL64
:C NULL64
:Token NULL64
:break_frame NULL64
:break_target_func NULL64
:break_target_head NULL64
:break_target_num NULL64
:current_count NULL64
:current_target NULL64
:function NULL64
:global_constant_list NULL64
:global_function_list NULL64
:global_symbol_list NULL64
:global_token NULL64
:global_types &prim_types %0
:globals_list NULL64
:output_list NULL64
:string_index NULL64
:strings_list NULL64

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

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


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


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

:alphas  "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"
:symbols  "<=>|&!-"
:strings
'22 27 00'

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

:declare_function_string_0  "# Defining function "
:declare_function_string_1  "
:FUNCTION_"
:declare_function_string_2  "RETURN
"
:declare_function_string_3  "
"

:statement_string_0  "	#C goto label
"
:statement_string_1a  "LOAD_W16_AHEAD
SKIP_32_DATA
&"
:statement_string_1b  "
BR_X16"
:statement_string_2  "
"
:statement_string_3  "
#continue statement
"
:statement_string_4  "ERROR in statement
Missing ;
"

:recursive_statement_string_0  "RETURN
"
:recursive_statement_string_1  "POP_X1  # _recursive_statement_locals
"

:return_result_string_0  "ERROR in return_result
MISSING ;
"
:return_result_string_1  "POP_X1    # _return_result_locals
"
:return_result_string_2  "RETURN
"

:collect_local_string_0  "# Defining local "
:collect_local_string_1  "
"
:collect_local_string_2  "ERROR in collect_local
Missing ;
"
:collect_local_string_3  "PUSH_X0	#"

:process_asm_string_0  "ERROR in process_asm
MISSING (
"
:process_asm_string_1
"
"
:process_asm_string_2  "ERROR in process_asm
MISSING )
"
:process_asm_string_3  "ERROR in process_asm
MISSING ;
"

:process_if_string_0  "# IF_"
:process_if_string_1  "ERROR in process_if
MISSING (
"
:process_if_string_2a  "CBNZ_X0_PAST_BR
LOAD_W16_AHEAD
SKIP_32_DATA
&ELSE_"
:process_if_string_2b  "
BR_X16
"
:process_if_string_3  "ERROR in process_if
MISSING )
"
:process_if_string_4  "LOAD_W16_AHEAD
SKIP_32_DATA
&_END_IF_"
:process_if_string_5  "
BR_X16
:ELSE_"
:process_if_string_6  ":_END_IF_"

:process_do_string_0  "DO_END_"
:process_do_string_1  ":DO_"
:process_do_string_2  "ERROR in process_do
MISSING while
"
:process_do_string_3  "ERROR in process_do
MISSING (
"
:process_do_string_4  "ERROR in process_do
MISSING )
"
:process_do_string_5  "ERROR in process_do
MISSING ;
"
:process_do_string_6a  "CBZ_X0_PAST_BR
LOAD_W16_AHEAD
SKIP_32_DATA
&DO_"
:process_do_string_6b  "
BR_X16
"
:process_do_string_7  ":DO_END_"

:process_while_string_0  "END_WHILE_"
:process_while_string_1  ":WHILE_"
:process_while_string_2  "ERROR in process_while
MISSING (
"
:process_while_string_3a  "CBNZ_X0_PAST_BR
LOAD_W16_AHEAD
SKIP_32_DATA
&END_WHILE_"
:process_while_string_3b  "
BR_X16
"
:process_while_string_4  "# THEN_while_"
:process_while_string_5  "ERROR in process_while
MISSING )
"
:process_while_string_6a  "LOAD_W16_AHEAD
SKIP_32_DATA
&WHILE_"
:process_while_string_6b  "
BR_X16
"
:process_while_string_7  ":END_WHILE_"

:process_for_string_0  "FOR_END_"
:process_for_string_1  "# FOR_initialization_"
:process_for_string_2  "ERROR in process_for
MISSING (
"
:process_for_string_3  ":FOR_"
:process_for_string_4  "ERROR in process_for
MISSING ;1
"
:process_for_string_5a  "CBNZ_X0_PAST_BR
LOAD_W16_AHEAD
SKIP_32_DATA
&FOR_END_"
:process_for_string_5b   "
BR_X16
"
:process_for_string_6a  "LOAD_W16_AHEAD
SKIP_32_DATA
&FOR_THEN_"
:process_for_string_6b   "
BR_X16
"
:process_for_string_7  ":FOR_ITER_"
:process_for_string_8  "ERROR in process_for
MISSING ;2
"
:process_for_string_9a  "LOAD_W16_AHEAD
SKIP_32_DATA
&FOR_"
:process_for_string_9b  "
BR_X16
"
:process_for_string_10  ":FOR_THEN_"
:process_for_string_11  "ERROR in process_for
MISSING )
"
:process_for_string_12a  "LOAD_W16_AHEAD
SKIP_32_DATA
&FOR_ITER_"
:process_for_string_12b  "
BR_X16
"
:process_for_string_13  ":FOR_END_"

:process_break_string_0  "Not inside of a loop or case statement"
:process_break_string_1  "POP_X1	# break_cleanup_locals
"
:process_break_string_2a  "LOAD_W16_AHEAD
SKIP_32_DATA
&"
:process_break_string_2b  "
BR_X16"
:process_break_string_3  "
"
:process_break_string_4  "ERROR in break statement
Missing ;
"

:expression_string_0  "STR_X0_[X1]
"
:expression_string_1  "STR_BYTE_W0_[X1]
"

:bitwise_expr_stub_string_0  "AND_X0_X1_X0
"
:bitwise_expr_stub_string_1  "OR_X0_X1_X0
"
:bitwise_expr_stub_string_2  "XOR_X0_X1_X0
"

:relational_expr_stub_string_0  "CMP_X1_X0
SET_X0_TO_1
SKIP_INST_LT
SET_X0_TO_0
"
:relational_expr_stub_string_1  "CMP_X1_X0
SET_X0_TO_1
SKIP_INST_LE
SET_X0_TO_0
"
:relational_expr_stub_string_2  "CMP_X1_X0
SET_X0_TO_1
SKIP_INST_GE
SET_X0_TO_0
"
:relational_expr_stub_string_3  "CMP_X1_X0
SET_X0_TO_1
SKIP_INST_GT
SET_X0_TO_0
"
:relational_expr_stub_string_4  "CMP_X1_X0
SET_X0_TO_1
SKIP_INST_EQ
SET_X0_TO_0
"
:relational_expr_stub_string_5  "CMP_X1_X0
SET_X0_TO_1
SKIP_INST_NE
SET_X0_TO_0
"

:additive_expr_stub_string_0  "ADD_X0_X1_X0
"
:additive_expr_stub_string_1  "SUB_X0_X1_X0
"
:additive_expr_stub_string_2  "MUL_X0_X1_X0
"
:additive_expr_stub_string_3  "SDIV_X0_X1_X0
"
:additive_expr_stub_string_4  "SDIV_X2_X1_X0
MSUB_X0_X0_X2_X1
"
:additive_expr_stub_string_5  "LSHIFT_X0_X1_X0
"
:additive_expr_stub_string_6  "ARITH_RSHIFT_X0_X1_X0
"

:unary_expr_sizeof_string_0  "ERROR in unary_expr
Missing (
"
:unary_expr_sizeof_string_1  "ERROR in unary_expr
Missing )
"
:unary_expr_sizeof_string_2  "LOAD_W0_AHEAD
SKIP_32_DATA
%"
:unary_expr_sizeof_string_3
"
"

:postfix_expr_array_string_0  "DEREF_X0
"
:postfix_expr_array_string_1  "DEREF_X0_BYTE
"
:postfix_expr_array_string_2  "LOAD_W2_AHEAD
SKIP_32_DATA
%"
:postfix_expr_array_string_3  "
LSHIFT_X0_X0_X2
"
:postfix_expr_array_string_4  "ADD_X0_X1_X0
"
:postfix_expr_array_string_5  "ERROR in postfix_expr
Missing ]
"
:postfix_expr_array_string_6
'00'

:postfix_expr_arrow_string_0  "# looking up offset
"
:postfix_expr_arrow_string_1  "# -> offset calculation
LOAD_W1_AHEAD
SKIP_32_DATA
%"
:postfix_expr_arrow_string_2  "
ADD_X0_X1_X0
"
:postfix_expr_arrow_string_3  "DEREF_X0
"

:primary_expr_string_0  "SET_X0_TO_0
"
:primary_expr_string_1  "SUB_X0_X1_X0
"
:primary_expr_string_2  "SET_X0_TO_1
"
:primary_expr_string_3  "XOR_X0_X1_X0
"
:primary_expr_string_4  "MVN_X0
"
:primary_expr_string_5  "Error in Primary expression
Didn't get )
"
:primary_expr_string_6  "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"
:primary_expr_string_7  "0123456789"

:primary_expr_variable_string_0  " is not a defined symbol
"
:primary_expr_variable_string_1  "
"
:primary_expr_variable_string_2  "LOAD_W0_AHEAD
SKIP_32_DATA
%"

:function_call_string_0  "ERROR in process_expression_list
No ( was found
"
:function_call_string_1  "PUSH_X16	# Protect a tmp register we're going to use
"
:function_call_string_2  "PUSH_LR	# Protect the old return pointer (link)
"
:function_call_string_3a  "PUSH_BP	# Protect the old base pointer
"
:function_call_string_3b  "SET_X16_FROM_SP	# The base pointer to-be
"
:function_call_string_4  "PUSH_X0	#_process_expression1
"
:function_call_string_5  "PUSH_X0	#_process_expression2
"
:function_call_string_6  "ERROR in process_expression_list
No ) was found
"
:function_call_string_7a  "SET_X0_FROM_BP
"
:function_call_string_7b  "LOAD_W1_AHEAD
SKIP_32_DATA
%"
:function_call_string_8  "
SUB_X0_X0_X1
"
:function_call_string_9  "DEREF_X0
"
:function_call_string_10a  "SET_BP_FROM_X16
"
:function_call_string_10b  "SET_X16_FROM_X0
"
:function_call_string_10c  "BLR_X16
"
:function_call_string_11  "LOAD_W16_AHEAD
SKIP_32_DATA
&FUNCTION_"
:function_call_string_12a  "
"
:function_call_string_12b  "BLR_X16
"
:function_call_string_13  "POP_X1	# _process_expression_locals
"
:function_call_string_14  "POP_BP	# Restore the old base pointer
"
:function_call_string_15  "POP_LR	# Restore the old return pointer (link)
"
:function_call_string_16  "POP_X16	# Restore a register we used as tmp
"

:variable_load_string_0  "SET_X0_FROM_BP
LOAD_W1_AHEAD
SKIP_32_DATA
%"
:variable_load_string_1  "
SUB_X0_X0_X1

"
:variable_load_string_2  "DEREF_X0
"

:function_load_string_0  "LOAD_W0_AHEAD
SKIP_32_DATA
&FUNCTION_"
:function_load_string_1
"
"

:global_load_string_0  "LOAD_W0_AHEAD
SKIP_32_DATA
&GLOBAL_"
:global_load_string_1  "
"
:global_load_string_2  "DEREF_X0
"

:primary_expr_number_string_0  "LOAD_W0_AHEAD
SKIP_32_DATA
%"
:primary_expr_number_string_1
"
"

:primary_expr_string_string_0  "LOAD_W0_AHEAD
SKIP_32_DATA
&STRING_"
:primary_expr_string_string_1  ":STRING_"

:primary_expr_char_string_0  "LOAD_W0_AHEAD
SKIP_32_DATA
%"
:primary_expr_char_string_1  "
"

:primary_expr_failure_string_0  "Received "
:primary_expr_failure_string_1  " in primary_expr
"

:common_recursion_string_0  "PUSH_X0	#_common_recursion
"
:common_recursion_string_1  "POP_X1	# _common_recursion
"

:uniqueID_string_0
"
"

:escape_lookup_string_0  "Unknown escape received: "
:escape_lookup_string_1  " Unable to process
"

:char2hex_string_0  "Tried to print non-hex number
"

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

:type_name_string_0  "Unknown type "
:type_name_string_1
"
"

:create_struct_string_0  "ERROR in create_struct
 Missing {
"
:create_struct_string_1  "ERROR in create_struct
 Missing ;
"

:lookup_member_string_0  "ERROR in lookup_member "
:lookup_member_string_1  " does not exist
"
:lookup_member_string_2
"
"


:build_member_string_0  "Struct only supports [num] form
"

:build_union_string_0  "ERROR in build_union
Missing {
"
:build_union_string_1  "ERROR in build_union
Missing ;
"

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

:ELF_end
