Fantasy 8Bit system (F8), is a fantasy 8bit console and a set of libraries for creating fantasy 8bit consoles.
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.

159 lines
2.6KB

  1. const OP = require('./op.js');
  2. function TokenStream(input){
  3. var pos = 0;
  4. function peek(){
  5. return (pos < input.length) ? input[pos] : null;
  6. }
  7. function next(){
  8. if (pos < input.length){
  9. let v = input[pos];
  10. pos += 1;
  11. return v;
  12. }
  13. return null;
  14. }
  15. function line(){
  16. return (pos < input.length) ? input[pos].line : -1;
  17. }
  18. function col(){
  19. return (pos < input.length) ? input[pos].col : -1;
  20. }
  21. function eol(){
  22. return (pos < input.length - 1) ? (input[pos].line !== input[pos+1].line) : true;
  23. }
  24. function eof(){
  25. return (pos >= input.length);
  26. }
  27. function die(msg){
  28. throw new Error(msg + " Line: " + input[pos].line + ", Col: " + input[pos].col);
  29. }
  30. return {
  31. peek: peek,
  32. next: next,
  33. line: line,
  34. col: col,
  35. eof, eof,
  36. die: die
  37. };
  38. }
  39. var stream = null;
  40. function isTokenType(type, val){
  41. let t = stream.peek();
  42. if (t !== null && t.type === type){
  43. if (!val || t.val === val)
  44. return true;
  45. }
  46. return false;
  47. }
  48. function isPunc(ch){
  49. return isTokenType("punc", ch);
  50. }
  51. function isOpCode(ch){
  52. return isTokenType("opcode", ch);
  53. }
  54. function isLabel(ch){
  55. return isTokenType("label", ch);
  56. }
  57. function isDirective(ch){
  58. return isTokenType("directive", ch);
  59. }
  60. function isOp(ch){
  61. return isTokenType("op", ch);
  62. }
  63. function skipPunc(ch){
  64. if (isPunc(ch)){
  65. stream.next();
  66. } else {
  67. stream.die("Unexpected punctuation '" + ch + "'.");
  68. }
  69. }
  70. parseDelimited(s,e,d,parser){
  71. let toEOL = (s === null || e === null);
  72. let a = [];
  73. let first = true;
  74. if (!toEOL){skipPunc(s);}
  75. while (!stream.eof() && ((!toEOL && isPunc(e)) || (toEOL && !stream.eol()))){
  76. if (first){
  77. first = false;
  78. } else {skipPunk(d);}
  79. a.push(parser());
  80. }
  81. if (!toEOL){skipPunc(e);}
  82. return a;
  83. }
  84. parseOpCode(){
  85. let val = stream.next();
  86. let mode = 0; // Guess between absolute and zero page.
  87. let args = []; // TODO: Finish figuring out how to get the argument list!
  88. if (isOp("#")){
  89. stream.next();
  90. mode = 1; // Immediate
  91. } else if (isPunc("(")){
  92. mode = 2; // Indirect
  93. // TODO: Use parseDelimited()
  94. }
  95. return {
  96. type: "opcode",
  97. val: val,
  98. args: args,
  99. mode: mode
  100. };
  101. }
  102. parseAtom(){
  103. if (isPunc("(")){
  104. stream.next();
  105. let exp = parseExpression();
  106. if (isPunc(")")){
  107. stream.next();
  108. return exp;
  109. }
  110. } else if (isOpCode()){
  111. return parseOpCode();
  112. }
  113. }
  114. parse(tokens){
  115. let p = {
  116. type: "prog",
  117. expressions: [];
  118. };
  119. stream = TokenStream(tokens);
  120. while (!stream.eof()){
  121. p.expressions.push(parseExpression());
  122. }
  123. stream = null;
  124. return p;
  125. }
  126. module.exports = Object.freeze({
  127. parse: parse
  128. });