starfish.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838
  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. this.Run();
  128. }
  129. /**
  130. * Make all the rows in the code box the same length
  131. *
  132. * All rows not long enough will have NOPs added until they're uniform in size.
  133. *
  134. * @param {int} [rowLength] The longest row in the code box
  135. */
  136. EqualizeBoxWidth(rowLength = null) {
  137. if(!rowLength) {
  138. for(const row of this.box) {
  139. if(row.length > rowLength) {
  140. rowLength = row.length;
  141. }
  142. }
  143. }
  144. for(const row of this.box) {
  145. while(row.length < rowLength) {
  146. row.push(" ");
  147. }
  148. }
  149. }
  150. /**
  151. * Print the value to the display
  152. *
  153. * @TODO Set up an actual display
  154. * @param {*} value
  155. */
  156. Output(value) {
  157. this.outputDOM.value += value;
  158. }
  159. /**
  160. * The main loop for the engine
  161. */
  162. Run() {
  163. let fin = null;
  164. try {
  165. while(!fin) {
  166. fin = this.Swim();
  167. }
  168. }
  169. catch(e) {
  170. console.error(e);
  171. }
  172. }
  173. Execute(instruction) {
  174. let output = null;
  175. try{
  176. switch(instruction) {
  177. // NOP
  178. case " ":
  179. break;
  180. // Numbers
  181. case "1":
  182. case "2":
  183. case "3":
  184. case "4":
  185. case "5":
  186. case "6":
  187. case "7":
  188. case "8":
  189. case "9":
  190. case "0":
  191. case "a":
  192. case "b":
  193. case "c":
  194. case "d":
  195. case "e":
  196. case "f":
  197. this.stacks[this.curr_stack].Push(parseInt(instruction, 16));
  198. break;
  199. // Operators
  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);
  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. case "=": {
  243. const x = this.stacks[this.curr_stack].Pop();
  244. const y = this.stacks[this.curr_stack].Pop();
  245. this.stacks[this.curr_stack].push(y == x ? 1 : 0);
  246. break;
  247. }
  248. //String mode
  249. case "\"":
  250. case "'":
  251. this.stringMode = !!this.stringMode ? 0 : dec(instruction);
  252. break;
  253. // Movement
  254. case "^":
  255. this.MoveUp();
  256. break;
  257. case ">":
  258. this.MoveRight();
  259. break;
  260. case "v":
  261. this.MoveDown();
  262. break;
  263. case "<":
  264. this.MoveLeft();
  265. break;
  266. // Mirrors
  267. case "/":
  268. this.ReflectForward();
  269. break;
  270. case "\\":
  271. this.ReflectBack();
  272. break;
  273. case "_":
  274. this.VerticalMirror();
  275. break;
  276. case "|":
  277. this.HorizontalMirror();
  278. break;
  279. case "#":
  280. this.OmniMirror();
  281. break;
  282. // Trampolines
  283. case "!":
  284. this.Move();
  285. break;
  286. case "?":
  287. if(this.stacks[this.curr_stack].Pop() === 0){ this.Move(); }
  288. break;
  289. // Stack manipulation
  290. case "&": {
  291. if (this.stacks[this.curr_stack].register == null) {
  292. this.stacks[this.curr_stack].register = this.stacks[this.curr_stack].Pop();
  293. }
  294. else {
  295. this.stacks[this.curr_stack].Push(this.stacks[this.curr_stack].register);
  296. this.stacks[this.curr_stack].register = null;
  297. }
  298. break;
  299. }
  300. case ":":
  301. this.stacks[this.curr_stack].Duplicate();
  302. break;
  303. case "~":
  304. this.stacks[this.curr_stack].Remove();
  305. break;
  306. case "$":
  307. this.stacks[this.curr_stack].SwapTwo();
  308. break;
  309. case "@":
  310. this.stacks[this.curr_stack].SwapThree();
  311. break;
  312. case "{":
  313. this.stacks[this.curr_stack].ShiftLeft();
  314. break;
  315. case "}":
  316. this.stacks[this.curr_stack].ShiftRight();
  317. break;
  318. case "r":
  319. this.stacks[this.curr_stack].Reverse();
  320. break;
  321. case "l":
  322. this.stacks[this.curr_stack].PushLength();
  323. break;
  324. case "[": {
  325. this.SpliceStack(this.stacks[this.curr_stack].Pop());
  326. break;
  327. }
  328. case "]":
  329. this.CollapseStack();
  330. break;
  331. case "I": {
  332. this.curr_stack++;
  333. if (this.curr_stack >= this.stacks.length) {
  334. throw new RangeError("curr_stack value out of bounds");
  335. }
  336. break;
  337. }
  338. case "D": {
  339. this.curr_stack--;
  340. if (this.curr_stack < 0) {
  341. throw new RangeError("curr_stack value out of bounds");
  342. }
  343. break;
  344. }
  345. // Output
  346. case "n":
  347. output = this.stacks[this.curr_stack].Pop();
  348. break;
  349. case "o":
  350. output = String.fromCharCode(this.stacks[this.curr_stack].Pop());
  351. break;
  352. // Time
  353. case "S":
  354. setTimeout(this.Run.bind(this), this.stacks[this.curr_stack].Pop() * 100);
  355. this.Move();
  356. output = true;
  357. break;
  358. case "h":
  359. this.stacks[this.curr_stack].Push(this.datetime.getUTCHours());
  360. break;
  361. case "m":
  362. this.stacks[this.curr_stack].Push(this.datetime.getUTCMinutes());
  363. break;
  364. case "s":
  365. this.stacks[this.curr_stack].Push(this.datetime.getUTCSeconds());
  366. break;
  367. // Code box manipulation
  368. case "g":
  369. this.PushFromCodeBox();
  370. break;
  371. case "p":
  372. this.PlaceIntoCodeBox();
  373. break;
  374. // End execution
  375. case ";":
  376. output = true;
  377. break;
  378. default:
  379. throw new Error(`Unknown instruction: ${instruction}`);
  380. }
  381. }
  382. catch(e) {
  383. console.error(`Something smells fishy!\n${e != "" ? `${e}\n` : ""}Instruction: ${instruction}\nStack: ${JSON.stringify(this.stacks[this.curr_stack].stack)}`);
  384. return true;
  385. }
  386. return output;
  387. }
  388. Swim() {
  389. const instruction = this.box[this.pointer.Y][this.pointer.X];
  390. this.datetime = new Date();
  391. if(this.stringMode != 0 && dec(instruction) != this.stringMode) {
  392. this.stacks[this.curr_stack].Push(dec(instruction));
  393. }
  394. else {
  395. const exeResult = this.Execute(instruction);
  396. if(exeResult === true) {
  397. return true;
  398. }
  399. else if(exeResult != null) {
  400. this.Output(exeResult);
  401. }
  402. }
  403. this.Move();
  404. }
  405. Move() {
  406. let newX = this.pointer.X + this.curr_direction[0];
  407. let newY = this.pointer.Y + this.curr_direction[1];
  408. // Keep the X coord in the boxes bounds
  409. if(newX < 0) {
  410. newX = this.maxBoxWidth;
  411. }
  412. else if(newX > this.maxBoxWidth) {
  413. newX = 0;
  414. }
  415. // Keep the Y coord in the boxes bounds
  416. if(newY < 0) {
  417. newY = this.maxBoxHeight;
  418. }
  419. else if(newY > this.maxBoxHeight) {
  420. newY = 0;
  421. }
  422. this.SetPointer(newX, newY);
  423. }
  424. /**
  425. * Implement C and .
  426. */
  427. SetPointer(x, y) {
  428. this.pointer = {X: x, Y: y};
  429. }
  430. /**
  431. * Implement ^
  432. *
  433. * Changes the swim direction upward
  434. */
  435. MoveUp() {
  436. this.curr_direction = this.directions.NORTH;
  437. }
  438. /**
  439. * Implement >
  440. *
  441. * Changes the swim direction rightward
  442. */
  443. MoveRight() {
  444. this.curr_direction = this.directions.EAST;
  445. this.dirWasLeft = false;
  446. }
  447. /**
  448. * Implement v
  449. *
  450. * Changes the swim direction downward
  451. */
  452. MoveDown() {
  453. this.curr_direction = this.directions.SOUTH;
  454. }
  455. /**
  456. * Implement <
  457. *
  458. * Changes the swim direction leftward
  459. */
  460. MoveLeft() {
  461. this.curr_direction = this.directions.WEST;
  462. this.dirWasLeft = true;
  463. }
  464. /**
  465. * Implement /
  466. *
  467. * Reflects the swim direction depending on its starting value
  468. */
  469. ReflectForward() {
  470. if (this.curr_direction == this.directions.NORTH) {
  471. this.MoveRight();
  472. }
  473. else if (this.curr_direction == this.directions.EAST) {
  474. this.MoveUp();
  475. }
  476. else if (this.curr_direction == this.directions.SOUTH) {
  477. this.MoveLeft();
  478. }
  479. else {
  480. this.MoveDown();
  481. }
  482. }
  483. /**
  484. * Implement \
  485. *
  486. * Reflects the swim direction depending on its starting value
  487. */
  488. ReflectBack() {
  489. if (this.curr_direction == this.directions.NORTH) {
  490. this.MoveLeft();
  491. }
  492. else if (this.curr_direction == this.directions.EAST) {
  493. this.MoveDown();
  494. }
  495. else if (this.curr_direction == this.directions.SOUTH) {
  496. this.MoveRight();
  497. }
  498. else {
  499. this.MoveUp();
  500. }
  501. }
  502. /**
  503. * Implement |
  504. *
  505. * Swaps the horizontal swim direction to its opposite
  506. */
  507. HorizontalMirror() {
  508. if (this.curr_direction == this.directions.EAST) {
  509. this.MoveLeft();
  510. }
  511. else {
  512. this.MoveRight();
  513. }
  514. }
  515. /**
  516. * Implement _
  517. *
  518. * Swaps the horizontal swim direction to its opposite
  519. */
  520. VerticalMirror() {
  521. if (this.curr_direction == this.directions.NORTH) {
  522. this.MoveDown();
  523. }
  524. else {
  525. this.MoveUp();
  526. }
  527. }
  528. /**
  529. * Implement #
  530. *
  531. * A combination of the vertical and the horizontal mirror
  532. */
  533. OmniMirror() {
  534. if (this.curr_direction[0]) {
  535. this.VerticalMirror();
  536. }
  537. else {
  538. this.HorizontalMirror();
  539. }
  540. }
  541. /**
  542. * Implement x
  543. *
  544. * Pseudo-randomly switches the swim direction
  545. */
  546. ShuffleDirection() {
  547. this.curr_direction = Object.values(this.directions)[Math.floor(Math.random() * 4)];
  548. }
  549. /**
  550. * Implement [
  551. *
  552. * Takes X number of elements out of a stack and into a new stack
  553. *
  554. * This action creates a new stack, and places it on top of the one it was created from.
  555. * So, if you have three stacks, A, B, and C, and you splice a stack off of stack B,
  556. * the new order will be: A, B, D, and C.
  557. *
  558. * @see {@link https://esolangs.org/wiki/Fish#Stacks ><> Documentation}
  559. *
  560. * @param {int} spliceCount The number of elements to pop into a new stack
  561. */
  562. SpliceStack(spliceCount) {
  563. const stackCount = this.stacks[this.curr_stack].stack.length;
  564. if (spliceCount > stackCount) {
  565. throw new RangeError(`Cannot remove ${spliceCount} elements from a stack of only ${stackCount} elements`);
  566. }
  567. const newStack = new Stack(this.stacks[this.curr_stack].stack.splice(stackCount - spliceCount, spliceCount));
  568. // We're at the top of the stacks stack, so we can use .push
  569. if (this.curr_stack == this.stacks.length - 1) {
  570. this.stacks.push(newStack);
  571. }
  572. else {
  573. this.stacks.splice(this.curr_stack + 1, 0, newStack);
  574. }
  575. this.curr_stack++;
  576. }
  577. /**
  578. * Implement ]
  579. *
  580. * Collapses the current stack onto the one below it
  581. * If the current stack is the only one, it is replaced with a blank stack
  582. */
  583. CollapseStack() {
  584. // Undefined behavior collapsing the first stack down when there are other stacks available
  585. if (this.curr_stack == 0 && this.stacks.length != 1) {
  586. throw new Error();
  587. }
  588. if (this.curr_stack == 0) {
  589. this.stacks = [new Stack()];
  590. }
  591. else {
  592. const collapsed = this.stacks.splice(this.curr_stack, 1).pop();
  593. this.curr_stack--;
  594. const currStackCount = this.stacks[this.curr_stack].stack.length;
  595. this.stacks[this.curr_stack].stack.splice(currStackCount, 0, ...collapsed.stack);
  596. }
  597. }
  598. /**
  599. * Implement g
  600. *
  601. * Pops `y` and `x` from the stack, and then pushes the value of the character
  602. * at `[x, y]` in the code box.
  603. *
  604. * NOP's and coords that are out of bounds are converted to 0.
  605. *
  606. * 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}
  607. */
  608. PushFromCodeBox() {
  609. const y = this.stacks[this.curr_stack].Pop();
  610. const x = this.stacks[this.curr_stack].Pop();
  611. let val = undefined;
  612. try {
  613. val = this.box[y][x] || " ";
  614. }
  615. catch (e) {
  616. val = " ";
  617. }
  618. const valParsed = val == " " ? 0 : dec(val);
  619. this.stacks[this.curr_stack].Push(valParsed);
  620. }
  621. /**
  622. * Implement p
  623. *
  624. * Pops `y`, `x`, and `v` off of the stack, and then places the string
  625. * representation of that value at `[x, y]` in the code box.
  626. */
  627. PlaceIntoCodeBox() {
  628. const y = this.stacks[this.curr_stack].Pop();
  629. const x = this.stacks[this.curr_stack].Pop();
  630. const v = this.stacks[this.curr_stack].Pop();
  631. while(y >= this.box.length) {
  632. this.box.push([]);
  633. }
  634. while(x >= this.box[y].length) {
  635. this.box[y].push(" ");
  636. }
  637. this.EqualizeBoxWidth();
  638. this.box[y][x] = String.fromCharCode(v);
  639. }
  640. /**
  641. * Implement `
  642. *
  643. * Changes the swim direction based on the previous direction
  644. * @see https://esolangs.org/wiki/Starfish#Fisherman
  645. */
  646. Fisherman() {
  647. if (this.curr_direction[0]) {
  648. if (this.dirWasLeft) {
  649. this.MoveLeft();
  650. }
  651. else {
  652. this.MoveRight();
  653. }
  654. }
  655. else {
  656. if (this.onTheHook) {
  657. this.onTheHook = false;
  658. this.MoveUp();
  659. }
  660. else {
  661. this.onTheHook = true;
  662. this.MoveDown();
  663. }
  664. }
  665. }
  666. }
  667. /**
  668. * The stack class
  669. */
  670. class Stack {
  671. /**
  672. * @param {int[]} stackValues An array of values to initialize the stack with
  673. */
  674. constructor(stackValues = []) {
  675. /**
  676. * The stack
  677. * @type {int[]}
  678. */
  679. this.stack = stackValues;
  680. /**
  681. * A single value saved off the stack
  682. * @type {int}
  683. */
  684. this.register = null;
  685. }
  686. /**
  687. * Wrapper function for Array.prototype.push
  688. * @param {*} newValue
  689. */
  690. Push(newValue) {
  691. this.stack.push(newValue);
  692. }
  693. /**
  694. * Wrapper function for Array.prototype.pop
  695. * @returns {*}
  696. */
  697. Pop() {
  698. const value = this.stack.pop();
  699. if(value == undefined){ throw new Error(); }
  700. return value;
  701. }
  702. /**
  703. * Implement }
  704. *
  705. * Shifts the entire stack leftward by one value
  706. */
  707. ShiftLeft() {
  708. const temp = this.stack.shift();
  709. this.stack.push(temp);
  710. }
  711. /**
  712. * Implement {
  713. *
  714. * Shifts the entire stack rightward by one value
  715. */
  716. ShiftRight() {
  717. const temp = this.stack.pop();
  718. this.stack.unshift(temp);
  719. }
  720. /**
  721. * Implement $
  722. *
  723. * Swaps the top two values of the stack
  724. */
  725. SwapTwo() {
  726. if(this.stack.length < 2) { throw new Error(); }
  727. const popped = this.stack.splice(this.stack.length - 2, 2);
  728. this.stack.push(...popped.reverse());
  729. }
  730. /**
  731. * Implement @
  732. *
  733. * Swaps the top three values of the stack
  734. */
  735. SwapThree() {
  736. if(this.stack.length < 3) { throw new Error(); }
  737. // Get the top three values
  738. const popped = this.stack.splice(this.stack.length - 3, 3);
  739. // Shift the elements to the right
  740. popped.unshift(popped.pop());
  741. this.stack.push(...popped);
  742. }
  743. /**
  744. * Implement :
  745. *
  746. * Duplicates the element on the top of the stack
  747. */
  748. Duplicate() {
  749. this.stack.push(this.stack[this.stack.length-1]);
  750. }
  751. /**
  752. * Implements ~
  753. *
  754. * Removes the element on the top of the stack
  755. */
  756. Remove() {
  757. this.stack.pop();
  758. }
  759. /**
  760. * Implement r
  761. *
  762. * Reverses the entire stack
  763. */
  764. Reverse() {
  765. this.stack.reverse();
  766. }
  767. /**
  768. * Implement l
  769. *
  770. * Pushes the length of the stack onto the top of the stack
  771. */
  772. PushLength() {
  773. this.stack.push(this.stack.length);
  774. }
  775. }
  776. /**
  777. * Get the char code of any character
  778. *
  779. * Can actually take any length of a value, but only returns the
  780. * char code of the first character.
  781. *
  782. * @param {*} value Any character
  783. * @returns {int} The value's char code
  784. */
  785. function dec(value) {
  786. return value.toString().charCodeAt(0);
  787. }