ugl_parse.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. /*
  2. ugl_file.c
  3. microcontroller game language
  4. bytecode
  5. return from procedure... all procedure leave a 16 bit value on the stack
  6. jump probably only for if/loop
  7. pop stack and jump on zero
  8. pop stack and jump on not zero
  9. call buildin procedure
  10. call bytecode procedure
  11. call toplevel buildin procedure
  12. call toplevel bytecode procedure
  13. push constant 16 bit value on stack
  14. pop constant value from stack
  15. proc <name> Starts a new procedure. Procedures must be terminated with "endproc"
  16. endproc End a procedure
  17. locals <num> Define the specified number of local variables, acces with arg(n), where n is between no_of_args+1 and no_of_args+<num>
  18. */
  19. #include <stdio.h>
  20. #include <string.h>
  21. #include "ugl.h"
  22. #include "ugl_bc.h"
  23. long ugl_current_local_variables = 0;
  24. long ugl_current_args = 0;
  25. #define UGL_MAX_INDENT 64
  26. #define UGL_INDENT_TYPE_PROC 0
  27. #define UGL_INDENT_TYPE_IF 1
  28. #define UGL_INDENT_TYPE_LOOP 2
  29. int ugl_indent_level;
  30. int ugl_indent_type[UGL_MAX_INDENT];
  31. char ugl_indent_identifier[UGL_MAX_INDENT][UGL_MAX_IDENTIFIER_LEN+1];
  32. struct ugl_buildin_cmd_struct
  33. {
  34. int code;
  35. const char *name;
  36. int args;
  37. };
  38. struct ugl_buildin_cmd_struct ugl_buildin_cmd_list[] = {
  39. { /* code=*/ 0, /* name=*/ "nop", /* args=*/ 0},
  40. { /* code=*/ 1, /* name=*/ "return", /* args=*/ 1 }, /* assign the return value of a user defined function */
  41. { /* code=*/ 2, /* name=*/ "a", /* args=*/ 1 }, /* return the value of the n-th argument of a user defined function */
  42. { /* code=*/ 3, /* name=*/ "a", /* args=*/ 2 }, /* reassign the value of the n-th argument of a user defined function */
  43. { /* code=*/ 4, /* name=*/ "add", /* args=*/ 2 },
  44. { /* code=*/ 5, /* name=*/ "print", /* args=*/ 1 },
  45. { /* code=*/ 6, /* name=*/ "setPos", /* args=*/ 2 },
  46. { /* code=*/ 7, /* name=*/ "setItemPos", /* args=*/ 1 },
  47. { /* code=*/ 8, /* name=*/ "setItemDir", /* args=*/ 2 },
  48. };
  49. void ugl_IncIndent(int type)
  50. {
  51. if ( ugl_indent_level > UGL_MAX_INDENT )
  52. ugl_err("max indent level reached");
  53. ugl_indent_type[ugl_indent_level] = type;
  54. ugl_indent_level++;
  55. }
  56. void ugl_DecIndent(int type)
  57. {
  58. if ( ugl_indent_level == 0 )
  59. ugl_err("can not decrement indent level, requested type = %d", type);
  60. ugl_indent_level--;
  61. if (ugl_indent_type[ugl_indent_level] != type )
  62. ugl_err("block end mismatch, %d != %d", ugl_indent_type[ugl_indent_level] , type );
  63. }
  64. static void skip_space(const char **s)
  65. {
  66. for(;;)
  67. {
  68. if ( **s == '#' )
  69. {
  70. while( **s != '\0' )
  71. (*s)++;
  72. break;
  73. }
  74. if ( **s == '\0' )
  75. break;
  76. if ( **s > ' ' )
  77. break;
  78. (*s)++;
  79. }
  80. }
  81. static long get_dec(const char **s)
  82. {
  83. long v = 0;
  84. for(;;)
  85. {
  86. if ( (**s) >= '0' && (**s) <= '9' )
  87. {
  88. v*=10;
  89. v+= (**s)-'0';
  90. (*s)++;
  91. }
  92. else
  93. {
  94. break;
  95. }
  96. }
  97. skip_space(s);
  98. return v;
  99. }
  100. static long get_hex(const char **s)
  101. {
  102. long v = 0;
  103. for(;;)
  104. {
  105. if ( (**s) >= '0' && (**s) <= '9' )
  106. {
  107. v*=16;
  108. v+= (**s)-'0';
  109. (*s)++;
  110. }
  111. else if ( (**s) >= 'a' && (**s) <= 'f' )
  112. {
  113. v*=16;
  114. v+= (**s)-'a'+10;
  115. (*s)++;
  116. }
  117. else if ( (**s) >= 'A' && (**s) <= 'F' )
  118. {
  119. v*=16;
  120. v+= (**s)-'A'+10;
  121. (*s)++;
  122. }
  123. else
  124. {
  125. break;
  126. }
  127. }
  128. skip_space(s);
  129. return v;
  130. }
  131. static long get_ascii(const char **s)
  132. {
  133. long v = 0;
  134. v = **s;
  135. (*s)++;
  136. skip_space(s);
  137. return v;
  138. }
  139. static long get_num(const char **s)
  140. {
  141. if ( (**s) == '$' )
  142. {
  143. (*s)++;
  144. return get_hex(s);
  145. }
  146. if ( (**s) == '\'' )
  147. {
  148. (*s)++;
  149. return get_ascii(s);
  150. }
  151. return get_dec(s);
  152. }
  153. static const char *get_identifier(const char **s)
  154. {
  155. static char buf[UGL_MAX_IDENTIFIER_LEN+1];
  156. int c;
  157. int i = 0;
  158. buf[0] = '\0';
  159. for(;;)
  160. {
  161. c = **s;
  162. if ( c < 'A' )
  163. break;
  164. if ( i > UGL_MAX_IDENTIFIER_LEN )
  165. break;
  166. buf[i++] = c;
  167. buf[i] = '\0';
  168. (*s)++;
  169. }
  170. skip_space(s);
  171. return buf;
  172. }
  173. /*======================================================*/
  174. /* code generator sub procedures */
  175. void ugl_bytecode_buildin_procedure(const char *name, int idx, int is_toplevel)
  176. {
  177. ugl_plog("BC %scall buildin '%s'", is_toplevel?"toplevel ":"", name);
  178. if ( is_toplevel == 0 )
  179. {
  180. ugl_AddBytecode(BC_CMD_CALL_BUILDIN | ((idx&0x0f00)>>4));
  181. ugl_AddBytecode(idx&0xff);
  182. }
  183. else
  184. {
  185. ugl_AddBytecode(BC_CMD_CALL_BUILDIN_POP_STACK | ((idx&0x0f00)>>4));
  186. ugl_AddBytecode(idx&0xff);
  187. }
  188. }
  189. void ugl_bytecode_call_procedure(const char *name, int is_toplevel, int arg_cnt)
  190. {
  191. uint16_t idx;
  192. ugl_plog("BC call %sbytecode '%s' with %d arg(s)", is_toplevel?"toplevel ":"", name, arg_cnt);
  193. idx = ugl_GetLabel(name);
  194. ugl_AddBytecode(BC_CMD_CALL_PROCEDURE | (arg_cnt<<4));
  195. ugl_AddBytecode(idx>>8);
  196. ugl_AddBytecode(idx&0x0ff);
  197. if ( is_toplevel != 0 )
  198. {
  199. ugl_AddBytecode(BC_CMD_POP_ARG_STACK ); // remove the return value from the stack
  200. }
  201. #ifdef NOTUSED
  202. if ( is_toplevel != 0 )
  203. {
  204. arg_cnt++; // one more element on the stack (return value) has to be removed, if this is called from top level
  205. }
  206. while( arg_cnt > 16 )
  207. {
  208. ugl_AddBytecode(BC_CMD_POP_ARG_STACK | 0x0f0);
  209. arg_cnt -= 16;
  210. }
  211. if ( arg_cnt > 0 )
  212. {
  213. arg_cnt--;
  214. ugl_AddBytecode(BC_CMD_POP_ARG_STACK | (arg_cnt<<4));
  215. }
  216. #endif
  217. }
  218. void ugl_bytecode_reserve_arg_stack(int arg_cnt)
  219. {
  220. while( arg_cnt > 16 )
  221. {
  222. ugl_AddBytecode(BC_CMD_PUSH_ARG_STACK | 0x0f0);
  223. arg_cnt -= 16;
  224. }
  225. if ( arg_cnt > 0 )
  226. {
  227. arg_cnt--;
  228. ugl_AddBytecode(BC_CMD_PUSH_ARG_STACK | (arg_cnt<<4));
  229. }
  230. }
  231. void ugl_bytecode_remove_arg_stack(int arg_cnt)
  232. {
  233. while( arg_cnt > 16 )
  234. {
  235. ugl_AddBytecode(BC_CMD_POP_ARG_STACK | 0x0f0);
  236. arg_cnt -= 16;
  237. }
  238. if ( arg_cnt > 0 )
  239. {
  240. arg_cnt--;
  241. ugl_AddBytecode(BC_CMD_POP_ARG_STACK | (arg_cnt<<4));
  242. }
  243. }
  244. void ugl_bytecode_constant_value(long num)
  245. {
  246. ugl_plog("BC constant value %ld", num);
  247. if ( num == 0 )
  248. {
  249. ugl_AddBytecode(BC_CMD_LOAD_0);
  250. }
  251. else if ( num == 1 )
  252. {
  253. ugl_AddBytecode(BC_CMD_LOAD_1);
  254. }
  255. else if ( num <= 0x0fff )
  256. {
  257. ugl_AddBytecode(BC_CMD_LOAD_12BIT | ((num&0x0f00)>>4));
  258. ugl_AddBytecode(num&0xff);
  259. }
  260. else
  261. {
  262. ugl_AddBytecode(BC_CMD_LOAD_16BIT );
  263. ugl_AddBytecode(num>>8);
  264. ugl_AddBytecode(num&0x0ff);
  265. }
  266. }
  267. void ugl_bytecode_return_from_procedure(void)
  268. {
  269. ugl_plog("BC return from procedure");
  270. ugl_AddBytecode(BC_CMD_RETURN_FROM_PROCEDURE );
  271. }
  272. void ugl_bytecode_branch(const char *s)
  273. {
  274. int idx;
  275. idx = ugl_GetLabel(s);
  276. ugl_plog("BC branch, label %s, idx=%d", s, idx);
  277. ugl_AddBytecode(BC_CMD_BRANCH | ((idx&0x0f00)>>4));
  278. ugl_AddBytecode(idx&0xff);
  279. }
  280. void ugl_bytecode_jmp_no_zero(const char *s)
  281. {
  282. int idx;
  283. idx = ugl_GetLabel(s);
  284. ugl_plog("BC jump on not zero, label idx=%d", idx);
  285. ugl_AddBytecode(BC_CMD_JUMP_NOT_ZERO );
  286. ugl_AddBytecode(idx>>8);
  287. ugl_AddBytecode(idx&0x0ff);
  288. }
  289. void ugl_bytecode_jmp_zero(const char *s)
  290. {
  291. int idx;
  292. idx = ugl_GetLabel(s);
  293. ugl_plog("BC jump on zero, label idx=%d", idx);
  294. ugl_AddBytecode(BC_CMD_JUMP_ZERO );
  295. ugl_AddBytecode(idx>>8);
  296. ugl_AddBytecode(idx&0x0ff);
  297. }
  298. /*======================================================*/
  299. int ugl_is_buildin_cmd(const char *name)
  300. {
  301. int i, cnt;
  302. cnt = sizeof(ugl_buildin_cmd_list)/sizeof(*ugl_buildin_cmd_list);
  303. for( i = 0; i < cnt; i++ )
  304. {
  305. if ( strcmp(ugl_buildin_cmd_list[i].name, name) == 0 )
  306. return 1;
  307. }
  308. return 0;
  309. }
  310. void ugl_call_proc(const char *name, int is_toplevel, int arg_cnt)
  311. {
  312. int i, cnt;
  313. int ii;
  314. cnt = sizeof(ugl_buildin_cmd_list)/sizeof(*ugl_buildin_cmd_list);
  315. ii = cnt;
  316. for( i = 0; i < cnt; i++ )
  317. {
  318. if ( strcmp(ugl_buildin_cmd_list[i].name, name) == 0 )
  319. ii = i;
  320. if ( strcmp(ugl_buildin_cmd_list[i].name, name) == 0 && ugl_buildin_cmd_list[i].args == arg_cnt)
  321. break;
  322. }
  323. if ( i < cnt )
  324. {
  325. ugl_bytecode_buildin_procedure(name, i, is_toplevel);
  326. }
  327. else
  328. {
  329. if ( ii != cnt )
  330. {
  331. ugl_err("Buildin procedure '%s' expects differnt number of args", name);
  332. }
  333. else
  334. {
  335. ugl_bytecode_call_procedure(name, is_toplevel, arg_cnt);
  336. }
  337. }
  338. }
  339. void ugl_parse_proc(const char **s, const char *id, int is_toplevel)
  340. {
  341. char procname[UGL_MAX_IDENTIFIER_LEN];
  342. int arg_cnt = 0;
  343. ugl_plog("parse procedure '%s'", id);
  344. strcpy(procname, id);
  345. if ( ugl_is_buildin_cmd(id) == 0 )
  346. {
  347. /* if this is a buildin cmd, then do nothing: buildin code takes care on the return value */
  348. /* for custom procedures push a value on the arg stack for the return value, but do this only if this is not a toplevel procedure */
  349. ugl_bytecode_constant_value(0); /* return value will be 0 by default */
  350. }
  351. if ( **s == '(' )
  352. {
  353. const char *name;
  354. for(;;)
  355. {
  356. (*s)++;
  357. skip_space(s);
  358. if ( **s == ')' )
  359. break;
  360. if ( (**s >= '0' && **s <= '9') || **s == '$' || **s == '\'' )
  361. {
  362. ugl_bytecode_constant_value(get_num(s));
  363. }
  364. else
  365. {
  366. name = get_identifier(s);
  367. ugl_parse_proc(s, name, 0);
  368. }
  369. arg_cnt++;
  370. if ( **s != ',' )
  371. break;
  372. }
  373. if ( **s != ')' )
  374. ugl_err("missing ')'");
  375. (*s)++;
  376. skip_space(s);
  377. ugl_call_proc(procname, is_toplevel, arg_cnt);
  378. }
  379. else
  380. {
  381. ugl_call_proc(procname, is_toplevel, 0);
  382. }
  383. }
  384. uint16_t uglStartNamelessProc(int args)
  385. {
  386. ugl_current_local_variables = 0;
  387. ugl_current_args = args;
  388. if ( ugl_indent_level != 0 )
  389. ugl_err("nested procedures not allowed");
  390. ugl_IncIndent(UGL_INDENT_TYPE_PROC);
  391. return ugl_bytecode_len;
  392. }
  393. int ugl_read_line(const char **s)
  394. {
  395. const char *id;
  396. skip_space(s);
  397. if ( **s == '\0' )
  398. return 1;
  399. id = get_identifier(s);
  400. if ( strcmp(id, "proc") == 0 )
  401. {
  402. const char *name = get_identifier(s);
  403. long args = get_num(s);
  404. ugl_plog("start procedure '%s' (args=%ld)", name, args);
  405. ugl_GetLabel(name); /* create a label for the procedure name */
  406. ugl_SetLabelBytecodePos(name, uglStartNamelessProc(args)); /* and set the label for it */
  407. }
  408. else if ( strcmp(id, "endproc") == 0 )
  409. {
  410. //ugl_bytecode_remove_arg_stack(ugl_current_local_variables);
  411. ugl_DecIndent(UGL_INDENT_TYPE_PROC);
  412. ugl_bytecode_return_from_procedure();
  413. ugl_current_local_variables = 0;
  414. ugl_current_args = 0;
  415. }
  416. else if ( strcmp(id, "locals") == 0 )
  417. {
  418. long n = get_num(s);
  419. if ( ugl_current_local_variables != 0 )
  420. ugl_err("only one 'locals' command allowed");
  421. if ( ugl_indent_level != 1 )
  422. ugl_err("'locals': only toplevel call allowed"); /* it must not be inside loops or ifs */
  423. ugl_current_local_variables = n;
  424. ugl_bytecode_reserve_arg_stack(ugl_current_local_variables);
  425. }
  426. else if ( strcmp(id, "if" ) == 0 )
  427. {
  428. ugl_parse_proc(s, get_identifier(s), 0);
  429. sprintf(ugl_indent_identifier[ugl_indent_level], ".if%d", ugl_bytecode_len); /* use the current bytecode position as unique identifier */
  430. ugl_bytecode_jmp_zero(ugl_indent_identifier[ugl_indent_level]);
  431. ugl_IncIndent(UGL_INDENT_TYPE_IF);
  432. }
  433. else if ( strcmp(id, "else" ) == 0 )
  434. {
  435. char s[UGL_MAX_IDENTIFIER_LEN+1];
  436. sprintf(s, ".else%d", ugl_bytecode_len); /* use the current bytecode position as unique identifier */
  437. ugl_bytecode_branch(s);
  438. ugl_SetLabelBytecodePos(ugl_indent_identifier[ugl_indent_level-1], ugl_bytecode_len);
  439. strcpy(ugl_indent_identifier[ugl_indent_level-1], s);
  440. }
  441. else if ( strcmp(id, "endif" ) == 0 )
  442. {
  443. ugl_DecIndent(UGL_INDENT_TYPE_IF);
  444. ugl_SetLabelBytecodePos(ugl_indent_identifier[ugl_indent_level], ugl_bytecode_len);
  445. }
  446. else
  447. {
  448. ugl_parse_proc(s, id, 1);
  449. }
  450. return 1;
  451. }
  452. /* returns 0 if "endproc" is found */
  453. int uglReadLine(const char **s)
  454. {
  455. ugl_read_line(s);
  456. if ( ugl_indent_level == 0 )
  457. return 0;
  458. return 1;
  459. }