]> git.xonotic.org Git - xonotic/gmqcc.git/blob - assembler.c
Indentation
[xonotic/gmqcc.git] / assembler.c
1 /*
2  * Copyright (C) 2012 
3  *     Dale Weiler
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy of
6  * this software and associated documentation files (the "Software"), to deal in
7  * the Software without restriction, including without limitation the rights to
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9  * of the Software, and to permit persons to whom the Software is furnished to do
10  * so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in all
13  * copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21  * SOFTWARE.
22  */
23 #include "gmqcc.h"
24 /*
25  * This is the assembler, gmqas, this is being implemented because I'm
26  * not exactly sure how codegen would work for the C compiler as of yet
27  * and also I plan to allow inline assembly for the compiler.
28  */
29 static const struct {
30     const char  *m; /* menomic     */
31     const size_t o; /* operands    */ 
32     const size_t l; /* menomic len */
33 } const asm_instr[] = {
34     [INSTR_DONE]       = { "DONE"      , 1, 4 },
35     [INSTR_MUL_F]      = { "MUL_F"     , 3, 5 },
36     [INSTR_MUL_V]      = { "MUL_V"     , 3, 5 },
37     [INSTR_MUL_FV]     = { "MUL_FV"    , 3, 6 },
38     [INSTR_MUL_VF]     = { "MUL_VF"    , 3, 6 },
39     [INSTR_DIV_F]      = { "DIV"       , 0, 3 },
40     [INSTR_ADD_F]      = { "ADD_F"     , 3, 5 },
41     [INSTR_ADD_V]      = { "ADD_V"     , 3, 5 },
42     [INSTR_SUB_F]      = { "SUB_F"     , 3, 5 },
43     [INSTR_SUB_V]      = { "DUB_V"     , 3, 5 },
44     [INSTR_EQ_F]       = { "EQ_F"      , 0, 4 },
45     [INSTR_EQ_V]       = { "EQ_V"      , 0, 4 },
46     [INSTR_EQ_S]       = { "EQ_S"      , 0, 4 },
47     [INSTR_EQ_E]       = { "EQ_E"      , 0, 4 },
48     [INSTR_EQ_FNC]     = { "ES_FNC"    , 0, 6 },
49     [INSTR_NE_F]       = { "NE_F"      , 0, 4 },
50     [INSTR_NE_V]       = { "NE_V"      , 0, 4 },
51     [INSTR_NE_S]       = { "NE_S"      , 0, 4 },
52     [INSTR_NE_E]       = { "NE_E"      , 0, 4 },
53     [INSTR_NE_FNC]     = { "NE_FNC"    , 0, 6 },
54     [INSTR_LE]         = { "LE"        , 0, 2 },
55     [INSTR_GE]         = { "GE"        , 0, 2 },
56     [INSTR_LT]         = { "LT"        , 0, 2 },
57     [INSTR_GT]         = { "GT"        , 0, 2 },
58     [INSTR_LOAD_F]     = { "FIELD_F"   , 0, 7 },
59     [INSTR_LOAD_V]     = { "FIELD_V"   , 0, 7 },
60     [INSTR_LOAD_S]     = { "FIELD_S"   , 0, 7 },
61     [INSTR_LOAD_ENT]   = { "FIELD_ENT" , 0, 9 },
62     [INSTR_LOAD_FLD]   = { "FIELD_FLD" , 0, 9 },
63     [INSTR_LOAD_FNC]   = { "FIELD_FNC" , 0, 9 },
64     [INSTR_ADDRESS]    = { "ADDRESS"   , 0, 7 },
65     [INSTR_STORE_F]    = { "STORE_F"   , 0, 7 },
66     [INSTR_STORE_V]    = { "STORE_V"   , 0, 7 },
67     [INSTR_STORE_S]    = { "STORE_S"   , 0, 7 },
68     [INSTR_STORE_ENT]  = { "STORE_ENT" , 0, 9 },
69     [INSTR_STORE_FLD]  = { "STORE_FLD" , 0, 9 },
70     [INSTR_STORE_FNC]  = { "STORE_FNC" , 0, 9 },
71     [INSTR_STOREP_F]   = { "STOREP_F"  , 0, 8 },
72     [INSTR_STOREP_V]   = { "STOREP_V"  , 0, 8 },
73     [INSTR_STOREP_S]   = { "STOREP_S"  , 0, 8 },
74     [INSTR_STOREP_ENT] = { "STOREP_ENT", 0, 10},
75     [INSTR_STOREP_FLD] = { "STOREP_FLD", 0, 10},
76     [INSTR_STOREP_FNC] = { "STOREP_FNC", 0, 10},
77     [INSTR_RETURN]     = { "RETURN"    , 0, 6 },
78     [INSTR_NOT_F]      = { "NOT_F"     , 0, 5 },
79     [INSTR_NOT_V]      = { "NOT_V"     , 0, 5 },
80     [INSTR_NOT_S]      = { "NOT_S"     , 0, 5 },
81     [INSTR_NOT_ENT]    = { "NOT_ENT"   , 0, 7 },
82     [INSTR_NOT_FNC]    = { "NOT_FNC"   , 0, 7 },
83     [INSTR_IF]         = { "IF"        , 0, 2 },
84     [INSTR_IFNOT]      = { "IFNOT"     , 0, 5 },
85     [INSTR_CALL0]      = { "CALL0"     , 0, 5 },
86     [INSTR_CALL1]      = { "CALL1"     , 0, 5 },
87     [INSTR_CALL2]      = { "CALL2"     , 0, 5 },
88     [INSTR_CALL3]      = { "CALL3"     , 0, 5 },
89     [INSTR_CALL4]      = { "CALL4"     , 0, 5 },
90     [INSTR_CALL5]      = { "CALL5"     , 0, 5 },
91     [INSTR_CALL6]      = { "CALL6"     , 0, 5 },
92     [INSTR_CALL7]      = { "CALL7"     , 0, 5 },
93     [INSTR_CALL8]      = { "CALL8"     , 0, 5 },
94     [INSTR_STATE]      = { "STATE"     , 0, 5 },
95     [INSTR_GOTO]       = { "GOTO"      , 0, 4 },
96     [INSTR_AND]        = { "AND"       , 0, 3 },
97     [INSTR_OR]         = { "OR"        , 0, 2 },
98     [INSTR_BITAND]     = { "BITAND"    , 0, 6 },
99     [INSTR_BITOR]      = { "BITOR"     , 0, 5 }
100 };
101
102 /*
103  * Some assembler keywords not part of the opcodes above: these are
104  * for creating functions, or constants.
105  */
106 const char *const asm_keys[] = {
107     "FLOAT"    , /* define float  */
108     "VECTOR"   , /* define vector */
109     "ENTITY"   , /* define ent    */
110     "FIELD"    , /* define field  */
111     "STRING"   , /* define string */
112     "FUNCTION"
113 };
114
115 static char *const asm_getline(size_t *byte, FILE *fp) {
116     char   *line = NULL;
117     ssize_t read = util_getline(&line, byte, fp);
118     *byte = read;
119     if (read == -1) {
120         mem_d (line);
121         return NULL;
122     }
123     return line;
124 }
125
126 #define asm_rmnewline(L,S) *((L)+*(S)-1) = '\0'
127 #define asm_skipwhite(L)             \
128     while((*(L)==' '||*(L)=='\t')) { \
129         (L)++;                       \
130     }
131     
132 void asm_init(const char *file, FILE **fp) {
133     *fp = fopen(file, "r");
134     code_init();
135 }
136
137 void asm_close(FILE *fp) {
138     fclose(fp);
139     code_write();
140 }
141
142 /*
143  * Following parse states:
144  *     ASM_FUNCTION -- in a function accepting input statements
145  *     ....
146  */
147 typedef enum {
148     ASM_NULL,
149     ASM_FUNCTION
150 } asm_state;
151
152 typedef struct {
153     char *name;   /* name of constant    */
154     int   offset; /* location in globals */
155 } globals;
156 VECTOR_MAKE(globals, assembly_constants);
157
158 void asm_clear() {
159     size_t i = 0;
160     for (; i < assembly_constants_elements; i++)
161         mem_d(assembly_constants_data[i].name);
162     mem_d(assembly_constants_data);
163 }
164
165 void asm_parse(FILE *fp) {
166     char     *data  = NULL;
167     char     *skip  = NULL;
168     long      line  = 1; /* current line */
169     size_t    size  = 0; /* size of line */
170     asm_state state = ASM_NULL;
171     
172     while ((data = asm_getline(&size, fp)) != NULL) {
173         skip = data;
174         asm_skipwhite(skip);
175         asm_rmnewline(skip, &size);
176         
177         #define DECLTYPE(X, CODE)                                         \
178             if (!strncmp(X, skip, strlen(X))) {                           \
179                 if (skip[strlen(X)] != ':') {                             \
180                     printf("%li: Missing `:` after decltype\n",line);     \
181                     exit (1);                                             \
182                 }                                                         \
183                 skip += strlen(X)+1;                                      \
184                 asm_skipwhite(skip);                                      \
185                 if(!isalpha(*skip)) {                                     \
186                     printf("%li: Invalid identififer: %s\n", line, skip); \
187                     exit (1);                                             \
188                 } else {                                                  \
189                     size_t offset_code      = code_statements_elements+1; \
190                     size_t offset_chars     = code_chars_elements     +1; \
191                     size_t offset_globals   = code_globals_elements   +1; \
192                     size_t offset_functions = code_functions_elements +1; \
193                     size_t offset_fields    = code_fields_elements    +1; \
194                     size_t offset_defs      = code_defs_elements      +1; \
195                     CODE                                                  \
196                     /* silent unused warnings */                          \
197                     (void)offset_code;                                    \
198                     (void)offset_chars;                                   \
199                     (void)offset_globals;                                 \
200                     (void)offset_functions;                               \
201                     (void)offset_fields;                                  \
202                     (void)offset_defs;                                    \
203                     assembly_constants_add((globals){                     \
204                         .name   = util_strdup("empty"),                   \
205                         .offset = offset_globals                          \
206                     });                                                   \
207                 }                                                         \
208                 goto end;                                                 \
209             }
210         
211         /* FLOAT    */
212         DECLTYPE(asm_keys[0], {
213             //util_debug("ASM", "Constant FLOAT\n");
214             code_defs_add((prog_section_def){
215                 .type   = TYPE_FLOAT,
216                 .offset = offset_globals, /* global table */
217                 .name   = offset_chars    /* string table TODO */
218             });
219             float f = 0; /*TODO*/
220             code_globals_add(*(int*)&f);
221             
222         });
223         /* VECTOR */
224         DECLTYPE(asm_keys[1], {
225             //util_debug("ASM", "Constant VECTOR\n");
226             code_defs_add((prog_section_def){
227                 .type   = TYPE_FLOAT,
228                 .offset = offset_globals, /* global table */
229                 .name   = offset_chars    /* string table TODO */
230             });
231             float f1 = 0;
232             float f2 = 0;
233             float f3 = 0;
234             code_globals_add(*(int*)&f1);
235             code_globals_add(*(int*)&f2);
236             code_globals_add(*(int*)&f3);
237         });
238         /* ENTITY   */ DECLTYPE(asm_keys[2], {util_debug("ASM", "Constant ENTITY\n");});
239         /* FIELD    */ DECLTYPE(asm_keys[3], {util_debug("ASM", "Constant FIELD\n");});
240         /* STRING   */
241         DECLTYPE(asm_keys[4], {
242             //util_debug("ASM", "Constant STRING\n");
243             code_defs_add((prog_section_def){
244                 .type   = TYPE_STRING,    
245                 .offset = offset_globals, /* offset to offset in string table (for data)*/
246                 .name   = offset_chars    /* location of name in string table (for name)*/
247             });
248             code_strings_add("hello world");
249         });
250         /* FUNCTION */
251         DECLTYPE(asm_keys[5], {
252             //util_debug("ASM", "Constant FUNCTION\n");
253             /* TODO: parse */
254             if (state != ASM_NULL) {
255                 printf("%li: Error unfinished function block, expected DONE or RETURN\n", line);
256                 goto end;
257             }
258             
259             state = ASM_FUNCTION;
260             code_defs_add((prog_section_def){
261                 .type   = TYPE_VOID,
262                 .offset = offset_globals,
263                 .name   = offset_chars
264             });
265             code_globals_add(offset_functions);
266             code_functions_add((prog_section_function){
267                 .entry      =  offset_code,      
268                 .firstlocal =  0,
269                 .locals     =  0,
270                 .profile    =  0,
271                 .name       =  offset_chars,
272                 .file       =  0,
273                 .nargs      =  0,
274                 .argsize    = {0}
275             });
276         });
277         
278         /* if we make it this far then we have statements */
279         {
280             size_t i = 0;    /* counter   */
281             size_t o = 0;    /* operands  */
282             char  *t = NULL; /* token     */
283             
284             /*
285              * Most ops a single statement can have is three.
286              * lets allocate some space for all of those here.
287              */
288             char op[3][32768] = {{0},{0},{0}};
289             for (; i < sizeof(asm_instr)/sizeof(*asm_instr); i++) {
290                 if (!strncmp(skip, asm_instr[i].m, asm_instr[i].l)) {
291                     if (state != ASM_FUNCTION) {
292                         printf("%li: Statement not inside function block\n", line);
293                         goto end;
294                     }
295                     
296                     /* update parser state */
297                     if (i == INSTR_DONE || i == INSTR_RETURN) {
298                         goto end;
299                         state = ASM_NULL;
300                     }
301                     
302                     /* parse the statement */
303                     o     = asm_instr[i].o; /* operands         */
304                     skip += asm_instr[i].l; /* skip instruction */
305                     t     = strtok(skip, " ,");
306                     i     = 0;
307                     while (t != NULL && i < 3) {
308                         strcpy(op[i], t);
309                         t = strtok(NULL, " ,");
310                         i ++;
311                     }
312                     
313                    // util_debug("ASM", "Operand 1: %s\n", util_strrnl(op[0]));
314                    // util_debug("ASM", "Operand 2: %s\n", util_strrnl(op[1]));
315                    // util_debug("ASM", "Operand 3: %s\n", util_strrnl(op[2]));
316                     
317                     /* check */
318                     if (i != o) {
319                         printf("not enough operands, expected: %li, got %li\n", o, i);
320                     }
321                     
322                     /* TODO: hashtable value LOAD .... etc */
323                     code_statements_add((prog_section_statement){
324                         i, {0}, {0}, {0}
325                     });
326                     goto end;
327                 }
328             }
329         }
330         
331         /* if we made it this far something is wrong */
332         if (*skip != '\0')
333             printf("%li: Invalid statement, expression, or decleration\n", line);
334         
335         end:
336         mem_d(data);
337         line ++;
338     }
339         asm_clear();
340 }