An embeddable, thread-safe implementation of the cubescript language
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

810 lines
27 KiB

4 years ago
3 months ago
5 years ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
5 years ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
  1. #include <cubescript/cubescript.hh>
  2. #include "cs_vm.hh"
  3. #include "cs_std.hh"
  4. #include "cs_parser.hh"
  5. #include "cs_error.hh"
  6. #include <cstdio>
  7. #include <cmath>
  8. #include <limits>
  9. namespace cubescript {
  10. static inline void push_alias(thread_state &ts, ident &id, ident_stack &st) {
  11. if (id.type() != ident_type::ALIAS) {
  12. return;
  13. }
  14. if (!static_cast<alias &>(id).is_arg()) {
  15. auto *aimp = static_cast<alias_impl *>(&id);
  16. auto ast = ts.get_astack(aimp);
  17. ast.push(st);
  18. ast.flags &= ~IDENT_FLAG_UNKNOWN;
  19. }
  20. }
  21. static inline void pop_alias(thread_state &ts, ident &id) {
  22. if (id.type() != ident_type::ALIAS) {
  23. return;
  24. }
  25. if (!static_cast<alias &>(id).is_arg()) {
  26. ts.get_astack(static_cast<alias *>(&id)).pop();
  27. }
  28. }
  29. void exec_command(
  30. thread_state &ts, command_impl *id, ident *self, any_value *args,
  31. any_value &res, std::size_t nargs, bool lookup
  32. ) {
  33. int i = -1, fakeargs = 0, numargs = int(nargs);
  34. bool rep = false;
  35. auto fmt = id->args();
  36. auto set_fake = [](
  37. int &idx, int &fargs, bool r, int argn, any_value *argp
  38. ) {
  39. if (++idx >= argn) {
  40. if (r) {
  41. return false;
  42. }
  43. argp[idx].set_none();
  44. ++fargs;
  45. return false;
  46. }
  47. return true;
  48. };
  49. for (auto it = fmt.begin(); it != fmt.end(); ++it) {
  50. switch (*it) {
  51. case 'i':
  52. if (set_fake(i, fakeargs, rep, numargs, args)) {
  53. args[i].force_integer();
  54. }
  55. break;
  56. case 'f':
  57. if (set_fake(i, fakeargs, rep, numargs, args)) {
  58. args[i].force_float();
  59. }
  60. break;
  61. case 's':
  62. if (set_fake(i, fakeargs, rep, numargs, args)) {
  63. args[i].force_string(*ts.pstate);
  64. }
  65. break;
  66. case 'a':
  67. set_fake(i, fakeargs, rep, numargs, args);
  68. break;
  69. case 'c':
  70. if (set_fake(i, fakeargs, rep, numargs, args)) {
  71. if (args[i].type() == value_type::STRING) {
  72. auto str = args[i].get_string(*ts.pstate);
  73. if (str.empty()) {
  74. args[i].set_integer(0);
  75. } else {
  76. args[i].force_code(*ts.pstate);
  77. }
  78. }
  79. }
  80. break;
  81. case 'b':
  82. if (set_fake(i, fakeargs, rep, numargs, args)) {
  83. args[i].force_code(*ts.pstate);
  84. }
  85. break;
  86. case 'v':
  87. if (set_fake(i, fakeargs, rep, numargs, args)) {
  88. args[i].force_ident(*ts.pstate);
  89. }
  90. break;
  91. case '$':
  92. i += 1;
  93. args[i].set_ident(*self);
  94. break;
  95. case '#':
  96. i += 1;
  97. args[i].set_integer(integer_type(lookup ? -1 : i - fakeargs));
  98. break;
  99. case '.':
  100. i = std::max(i + 1, numargs);
  101. id->call(ts, span_type<any_value>{args, std::size_t(i)}, res);
  102. return;
  103. case '1':
  104. case '2':
  105. case '3':
  106. case '4':
  107. if (i + 1 < numargs) {
  108. it -= *it - '0' + 1;
  109. rep = true;
  110. }
  111. break;
  112. }
  113. }
  114. ++i;
  115. id->call(ts, span_type<any_value>{args, std::size_t(i)}, res);
  116. res.force_plain();
  117. }
  118. any_value exec_alias(
  119. thread_state &ts, alias *a, any_value *args,
  120. std::size_t callargs, alias_stack &astack
  121. ) {
  122. /* excess arguments get ignored (make error maybe?) */
  123. any_value ret;
  124. callargs = std::min(callargs, MAX_ARGUMENTS);
  125. builtin_var *anargs = ts.istate->ivar_numargs;
  126. argset uargs{};
  127. std::size_t noff = ts.idstack.size();
  128. for(std::size_t i = 0; i < callargs; i++) {
  129. auto &ast = ts.get_astack(
  130. static_cast<alias *>(ts.istate->identmap[i])
  131. );
  132. auto &st = ts.idstack.emplace_back();
  133. ast.push(st);
  134. st.val_s = std::move(args[i]);
  135. uargs[i] = true;
  136. }
  137. auto oldargs = anargs->value();
  138. auto oldflags = ts.ident_flags;
  139. ts.ident_flags = astack.flags;
  140. any_value cv;
  141. cv.set_integer(integer_type(callargs));
  142. anargs->set_raw_value(*ts.pstate, std::move(cv));
  143. auto &lev = ts.callstack.emplace_back(*a);
  144. lev.usedargs = std::move(uargs);
  145. if (!astack.node->code) {
  146. try {
  147. gen_state gs{ts};
  148. gs.gen_main(astack.node->val_s.get_string(*ts.pstate));
  149. astack.node->code = gs.steal_ref();
  150. } catch (...) {
  151. ts.callstack.pop_back();
  152. throw;
  153. }
  154. }
  155. bcode_ref coderef = astack.node->code;
  156. auto cleanup = [](
  157. auto &tss, std::size_t cargs, std::size_t nids, auto oflags
  158. ) {
  159. auto amask = tss.callstack.back().usedargs;
  160. tss.callstack.pop_back();
  161. tss.ident_flags = oflags;
  162. for (std::size_t i = 0; i < cargs; i++) {
  163. tss.get_astack(
  164. static_cast<alias *>(tss.istate->identmap[i])
  165. ).pop();
  166. amask[i] = false;
  167. }
  168. for (; amask.any(); ++cargs) {
  169. if (amask[cargs]) {
  170. tss.get_astack(
  171. static_cast<alias *>(tss.istate->identmap[cargs])
  172. ).pop();
  173. amask[cargs] = false;
  174. }
  175. }
  176. tss.idstack.resize(nids);
  177. };
  178. try {
  179. vm_exec(ts, bcode_p{coderef}.get()->raw(), ret);
  180. } catch (...) {
  181. cleanup(ts, callargs, noff, oldflags);
  182. anargs->set_raw_value(*ts.pstate, std::move(oldargs));
  183. throw;
  184. }
  185. cleanup(ts, callargs, noff, oldflags);
  186. anargs->set_raw_value(*ts.pstate, std::move(oldargs));
  187. return ret;
  188. }
  189. any_value exec_code_with_args(thread_state &ts, bcode_ref const &body) {
  190. if (ts.callstack.empty()) {
  191. return body.call(*ts.pstate);
  192. }
  193. auto mask = ts.callstack.back().usedargs;
  194. std::size_t noff = ts.idstack.size();
  195. for (std::size_t i = 0; mask.any(); ++i) {
  196. if (mask[0]) {
  197. auto &ast = ts.get_astack(
  198. static_cast<alias *>(ts.istate->identmap[i])
  199. );
  200. auto &st = ts.idstack.emplace_back();
  201. st.next = ast.node;
  202. ast.node = ast.node->next;
  203. }
  204. mask >>= 1;
  205. }
  206. ident_level *prevstack = nullptr;
  207. if (ts.callstack.size() >= 2) {
  208. prevstack = &ts.callstack[ts.callstack.size() - 2];
  209. }
  210. auto &lev = ts.callstack.emplace_back(ts.callstack.back().id);
  211. if (!prevstack) {
  212. lev.usedargs.set();
  213. } else {
  214. lev.usedargs = prevstack->usedargs;
  215. }
  216. auto cleanup = [](auto &tss, ident_level *pstack, std::size_t offn) {
  217. auto mask2 = tss.callstack.back().usedargs;
  218. if (pstack) {
  219. pstack->usedargs = mask2;
  220. }
  221. tss.callstack.pop_back();
  222. for (std::size_t i = 0, nredo = 0; mask2.any(); ++i) {
  223. if (mask2[0]) {
  224. tss.get_astack(
  225. static_cast<alias *>(tss.istate->identmap[i])
  226. ).node = tss.idstack[offn + nredo++].next;
  227. }
  228. mask2 >>= 1;
  229. }
  230. };
  231. any_value ret;
  232. try {
  233. ret = body.call(*ts.pstate);
  234. } catch (...) {
  235. cleanup(ts, prevstack, noff);
  236. ts.idstack.resize(noff);
  237. throw;
  238. }
  239. cleanup(ts, prevstack, noff);
  240. ts.idstack.resize(noff);
  241. return ret;
  242. }
  243. struct vm_guard {
  244. vm_guard(thread_state &s): ts{s}, oldtop{s.vmstack.size()} {
  245. if (s.max_call_depth && (s.call_depth >= s.max_call_depth)) {
  246. throw error{*s.pstate, "exceeded recursion limit"};
  247. }
  248. ++s.call_depth;
  249. }
  250. ~vm_guard() {
  251. --ts.call_depth;
  252. ts.vmstack.resize(oldtop);
  253. }
  254. thread_state &ts;
  255. std::size_t oldtop;
  256. };
  257. std::uint32_t *vm_exec(
  258. thread_state &ts, std::uint32_t *code, any_value &result
  259. ) {
  260. result.set_none();
  261. auto &cs = *ts.pstate;
  262. vm_guard scope{ts}; /* keep track of recursion depth + manage stack */
  263. auto &args = ts.vmstack;
  264. auto &chook = cs.call_hook();
  265. if (chook) {
  266. chook(cs);
  267. }
  268. auto force_val = [](state &s, any_value &v, int opn) {
  269. switch (opn & BC_INST_RET_MASK) {
  270. case BC_RET_STRING:
  271. v.force_string(s);
  272. break;
  273. case BC_RET_INT:
  274. v.force_integer();
  275. break;
  276. case BC_RET_FLOAT:
  277. v.force_float();
  278. break;
  279. }
  280. };
  281. for (;;) {
  282. std::uint32_t op = *code++;
  283. switch (op & BC_INST_OP_MASK) {
  284. case BC_INST_START:
  285. case BC_INST_OFFSET:
  286. continue;
  287. case BC_INST_NULL:
  288. result.set_none();
  289. goto use_result;
  290. case BC_INST_FALSE:
  291. result.set_integer(0);
  292. goto use_result;
  293. case BC_INST_TRUE:
  294. result.set_integer(1);
  295. goto use_result;
  296. case BC_INST_NOT:
  297. result.set_integer(!args.back().get_bool());
  298. args.pop_back();
  299. goto use_result;
  300. case BC_INST_POP:
  301. args.pop_back();
  302. continue;
  303. case BC_INST_ENTER:
  304. code = vm_exec(ts, code, args.emplace_back());
  305. continue;
  306. case BC_INST_ENTER_RESULT:
  307. code = vm_exec(ts, code, result);
  308. continue;
  309. case BC_INST_EXIT:
  310. goto use_exit;
  311. case BC_INST_RESULT:
  312. result = std::move(args.back());
  313. args.pop_back();
  314. goto use_result;
  315. case BC_INST_RESULT_ARG:
  316. args.emplace_back(std::move(result));
  317. goto use_top;
  318. case BC_INST_FORCE:
  319. goto use_top;
  320. case BC_INST_DUP: {
  321. auto &v = args.back();
  322. args.emplace_back() = v;
  323. goto use_top;
  324. }
  325. case BC_INST_VAL:
  326. switch (op & BC_INST_RET_MASK) {
  327. case BC_RET_STRING: {
  328. auto len = op >> 8;
  329. char const *str;
  330. std::memcpy(&str, &code, sizeof(str));
  331. std::string_view sv{str, len};
  332. args.emplace_back().set_string(sv, cs);
  333. code += len / sizeof(std::uint32_t) + 1;
  334. continue;
  335. }
  336. case BC_RET_INT: {
  337. integer_type i;
  338. std::memcpy(&i, code, sizeof(i));
  339. args.emplace_back().set_integer(i);
  340. code += bc_store_size<integer_type>;
  341. continue;
  342. }
  343. case BC_RET_FLOAT: {
  344. float_type f;
  345. std::memcpy(&f, code, sizeof(f));
  346. args.emplace_back().set_float(f);
  347. code += bc_store_size<float_type>;
  348. continue;
  349. }
  350. default:
  351. break;
  352. }
  353. args.emplace_back().set_none();
  354. continue;
  355. case BC_INST_VAL_INT:
  356. switch (op & BC_INST_RET_MASK) {
  357. case BC_RET_STRING: {
  358. char s[4] = {
  359. char((op >> 8) & 0xFF),
  360. char((op >> 16) & 0xFF),
  361. char((op >> 24) & 0xFF), '\0'
  362. };
  363. /* gotta cast or r.size() == potentially 3 */
  364. args.emplace_back().set_string(s, cs);
  365. continue;
  366. }
  367. case BC_RET_INT:
  368. args.emplace_back().set_integer(integer_type(op) >> 8);
  369. continue;
  370. case BC_RET_FLOAT:
  371. args.emplace_back().set_float(
  372. float_type(integer_type(op) >> 8)
  373. );
  374. continue;
  375. default:
  376. break;
  377. }
  378. args.emplace_back().set_none();
  379. continue;
  380. case BC_INST_LOCAL: {
  381. std::size_t numlocals = op >> 8;
  382. std::size_t offset = args.size() - numlocals;
  383. std::size_t idstsz = ts.idstack.size();
  384. for (std::size_t i = 0; i < numlocals; ++i) {
  385. push_alias(
  386. ts, args[offset + i].get_ident(cs),
  387. ts.idstack.emplace_back()
  388. );
  389. }
  390. auto cleanup = [](
  391. auto &css, std::size_t off, std::size_t isz, auto &av
  392. ) {
  393. for (std::size_t i = off; i < av.size(); ++i) {
  394. pop_alias(state_p{css}.ts(), av[i].get_ident(css));
  395. }
  396. state_p{css}.ts().idstack.resize(isz);
  397. };
  398. try {
  399. code = vm_exec(ts, code, result);
  400. } catch (...) {
  401. cleanup(cs, offset, idstsz, args);
  402. throw;
  403. }
  404. cleanup(cs, offset, idstsz, args);
  405. return code;
  406. }
  407. case BC_INST_DO_ARGS: {
  408. auto v = std::move(args.back());
  409. args.pop_back();
  410. result = exec_code_with_args(ts, v.get_code());
  411. goto use_result;
  412. }
  413. case BC_INST_DO: {
  414. auto v = std::move(args.back());
  415. args.pop_back();
  416. result = v.get_code().call(cs);
  417. goto use_result;
  418. }
  419. case BC_INST_JUMP: {
  420. std::uint32_t len = op >> 8;
  421. code += len;
  422. continue;
  423. }
  424. case BC_INST_JUMP_B: {
  425. std::uint32_t len = op >> 8;
  426. /* BC_INST_FLAG_TRUE/FALSE */
  427. if (args.back().get_bool() == !!(op & BC_INST_RET_MASK)) {
  428. code += len;
  429. }
  430. args.pop_back();
  431. continue;
  432. }
  433. case BC_INST_JUMP_RESULT: {
  434. std::uint32_t len = op >> 8;
  435. auto v = std::move(args.back());
  436. args.pop_back();
  437. if (v.type() == value_type::CODE) {
  438. result = v.get_code().call(cs);
  439. } else {
  440. result = std::move(v);
  441. }
  442. /* BC_INST_FLAG_TRUE/FALSE */
  443. if (result.get_bool() == !!(op & BC_INST_RET_MASK)) {
  444. code += len;
  445. }
  446. continue;
  447. }
  448. case BC_INST_BREAK:
  449. if (ts.loop_level) {
  450. if (op & BC_INST_RET_MASK) {
  451. throw continue_exception{};
  452. } else {
  453. throw break_exception{};
  454. }
  455. } else {
  456. if (op & BC_INST_RET_MASK) {
  457. throw error{cs, "no loop to continue"};
  458. } else {
  459. throw error{cs, "no loop to break"};
  460. }
  461. }
  462. break;
  463. case BC_INST_BLOCK: {
  464. std::uint32_t len = op >> 8;
  465. bcode *b;
  466. code += 1;
  467. std::memcpy(&b, &code, sizeof(b));
  468. args.emplace_back().set_code(bcode_p::make_ref(b));
  469. code += len - 1;
  470. continue;
  471. }
  472. case BC_INST_EMPTY:
  473. args.emplace_back().set_code(bcode_p::make_ref(
  474. bcode_get_empty(ts.istate->empty, op & BC_INST_RET_MASK)
  475. ));
  476. break;
  477. case BC_INST_COMPILE: {
  478. any_value &arg = args.back();
  479. gen_state gs{ts};
  480. switch (arg.type()) {
  481. case value_type::INTEGER:
  482. gs.gen_main_integer(arg.get_integer());
  483. break;
  484. case value_type::FLOAT:
  485. gs.gen_main_float(arg.get_float());
  486. break;
  487. case value_type::STRING:
  488. gs.gen_main(arg.get_string(cs));
  489. break;
  490. default:
  491. gs.gen_main_null();
  492. break;
  493. }
  494. arg.set_code(gs.steal_ref());
  495. continue;
  496. }
  497. case BC_INST_COND: {
  498. any_value &arg = args.back();
  499. switch (arg.type()) {
  500. case value_type::STRING: {
  501. std::string_view s = arg.get_string(cs);
  502. if (!s.empty()) {
  503. gen_state gs{ts};
  504. gs.gen_main(s);
  505. arg.set_code(gs.steal_ref());
  506. } else {
  507. arg.force_none();
  508. }
  509. break;
  510. }
  511. default:
  512. break;
  513. }
  514. continue;
  515. }
  516. case BC_INST_IDENT: {
  517. alias *a = static_cast<alias *>(
  518. ts.istate->identmap[op >> 8]
  519. );
  520. if (a->is_arg() && !ident_is_used_arg(a, ts)) {
  521. ts.get_astack(a).push(ts.idstack.emplace_back());
  522. ts.callstack.back().usedargs[a->index()] = true;
  523. }
  524. args.emplace_back().set_ident(*a);
  525. continue;
  526. }
  527. case BC_INST_IDENT_U: {
  528. any_value &arg = args.back();
  529. ident *id = ts.istate->id_dummy;
  530. if (arg.type() == value_type::STRING) {
  531. id = &ts.istate->new_ident(
  532. cs, arg.get_string(cs), IDENT_FLAG_UNKNOWN
  533. );
  534. }
  535. alias *a = static_cast<alias *>(id);
  536. if (a->is_arg() && !ident_is_used_arg(id, ts)) {
  537. ts.get_astack(a).push(ts.idstack.emplace_back());
  538. ts.callstack.back().usedargs[id->index()] = true;
  539. }
  540. arg.set_ident(*id);
  541. continue;
  542. }
  543. case BC_INST_LOOKUP_U:
  544. args.back() = cs.lookup_value(args.back().get_string(cs));
  545. goto use_top;
  546. case BC_INST_LOOKUP: {
  547. ident *id = ts.istate->identmap[op >> 8];
  548. if (static_cast<alias *>(id)->is_arg()) {
  549. auto &v = args.emplace_back();
  550. if (ident_is_used_arg(id, ts)) {
  551. v = ts.get_astack(static_cast<alias *>(id)).node->val_s;
  552. }
  553. goto use_top;
  554. }
  555. auto &ast = ts.get_astack(static_cast<alias *>(id));
  556. if (ast.flags & IDENT_FLAG_UNKNOWN) {
  557. throw error_p::make(
  558. *ts.pstate, "unknown alias lookup: %s",
  559. id->name().data()
  560. );
  561. }
  562. args.emplace_back() = ast.node->val_s;
  563. goto use_top;
  564. }
  565. case BC_INST_CONC:
  566. case BC_INST_CONC_W: {
  567. std::size_t numconc = op >> 8;
  568. auto buf = concat_values(
  569. cs, span_type<any_value>{
  570. &args[args.size() - numconc], numconc
  571. }, ((op & BC_INST_OP_MASK) == BC_INST_CONC) ? " " : ""
  572. );
  573. args.resize(args.size() - numconc);
  574. args.emplace_back().set_string(buf);
  575. goto use_top;
  576. }
  577. case BC_INST_VAR:
  578. args.emplace_back() = static_cast<builtin_var *>(
  579. ts.istate->identmap[op >> 8]
  580. )->value();
  581. goto use_top;
  582. case BC_INST_ALIAS: {
  583. auto *a = static_cast<alias *>(
  584. ts.istate->identmap[op >> 8]
  585. );
  586. auto &ast = ts.get_astack(a);
  587. if (a->is_arg()) {
  588. ast.set_arg(a, ts, args.back());
  589. } else {
  590. ast.set_alias(a, ts, args.back());
  591. }
  592. args.pop_back();
  593. continue;
  594. }
  595. case BC_INST_ALIAS_U: {
  596. auto v = std::move(args.back());
  597. args.pop_back();
  598. cs.assign_value(args.back().get_string(cs), std::move(v));
  599. args.pop_back();
  600. continue;
  601. }
  602. case BC_INST_CALL: {
  603. result.force_none();
  604. ident *id = ts.istate->identmap[op >> 8];
  605. std::size_t callargs = *code++;
  606. std::size_t offset = args.size() - callargs;
  607. auto *imp = static_cast<alias_impl *>(id);
  608. if (imp->is_arg()) {
  609. if (!ident_is_used_arg(id, ts)) {
  610. args.resize(offset);
  611. goto use_result;
  612. }
  613. }
  614. auto &ast = ts.get_astack(imp);
  615. if (ast.flags & IDENT_FLAG_UNKNOWN) {
  616. throw error_p::make(
  617. cs, "unknown command: %s", id->name().data()
  618. );
  619. }
  620. result = exec_alias(ts, imp, &args[offset], callargs, ast);
  621. args.resize(offset);
  622. goto use_result;
  623. }
  624. case BC_INST_CALL_U: {
  625. std::size_t callargs = op >> 8;
  626. std::size_t offset = args.size() - callargs;
  627. any_value &idarg = args[offset - 1];
  628. if (idarg.type() != value_type::STRING) {
  629. litval:
  630. result = std::move(idarg);
  631. args.resize(offset - 1);
  632. goto use_result;
  633. }
  634. auto idn = idarg.get_string(cs);
  635. auto id = cs.get_ident(idn);
  636. if (!id) {
  637. noid:
  638. if (!is_valid_name(idn)) {
  639. goto litval;
  640. }
  641. std::string_view ids{idn};
  642. throw error_p::make(
  643. cs, "unknown command: %s", ids.data()
  644. );
  645. }
  646. result.force_none();
  647. switch (ident_p{id->get()}.impl().p_type) {
  648. default:
  649. if (!ident_is_callable(&id->get())) {
  650. args.resize(offset - 1);
  651. goto use_result;
  652. }
  653. /* fallthrough */
  654. case ID_COMMAND: {
  655. auto *cimp = static_cast<command_impl *>(&id->get());
  656. args.resize(offset + std::max(
  657. std::size_t(cimp->arg_count()), callargs
  658. ));
  659. exec_command(
  660. ts, cimp, cimp, &args[offset], result, callargs
  661. );
  662. args.resize(offset - 1);
  663. goto use_result;
  664. }
  665. case ID_LOCAL: {
  666. std::size_t idstsz = ts.idstack.size();
  667. for (std::size_t j = 0; j < callargs; ++j) {
  668. push_alias(
  669. ts, args[offset + j].force_ident(cs),
  670. ts.idstack.emplace_back()
  671. );
  672. }
  673. try {
  674. code = vm_exec(ts, code, result);
  675. } catch (...) {
  676. for (std::size_t j = 0; j < callargs; ++j) {
  677. pop_alias(ts, args[offset + j].get_ident(cs));
  678. }
  679. ts.idstack.resize(idstsz);
  680. throw;
  681. }
  682. for (std::size_t j = 0; j < callargs; ++j) {
  683. pop_alias(ts, args[offset + j].get_ident(cs));
  684. }
  685. ts.idstack.resize(idstsz);
  686. return code;
  687. }
  688. case ID_VAR: {
  689. auto *hid = static_cast<var_impl &>(
  690. id->get()
  691. ).get_setter(ts);
  692. auto *cimp = static_cast<command_impl *>(hid);
  693. /* the $ argument */
  694. args.insert(offset, any_value{});
  695. args.resize(offset + std::max(
  696. std::size_t(cimp->arg_count()), callargs
  697. ));
  698. exec_command(
  699. ts, cimp, &id->get(), &args[offset],
  700. result, callargs
  701. );
  702. args.resize(offset - 1);
  703. goto use_result;
  704. }
  705. case ID_ALIAS: {
  706. alias *a = static_cast<alias *>(&id->get());
  707. if (a->is_arg() && !ident_is_used_arg(a, ts)) {
  708. args.resize(offset - 1);
  709. goto use_result;
  710. }
  711. auto &ast = ts.get_astack(a);
  712. if (ast.node->val_s.type() == value_type::NONE) {
  713. goto noid;
  714. }
  715. result = exec_alias(
  716. ts, a, &args[offset], callargs, ast
  717. );
  718. args.resize(offset - 1);
  719. goto use_result;
  720. }
  721. }
  722. }
  723. case BC_INST_COM: {
  724. command_impl *id = static_cast<command_impl *>(
  725. ts.istate->identmap[op >> 8]
  726. );
  727. std::size_t offset = args.size() - id->arg_count();
  728. result.force_none();
  729. id->call(ts, span_type<any_value>{
  730. &args[offset], std::size_t(id->arg_count())
  731. }, result);
  732. args.resize(offset);
  733. goto use_result;
  734. }
  735. case BC_INST_COM_V: {
  736. command_impl *id = static_cast<command_impl *>(
  737. ts.istate->identmap[op >> 8]
  738. );
  739. std::size_t callargs = *code++;
  740. std::size_t offset = args.size() - callargs;
  741. result.force_none();
  742. id->call(
  743. ts, span_type<any_value>{&args[offset], callargs}, result
  744. );
  745. args.resize(offset);
  746. goto use_result;
  747. }
  748. }
  749. continue;
  750. use_result:
  751. force_val(cs, result, op);
  752. continue;
  753. use_top:
  754. force_val(cs, args.back(), op);
  755. continue;
  756. use_exit:
  757. force_val(cs, result, op);
  758. break;
  759. }
  760. return code;
  761. }
  762. } /* namespace cubescript */