ncl.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728
  1. package nncl
  2. import "fmt"
  3. import "os"
  4. import "io"
  5. import "strings"
  6. const MAX_VAR_LENGTH=256
  7. type Ncl struct {
  8. }
  9. func (ncl *Ncl) eval(s string) {
  10. }
  11. /* Token type and control flow constants */
  12. const (
  13. TCMD = iota
  14. TWORD
  15. TPART
  16. TERROR
  17. )
  18. const (
  19. FERROR = iota
  20. FNORMAL
  21. FRETURN
  22. FBREAK
  23. FAGAIN
  24. )
  25. func isSpecial(c rune, quote bool) bool {
  26. return (c == '$' || (!quote && (c == '{' || c == '}' || c == ';' || c == '\r' ||
  27. c == '\n')) ||
  28. c == '[' || c == ']' || c == '"')
  29. }
  30. func isSpace(c rune) bool {
  31. return (c == ' ' || c == '\t')
  32. }
  33. func isEnd(c rune) bool {
  34. return (c == '\n' || c == '\r' || c == ';');
  35. }
  36. func scan(in RuneScanner, line, col *int) (rune, error) {
  37. r, _, err := in.ReadRune()
  38. if r == '\n' {
  39. *col = 0
  40. *rows = *rows + 1
  41. }
  42. *col = *col + 1
  43. return r, err
  44. }
  45. func next(in RuneScanner, token * string.Builder, quote *bool, line, col *int) (int, error) {
  46. var err error
  47. var c rune
  48. depth := 0
  49. var open rune
  50. var end rune
  51. for c, err = scan(in, line, col); err != nil ; c, err = scan(in, line, col) {
  52. if !*quote {
  53. /* Skip leading spaces if not quoted */
  54. for ; isSpace(c); c,err = scan(in, line, col) {
  55. if err != nil {
  56. return TERROR, err
  57. }
  58. }
  59. /* Terminate command if not quoted */
  60. if isEnd(c) {
  61. token.WriteRune(c)
  62. return TCMD, nil
  63. }
  64. }
  65. if (c == '$') { /* Variable token, must not start with a space or quote */
  66. c2, err := scan(in, line, col)
  67. if err != nil {
  68. return TERROR, err
  69. }
  70. if is_space(c2) {
  71. return TERROR, fmt.Errorf("Space not allowed after $.")
  72. }
  73. token.WriteRune(c)
  74. mode := *quote
  75. *quote = false
  76. r, err := next(in, token, quote);
  77. *quote = mode
  78. if r == TWORD && quote && err == nil {
  79. return TPART, nil
  80. }
  81. return r, err
  82. }
  83. if (c == '[' || (!*quote && c == '{')) {
  84. /* Interleaving pairs are not welcome, but it simplifies the code */
  85. open = c
  86. end = '}'
  87. if open == '[' {
  88. end = ']'
  89. }
  90. for c,_,err, depth = in.ReadRune(), 1 ; depth != 0; c,_,err = in.ReadRune() {
  91. if err != nil {
  92. return TERROR, error
  93. }
  94. if c == open {
  95. depth++
  96. } else if c == end {
  97. depth--
  98. }
  99. token.WriteRune(c)
  100. }
  101. } else if c == '"' {
  102. *quote = !*quote;
  103. if (*quote) {
  104. return TPART;
  105. }
  106. if (n < 2 || (!ncl_is_space(s[1]) && !ncl_is_end(s[1]))) {
  107. return TERROR;
  108. }
  109. *from = *to = s + 1;
  110. return TWORD;
  111. } else if (*s == ']' || *s == '}') {
  112. /* Unbalanced bracket or brace */
  113. return TERROR;
  114. } else {
  115. while (i < n && (*q || !ncl_is_space(s[i])) && !ncl_is_special(s[i], *q)) {
  116. i++;
  117. }
  118. }
  119. *to = s + i;
  120. if (i == n) {
  121. return TERROR;
  122. }
  123. if (*q) {
  124. return TPART;
  125. }
  126. return (ncl_is_space(s[i]) || ncl_is_end(s[i])) ? TWORD : TPART;
  127. }
  128. /* A helper parser struct and macro (requires C99) */
  129. struct ncl_parser {
  130. const char *from;
  131. const char *to;
  132. const char *start;
  133. const char *end;
  134. int q;
  135. int token;
  136. };
  137. #define ncl_each(s, len, skiperr) \
  138. for (struct ncl_parser p = {NULL, NULL, (s), (s) + (len), 0, TERROR}; \
  139. p.start < p.end && \
  140. (((p.token = ncl_next(p.start, p.end - p.start, &p.from, &p.to, \
  141. &p.q)) != TERROR) || \
  142. (skiperr)); \
  143. p.start = p.to)
  144. /* ------------------------------------------------------- */
  145. /* ------------------------------------------------------- */
  146. /* ------------------------------------------------------- */
  147. /* ------------------------------------------------------- */
  148. /* ------------------------------------------------------- */
  149. typedef char ncl_value_t;
  150. const char *ncl_string(ncl_value_t *v) { return v; }
  151. int ncl_int(ncl_value_t *v) { return atoi(v); }
  152. int ncl_length(ncl_value_t *v) { return v == NULL ? 0 : strlen(v); }
  153. void ncl_free(ncl_value_t *v) { free(v); }
  154. ncl_value_t *ncl_append_string(ncl_value_t *v, s string) {
  155. size_t n = ncl_length(v);
  156. v = realloc(v, n + len + 1);
  157. memset((char *)ncl_string(v) + n, 0, len + 1);
  158. strncpy((char *)ncl_string(v) + n, s, len);
  159. return v;
  160. }
  161. ncl_value_t *ncl_append(ncl_value_t *v, ncl_value_t *tail) {
  162. v = ncl_append_string(v, ncl_string(tail), ncl_length(tail));
  163. ncl_free(tail);
  164. return v;
  165. }
  166. ncl_value_t *ncl_alloc(s string) {
  167. return ncl_append_string(NULL, s, len);
  168. }
  169. ncl_value_t *ncl_dup(ncl_value_t *v) {
  170. return ncl_alloc(ncl_string(v), ncl_length(v));
  171. }
  172. ncl_value_t *ncl_list_alloc() { return ncl_alloc("", 0); }
  173. int ncl_list_length(ncl_value_t *v) {
  174. int count = 0;
  175. ncl_each(ncl_string(v), ncl_length(v) + 1, 0) {
  176. if (p.token == TWORD) {
  177. count++;
  178. }
  179. }
  180. return count;
  181. }
  182. void ncl_list_free(ncl_value_t *v) { free(v); }
  183. ncl_value_t *ncl_list_at(ncl_value_t *v, int index) {
  184. int i = 0;
  185. ncl_each(ncl_string(v), ncl_length(v) + 1, 0) {
  186. if (p.token == TWORD) {
  187. if (i == index) {
  188. if (p.from[0] == '{') {
  189. return ncl_alloc(p.from + 1, p.to - p.from - 2);
  190. }
  191. return ncl_alloc(p.from, p.to - p.from);
  192. }
  193. i++;
  194. }
  195. }
  196. return NULL;
  197. }
  198. ncl_value_t *ncl_list_append(ncl_value_t *v, ncl_value_t *tail) {
  199. if (ncl_length(v) > 0) {
  200. v = ncl_append(v, ncl_alloc(" ", 2));
  201. }
  202. if (ncl_length(tail) > 0) {
  203. int q = 0;
  204. const char *p;
  205. for (p = ncl_string(tail); *p; p++) {
  206. if (ncl_is_space(*p) || ncl_is_special(*p, 0)) {
  207. q = 1;
  208. break;
  209. }
  210. }
  211. if (q) {
  212. v = ncl_append(v, ncl_alloc("{", 1));
  213. }
  214. v = ncl_append(v, ncl_dup(tail));
  215. if (q) {
  216. v = ncl_append(v, ncl_alloc("}", 1));
  217. }
  218. } else {
  219. v = ncl_append(v, ncl_alloc("{}", 2));
  220. }
  221. return v;
  222. }
  223. /* ----------------------------- */
  224. /* ----------------------------- */
  225. /* ----------------------------- */
  226. /* ----------------------------- */
  227. typedef int (*ncl_cmd_fn_t)(struct ncl *, ncl_value_t *, void *);
  228. struct ncl_cmd {
  229. ncl_value_t *name;
  230. int arity;
  231. ncl_cmd_fn_t fn;
  232. void *arg;
  233. struct ncl_cmd *next;
  234. };
  235. struct ncl_var {
  236. ncl_value_t *name;
  237. ncl_value_t *value;
  238. struct ncl_var *next;
  239. };
  240. struct ncl_env {
  241. struct ncl_var *vars;
  242. struct ncl_env *parent;
  243. };
  244. static struct ncl_env *ncl_env_alloc(struct ncl_env *parent) {
  245. struct ncl_env *env = malloc(sizeof(*env));
  246. env->vars = NULL;
  247. env->parent = parent;
  248. return env;
  249. }
  250. static struct ncl_var *ncl_env_var(struct ncl_env *env, ncl_value_t *name) {
  251. struct ncl_var *var = malloc(sizeof(struct ncl_var));
  252. var->name = ncl_dup(name);
  253. var->next = env->vars;
  254. var->value = ncl_alloc("", 0);
  255. env->vars = var;
  256. return var;
  257. }
  258. static struct ncl_env *ncl_env_free(struct ncl_env *env) {
  259. struct ncl_env *parent = env->parent;
  260. while (env->vars) {
  261. struct ncl_var *var = env->vars;
  262. env->vars = env->vars->next;
  263. ncl_free(var->name);
  264. ncl_free(var->value);
  265. free(var);
  266. }
  267. free(env);
  268. return parent;
  269. }
  270. struct ncl {
  271. struct ncl_env *env;
  272. struct ncl_cmd *cmds;
  273. ncl_value_t *result;
  274. };
  275. ncl_value_t *ncl_var(struct ncl *ncl, ncl_value_t *name, ncl_value_t *v) {
  276. DBG("var(%s := %.*s)\n", ncl_string(name), ncl_length(v), ncl_string(v));
  277. struct ncl_var *var;
  278. for (var = ncl->env->vars; var != NULL; var = var->next) {
  279. if (strcmp(var->name, ncl_string(name)) == 0) {
  280. break;
  281. }
  282. }
  283. if (var == NULL) {
  284. var = ncl_env_var(ncl->env, name);
  285. }
  286. if (v != NULL) {
  287. ncl_free(var->value);
  288. var->value = ncl_dup(v);
  289. ncl_free(v);
  290. }
  291. return var->value;
  292. }
  293. int ncl_result(struct ncl *ncl, int flow, ncl_value_t *result) {
  294. DBG("ncl_result %.*s, flow=%d\n", ncl_length(result), ncl_string(result),
  295. flow);
  296. ncl_free(ncl->result);
  297. ncl->result = result;
  298. return flow;
  299. }
  300. int ncl_subst(struct ncl *ncl, s string) {
  301. DBG("subst(%.*s)\n", (int)len, s);
  302. if (len == 0) {
  303. return ncl_result(ncl, FNORMAL, ncl_alloc("", 0));
  304. }
  305. switch (s[0]) {
  306. case '{':
  307. if (len <= 1) {
  308. return ncl_result(ncl, FERROR, ncl_alloc("", 0));
  309. }
  310. return ncl_result(ncl, FNORMAL, ncl_alloc(s + 1, len - 2));
  311. case '$': {
  312. if (len >= MAX_VAR_LENGTH) {
  313. return ncl_result(ncl, FERROR, ncl_alloc("", 0));
  314. }
  315. char buf[5 + MAX_VAR_LENGTH] = "set ";
  316. strncat(buf, s + 1, len - 1);
  317. return ncl_eval(ncl, buf, strlen(buf) + 1);
  318. }
  319. case '[': {
  320. ncl_value_t *expr = ncl_alloc(s + 1, len - 2);
  321. int r = ncl_eval(ncl, ncl_string(expr), ncl_length(expr) + 1);
  322. ncl_free(expr);
  323. return r;
  324. }
  325. default:
  326. return ncl_result(ncl, FNORMAL, ncl_alloc(s, len));
  327. }
  328. }
  329. int ncl_eval(struct ncl *ncl, s string) {
  330. DBG("eval(%.*s)->\n", (int)len, s);
  331. ncl_value_t *list = ncl_list_alloc();
  332. ncl_value_t *cur = NULL;
  333. ncl_each(s, len, 1) {
  334. DBG("ncl_next %d %.*s\n", p.token, (int)(p.to - p.from), p.from);
  335. switch (p.token) {
  336. case TERROR:
  337. DBG("eval: FERROR, lexer error\n");
  338. return ncl_result(ncl, FERROR, ncl_alloc("", 0));
  339. case TWORD:
  340. DBG("token %.*s, length=%d, cur=%p (3.1.1)\n", (int)(p.to - p.from),
  341. p.from, (int)(p.to - p.from), cur);
  342. if (cur != NULL) {
  343. ncl_subst(ncl, p.from, p.to - p.from);
  344. ncl_value_t *part = ncl_dup(ncl->result);
  345. cur = ncl_append(cur, part);
  346. } else {
  347. ncl_subst(ncl, p.from, p.to - p.from);
  348. cur = ncl_dup(ncl->result);
  349. }
  350. list = ncl_list_append(list, cur);
  351. ncl_free(cur);
  352. cur = NULL;
  353. break;
  354. case TPART:
  355. ncl_subst(ncl, p.from, p.to - p.from);
  356. ncl_value_t *part = ncl_dup(ncl->result);
  357. cur = ncl_append(cur, part);
  358. break;
  359. case TCMD:
  360. if (ncl_list_length(list) == 0) {
  361. ncl_result(ncl, FNORMAL, ncl_alloc("", 0));
  362. } else {
  363. ncl_value_t *cmdname = ncl_list_at(list, 0);
  364. struct ncl_cmd *cmd = NULL;
  365. int r = FERROR;
  366. for (cmd = ncl->cmds; cmd != NULL; cmd = cmd->next) {
  367. if (strcmp(ncl_string(cmdname), ncl_string(cmd->name)) == 0) {
  368. if (cmd->arity == 0 || cmd->arity == ncl_list_length(list)) {
  369. r = cmd->fn(ncl, list, cmd->arg);
  370. break;
  371. }
  372. }
  373. }
  374. ncl_free(cmdname);
  375. if (cmd == NULL || r != FNORMAL) {
  376. ncl_list_free(list);
  377. return r;
  378. }
  379. }
  380. ncl_list_free(list);
  381. list = ncl_list_alloc();
  382. break;
  383. }
  384. }
  385. ncl_list_free(list);
  386. return FNORMAL;
  387. }
  388. /* --------------------------------- */
  389. /* --------------------------------- */
  390. /* --------------------------------- */
  391. /* --------------------------------- */
  392. /* --------------------------------- */
  393. void ncl_register(struct ncl *ncl, const char *name, ncl_cmd_fn_t fn, int arity,
  394. void *arg) {
  395. struct ncl_cmd *cmd = malloc(sizeof(struct ncl_cmd));
  396. cmd->name = ncl_alloc(name, strlen(name));
  397. cmd->fn = fn;
  398. cmd->arg = arg;
  399. cmd->arity = arity;
  400. cmd->next = ncl->cmds;
  401. ncl->cmds = cmd;
  402. }
  403. static int ncl_cmd_set(struct ncl *ncl, ncl_value_t *args, void *arg) {
  404. (void)arg;
  405. ncl_value_t *var = ncl_list_at(args, 1);
  406. ncl_value_t *val = ncl_list_at(args, 2);
  407. int r = ncl_result(ncl, FNORMAL, ncl_dup(ncl_var(ncl, var, val)));
  408. ncl_free(var);
  409. return r;
  410. }
  411. static int ncl_cmd_subst(struct ncl *ncl, ncl_value_t *args, void *arg) {
  412. (void)arg;
  413. ncl_value_t *s = ncl_list_at(args, 1);
  414. int r = ncl_subst(ncl, ncl_string(s), ncl_length(s));
  415. ncl_free(s);
  416. return r;
  417. }
  418. #ifndef TCL_DISABLE_PUTS
  419. static int ncl_cmd_puts(struct ncl *ncl, ncl_value_t *args, void *arg) {
  420. (void)arg;
  421. ncl_value_t *text = ncl_list_at(args, 1);
  422. puts(ncl_string(text));
  423. putchar('\n');
  424. return ncl_result(ncl, FNORMAL, text);
  425. }
  426. #endif
  427. static int ncl_user_proc(struct ncl *ncl, ncl_value_t *args, void *arg) {
  428. ncl_value_t *code = (ncl_value_t *)arg;
  429. ncl_value_t *params = ncl_list_at(code, 2);
  430. ncl_value_t *body = ncl_list_at(code, 3);
  431. ncl->env = ncl_env_alloc(ncl->env);
  432. for (int i = 0; i < ncl_list_length(params); i++) {
  433. ncl_value_t *param = ncl_list_at(params, i);
  434. ncl_value_t *v = ncl_list_at(args, i + 1);
  435. ncl_var(ncl, param, v);
  436. ncl_free(param);
  437. }
  438. ncl_eval(ncl, ncl_string(body), ncl_length(body) + 1);
  439. ncl->env = ncl_env_free(ncl->env);
  440. ncl_free(params);
  441. ncl_free(body);
  442. return FNORMAL;
  443. }
  444. static int ncl_cmd_proc(struct ncl *ncl, ncl_value_t *args, void *arg) {
  445. (void)arg;
  446. ncl_value_t *name = ncl_list_at(args, 1);
  447. ncl_register(ncl, ncl_string(name), ncl_user_proc, 0, ncl_dup(args));
  448. ncl_free(name);
  449. return ncl_result(ncl, FNORMAL, ncl_alloc("", 0));
  450. }
  451. static int ncl_cmd_if(struct ncl *ncl, ncl_value_t *args, void *arg) {
  452. (void)arg;
  453. int i = 1;
  454. int n = ncl_list_length(args);
  455. int r = FNORMAL;
  456. while (i < n) {
  457. ncl_value_t *cond = ncl_list_at(args, i);
  458. ncl_value_t *branch = NULL;
  459. if (i + 1 < n) {
  460. branch = ncl_list_at(args, i + 1);
  461. }
  462. r = ncl_eval(ncl, ncl_string(cond), ncl_length(cond) + 1);
  463. ncl_free(cond);
  464. if (r != FNORMAL) {
  465. ncl_free(branch);
  466. break;
  467. }
  468. if (ncl_int(ncl->result)) {
  469. r = ncl_eval(ncl, ncl_string(branch), ncl_length(branch) + 1);
  470. ncl_free(branch);
  471. break;
  472. }
  473. i = i + 2;
  474. ncl_free(branch);
  475. }
  476. return r;
  477. }
  478. static int ncl_cmd_flow(struct ncl *ncl, ncl_value_t *args, void *arg) {
  479. (void)arg;
  480. int r = FERROR;
  481. ncl_value_t *flowval = ncl_list_at(args, 0);
  482. const char *flow = ncl_string(flowval);
  483. if (strcmp(flow, "break") == 0) {
  484. r = FBREAK;
  485. } else if (strcmp(flow, "continue") == 0) {
  486. r = FAGAIN;
  487. } else if (strcmp(flow, "return") == 0) {
  488. r = ncl_result(ncl, FRETURN, ncl_list_at(args, 1));
  489. }
  490. ncl_free(flowval);
  491. return r;
  492. }
  493. static int ncl_cmd_while(struct ncl *ncl, ncl_value_t *args, void *arg) {
  494. (void)arg;
  495. ncl_value_t *cond = ncl_list_at(args, 1);
  496. ncl_value_t *loop = ncl_list_at(args, 2);
  497. int r;
  498. for (;;) {
  499. r = ncl_eval(ncl, ncl_string(cond), ncl_length(cond) + 1);
  500. if (r != FNORMAL) {
  501. ncl_free(cond);
  502. ncl_free(loop);
  503. return r;
  504. }
  505. if (!ncl_int(ncl->result)) {
  506. ncl_free(cond);
  507. ncl_free(loop);
  508. return FNORMAL;
  509. }
  510. int r = ncl_eval(ncl, ncl_string(loop), ncl_length(loop) + 1);
  511. switch (r) {
  512. case FBREAK:
  513. ncl_free(cond);
  514. ncl_free(loop);
  515. return FNORMAL;
  516. case FRETURN:
  517. ncl_free(cond);
  518. ncl_free(loop);
  519. return FRETURN;
  520. case FAGAIN:
  521. continue;
  522. case FERROR:
  523. ncl_free(cond);
  524. ncl_free(loop);
  525. return FERROR;
  526. }
  527. }
  528. }
  529. #ifndef TCL_DISABLE_MATH
  530. static int ncl_cmd_math(struct ncl *ncl, ncl_value_t *args, void *arg) {
  531. (void)arg;
  532. char buf[64];
  533. ncl_value_t *opval = ncl_list_at(args, 0);
  534. ncl_value_t *aval = ncl_list_at(args, 1);
  535. ncl_value_t *bval = ncl_list_at(args, 2);
  536. const char *op = ncl_string(opval);
  537. int a = ncl_int(aval);
  538. int b = ncl_int(bval);
  539. int c = 0;
  540. if (op[0] == '+') {
  541. c = a + b;
  542. } else if (op[0] == '-') {
  543. c = a - b;
  544. } else if (op[0] == '*') {
  545. c = a * b;
  546. } else if (op[0] == '/') {
  547. c = a / b;
  548. } else if (op[0] == '>' && op[1] == '\0') {
  549. c = a > b;
  550. } else if (op[0] == '>' && op[1] == '=') {
  551. c = a >= b;
  552. } else if (op[0] == '<' && op[1] == '\0') {
  553. c = a < b;
  554. } else if (op[0] == '<' && op[1] == '=') {
  555. c = a <= b;
  556. } else if (op[0] == '=' && op[1] == '=') {
  557. c = a == b;
  558. } else if (op[0] == '!' && op[1] == '=') {
  559. c = a != b;
  560. }
  561. char *p = buf + sizeof(buf) - 1;
  562. char neg = (c < 0);
  563. *p-- = 0;
  564. if (neg) {
  565. c = -c;
  566. }
  567. do {
  568. *p-- = '0' + (c % 10);
  569. c = c / 10;
  570. } while (c > 0);
  571. if (neg) {
  572. *p-- = '-';
  573. }
  574. p++;
  575. ncl_free(opval);
  576. ncl_free(aval);
  577. ncl_free(bval);
  578. return ncl_result(ncl, FNORMAL, ncl_alloc(p, strlen(p)));
  579. }
  580. #endif
  581. void ncl_init(struct ncl *ncl) {
  582. ncl->env = ncl_env_alloc(NULL);
  583. ncl->result = ncl_alloc("", 0);
  584. ncl->cmds = NULL;
  585. ncl_register(ncl, "set", ncl_cmd_set, 0, NULL);
  586. ncl_register(ncl, "subst", ncl_cmd_subst, 2, NULL);
  587. #ifndef TCL_DISABLE_PUTS
  588. ncl_register(ncl, "puts", ncl_cmd_puts, 2, NULL);
  589. #endif
  590. ncl_register(ncl, "proc", ncl_cmd_proc, 4, NULL);
  591. ncl_register(ncl, "if", ncl_cmd_if, 0, NULL);
  592. ncl_register(ncl, "while", ncl_cmd_while, 3, NULL);
  593. ncl_register(ncl, "return", ncl_cmd_flow, 0, NULL);
  594. ncl_register(ncl, "break", ncl_cmd_flow, 1, NULL);
  595. ncl_register(ncl, "continue", ncl_cmd_flow, 1, NULL);
  596. #ifndef TCL_DISABLE_MATH
  597. char *math[] = {"+", "-", "*", "/", ">", ">=", "<", "<=", "==", "!="};
  598. for (unsigned int i = 0; i < (sizeof(math) / sizeof(math[0])); i++) {
  599. ncl_register(ncl, math[i], ncl_cmd_math, 3, NULL);
  600. }
  601. #endif
  602. }
  603. void ncl_destroy(struct ncl *ncl) {
  604. while (ncl->env) {
  605. ncl->env = ncl_env_free(ncl->env);
  606. }
  607. while (ncl->cmds) {
  608. struct ncl_cmd *cmd = ncl->cmds;
  609. ncl->cmds = ncl->cmds->next;
  610. ncl_free(cmd->name);
  611. free(cmd->arg);
  612. free(cmd);
  613. }
  614. ncl_free(ncl->result);
  615. }
  616. #ifndef TEST
  617. #define CHUNK 1024
  618. int main() {
  619. struct ncl ncl;
  620. int buflen = CHUNK;
  621. char *buf = malloc(buflen);
  622. int i = 0;
  623. ncl_init(&ncl);
  624. while (1) {
  625. int inp = fgetc(stdin);
  626. if (i > buflen - 1) {
  627. buf = realloc(buf, buflen += CHUNK);
  628. }
  629. if (inp == 0 || inp == EOF) {
  630. break;
  631. }
  632. buf[i++] = inp;
  633. ncl_each(buf, i, 1) {
  634. if (p.token == TERROR && (p.to - buf) != i) {
  635. memset(buf, 0, buflen);
  636. i = 0;
  637. break;
  638. } else if (p.token == TCMD && *(p.from) != '\0') {
  639. int r = ncl_eval(&ncl, buf, strlen(buf));
  640. if (r != FERROR) {
  641. printf("result> %.*s\n", ncl_length(ncl.result),
  642. ncl_string(ncl.result));
  643. } else {
  644. printf("?!\n");
  645. }
  646. memset(buf, 0, buflen);
  647. i = 0;
  648. break;
  649. }
  650. }
  651. }
  652. free(buf);
  653. if (i) {
  654. printf("incomplete input\n");
  655. return -1;
  656. }
  657. return 0;
  658. }
  659. #endif