+static GMQCC_INLINE bool asm_parse_stmt(const char *skip, size_t line, asm_state *state) {
+ /*
+ * This parses a valid statement in assembly and adds it to the code
+ * table to be wrote. This needs to handle correct checking of all
+ * statements to ensure the correct amount of operands are passed to
+ * the menomic. This must also check for valid function calls (ensure
+ * the names selected exist in the program scope) and ensure the correct
+ * CALL* is used (depending on the amount of arguments the function
+ * is expected to take)
+ */
+ enum {
+ EXPECT_FUNCTION = 1,
+ EXPECT_VARIABLE = 2,
+ EXPECT_VALUE = 3
+ };
+
+ char *c = (char*)skip;
+ size_t i = 0;
+ char expect = 0;
+ prog_section_statement s;
+ memset(&s, 0, sizeof(prog_section_statement));
+
+ /*
+ * statements are only allowed when inside a function body
+ * otherwise the assembly is invalid.
+ */
+ if (*state != ASM_FUNCTION)
+ return false;
+
+ /*
+ * Skip any possible whitespace, it's not wanted we're searching
+ * for an instruction. TODO: recrusive decent parser skip on line
+ * entry instead of pre-op.
+ */
+ while (*skip == ' ' || *skip == '\t')
+ skip++;
+
+ for (; i < sizeof(asm_instr)/sizeof(*asm_instr); i++) {
+ /*
+ * Iterate all possible instructions and check if the selected
+ * instructure in the input stream `skip` is actually a valid
+ * instruction.
+ */
+ if (!strncmp(skip, asm_instr[i].m, asm_instr[i].l)) {
+
+ /*
+ * We hit the end of a function scope, retarget the state
+ * and add a DONE statement to the statment table.
+ */
+ if (i == AINSTR_END) {
+ s.opcode = i;
+ code_statements_add(s);
+ *state = ASM_NULL;
+ return true;
+ }
+
+ /*
+ * Check the instruction type to see what sort of data
+ * it's expected to have.
+ */
+ if (i >= INSTR_CALL0 && i <= INSTR_CALL8)
+ expect = EXPECT_FUNCTION;
+ else
+ expect = EXPECT_VARIABLE;
+
+ util_debug(
+ "ASM",
+ "found statement %s expecting: `%s` (%ld operand(s))\n",
+ asm_instr[i].m,
+ (expect == EXPECT_FUNCTION)?"function name":(
+ (expect == EXPECT_VARIABLE)?"variable name":(
+ (expect == EXPECT_VALUE ?"value" : "unknown"))),
+ asm_instr[i].o
+ );
+ /*
+ * Parse the operands for `i` (the instruction). The order
+ * of asm_instr is in the order of the menomic encoding so
+ * `i` == menomic encoding.
+ */
+ s.opcode = i;
+ switch (asm_instr[i].o) {
+ /*
+ * Each instruction can have from 0-3 operands; and can
+ * be used with less or more operands depending on it's
+ * selected use.
+ *
+ * DONE for example can use either 0 operands, or 1 (to
+ * emulate the effect of RETURN)
+ *
+ * TODO: parse operands correctly figure out what it is
+ * that the assembly is trying to do, i.e string table
+ * lookup, function calls etc.
+ *
+ * This needs to have a fall state, we start from the
+ * end of the string and work backwards.
+ */
+ #define OPEATS(X,Y) X##Y
+ #define OPCCAT(X,Y) OPEATS(X,Y)
+ #define OPLOAD(X,Y) \
+ do { \
+ util_debug("ASM", "loading operand data ...\n"); \
+ if (expect == EXPECT_VARIABLE) { \
+ size_t f=0; \
+ for (; f<asm_symbols_elements; f++) { \
+ if (!strncmp(asm_symbols_data[f].name, (Y), strlen(Y)) && \
+ asm_symbols_data[f].type != TYPE_FUNCTION) { \
+ (X)=asm_symbols_data[f].offset; \
+ goto OPCCAT(foundv, __LINE__); \
+ } \
+ } \
+ printf("no variable named %s\n", (Y)); \
+ break; \
+ OPCCAT(foundv,__LINE__) : \
+ printf("operand loaded for %s\n", (Y)); \
+ } else if (expect == EXPECT_FUNCTION) { \
+ /* \
+ * It's a function call not a variable association with an instruction \
+ * these are harder to handle. \
+ */ \
+ size_t f=0; \
+ if (strchr(Y, ' ')) { \
+ *strchr(Y, ' ')='\0'; \
+ } \
+ for (; f<asm_symbols_elements; f++) { \
+ if (!strncmp(asm_symbols_data[f].name, (Y), strlen(Y)) && \
+ asm_symbols_data[f].type == TYPE_FUNCTION) { \
+ (X)=asm_symbols_data[f].offset; \
+ goto OPCCAT(foundf, __LINE__); \
+ } \
+ } \
+ printf("no function named [%s]\n", (Y)); \
+ break; \
+ OPCCAT(foundf,__LINE__) : \
+ printf("operand loaded for [%s]\n", (Y)); \
+ } \
+ } while (0)
+ case 3: { OPLOAD(s.o3.s1,c); break; }
+ case 2: { OPLOAD(s.o2.s1,c); break; }
+ case 1: {
+ while (*c == ' ' || *c == '\t') c++;
+ c += asm_instr[i].l;
+ while (*c == ' ' || *c == '\t') c++;
+ OPLOAD(s.o1.s1, c);
+ break;
+ }
+ #undef OPLOAD
+ #undef OPCCAT
+ }
+ /* add the statement now */
+ code_statements_add(s);
+ }
+ }
+ return true;
+}
+