]> de.git.xonotic.org Git - xonotic/gmqcc.git/blob - assembler.c
replaced getline with util_getline
[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_parse(FILE *fp) {
159         char     *data  = NULL;
160         char     *skip  = NULL;
161         long      line  = 1; /* current line */
162         size_t    size  = 0; /* size of line */
163         asm_state state = ASM_NULL;
164         
165         while ((data = asm_getline(&size, fp)) != NULL) {
166                 skip = data;
167                 asm_skipwhite(skip);
168                 asm_rmnewline(skip, &size);
169                 
170                 #define DECLTYPE(X, CODE)                                         \
171                     if (!strncmp(X, skip, strlen(X))) {                           \
172                         if (skip[strlen(X)] != ':') {                             \
173                             printf("%li: Missing `:` after decltype\n",line);     \
174                             exit (1);                                             \
175                         }                                                         \
176                         skip += strlen(X)+1;                                      \
177                         asm_skipwhite(skip);                                      \
178                         if(!isalpha(*skip)) {                                     \
179                             printf("%li: Invalid identififer: %s\n", line, skip); \
180                             exit (1);                                             \
181                         } else {                                                  \
182                             size_t offset_code      = code_statements_elements+1; \
183                             size_t offset_chars     = code_strings_elements   +1; \
184                             size_t offset_globals   = code_globals_elements   +1; \
185                             size_t offset_functions = code_functions_elements +1; \
186                             size_t offset_fields    = code_fields_elements    +1; \
187                             size_t offset_defs      = code_defs_elements      +1; \
188                             CODE                                                  \
189                             /* silent unused warnings */                          \
190                             (void)offset_code;                                    \
191                             (void)offset_chars;                                   \
192                             (void)offset_globals;                                 \
193                             (void)offset_functions;                               \
194                             (void)offset_fields;                                  \
195                             (void)offset_defs;                                    \
196                             assembly_constants_add((globals){                     \
197                                 .name   = util_strdup(skip),                      \
198                                 .offset = offset_globals                          \
199                             });                                                   \
200                         }                                                         \
201                         goto end;                                                 \
202                     }
203                 
204                 /* FLOAT    */
205                 DECLTYPE(asm_keys[0], {
206                         code_defs_add((prog_section_def){
207                                 .type   = TYPE_FLOAT,
208                                 .offset = offset_globals, /* global table */
209                                 .name   = offset_chars    /* string table TODO */
210                         });
211                         float f = 0; /*TODO*/
212                         code_globals_add(*(int*)&f);
213                         
214                 });
215                 DECLTYPE(asm_keys[1], {
216                         code_defs_add((prog_section_def){
217                                 .type   = TYPE_FLOAT,
218                                 .offset = offset_globals, /* global table */
219                                 .name   = offset_chars    /* string table TODO */
220                         });
221                         float f1 = 0;
222                         float f2 = 0;
223                         float f3 = 0;
224                         code_globals_add(*(int*)&f1);
225                         code_globals_add(*(int*)&f2);
226                         code_globals_add(*(int*)&f3);
227                 });
228                 /* ENTITY   */ DECLTYPE(asm_keys[2], {});
229                 /* FIELD    */ DECLTYPE(asm_keys[3], {});
230                 /* STRING   */
231                 DECLTYPE(asm_keys[4], {
232                         code_defs_add((prog_section_def){
233                                 .type   = TYPE_STRING,    
234                                 .offset = offset_globals, /* offset to offset in string table (for data)*/
235                                 .name   = offset_chars    /* location of name in string table (for name)*/
236                         });
237                         code_strings_add('h');
238                 });
239                 /* FUNCTION */
240                 DECLTYPE(asm_keys[5], {
241                         /* TODO: parse */
242                         if (state != ASM_NULL) {
243                                 printf("%li: Error unfinished function block, expected DONE or RETURN\n", line);
244                                 goto end;
245                         }
246                         
247                         state = ASM_FUNCTION;
248                         code_defs_add((prog_section_def){
249                                 .type   = TYPE_VOID,
250                                 .offset = offset_globals,
251                                 .name   = offset_chars
252                         });
253                         code_globals_add(offset_functions);
254                         code_functions_add((prog_section_function){
255                                 .entry      =  offset_code,      
256                                 .firstlocal =  0,
257                                 .locals     =  0,
258                                 .profile    =  0,
259                                 .name       =  offset_chars,
260                                 .file       =  0,
261                                 .nargs      =  0,
262                                 .argsize    = {0}
263                         });
264                 });
265                 
266                 /* if we make it this far then we have statements */
267                 {
268                         size_t i = 0;    /* counter   */
269                         size_t o = 0;    /* operands  */
270                         char  *t = NULL; /* token     */
271                         
272                         /*
273                          * Most ops a single statement can have is three.
274                          * lets allocate some space for all of those here.
275                          */
276                         char op[3][32768] = {{0},{0},{0}};
277                         for (; i < sizeof(asm_instr)/sizeof(*asm_instr); i++) {
278                                 if (!strncmp(skip, asm_instr[i].m, asm_instr[i].l)) {
279                                         if (state != ASM_FUNCTION) {
280                                                 printf("%li: Statement not inside function block\n", line);
281                                                 goto end;
282                                         }
283                                         
284                                         /* update parser state */
285                                         if (i == INSTR_DONE || i == INSTR_RETURN) {
286                                                 goto end;
287                                                 state = ASM_NULL;
288                                         }
289                                         
290                                         /* parse the statement */
291                                         o     = asm_instr[i].o; /* operands         */
292                                         skip += asm_instr[i].l; /* skip instruction */
293                                         t     = strtok(skip, " ,");
294                                         i     = 0;
295                                         while (t != NULL && i < 3) {
296                                                 strcpy(op[i], t);
297                                                 t = strtok(NULL, " ,");
298                                                 i ++;
299                                         }
300                                         
301                                         util_debug("ASM", "Operand 1: %s\n", util_strrnl(op[0]));
302                                         util_debug("ASM", "Operand 2: %s\n", util_strrnl(op[1]));
303                                         util_debug("ASM", "Operand 3: %s\n", util_strrnl(op[2]));
304                                         
305                                         /* check */
306                                         if (i != o) {
307                                                 printf("not enough operands, expected: %li, got %li\n", o, i);
308                                         }
309                                         
310                                         /* TODO: hashtable value LOAD .... etc */
311                                         code_statements_add((prog_section_statement){
312                                                 i, {0}, {0}, {0}
313                                         });
314                                         goto end;
315                                 }
316                         }
317                 }
318                 
319                 /* if we made it this far something is wrong */
320                 if (*skip != '\0')
321                         printf("%li: Invalid statement, expression, or decleration\n", line);
322                 
323                 end:
324                 //free(data);
325                 mem_d(data);
326                 line ++;
327         }
328 }