starfish.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828
  1. /**
  2. * The code box class
  3. */
  4. class CodeBox {
  5. constructor(codeBoxID, initialStackID, outputID) {
  6. /**
  7. * Possible vectors the pointer can move in
  8. * @type {Object}
  9. */
  10. this.directions = {
  11. NORTH: [ 0, -1],
  12. EAST: [ 1, 0],
  13. SOUTH: [ 0, 1],
  14. WEST: [-1, 0],
  15. };
  16. /**
  17. * The current vector of the pointer
  18. * @type {int[]}
  19. */
  20. this.curr_direction = this.directions.EAST;
  21. /**
  22. * The Set of instructions to execute
  23. *
  24. * Either a 1 or 2-dimensional array
  25. * @type {Array|Array[]}
  26. */
  27. this.box = [];
  28. /**
  29. * The farthest right the box goes
  30. * @type {int}
  31. */
  32. this.maxBoxWidth = 0;
  33. /**
  34. * The bottom of the box
  35. *
  36. * @type {int}
  37. */
  38. this.maxBoxHeight = 0;
  39. /**
  40. * The coordinates of the currently executing instruction inside the code box
  41. * @type {Object}
  42. */
  43. this.pointer = {
  44. X: 0,
  45. Y: 0,
  46. };
  47. /**
  48. * Was the instruction last moving in the left direction
  49. *
  50. * Used by the {@link Fisherman}
  51. * @type {boolean}
  52. */
  53. this.dirWasLeft = false;
  54. /**
  55. * Are we currently under the influence of the {@link Fisherman}
  56. * @type {boolean}
  57. */
  58. this.onTheHook = false;
  59. /**
  60. * Are we currently processing code box instructions as a string
  61. *
  62. * 0 when false, otherwise it holds the char code for string delimiter, either
  63. * 34 or 39
  64. *
  65. * @type {int}
  66. */
  67. this.stringMode = 0;
  68. /**
  69. * A list of stacks for the script to work with
  70. *
  71. * @type {Stack[]}
  72. */
  73. this.stacks = [new Stack()];
  74. /**
  75. * The index of the currently used stack
  76. *
  77. * @type {int}
  78. */
  79. this.curr_stack = 0;
  80. /**
  81. * The current date
  82. *
  83. * This value is updated every tick
  84. * @type {?Date}
  85. */
  86. this.datetime = null;
  87. this.codeBoxDOM = document.getElementById(codeBoxID);
  88. if(!this.codeBoxDOM) {
  89. throw new Error(`Failed to find textarea with ID: ${codeBoxID}`);
  90. }
  91. this.outputDOM = document.getElementById(outputID);
  92. if(!this.outputDOM) {
  93. throw new Error(`Failed to find textarea with ID: ${outputID}`);
  94. }
  95. this.initialStackDOM = document.getElementById(initialStackID);
  96. if(!this.initialStackDOM) {
  97. throw new Error(`Failed to find input with ID: ${initialStackID}`);
  98. }
  99. }
  100. /**
  101. * Parse the initial code box
  102. *
  103. * Transforms the textual code box into usable matrix
  104. */
  105. ParseCodeBox() {
  106. // Reset some fields for a clean run
  107. this.box = [];
  108. this.stacks = [new Stack()];
  109. this.curr_stack = 0;
  110. this.pointer = {X: 0, Y: 0};
  111. this.curr_direction = this.directions.EAST;
  112. this.outputDOM.value = "";
  113. const cbRaw = this.codeBoxDOM.value;
  114. const rows = cbRaw.split("\n");
  115. let maxRowLength = 0;
  116. for(const row of rows) {
  117. const rowSplit = row.split("");
  118. // Store this for later processing
  119. while(rowSplit.length > maxRowLength) {
  120. maxRowLength = rowSplit.length
  121. }
  122. this.box.push(rowSplit);
  123. }
  124. this.EqualizeBoxWidth(maxRowLength);
  125. this.maxBoxWidth = maxRowLength - 1;
  126. this.maxBoxHeight = this.box.length - 1;
  127. let fin = null;
  128. try {
  129. while(!fin) {
  130. fin = this.Swim();
  131. }
  132. }
  133. catch(e) {
  134. console.error(e);
  135. }
  136. }
  137. /**
  138. * Make all the rows in the code box the same length
  139. *
  140. * All rows not long enough will have NOPs added until they're uniform in size.
  141. *
  142. * @param {int} [rowLength] The longest row in the code box
  143. */
  144. EqualizeBoxWidth(rowLength = null) {
  145. if(!rowLength) {
  146. for(const row of this.box) {
  147. if(row.length > rowLength) {
  148. rowLength = row.length;
  149. }
  150. }
  151. }
  152. for(const row of this.box) {
  153. while(row.length < rowLength) {
  154. row.push(" ");
  155. }
  156. }
  157. }
  158. /**
  159. * Print the value to the display
  160. *
  161. * @TODO Set up an actual display
  162. * @param {*} value
  163. */
  164. Output(value) {
  165. this.outputDOM.value += value;
  166. }
  167. Execute(instruction) {
  168. let output = null;
  169. try{
  170. switch(instruction) {
  171. // NOP
  172. case " ":
  173. break;
  174. // Numbers
  175. case "1":
  176. case "2":
  177. case "3":
  178. case "4":
  179. case "5":
  180. case "6":
  181. case "7":
  182. case "8":
  183. case "9":
  184. case "0":
  185. case "a":
  186. case "b":
  187. case "c":
  188. case "d":
  189. case "e":
  190. case "f":
  191. this.stacks[this.curr_stack].Push(parseInt(instruction, 16));
  192. break;
  193. // Operators
  194. case "+": {
  195. const x = this.stacks[this.curr_stack].Pop();
  196. const y = this.stacks[this.curr_stack].Pop();
  197. this.stacks[this.curr_stack].Push(y + x);
  198. break;
  199. }
  200. case "-": {
  201. const x = this.stacks[this.curr_stack].Pop();
  202. const y = this.stacks[this.curr_stack].Pop();
  203. this.stacks[this.curr_stack].Push(y - x);
  204. break;
  205. }
  206. case "*": {
  207. const x = this.stacks[this.curr_stack].Pop();
  208. const y = this.stacks[this.curr_stack].Pop();
  209. this.stacks[this.curr_stack].Push(y * x);
  210. break;
  211. }
  212. case ",": {
  213. const x = this.stacks[this.curr_stack].Pop();
  214. const y = this.stacks[this.curr_stack].Pop();
  215. this.stacks[this.curr_stack].Push(y / x);
  216. break;
  217. }
  218. case "%": {
  219. const x = this.stacks[this.curr_stack].Pop();
  220. const y = this.stacks[this.curr_stack].Pop();
  221. this.stacks[this.curr_stack].Push(y % x);
  222. break;
  223. }
  224. case "(": {
  225. const x = this.stacks[this.curr_stack].Pop();
  226. const y = this.stacks[this.curr_stack].Pop();
  227. this.stacks[this.curr_stack].Push(y < x ? 1 : 0);
  228. break;
  229. }
  230. case ")": {
  231. const x = this.stacks[this.curr_stack].Pop();
  232. const y = this.stacks[this.curr_stack].Pop();
  233. this.stacks[this.curr_stack].Push(y > x ? 1 : 0);
  234. break;
  235. }
  236. case "=": {
  237. const x = this.stacks[this.curr_stack].Pop();
  238. const y = this.stacks[this.curr_stack].Pop();
  239. this.stacks[this.curr_stack].push(y == x ? 1 : 0);
  240. break;
  241. }
  242. //String mode
  243. case "\"":
  244. case "'":
  245. this.stringMode = !!this.stringMode ? 0 : dec(instruction);
  246. break;
  247. // Movement
  248. case "^":
  249. this.MoveUp();
  250. break;
  251. case ">":
  252. this.MoveRight();
  253. break;
  254. case "v":
  255. this.MoveDown();
  256. break;
  257. case "<":
  258. this.MoveLeft();
  259. break;
  260. // Mirrors
  261. case "/":
  262. this.ReflectForward();
  263. break;
  264. case "\\":
  265. this.ReflectBack();
  266. break;
  267. case "_":
  268. this.VerticalMirror();
  269. break;
  270. case "|":
  271. this.HorizontalMirror();
  272. break;
  273. case "#":
  274. this.OmniMirror();
  275. break;
  276. // Trampolines
  277. case "!":
  278. this.Move();
  279. break;
  280. case "?":
  281. if(this.stacks[this.curr_stack].Pop() === 0){ this.Move(); }
  282. break;
  283. // Stack manipulation
  284. case "&": {
  285. if (this.stacks[this.curr_stack].register == null) {
  286. this.stacks[this.curr_stack].register = this.stacks[this.curr_stack].Pop();
  287. }
  288. else {
  289. this.stacks[this.curr_stack].Push(this.stacks[this.curr_stack].register);
  290. this.stacks[this.curr_stack].register = null;
  291. }
  292. }
  293. case ":":
  294. this.stacks[this.curr_stack].Duplicate();
  295. break;
  296. case "~":
  297. this.stacks[this.curr_stack].Remove();
  298. break;
  299. case "$":
  300. this.stacks[this.curr_stack].SwapTwo();
  301. break;
  302. case "@":
  303. this.stacks[this.curr_stack].SwapThree();
  304. break;
  305. case "{":
  306. this.stacks[this.curr_stack].ShiftLeft();
  307. break;
  308. case "}":
  309. this.stacks[this.curr_stack].ShiftRight();
  310. break;
  311. case "r":
  312. this.stacks[this.curr_stack].Reverse();
  313. break;
  314. case "l":
  315. this.stacks[this.curr_stack].PushLength();
  316. break;
  317. case "[": {
  318. this.SpliceStack(this.stacks[this.curr_stack].Pop());
  319. break;
  320. }
  321. case "]":
  322. this.CollapseStack();
  323. break;
  324. case "I": {
  325. this.curr_stack++;
  326. if (this.curr_stack >= this.stacks.length) {
  327. throw new RangeError("curr_stack value out of bounds");
  328. }
  329. break;
  330. }
  331. case "D": {
  332. this.curr_stack--;
  333. if (this.curr_stack < 0) {
  334. throw new RangeError("curr_stack value out of bounds");
  335. }
  336. break;
  337. }
  338. // Output
  339. case "n":
  340. output = this.stacks[this.curr_stack].Pop();
  341. break;
  342. case "o":
  343. output = String.fromCharCode(this.stacks[this.curr_stack].Pop());
  344. break;
  345. // Time
  346. case "S":
  347. // TODO
  348. break;
  349. case "h":
  350. this.stacks[this.curr_stack].Push(this.datetime.getUTCHours());
  351. break;
  352. case "m":
  353. this.stacks[this.curr_stack].Push(this.datetime.getUTCMinutes());
  354. break;
  355. case "s":
  356. this.stacks[this.curr_stack].Push(this.datetime.getUTCSeconds());
  357. break;
  358. // Code box manipulation
  359. case "g":
  360. this.PushFromCodeBox();
  361. break;
  362. case "p":
  363. this.PlaceIntoCodeBox();
  364. break;
  365. // End execution
  366. case ";":
  367. output = true;
  368. break;
  369. default:
  370. throw new Error(`Unknown instruction: ${instruction}`);
  371. }
  372. }
  373. catch(e) {
  374. console.error(`Something smells fishy!\n${e != "" ? `${e}\n` : ""}Instruction: ${instruction}\nStack: ${JSON.stringify(this.stacks[this.curr_stack].stack)}`);
  375. return true;
  376. }
  377. return output;
  378. }
  379. Swim() {
  380. const instruction = this.box[this.pointer.Y][this.pointer.X];
  381. this.datetime = new Date();
  382. if(this.stringMode != 0 && dec(instruction) != this.stringMode) {
  383. this.stacks[this.curr_stack].Push(dec(instruction));
  384. }
  385. else {
  386. const exeResult = this.Execute(instruction);
  387. if(exeResult === true) {
  388. return true;
  389. }
  390. else if(exeResult != null) {
  391. this.Output(exeResult);
  392. }
  393. }
  394. this.Move();
  395. }
  396. Move() {
  397. let newX = this.pointer.X + this.curr_direction[0];
  398. let newY = this.pointer.Y + this.curr_direction[1];
  399. // Keep the X coord in the boxes bounds
  400. if(newX < 0) {
  401. newX = this.maxBoxWidth;
  402. }
  403. else if(newX > this.maxBoxWidth) {
  404. newX = 0;
  405. }
  406. // Keep the Y coord in the boxes bounds
  407. if(newY < 0) {
  408. newY = this.maxBoxHeight;
  409. }
  410. else if(newY > this.maxBoxHeight) {
  411. newY = 0;
  412. }
  413. this.SetPointer(newX, newY);
  414. }
  415. /**
  416. * Implement C and .
  417. */
  418. SetPointer(x, y) {
  419. this.pointer = {X: x, Y: y};
  420. }
  421. /**
  422. * Implement ^
  423. *
  424. * Changes the swim direction upward
  425. */
  426. MoveUp() {
  427. this.curr_direction = this.directions.NORTH;
  428. }
  429. /**
  430. * Implement >
  431. *
  432. * Changes the swim direction rightward
  433. */
  434. MoveRight() {
  435. this.curr_direction = this.directions.EAST;
  436. this.dirWasLeft = false;
  437. }
  438. /**
  439. * Implement v
  440. *
  441. * Changes the swim direction downward
  442. */
  443. MoveDown() {
  444. this.curr_direction = this.directions.SOUTH;
  445. }
  446. /**
  447. * Implement <
  448. *
  449. * Changes the swim direction leftward
  450. */
  451. MoveLeft() {
  452. this.curr_direction = this.directions.WEST;
  453. this.dirWasLeft = true;
  454. }
  455. /**
  456. * Implement /
  457. *
  458. * Reflects the swim direction depending on its starting value
  459. */
  460. ReflectForward() {
  461. if (this.curr_direction == this.directions.NORTH) {
  462. this.MoveRight();
  463. }
  464. else if (this.curr_direction == this.directions.EAST) {
  465. this.MoveUp();
  466. }
  467. else if (this.curr_direction == this.directions.SOUTH) {
  468. this.MoveLeft();
  469. }
  470. else {
  471. this.MoveDown();
  472. }
  473. }
  474. /**
  475. * Implement \
  476. *
  477. * Reflects the swim direction depending on its starting value
  478. */
  479. ReflectBack() {
  480. if (this.curr_direction == this.directions.NORTH) {
  481. this.MoveLeft();
  482. }
  483. else if (this.curr_direction == this.directions.EAST) {
  484. this.MoveDown();
  485. }
  486. else if (this.curr_direction == this.directions.SOUTH) {
  487. this.MoveRight();
  488. }
  489. else {
  490. this.MoveUp();
  491. }
  492. }
  493. /**
  494. * Implement |
  495. *
  496. * Swaps the horizontal swim direction to its opposite
  497. */
  498. HorizontalMirror() {
  499. if (this.curr_direction == this.directions.EAST) {
  500. this.MoveLeft();
  501. }
  502. else {
  503. this.MoveRight();
  504. }
  505. }
  506. /**
  507. * Implement _
  508. *
  509. * Swaps the horizontal swim direction to its opposite
  510. */
  511. VerticalMirror() {
  512. if (this.curr_direction == this.directions.NORTH) {
  513. this.MoveDown();
  514. }
  515. else {
  516. this.MoveUp();
  517. }
  518. }
  519. /**
  520. * Implement #
  521. *
  522. * A combination of the vertical and the horizontal mirror
  523. */
  524. OmniMirror() {
  525. if (this.curr_direction[0]) {
  526. this.VerticalMirror();
  527. }
  528. else {
  529. this.HorizontalMirror();
  530. }
  531. }
  532. /**
  533. * Implement x
  534. *
  535. * Pseudo-randomly switches the swim direction
  536. */
  537. ShuffleDirection() {
  538. this.curr_direction = Object.values(this.directions)[Math.floor(Math.random() * 4)];
  539. }
  540. /**
  541. * Implement [
  542. *
  543. * Takes X number of elements out of a stack and into a new stack
  544. *
  545. * This action creates a new stack, and places it on top of the one it was created from.
  546. * So, if you have three stacks, A, B, and C, and you splice a stack off of stack B,
  547. * the new order will be: A, B, D, and C.
  548. *
  549. * @see {@link https://esolangs.org/wiki/Fish#Stacks ><> Documentation}
  550. *
  551. * @param {int} spliceCount The number of elements to pop into a new stack
  552. */
  553. SpliceStack(spliceCount) {
  554. const stackCount = this.stacks[this.curr_stack].stack.length;
  555. if (spliceCount > stackCount) {
  556. throw new RangeError(`Cannot remove ${spliceCount} elements from a stack of only ${stackCount} elements`);
  557. }
  558. const newStack = new Stack(this.stacks[this.curr_stack].stack.splice(stackCount - spliceCount, spliceCount));
  559. // We're at the top of the stacks stack, so we can use .push
  560. if (this.curr_stack == this.stacks.length - 1) {
  561. this.stacks.push(newStack);
  562. }
  563. else {
  564. this.stacks.splice(this.curr_stack + 1, 0, newStack);
  565. }
  566. this.curr_stack++;
  567. }
  568. /**
  569. * Implement ]
  570. *
  571. * Collapses the current stack onto the one below it
  572. * If the current stack is the only one, it is replaced with a blank stack
  573. */
  574. CollapseStack() {
  575. // Undefined behavior collapsing the first stack down when there are other stacks available
  576. if (this.curr_stack == 0 && this.stacks.length != 1) {
  577. throw new Error();
  578. }
  579. if (this.curr_stack == 0) {
  580. this.stacks = [new Stack()];
  581. }
  582. else {
  583. const collapsed = this.stacks.splice(this.curr_stack, 1).pop();
  584. this.curr_stack--;
  585. const currStackCount = this.stacks[this.curr_stack].stack.length;
  586. this.stacks[this.curr_stack].stack.splice(currStackCount, 0, ...collapsed.stack);
  587. }
  588. }
  589. /**
  590. * Implement g
  591. *
  592. * Pops `y` and `x` from the stack, and then pushes the value of the character
  593. * at `[x, y]` in the code box.
  594. *
  595. * NOP's and coords that are out of bounds are converted to 0.
  596. *
  597. * Implements the behavior as defined by the original {@link https://gist.github.com/anonymous/6392418#file-fish-py-L306 ><>}, and not {@link https://github.com/redstarcoder/go-starfish/blob/master/starfish/starfish.go#L378 go-starfish}
  598. */
  599. PushFromCodeBox() {
  600. const y = this.stacks[this.curr_stack].Pop();
  601. const x = this.stacks[this.curr_stack].Pop();
  602. let val = undefined;
  603. try {
  604. val = this.box[y][x] || " ";
  605. }
  606. catch (e) {
  607. val = " ";
  608. }
  609. const valParsed = val == " " ? 0 : dec(val);
  610. this.stacks[this.curr_stack].Push(valParsed);
  611. }
  612. /**
  613. * Implement p
  614. *
  615. * Pops `y`, `x`, and `v` off of the stack, and then places the string
  616. * representation of that value at `[x, y]` in the code box.
  617. */
  618. PlaceIntoCodeBox() {
  619. const y = this.stacks[this.curr_stack].Pop();
  620. const x = this.stacks[this.curr_stack].Pop();
  621. const v = this.stacks[this.curr_stack].Pop();
  622. while(y >= this.box.length) {
  623. this.box.push([]);
  624. }
  625. while(x >= this.box[y].length) {
  626. this.box[y].push(" ");
  627. }
  628. this.EqualizeBoxWidth();
  629. this.box[y][x] = String.fromCharCode(v);
  630. }
  631. /**
  632. * Implement `
  633. *
  634. * Changes the swim direction based on the previous direction
  635. * @see https://esolangs.org/wiki/Starfish#Fisherman
  636. */
  637. Fisherman() {
  638. if (this.curr_direction[0]) {
  639. if (this.dirWasLeft) {
  640. this.MoveLeft();
  641. }
  642. else {
  643. this.MoveRight();
  644. }
  645. }
  646. else {
  647. if (this.onTheHook) {
  648. this.onTheHook = false;
  649. this.MoveUp();
  650. }
  651. else {
  652. this.onTheHook = true;
  653. this.MoveDown();
  654. }
  655. }
  656. }
  657. }
  658. /**
  659. * The stack class
  660. */
  661. class Stack {
  662. /**
  663. * @param {int[]} stackValues An array of values to initialize the stack with
  664. */
  665. constructor(stackValues = []) {
  666. /**
  667. * The stack
  668. * @type {int[]}
  669. */
  670. this.stack = stackValues;
  671. /**
  672. * A single value saved off the stack
  673. * @type {int}
  674. */
  675. this.register = null;
  676. }
  677. /**
  678. * Wrapper function for Array.prototype.push
  679. * @param {*} newValue
  680. */
  681. Push(newValue) {
  682. this.stack.push(newValue);
  683. }
  684. /**
  685. * Wrapper function for Array.prototype.pop
  686. * @returns {*}
  687. */
  688. Pop() {
  689. const value = this.stack.pop();
  690. if(value == undefined){ throw new Error(); }
  691. return value;
  692. }
  693. /**
  694. * Implement }
  695. *
  696. * Shifts the entire stack leftward by one value
  697. */
  698. ShiftLeft() {
  699. const temp = this.stack.shift();
  700. this.stack.push(temp);
  701. }
  702. /**
  703. * Implement {
  704. *
  705. * Shifts the entire stack rightward by one value
  706. */
  707. ShiftRight() {
  708. const temp = this.stack.pop();
  709. this.stack.unshift(temp);
  710. }
  711. /**
  712. * Implement $
  713. *
  714. * Swaps the top two values of the stack
  715. */
  716. SwapTwo() {
  717. if(this.stack.length < 2) { throw new Error(); }
  718. const popped = this.stack.splice(this.stack.length - 2, 2);
  719. this.stack.push(...popped.reverse());
  720. }
  721. /**
  722. * Implement @
  723. *
  724. * Swaps the top three values of the stack
  725. */
  726. SwapThree() {
  727. if(this.stack.length < 3) { throw new Error(); }
  728. // Get the top three values
  729. const popped = this.stack.splice(this.stack.length - 3, 3);
  730. // Shift the elements to the right
  731. popped.unshift(popped.pop());
  732. this.stack.push(...popped);
  733. }
  734. /**
  735. * Implement :
  736. *
  737. * Duplicates the element on the top of the stack
  738. */
  739. Duplicate() {
  740. this.stack.push(this.stack[this.stack.length-1]);
  741. }
  742. /**
  743. * Implements ~
  744. *
  745. * Removes the element on the top of the stack
  746. */
  747. Remove() {
  748. this.stack.pop();
  749. }
  750. /**
  751. * Implement r
  752. *
  753. * Reverses the entire stack
  754. */
  755. Reverse() {
  756. this.stack.reverse();
  757. }
  758. /**
  759. * Implement l
  760. *
  761. * Pushes the length of the stack onto the top of the stack
  762. */
  763. PushLength() {
  764. this.stack.push(this.stack.length);
  765. }
  766. }
  767. /**
  768. * Get the char code of any character
  769. *
  770. * Can actually take any length of a value, but only returns the
  771. * char code of the first character.
  772. *
  773. * @param {*} value Any character
  774. * @returns {int} The value's char code
  775. */
  776. function dec(value) {
  777. return value.toString().charCodeAt(0);
  778. }