summaryrefslogtreecommitdiff
path: root/apps/plugins/lua/loadlib.c
blob: 1cc7ebd7db446d1186e58a1824db032d55979389 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
/*
** $Id: loadlib.c,v 1.52.1.3 2008/08/06 13:29:28 roberto Exp $
** Dynamic library loader for Lua
** See Copyright Notice in lua.h
**
** This module contains an implementation of loadlib for Unix systems
** that have dlfcn, an implementation for Darwin (Mac OS X), an
** implementation for Windows, and a stub for other systems.
*/


#include <stdlib.h>
#include <string.h>


#define loadlib_c
#define LUA_LIB

#include "lua.h"

#include "lauxlib.h"
#include "lualib.h"
#include "rocklib.h"


#define setprogdir(L)       ((void)0)


/*
** {======================================================
** 'require' function
** =======================================================
*/


static int readable (const char *filename) {
  int f = rb->open(filename, O_RDONLY);  /* try to open file */
  if (f < 0) return 0;  /* open failed */
  rb->close(f);
  return 1;
}


static const char *pushnexttemplate (lua_State *L, const char *path) {
  const char *l;
  while (*path == *LUA_PATHSEP) path++;  /* skip separators */
  if (*path == '\0') return NULL;  /* no more templates */
  l = strchr(path, *LUA_PATHSEP);  /* find next separator */
  if (l == NULL) l = path + strlen(path);
  lua_pushlstring(L, path, l - path);  /* template */
  return l;
}


static const char *findfile (lua_State *L, const char *name,
                                           const char *pname) {
  const char *path, *current_path = get_current_path(L, 2);
  name = luaL_gsub(L, name, ".", LUA_DIRSEP);
  lua_getfield(L, LUA_ENVIRONINDEX, pname);
  path = lua_tostring(L, -1);
  if (path == NULL)
    luaL_error(L, LUA_QL("package.%s") " must be a string", pname);
  lua_pushliteral(L, "");  /* error accumulator */
  while ((path = pushnexttemplate(L, path)) != NULL) {
    const char *filename;
    filename = luaL_gsub(L, lua_tostring(L, -1), LUA_PATH_MARK, name);
    if(current_path != NULL) filename = luaL_gsub(L, filename, "$", current_path);
    lua_remove(L, -2);  /* remove path template */
    if (readable(filename))  /* does file exist and is readable? */
      return filename;  /* return that file name */
    lua_pushfstring(L, "\n\tno file " LUA_QS, filename);
    lua_remove(L, -2);  /* remove file name */
    lua_concat(L, 2);  /* add entry to possible error message */
  }
  return NULL;  /* not found */
}


static void loaderror (lua_State *L, const char *filename) {
  luaL_error(L, "error loading module " LUA_QS " from file " LUA_QS ":\n\t%s",
                lua_tostring(L, 1), filename, lua_tostring(L, -1));
}


static int loader_Lua (lua_State *L) {
  const char *filename;
  const char *name = luaL_checkstring(L, 1);
  filename = findfile(L, name, "path");
  if (filename == NULL) return 1;  /* library not found in this path */
  if (luaL_loadfile(L, filename) != 0)
    loaderror(L, filename);
  return 1;  /* library loaded successfully */
}


static int loader_preload (lua_State *L) {
  const char *name = luaL_checkstring(L, 1);
  lua_getfield(L, LUA_ENVIRONINDEX, "preload");
  if (!lua_istable(L, -1))
    luaL_error(L, LUA_QL("package.preload") " must be a table");
  lua_getfield(L, -1, name);
  if (lua_isnil(L, -1))  /* not found? */
    lua_pushfstring(L, "\n\tno field package.preload['%s']", name);
  return 1;
}


static const int sentinel_ = 0;
#define sentinel    ((void *)&sentinel_)


static int ll_require (lua_State *L) {
  const char *name = luaL_checkstring(L, 1);
  int i;
  lua_settop(L, 1);  /* _LOADED table will be at index 2 */
  lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
  lua_getfield(L, 2, name);
  if (lua_toboolean(L, -1)) {  /* is it there? */
    if (lua_touserdata(L, -1) == sentinel)  /* check loops */
      luaL_error(L, "loop or previous error loading module " LUA_QS, name);
    return 1;  /* package is already loaded */
  }
  /* else must load it; iterate over available loaders */
  lua_getfield(L, LUA_ENVIRONINDEX, "loaders");
  if (!lua_istable(L, -1))
    luaL_error(L, LUA_QL("package.loaders") " must be a table");
  lua_pushliteral(L, "");  /* error message accumulator */
  for (i=1; ; i++) {
    lua_rawgeti(L, -2, i);  /* get a loader */
    if (lua_isnil(L, -1))
      luaL_error(L, "module " LUA_QS " not found:%s",
                    name, lua_tostring(L, -2));
    lua_pushstring(L, name);
    lua_call(L, 1, 1);  /* call it */
    if (lua_isfunction(L, -1))  /* did it find module? */
      break;  /* module loaded successfully */
    else if (lua_isstring(L, -1))  /* loader returned error message? */
      lua_concat(L, 2);  /* accumulate it */
    else
      lua_pop(L, 1);
  }
  lua_pushlightuserdata(L, sentinel);
  lua_setfield(L, 2, name);  /* _LOADED[name] = sentinel */
  lua_pushstring(L, name);  /* pass name as argument to module */
  lua_call(L, 1, 1);  /* run loaded module */
  if (!lua_isnil(L, -1))  /* non-nil return? */
    lua_setfield(L, 2, name);  /* _LOADED[name] = returned value */
  lua_getfield(L, 2, name);
  if (lua_touserdata(L, -1) == sentinel) {   /* module did not set a value? */
    lua_pushboolean(L, 1);  /* use true as result */
    lua_pushvalue(L, -1);  /* extra copy to be returned */
    lua_setfield(L, 2, name);  /* _LOADED[name] = true */
  }
  return 1;
}

/* }====================================================== */



/*
** {======================================================
** 'module' function
** =======================================================
*/
  

static void setfenv (lua_State *L) {
  lua_Debug ar;
  if (lua_getstack(L, 1, &ar) == 0 ||
      lua_getinfo(L, "f", &ar) == 0 ||  /* get calling function */
      lua_iscfunction(L, -1))
    luaL_error(L, LUA_QL("module") " not called from a Lua function");
  lua_pushvalue(L, -2);
  lua_setfenv(L, -2);
  lua_pop(L, 1);
}


static void dooptions (lua_State *L, int n) {
  int i;
  for (i = 2; i <= n; i++) {
    lua_pushvalue(L, i);  /* get option (a function) */
    lua_pushvalue(L, -2);  /* module */
    lua_call(L, 1, 0);
  }
}


static void modinit (lua_State *L, const char *modname) {
  const char *dot;
  lua_pushvalue(L, -1);
  lua_setfield(L, -2, "_M");  /* module._M = module */
  lua_pushstring(L, modname);
  lua_setfield(L, -2, "_NAME");
  dot = rb->strrchr(modname, '.');  /* look for last dot in module name */
  if (dot == NULL) dot = modname;
  else dot++;
  /* set _PACKAGE as package name (full module name minus last part) */
  lua_pushlstring(L, modname, dot - modname);
  lua_setfield(L, -2, "_PACKAGE");
}


static int ll_module (lua_State *L) {
  const char *modname = luaL_checkstring(L, 1);
  int loaded = lua_gettop(L) + 1;  /* index of _LOADED table */
  lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
  lua_getfield(L, loaded, modname);  /* get _LOADED[modname] */
  if (!lua_istable(L, -1)) {  /* not found? */
    lua_pop(L, 1);  /* remove previous result */
    /* try global variable (and create one if it does not exist) */
    if (luaL_findtable(L, LUA_GLOBALSINDEX, modname, 1) != NULL)
      return luaL_error(L, "name conflict for module " LUA_QS, modname);
    lua_pushvalue(L, -1);
    lua_setfield(L, loaded, modname);  /* _LOADED[modname] = new table */
  }
  /* check whether table already has a _NAME field */
  lua_getfield(L, -1, "_NAME");
  if (!lua_isnil(L, -1))  /* is table an initialized module? */
    lua_pop(L, 1);
  else {  /* no; initialize it */
    lua_pop(L, 1);
    modinit(L, modname);
  }
  lua_pushvalue(L, -1);
  setfenv(L);
  dooptions(L, loaded - 1);
  return 0;
}


static int ll_seeall (lua_State *L) {
  luaL_checktype(L, 1, LUA_TTABLE);
  if (!lua_getmetatable(L, 1)) {
    lua_createtable(L, 0, 1); /* create new metatable */
    lua_pushvalue(L, -1);
    lua_setmetatable(L, 1);
  }
  lua_pushvalue(L, LUA_GLOBALSINDEX);
  lua_setfield(L, -2, "__index");  /* mt.__index = _G */
  return 0;
}


/* }====================================================== */



/* auxiliary mark (for internal use) */
#define AUXMARK     "\1"

static void setpath (lua_State *L, const char *fieldname, const char *envname,
                                   const char *def) {
  (void)envname;
  lua_pushstring(L, def);  /* use default */
  setprogdir(L);
  lua_setfield(L, -2, fieldname);
}


static const luaL_Reg pk_funcs[] = {
  {"seeall", ll_seeall},
  {NULL, NULL}
};


static const luaL_Reg ll_funcs[] = {
  {"module", ll_module},
  {"require", ll_require},
  {NULL, NULL}
};


static const lua_CFunction loaders[] =
  {loader_preload, loader_Lua, NULL};


LUALIB_API int luaopen_package (lua_State *L) {
  int i;
  /* create new type _LOADLIB */
  luaL_newmetatable(L, "_LOADLIB");
  /* create `package' table */
  luaL_register(L, LUA_LOADLIBNAME, pk_funcs);
#if defined(LUA_COMPAT_LOADLIB) 
  lua_getfield(L, -1, "loadlib");
  lua_setfield(L, LUA_GLOBALSINDEX, "loadlib");
#endif
  lua_pushvalue(L, -1);
  lua_replace(L, LUA_ENVIRONINDEX);
  /* create `loaders' table */
  lua_createtable(L, 0, sizeof(loaders)/sizeof(loaders[0]) - 1);
  /* fill it with pre-defined loaders */
  for (i=0; loaders[i] != NULL; i++) {
    lua_pushcfunction(L, loaders[i]);
    lua_rawseti(L, -2, i+1);
  }
  lua_setfield(L, -2, "loaders");  /* put it in field `loaders' */
  setpath(L, "path", LUA_PATH, LUA_PATH_DEFAULT);  /* set field `path' */
  /* store config information */
  lua_pushliteral(L, LUA_DIRSEP "\n" LUA_PATHSEP "\n" LUA_PATH_MARK "\n"
                     LUA_EXECDIR "\n" LUA_IGMARK);
  lua_setfield(L, -2, "config");
  /* set field `loaded' */
  luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 2);
  lua_setfield(L, -2, "loaded");
  /* set field `preload' */
  lua_newtable(L);
  lua_setfield(L, -2, "preload");
  lua_pushvalue(L, LUA_GLOBALSINDEX);
  luaL_register(L, NULL, ll_funcs);  /* open lib into global table */
  lua_pop(L, 1);
  return 1;  /* return 'package' table */
}

b">struct context_t *ctx, struct lexem_t *lexem) { locate_lexem(lexem, ctx); /* check base */ int base = 10; if(cur_char(ctx) == '0' && next_valid(ctx, 1) && next_char(ctx, 1) == 'x') { advance(ctx, 2); base = 16; } lexem->type = LEX_NUMBER; lexem->num = 0; while(!eof(ctx) && isxdigit(cur_char(ctx))) { if(base == 10 && !isdigit(cur_char(ctx))) break; byte v; if(convxdigit(cur_char(ctx), &v)) break; lexem->num = base * lexem->num + v; advance(ctx, 1); } } static void parse_identifier(struct context_t *ctx, struct lexem_t *lexem) { locate_lexem(lexem, ctx); /* remember position */ char *old = ctx->ptr; while(!eof(ctx) && (isalnum(cur_char(ctx)) || cur_char(ctx) == '_')) advance(ctx, 1); lexem->type = LEX_IDENTIFIER; int len = ctx->ptr - old; lexem->str = xmalloc(len + 1); lexem->str[len] = 0; memcpy(lexem->str, old, len); } static void next_lexem(struct context_t *ctx, struct lexem_t *lexem) { #define ret_simple(t, adv) \ do {locate_lexem(lexem, ctx); \ lexem->type = t; \ advance(ctx, adv); \ return;} while(0) while(!eof(ctx)) { char c = cur_char(ctx); /* skip whitespace */ if(c == ' ' || c == '\t' || c == '\n' || c == '\r') { advance(ctx, 1); continue; } /* skip C++ style comments */ if(c == '/' && next_valid(ctx, 1) && next_char(ctx, 1) == '/') { while(!eof(ctx) && cur_char(ctx) != '\n') advance(ctx, 1); continue; } /* skip C-style comments */ if(c == '/' && next_valid(ctx, 1) && next_char(ctx, 1) == '*') { advance(ctx, 2); while(true) { if(!next_valid(ctx, 1)) parse_error(ctx, "Unterminated comment"); if(cur_char(ctx) == '*' && next_char(ctx, 1) == '/') { advance(ctx, 2); break; } advance(ctx, 1); } continue; } break; } if(eof(ctx)) ret_simple(LEX_EOF, 0); char c = cur_char(ctx); bool nv = next_valid(ctx, 1); char nc = nv ? next_char(ctx, 1) : 0; if(c == '(') ret_simple(LEX_LPAREN, 1); if(c == ')') ret_simple(LEX_RPAREN, 1); if(c == '{') ret_simple(LEX_LBRACE, 1); if(c == '}') ret_simple(LEX_RBRACE, 1); if(c == '>') ret_simple(LEX_RANGLE, 1); if(c == '=') ret_simple(LEX_EQUAL, 1); if(c == ';') ret_simple(LEX_SEMICOLON, 1); if(c == ',') ret_simple(LEX_COLON, 1); if(c == '|') ret_simple(LEX_OR, 1); if(c == '<' && nv && nc == '<') ret_simple(LEX_LSHIFT, 2); if(c == '<' && nv && nc == '=') ret_simple(LEX_LE, 2); if(c == '"') return parse_string(ctx, lexem); if(c == '\'') return parse_ascii_number(ctx, lexem); if(isdigit(c)) return parse_number(ctx, lexem); if(isalpha(c) || c == '_') return parse_identifier(ctx, lexem); parse_error(ctx, "Unexpected character '%c'\n", c); #undef ret_simple } #if 0 static void log_lexem(struct lexem_t *lexem) { switch(lexem->type) { case LEX_EOF: printf("<eof>"); break; case LEX_EQUAL: printf("="); break; case LEX_IDENTIFIER: printf("id(%s)", lexem->str); break; case LEX_LPAREN: printf("("); break; case LEX_RPAREN: printf(")"); break; case LEX_LBRACE: printf("{"); break; case LEX_RBRACE: printf("}"); break; case LEX_SEMICOLON: printf(";"); break; case LEX_NUMBER: printf("num(%d)", lexem->num); break; case LEX_STRING: printf("str(%s)", lexem->str); break; case LEX_OR: printf("|"); break; case LEX_LSHIFT: printf("<<"); break; default: printf("<unk>"); } } #endif struct cmd_option_t *db_add_opt(struct cmd_option_t **opt, const char *identifier, bool is_str) { while(*opt) opt = &(*opt)->next; *opt = xmalloc(sizeof(struct cmd_option_t)); memset(*opt, 0, sizeof(struct cmd_option_t)); (*opt)->name = strdup(identifier); (*opt)->is_string = is_str; return *opt; } void db_add_str_opt(struct cmd_option_t **opt, const char *name, const char *value) { db_add_opt(opt, name, true)->str = strdup(value); } void db_add_int_opt(struct cmd_option_t **opt, const char *name, uint32_t value) { db_add_opt(opt, name, false)->val = value; } static struct cmd_source_t *db_add_src(struct cmd_source_t **src, const char *identifier, bool is_extern) { while(*src) src = &(*src)->next; *src = xmalloc(sizeof(struct cmd_source_t)); memset(*src, 0, sizeof(struct cmd_source_t)); (*src)->identifier = strdup(identifier); (*src)->is_extern = is_extern; return *src; } void db_add_source(struct cmd_file_t *cmd_file, const char *identifier, const char *filename) { db_add_src(&cmd_file->source_list, identifier, false)->filename = strdup(filename); } void db_add_extern_source(struct cmd_file_t *cmd_file, const char *identifier, int extern_nr) { db_add_src(&cmd_file->source_list, identifier, true)->extern_nr = extern_nr; } static struct cmd_inst_t *db_add_inst(struct cmd_inst_t **list, enum cmd_inst_type_t type, uint32_t argument) { while(*list) list = &(*list)->next; *list = xmalloc(sizeof(struct cmd_inst_t)); memset(*list, 0, sizeof(struct cmd_inst_t)); (*list)->type = type; (*list)->argument = argument; return *list; } void db_add_inst_id(struct cmd_section_t *cmd_section, enum cmd_inst_type_t type, const char *identifier, uint32_t argument) { db_add_inst(&cmd_section->inst_list, type, argument)->identifier = strdup(identifier); } void db_add_inst_addr(struct cmd_section_t *cmd_section, enum cmd_inst_type_t type, uint32_t addr, uint32_t argument) { db_add_inst(&cmd_section->inst_list, type, argument)->addr = addr; } struct cmd_section_t *db_add_section(struct cmd_file_t *cmd_file, uint32_t identifier, bool data) { struct cmd_section_t **prev = &cmd_file->section_list; while(*prev) prev = &(*prev)->next; *prev = xmalloc(sizeof(struct cmd_section_t)); memset(*prev, 0, sizeof(struct cmd_section_t)); (*prev)->identifier = identifier; (*prev)->is_data = data; return *prev; } struct cmd_source_t *db_find_source_by_id(struct cmd_file_t *cmd_file, const char *id) { struct cmd_source_t *src = cmd_file->source_list; while(src) { if(strcmp(src->identifier, id) == 0) return src; src = src->next; } return NULL; } struct cmd_option_t *db_find_option_by_id(struct cmd_option_t *opt, const char *name) { while(opt) { if(strcmp(opt->name, name) == 0) return opt; opt = opt->next; } return NULL; } #define INVALID_SB_SUBVERSION 0xffff static const char *parse_sb_subversion(const char *str, uint16_t *v) { int len = 0; *v = 0; while(isdigit(str[len]) && len < 3) *v = (*v) << 4 | (str[len++] - '0'); if(len == 0) *v = INVALID_SB_SUBVERSION; return str + len; } bool db_parse_sb_version(struct sb_version_t *ver, const char *str) { str = parse_sb_subversion(str, &ver->major); if(ver->major == INVALID_SB_SUBVERSION || *str != '.') return false; str = parse_sb_subversion(str + 1, &ver->minor); if(ver->minor == INVALID_SB_SUBVERSION || *str != '.') return false; str = parse_sb_subversion(str + 1, &ver->revision); if(ver->revision == INVALID_SB_SUBVERSION || *str != 0) return false; return true; } static bool db_generate_sb_subversion(uint16_t subver, char *str) { str[0] = '0' + ((subver >> 8) & 0xf); str[1] = '0' + ((subver >> 4) & 0xf); str[2] = '0' + (subver & 0xf); return true; } bool db_generate_sb_version(struct sb_version_t *ver, char *str, int size) { if(size < 12) return false; str[3] = '.'; str[7] = '.'; str[11] = 0; return db_generate_sb_subversion(ver->major, str) && db_generate_sb_subversion(ver->minor, str + 4) && db_generate_sb_subversion(ver->revision, str + 8); } #undef parse_error #define parse_error(lexem, ...) \ do { fprintf(stderr, "%s:%d: ", lexem.file, lexem.line); \ fprintf(stderr, __VA_ARGS__); exit(2); } while(0) struct lex_ctx_t { struct context_t ctx; struct lexem_t lexem; }; /* When lexems hold strings (like identifier), it might be useful to steal * the pointer and don't clean the lexem but in other case, one don't want * to keep the pointer to the string and just want to release the memory. * Thus clean_lexem should be true except when one keeps a pointer */ static inline void next(struct lex_ctx_t *ctx, bool clean_lexem) { if(clean_lexem) free(ctx->lexem.str); memset(&ctx->lexem, 0, sizeof(struct lexem_t)); next_lexem(&ctx->ctx, &ctx->lexem); } static uint32_t parse_term_expr(struct lex_ctx_t *ctx, struct cmd_option_t *const_list) { uint32_t ret = 0; if(ctx->lexem.type == LEX_NUMBER) ret = ctx->lexem.num; else if(ctx->lexem.type == LEX_IDENTIFIER) { struct cmd_option_t *c = db_find_option_by_id(const_list, ctx->lexem.str); if(c == NULL) parse_error(ctx->lexem, "Undefined reference to constant '%s'\n", ctx->lexem.str); if(c->is_string) parse_error(ctx->lexem, "Internal error: constant '%s' is not an integer\n", ctx->lexem.str); ret = c->val; } else parse_error(ctx->lexem, "Number or constant identifier expected\n"); next(ctx, true); return ret; } static uint32_t parse_shift_expr(struct lex_ctx_t *ctx, struct cmd_option_t *const_list) { uint32_t v = parse_term_expr(ctx, const_list); while(ctx->lexem.type == LEX_LSHIFT) { next(ctx, true); v <<= parse_term_expr(ctx, const_list); } return v; } static uint32_t parse_or_expr(struct lex_ctx_t *ctx, struct cmd_option_t *const_list) { uint32_t v = parse_shift_expr(ctx, const_list); while(ctx->lexem.type == LEX_OR) { next(ctx, true); v |= parse_shift_expr(ctx, const_list); } return v; } static uint32_t parse_intexpr(struct lex_ctx_t *ctx, struct cmd_option_t *const_list) { return parse_or_expr(ctx, const_list); } #define NR_INITIAL_CONSTANTS 4 static char *init_const_name[NR_INITIAL_CONSTANTS] = {"true", "false", "yes", "no"}; static uint32_t init_const_value[NR_INITIAL_CONSTANTS] = {1, 0, 1, 0}; struct cmd_file_t *db_parse_file(const char *file) { size_t size; FILE *f = fopen(file, "r"); if(f == NULL) { if(g_debug) perror("Cannot open db file"); return NULL; } fseek(f, 0, SEEK_END); size = ftell(f); fseek(f, 0, SEEK_SET); char *buf = xmalloc(size); if(fread(buf, size, 1, f) != 1) { if(g_debug) perror("Cannot read db file"); return NULL; } fclose(f); if(g_debug) printf("Parsing db file '%s'\n", file); struct cmd_file_t *cmd_file = xmalloc(sizeof(struct cmd_file_t)); memset(cmd_file, 0, sizeof(struct cmd_file_t)); /* add initial constants */ for(int i = 0; i < NR_INITIAL_CONSTANTS; i++) db_add_int_opt(&cmd_file->constant_list, init_const_name[i], init_const_value[i]); struct lex_ctx_t lctx; lctx.ctx.file = file; lctx.ctx.line = 1; lctx.ctx.begin = buf; lctx.ctx.ptr = buf; lctx.ctx.end = buf + size; #define next(clean_lexem) next(&lctx, clean_lexem) #define lexem lctx.lexem /* init lexer */ next(false); /* don't clean init lexem because it doesn't exist */ /* constants ? */ if(lexem.type == LEX_IDENTIFIER && !strcmp(lexem.str, "constants")) { next(true); if(lexem.type != LEX_LBRACE) parse_error(lexem, "'{' expected after 'constants'\n"); while(true) { next(true); if(lexem.type == LEX_RBRACE) break; if(lexem.type != LEX_IDENTIFIER) parse_error(lexem, "Identifier expected in constants\n"); const char *name = lexem.str; next(false); /* lexem string is kept as option name */ if(lexem.type != LEX_EQUAL) parse_error(lexem, "'=' expected after identifier\n"); next(true); db_add_int_opt(&cmd_file->constant_list, name, parse_intexpr(&lctx, cmd_file->constant_list)); if(lexem.type != LEX_SEMICOLON) parse_error(lexem, "';' expected after string\n"); } next(true); } /* options ? */ if(lexem.type == LEX_IDENTIFIER && !strcmp(lexem.str, "options")) { next(true); if(lexem.type != LEX_LBRACE) parse_error(lexem, "'{' expected after 'options'\n"); while(true) { next(true); if(lexem.type == LEX_RBRACE) break; if(lexem.type != LEX_IDENTIFIER) parse_error(lexem, "Identifier expected in options\n"); const char *name = lexem.str; next(false); /* lexem string is kept as option name */ if(lexem.type != LEX_EQUAL) parse_error(lexem, "'=' expected after identifier\n"); next(true); if(lexem.type == LEX_STRING) { db_add_str_opt(&cmd_file->opt_list, name, lexem.str); next(true); } else db_add_int_opt(&cmd_file->opt_list, name, parse_intexpr(&lctx, cmd_file->constant_list)); if(lexem.type != LEX_SEMICOLON) parse_error(lexem, "';' expected after string\n"); } next(true); } /* sources */ if(lexem.type != LEX_IDENTIFIER || strcmp(lexem.str, "sources")) parse_error(lexem, "'sources' expected\n"); next(true); if(lexem.type != LEX_LBRACE) parse_error(lexem, "'{' expected after 'sources'\n"); while(true) { next(true); if(lexem.type == LEX_RBRACE) break; if(lexem.type != LEX_IDENTIFIER) parse_error(lexem, "identifier expected in sources\n"); const char *srcid = lexem.str; if(db_find_source_by_id(cmd_file, srcid) != NULL) parse_error(lexem, "Duplicate source identifier\n"); next(false); /* lexem string is kept as source name */ if(lexem.type != LEX_EQUAL) parse_error(lexem, "'=' expected after identifier\n"); next(true); if(lexem.type == LEX_STRING) { db_add_source(cmd_file, srcid, lexem.str); next(true); } else if(lexem.type == LEX_IDENTIFIER && !strcmp(lexem.str, "extern")) { next(true); if(lexem.type != LEX_LPAREN) parse_error(lexem, "'(' expected after 'extern'\n"); next(true); db_add_extern_source(cmd_file, srcid, parse_intexpr(&lctx, cmd_file->constant_list)); if(lexem.type != LEX_RPAREN) parse_error(lexem, "')' expected\n"); next(true); } else parse_error(lexem, "String or 'extern' expected after '='\n"); if(lexem.type != LEX_SEMICOLON) parse_error(lexem, "';' expected\n"); } /* sections */ while(true) { next(true); if(lexem.type == LEX_EOF) break; if(lexem.type != LEX_IDENTIFIER || strcmp(lexem.str, "section") != 0) parse_error(lexem, "'section' expected\n"); next(true); if(lexem.type != LEX_LPAREN) parse_error(lexem, "'(' expected after 'section'\n"); next(true); /* can be any number */ struct cmd_section_t *sec = db_add_section(cmd_file, parse_intexpr(&lctx, cmd_file->constant_list), false); /* options ? */ if(lexem.type == LEX_SEMICOLON) { do { next(true); if(lexem.type != LEX_IDENTIFIER) parse_error(lexem, "Identifier expected for section option\n"); const char *name = lexem.str; next(false); /* lexem string is kept as option name */ if(lexem.type != LEX_EQUAL) parse_error(lexem, "'=' expected after option identifier\n"); next(true); if(lexem.type == LEX_STRING) { db_add_str_opt(&sec->opt_list, name, lexem.str); next(true); } else db_add_int_opt(&sec->opt_list, name, parse_intexpr(&lctx, cmd_file->constant_list)); }while(lexem.type == LEX_COLON); } if(lexem.type != LEX_RPAREN) parse_error(lexem, "')' expected after section identifier\n"); next(true); if(lexem.type == LEX_LBRACE) { sec->is_data = false; /* commands */ while(true) { next(true); if(lexem.type == LEX_RBRACE) break; struct cmd_inst_t *inst = db_add_inst(&sec->inst_list, CMD_LOAD, 0); if(lexem.type != LEX_IDENTIFIER) parse_error(lexem, "Instruction expected in section\n"); if(strcmp(lexem.str, "load") == 0) inst->type = CMD_LOAD; else if(strcmp(lexem.str, "call") == 0) inst->type = CMD_CALL; else if(strcmp(lexem.str, "jump") == 0) inst->type = CMD_JUMP; else if(strcmp(lexem.str, "mode") == 0) inst->type = CMD_MODE; else parse_error(lexem, "Instruction expected in section\n"); next(true); if(inst->type == CMD_LOAD) { if(lexem.type != LEX_IDENTIFIER) parse_error(lexem, "Identifier expected after instruction\n"); inst->identifier = lexem.str; if(db_find_source_by_id(cmd_file, inst->identifier) == NULL) parse_error(lexem, "Undefined reference to source '%s'\n", inst->identifier); next(false); /* lexem string kept as identifier */ if(lexem.type == LEX_RANGLE) { // load at inst->type = CMD_LOAD_AT; next(true); inst->addr = parse_intexpr(&lctx, cmd_file->constant_list); } if(lexem.type != LEX_SEMICOLON) parse_error(lexem, "';' expected after command\n"); } else if(inst->type == CMD_CALL || inst->type == CMD_JUMP) { if(lexem.type == LEX_IDENTIFIER) { inst->identifier = lexem.str; if(db_find_source_by_id(cmd_file, inst->identifier) == NULL) parse_error(lexem, "Undefined reference to source '%s'\n", inst->identifier); next(false); /* lexem string kept as identifier */ } else { inst->type = (inst->type == CMD_CALL) ? CMD_CALL_AT : CMD_JUMP_AT; inst->addr = parse_intexpr(&lctx, cmd_file->constant_list); } if(lexem.type == LEX_LPAREN) { next(true); inst->argument = parse_intexpr(&lctx, cmd_file->constant_list); if(lexem.type != LEX_RPAREN) parse_error(lexem, "Expected closing brace\n"); next(true); } if(lexem.type != LEX_SEMICOLON) parse_error(lexem, "';' expected after command\n"); } else if(inst->type == CMD_MODE) { inst->argument = parse_intexpr(&lctx, cmd_file->constant_list); if(lexem.type != LEX_SEMICOLON) parse_error(lexem, "Expected ';' after command\n"); } else parse_error(lexem, "Internal error"); } } else if(lexem.type == LEX_LE) { sec->is_data = true; next(true); if(lexem.type != LEX_IDENTIFIER) parse_error(lexem, "Identifier expected after '<='\n"); sec->source_id = lexem.str; next(false); /* lexem string is kept as source id */ if(lexem.type != LEX_SEMICOLON) parse_error(lexem, "';' expected after identifier\n"); } else parse_error(lexem, "'{' or '<=' expected after section directive\n"); } #undef lexem #undef next free(buf); return cmd_file; } void db_free_option_list(struct cmd_option_t *opt_list) { while(opt_list) { struct cmd_option_t *next = opt_list->next; fflush(stdout); free(opt_list->name); free(opt_list->str); free(opt_list); opt_list = next; } } static bool db_generate_options(FILE *f, const char *secname, struct cmd_option_t *list) { fprintf(f, "%s\n", secname); fprintf(f, "{\n"); while(list) { fprintf(f, " %s = ", list->name); if(list->is_string) fprintf(f, "\"%s\";\n", list->str); // FIXME handle escape else fprintf(f, "0x%x;\n", list->val); list = list->next; } fprintf(f, "}\n"); return true; } static bool db_generate_section_options(FILE *f, struct cmd_option_t *list) { bool first = true; while(list) { fprintf(f, "%c %s = ", first ? ';' : ',', list->name); if(list->is_string) fprintf(f, "\"%s\"", list->str); // FIXME handle escape else fprintf(f, "0x%x", list->val); first = false; list = list->next; } return true; } static bool db_generate_sources(FILE *f, struct cmd_source_t *list) { fprintf(f, "sources\n"), fprintf(f, "{\n"); while(list) { fprintf(f, " %s = ", list->identifier); if(list->is_extern) fprintf(f, "extern(%d);\n", list->extern_nr); else fprintf(f, "\"%s\";\n", list->filename); // FIXME handle escape list = list->next; } fprintf(f, "}\n"); return true; } static bool db_generate_section(FILE *f, struct cmd_section_t *section) { fprintf(f, "section(%#x", section->identifier); db_generate_section_options(f, section->opt_list); if(section->is_data) { fprintf(f, ") <= %s;\n", section->source_id); return true; } fprintf(f, ")\n{\n"); struct cmd_inst_t *inst = section->inst_list; while(inst) { fprintf(f, " "); switch(inst->type) { case CMD_LOAD: fprintf(f, "load %s;\n", inst->identifier); break; case CMD_LOAD_AT: fprintf(f, "load %s > %#x;\n", inst->identifier, inst->addr); break; case CMD_CALL: fprintf(f, "call %s(%#x);\n", inst->identifier, inst->argument); break; case CMD_CALL_AT: fprintf(f, "call %#x(%#x);\n", inst->addr, inst->argument); break; case CMD_JUMP: fprintf(f, "jump %s(%#x);\n", inst->identifier, inst->argument); break; case CMD_JUMP_AT: fprintf(f, "jump %#x(%#x);\n", inst->addr, inst->argument); break; case CMD_MODE: fprintf(f, "mode %#x;\n", inst->argument); break; default: bug("die"); } inst = inst->next; } fprintf(f, "}\n"); return true; } static bool db_generate_sections(FILE *f, struct cmd_section_t *section) { while(section) if(!db_generate_section(f, section)) return false; else section = section->next; return true; } bool db_generate_file(struct cmd_file_t *file, const char *filename, void *user, db_color_printf printf) { FILE *f = fopen(filename, "w"); if(f == NULL) return printf(user, true, GREY, "Cannot open '%s' for writing: %m\n", filename), false; if(!db_generate_options(f, "constants", file->constant_list)) goto Lerr; if(!db_generate_options(f, "options", file->opt_list)) goto Lerr; if(!db_generate_sources(f, file->source_list)) goto Lerr; if(!db_generate_sections(f, file->section_list)) goto Lerr; fclose(f); return true; Lerr: fclose(f); return false; } void db_free(struct cmd_file_t *file) { db_free_option_list(file->opt_list); db_free_option_list(file->constant_list); struct cmd_source_t *src = file->source_list; while(src) { struct cmd_source_t *next = src->next; free(src->identifier); fflush(stdout); free(src->filename); if(src->loaded) { if(src->type == CMD_SRC_BIN) free(src->bin.data); if(src->type == CMD_SRC_ELF) elf_release(&src->elf); } free(src); src = next; } struct cmd_section_t *sec = file->section_list; while(sec) { struct cmd_section_t *next = sec->next; db_free_option_list(sec->opt_list); free(sec->source_id); struct cmd_inst_t *inst = sec->inst_list; while(inst) { struct cmd_inst_t *next = inst->next; free(inst->identifier); free(inst); inst = next; } free(sec); sec = next; } free(file); }