CE Command.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. namespace EE{
  4. namespace Edit{
  5. /******************************************************************************/
  6. // WRITE COMMANDS
  7. /******************************************************************************/
  8. static Str TextTokens(Memc<Token*> &tokens, Int from, Int to)
  9. {
  10. Str text; TOKEN_TYPE last_type=TOKEN_NONE;
  11. for(Int i=from; i<=to; i++)
  12. {
  13. Token &token=*tokens[i];
  14. if(MustSeparate(last_type, token.type))text+=' ';
  15. text+=token;
  16. last_type=token.type;
  17. }
  18. return text;
  19. }
  20. /******************************************************************************
  21. static void ListCommands(FileText &ft, Memc<Command> &cmds, Memc<Token*> &tokens)
  22. {
  23. FREPA(cmds)
  24. {
  25. Str line;
  26. Command &cmd=cmds[i];
  27. switch( cmd.type)
  28. {
  29. case CMD_INSTRUCT : line=S+ TextTokens(tokens, cmd. raw_range.x, cmd. raw_range.y)+";"; break;
  30. case CMD_IF : line=S+"if(" +TextTokens(tokens, cmd.cond_range.x, cmd.cond_range.y)+")"; break;
  31. case CMD_FOR : line=S+"for(" +TextTokens(tokens, cmd.init_range.x, cmd.init_range.y)+"; "+TextTokens(tokens, cmd.cond_range.x, cmd.cond_range.y)+"; "+TextTokens(tokens, cmd.step_range.x, cmd.step_range.y)+")"; break;
  32. case CMD_WHILE : line=S+"while(" +TextTokens(tokens, cmd.cond_range.x, cmd.cond_range.y)+")"; break;
  33. case CMD_DO : line= "do" ; break;
  34. case CMD_SWITCH : line=S+"switch("+TextTokens(tokens, cmd. raw_range.x, cmd. raw_range.y)+")"; break;
  35. case CMD_RETURN : line=S+"return "+TextTokens(tokens, cmd. raw_range.x, cmd. raw_range.y)+";"; break;
  36. case CMD_BREAK : line=S+"break;" ; break;
  37. case CMD_CONTINUE : line=S+"continue;" ; break;
  38. case CMD_GOTO : line=S+"goto " +TextTokens(tokens, cmd. raw_range.x, cmd. raw_range.y)+";"; break;
  39. case CMD_GOTO_COND : line=S+"if(" +TextTokens(tokens, cmd.cond_range.x, cmd.cond_range.y)+")goto @"+cmd.label_index+";"; break;
  40. case CMD_GOTO_COND_N: line=S+"if(!" +TextTokens(tokens, cmd.cond_range.x, cmd.cond_range.y)+")goto @"+cmd.label_index+";"; break;
  41. case CMD_LABEL : line=S+"@" +cmd.label_index+":"; break;
  42. }
  43. if(line.is())ft.putLine(line);
  44. if(cmd.cmds.elms())
  45. {
  46. ft++;
  47. ListCommands(ft, cmd.cmds, tokens);
  48. ft--;
  49. }
  50. if(cmd.cmds_false.elms())
  51. {
  52. ft.putLine("else");
  53. ft++;
  54. ListCommands(ft, cmd.cmds_false, tokens);
  55. ft--;
  56. }
  57. if(cmd.type==CMD_DO)ft.putLine(S+"while("+TextTokens(tokens, cmd.cond_range.x, cmd.cond_range.y)+");");
  58. }
  59. }
  60. /******************************************************************************
  61. static void ListFunction(FileText &ft, Symbol &func, Memc<Command> &cmds)
  62. {
  63. if(func.source)
  64. {
  65. ft.putLine(SEP_LINE);
  66. ft.putLine(func.definition());
  67. ft++;
  68. ListCommands(ft, cmds, func.source->tokens);
  69. ft--;
  70. }
  71. }
  72. /******************************************************************************/
  73. // READ COMMANDS
  74. /******************************************************************************/
  75. static void ReadInstruction(Source &source, Int &token_index, Symbol &set_parent, Memc<Command> &cmds, Memc<Message> &msgs, CMD_TYPE type) // read until ; encountered (eat the ';'), if { or } then don't set its parents set error and return
  76. {
  77. Memc<Token*> &tokens=source.tokens;
  78. for(Int start=token_index; token_index<tokens.elms(); )
  79. {
  80. Token &token=*tokens[token_index];
  81. if((token.type==TOKEN_OPERATOR && (token=='{' || token=='}'))
  82. || (token.type==TOKEN_KEYWORD && (token=="if" || token=="for" || token=="while" || token=="do" || token=="return" || token=="break" || token=="continue" || token=="goto" || token=="else"))){msgs.New().error(S+"Unexpected '"+token+"'", &token); return;}
  83. token.parent=&set_parent; token_index++;
  84. if(token==';')
  85. {
  86. Int end=token_index-2;
  87. if((type==CMD_INSTRUCT) ? end>=start : true) // don't process empty instruction ";" when processing CMD_INSTRUCT
  88. {
  89. Command &cmd=cmds.New();
  90. cmd.type=type;
  91. cmd.raw_range.set(start, end);
  92. }
  93. return;
  94. }
  95. }
  96. }
  97. /******************************************************************************/
  98. static Bool ReadCommand(Source &source, Int &token_index, Symbol &set_parent, Memc<Command> &cmds, Memc<Message> &msgs)
  99. {
  100. Memc<Token*> &tokens=source.tokens;
  101. if(InRange(token_index, tokens))
  102. {
  103. Token &token=*tokens[token_index++]; token.parent=&set_parent;
  104. if(token.type==TOKEN_KEYWORD)
  105. {
  106. if(token=="if") // spaces will be: "if(cond_space)true_space;else false_space;", 'true_space' and 'false_space' are child of 'cond_space'
  107. {
  108. Command &cmd=cmds.New();
  109. cmd.type=CMD_IF;
  110. if(InRange(token_index, tokens)) // check for "(condition)" after 'if'
  111. {
  112. Token &token=*tokens[token_index];
  113. if(token!='(')msgs.New().error(S+"'if' should be followed by '(' : \"if(..)\", while encountered '"+token+"'", &token);else
  114. {
  115. token.parent=&set_parent; token_index++; // 'token_index' now points after '('
  116. Symbol *cond_space=source.createSpace(set_parent, token_index);
  117. cmd._for.cond_range.x=token_index;
  118. Bool valid=false;
  119. for(Int round_level=0, bracket_level=0, semicolons=0; token_index<tokens.elms(); )
  120. {
  121. Token &token=*tokens[token_index];
  122. if(token=='}')if(!bracket_level--){msgs.New().error(S+"Unexpected '}' encountered after \"if(..\"", &token); break;}
  123. if(token=='{') bracket_level++;
  124. if(token=='(') round_level++;
  125. if(token==')')if(! round_level--){if(semicolons==0){valid=true; cmd._for.cond_range.y=token_index-1;}else msgs.New().error("Unexpected ';' inside \"if(..)\"", &token); token.parent=&set_parent; token_index++; break;}
  126. if(token==';') semicolons++;
  127. token.parent=cond_space; token_index++;
  128. }
  129. if(valid && InRange(token_index, tokens)) // check for commands after "if(..)"
  130. {
  131. Token &token =*tokens[token_index];
  132. Symbol *true_space=source.createSpace(*cond_space, token_index);
  133. if(token=='}')msgs.New().error("No commands after \"if(..)\"", &token);else
  134. if(token=='{'){token.parent=true_space; token_index++; ReadCommands(source, token_index, *true_space, cmd.cmds, msgs);}else
  135. ReadCommand (source, token_index, *true_space, cmd.cmds, msgs);
  136. if(InRange(token_index, tokens)) // check for 'else'
  137. {
  138. Token &token=*tokens[token_index];
  139. if(token=="else")
  140. {
  141. token.parent=&set_parent; token_index++;
  142. if(InRange(token_index, tokens))
  143. {
  144. Token &token =*tokens[token_index];
  145. Symbol *false_space=source.createSpace(*cond_space, token_index);
  146. if(token=='}')msgs.New().error("No commands after \"if(..).. else\"", &token);else
  147. if(token=='{'){token.parent=false_space; token_index++; ReadCommands(source, token_index, *false_space, cmd.cmds_false, msgs);}else
  148. ReadCommand (source, token_index, *false_space, cmd.cmds_false, msgs);
  149. }
  150. }
  151. }
  152. }
  153. }
  154. }
  155. return true;
  156. }else
  157. if(token=="for") // spaces will be: "for(space; space; space_step)space;", 'space_step' is a child of 'space'
  158. {
  159. Command &cmd=cmds.New();
  160. cmd.type=CMD_FOR;
  161. if(InRange(token_index, tokens)) // check for "(init; condition; step)" after 'for'
  162. {
  163. Token &token=*tokens[token_index];
  164. if(token!='(')msgs.New().error(S+"'for' should be followed by '(' : \"for(..)\", while encountered '"+token+"'", &token);else
  165. {
  166. token.parent=&set_parent; token_index++; // 'token_index' now points after '('
  167. Symbol *space=source.createSpace(set_parent, token_index), *set_space=space;
  168. cmd._for.init_range.x=token_index;
  169. Bool valid=false;
  170. for(Int round_level=0, bracket_level=0, semicolons=0; token_index<tokens.elms(); )
  171. {
  172. Token &token=*tokens[token_index];
  173. if(token=='}')if(!bracket_level--){msgs.New().error(S+"Unexpected '}' encountered after \"for(..\"", &token); break;}
  174. if(token=='{') bracket_level++;
  175. if(token=='(') round_level++;
  176. if(token==')')if(! round_level--){if(semicolons==2){valid=true; cmd._for.step_range.y=token_index-1;}else msgs.New().error("'for' should be of following format: \"for(initialize; condition; step)\"", &token); token.parent=&set_parent; token_index++; break;}
  177. if(token==';')
  178. {
  179. if(semicolons==0){cmd._for.init_range.y=token_index-1; cmd._for.cond_range.x=token_index+1;}else
  180. if(semicolons==1){cmd._for.cond_range.y=token_index-1; cmd._for.step_range.x=token_index+1; set_space=source.createSpace(*set_space, token_index);} // create new space for the 'step'
  181. semicolons++;
  182. }
  183. token.parent=set_space; token_index++;
  184. }
  185. if(valid && InRange(token_index, tokens))
  186. {
  187. Token &token=*tokens[token_index];
  188. if(token=='}')msgs.New().error("No commands after \"for(..)\"", &token);else
  189. if(token=='{'){token.parent=space; token_index++; ReadCommands(source, token_index, *space, cmd.cmds, msgs);}else
  190. ReadCommand (source, token_index, *space, cmd.cmds, msgs);
  191. }
  192. }
  193. }
  194. return true;
  195. }else
  196. if(token=="while") // spaces will be: "while(space)space;"
  197. {
  198. Command &cmd=cmds.New();
  199. cmd.type=CMD_WHILE;
  200. if(InRange(token_index, tokens)) // check for "(condition)" after 'while'
  201. {
  202. Token &token=*tokens[token_index];
  203. if(token!='(')msgs.New().error(S+"'while' should be followed by '(' : \"while(..)\", while encountered '"+token+"'", &token);else
  204. {
  205. token.parent=&set_parent; token_index++; // 'token_index' now points after '('
  206. Symbol *cond_space=source.createSpace(set_parent, token_index);
  207. cmd._for.cond_range.x=token_index;
  208. Bool valid=false;
  209. for(Int round_level=0, bracket_level=0, semicolons=0; token_index<tokens.elms(); )
  210. {
  211. Token &token=*tokens[token_index];
  212. if(token=='}')if(!bracket_level--){msgs.New().error(S+"Unexpected '}' encountered after \"while(..\"", &token); break;}
  213. if(token=='{') bracket_level++;
  214. if(token=='(') round_level++;
  215. if(token==')')if(! round_level--){if(semicolons==0){valid=true; cmd._for.cond_range.y=token_index-1;}else msgs.New().error("Unexpected ';' inside \"while(..)\"", &token); token.parent=&set_parent; token_index++; break;}
  216. if(token==';') semicolons++;
  217. token.parent=cond_space; token_index++;
  218. }
  219. if(valid && InRange(token_index, tokens))
  220. {
  221. Token &token=*tokens[token_index];
  222. if(token=='}')msgs.New().error("No commands after \"while(..)\"", &token);else
  223. if(token=='{'){token.parent=cond_space; token_index++; ReadCommands(source, token_index, *cond_space, cmd.cmds, msgs);}else
  224. ReadCommand (source, token_index, *cond_space, cmd.cmds, msgs);
  225. }
  226. }
  227. }
  228. return true;
  229. }else
  230. if(token=="do") // spaces will be: "do cmds_space; while(cond_space);", 'cond_space' is NOT a child of 'cmds_space'
  231. {
  232. Command &cmd=cmds.New();
  233. cmd.type=CMD_DO;
  234. if(InRange(token_index, tokens)) // check for commands after 'do'
  235. {
  236. Token &token =*tokens[token_index];
  237. Symbol *cmds_space=source.createSpace(set_parent, token_index);
  238. if(token=='}')msgs.New().error("No commands after 'do'", &token);else
  239. if(token=='{'){token.parent=cmds_space; token_index++; ReadCommands(source, token_index, *cmds_space, cmd.cmds, msgs);}else
  240. ReadCommand (source, token_index, *cmds_space, cmd.cmds, msgs);
  241. if(InRange(token_index, tokens)) // check for 'while'
  242. {
  243. Token &token=*tokens[token_index];
  244. if(token!="while")msgs.New().error(S+"\"do ..\" should be followed by 'while' : \"do .. while\", while encountered '"+token+"'", &token);else
  245. {
  246. token.parent=&set_parent; token_index++;
  247. if(InRange(token_index, tokens)) // check for "(condition)" after 'while'
  248. {
  249. Token &token=*tokens[token_index];
  250. if(token!='(')msgs.New().error(S+"'while' should be followed by '(' : \"while(..)\", while encountered '"+token+"'", &token);else
  251. {
  252. token.parent=&set_parent; token_index++; // 'token_index' now points after '('
  253. Symbol *cond_space=source.createSpace(set_parent, token_index);
  254. cmd._for.cond_range.x=token_index;
  255. Bool valid=false;
  256. for(Int round_level=0, bracket_level=0, semicolons=0; token_index<tokens.elms(); )
  257. {
  258. Token &token=*tokens[token_index];
  259. if(token=='}')if(!bracket_level--){msgs.New().error(S+"Unexpected '}' encountered after \"while(..\"", &token); break;}
  260. if(token=='{') bracket_level++;
  261. if(token=='(') round_level++;
  262. if(token==')')if(! round_level--){if(semicolons==0){valid=true; cmd._for.cond_range.y=token_index-1;}else msgs.New().error("Unexpected ';' inside \"while(..)\"", &token); token.parent=&set_parent; token_index++; break;}
  263. if(token==';') semicolons++;
  264. token.parent=cond_space; token_index++;
  265. }
  266. if(valid && InRange(token_index, tokens)) // check for ';' after "while(..)"
  267. {
  268. Token &token=*tokens[token_index];
  269. if(token!=';')msgs.New().error(S+"\"do .. while(..)\" should be followed by ';', while encountered '"+token+"'", &token);
  270. else {token.parent=&set_parent; token_index++;}
  271. }
  272. }
  273. }
  274. }
  275. }
  276. }
  277. return true;
  278. }else
  279. if(token=="switch") // spaces will be: "switch(space)space;"
  280. {
  281. Command &cmd=cmds.New();
  282. cmd.type=CMD_SWITCH;
  283. if(InRange(token_index, tokens)) // check for "(expression)" after 'switch'
  284. {
  285. Token &token=*tokens[token_index];
  286. if(token!='(')msgs.New().error(S+"'switch' should be followed by '(' : \"switch(..)\", while encountered '"+token+"'", &token);else
  287. {
  288. token.parent=&set_parent; token_index++; // 'token_index' now points after '('
  289. Symbol *switch_space=source.createSpace(set_parent, token_index);
  290. cmd.raw_range.x=token_index;
  291. Bool valid=false;
  292. for(Int round_level=0, bracket_level=0, semicolons=0; token_index<tokens.elms(); )
  293. {
  294. Token &token=*tokens[token_index];
  295. if(token=='}')if(!bracket_level--){msgs.New().error(S+"Unexpected '}' encountered after \"switch(..\"", &token); break;}
  296. if(token=='{') bracket_level++;
  297. if(token=='(') round_level++;
  298. if(token==')')if(! round_level--){if(semicolons==0){valid=true; cmd.raw_range.y=token_index-1;}else msgs.New().error("Unexpected ';' inside \"switch(..)\"", &token); token.parent=&set_parent; token_index++; break;}
  299. if(token==';') semicolons++;
  300. token.parent=switch_space; token_index++;
  301. }
  302. if(valid && InRange(token_index, tokens))
  303. {
  304. Token &token=*tokens[token_index];
  305. if(token=='}')msgs.New().error("No commands after \"switch(..)\"", &token);else
  306. if(token=='{'){token.parent=switch_space; token_index++; ReadCommands(source, token_index, *switch_space, cmd.cmds, msgs);}else
  307. ReadCommand (source, token_index, *switch_space, cmd.cmds, msgs);
  308. }
  309. }
  310. }
  311. return true;
  312. }else
  313. if(token=="return")
  314. {
  315. ReadInstruction(source, token_index, set_parent, cmds, msgs, CMD_RETURN);
  316. return true;
  317. }else
  318. if(token=="break")
  319. {
  320. ReadInstruction(source, token_index, set_parent, cmds, msgs, CMD_BREAK);
  321. return true;
  322. }else
  323. if(token=="continue")
  324. {
  325. ReadInstruction(source, token_index, set_parent, cmds, msgs, CMD_CONTINUE);
  326. return true;
  327. }else
  328. if(token=="goto")
  329. {
  330. ReadInstruction(source, token_index, set_parent, cmds, msgs, CMD_GOTO);
  331. return true;
  332. }else
  333. if(token=="else")
  334. {
  335. msgs.New().error(S+"Unexpected 'else'", &token);
  336. return true;
  337. }
  338. }else
  339. if(token=='{')
  340. {
  341. Command &cmd=cmds.New();
  342. cmd.type=CMD_GROUP;
  343. cmd.raw_range.x=token_index; // skip '{', at this moment 'token_index' is after '{'
  344. token.parent=source.createSpace(set_parent, token_index-1); // set '{' parent to point to newly created sub space
  345. ReadCommands(source, token_index, *token.parent, cmd.cmds, msgs);
  346. cmd.raw_range.y=token_index-2; // skip '}', at this moment 'token_index' is after '}', so -2 is used to go back to '}' and later before '}'
  347. return true;
  348. }else
  349. if(token=='}')
  350. {
  351. return false; // false to stop processing this level
  352. }else
  353. if(token.type==TOKEN_CODE && InRange(token_index, tokens) && *tokens[token_index]==':') // X: label
  354. {
  355. Symbol *func=set_parent.func();
  356. token.symbol=source.symbols.New().require((func ? func->full_name+SEP : S)+'@'+token).set(func, Symbol::LABEL, token_index-1, &source);
  357. if(token.symbol->valid>1)msgs.New().error("Label redefinition", &source, token_index-1); // name used more than once
  358. else func->children.add(token.symbol()); // add to children list
  359. Command &cmd=cmds.New();
  360. cmd.type=CMD_LABEL;
  361. cmd.raw_range.set(token_index-1, token_index-1); // label:
  362. tokens[token_index++]->parent=&set_parent;
  363. return true;
  364. }
  365. ReadInstruction(source, --token_index, set_parent, cmds, msgs, CMD_INSTRUCT);
  366. return true;
  367. }
  368. return false;
  369. }
  370. /******************************************************************************/
  371. void ReadCommands(Source &source, Int &token_index, Symbol &set_parent, Memc<Command> &cmds, Memc<Message> &msgs)
  372. {
  373. for(; ReadCommand(source, token_index, set_parent, cmds, msgs); );
  374. }
  375. /******************************************************************************/
  376. Int Command::startTokenIndex()
  377. {
  378. switch(type)
  379. {
  380. case CMD_INSTRUCT : return raw_range.x;
  381. case CMD_GROUP : return raw_range.x-1;
  382. case CMD_IF : return _for.cond_range.x-2; // 'if' , '('
  383. case CMD_FOR : return _for.init_range.x-2; // 'for' , '('
  384. case CMD_WHILE : return _for.cond_range.x-2; // 'while' , '('
  385. case CMD_DO : return cmds.elms() ? cmds[0].startTokenIndex()-1 : _for.cond_range.x-3; // x ? 'do' : 'do', 'while', '('
  386. case CMD_SWITCH : return raw_range.x-2; // 'switch', '('
  387. case CMD_RETURN : return raw_range.x-1; // 'return'
  388. case CMD_BREAK : return raw_range.x-1; // 'break'
  389. case CMD_CONTINUE : return raw_range.x-1; // 'continue'
  390. case CMD_GOTO : return raw_range.x-1; // 'goto'
  391. case CMD_GOTO_COND : return _for.cond_range.x-2; // 'if' , '('
  392. case CMD_GOTO_COND_N: return _for.cond_range.x-2; // 'if' , '('
  393. case CMD_LABEL : return raw_range.x;
  394. }
  395. return -1;
  396. }
  397. /******************************************************************************/
  398. }}
  399. /******************************************************************************/