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.

193 lines
3.5KB

  1. function GetTextStream(input){
  2. var pos = 0;
  3. var line = 0;
  4. var col = 0;
  5. function peek(){
  6. return input.getChar(pos);
  7. }
  8. function next(){
  9. let c = input.charAt(pos);
  10. pos += 1;
  11. if (c === "\n"){
  12. line += 1;
  13. col = 0;
  14. } else {col += 1;}
  15. return c;
  16. }
  17. function eof(){
  18. return (input.charAt(pos) === "");
  19. }
  20. function die(msg){
  21. throw new Error(msg + " Line: " + line + ", Col: " + col);
  22. }
  23. return {
  24. peek: peek,
  25. get: next,
  26. eof: eof
  27. };
  28. }
  29. // ----------------------------------------------
  30. // VALIDATORS
  31. // ----------------------------------------------
  32. function isWhiteSpace(c){
  33. return ("\t\n ".indexOf(c) >= 0);
  34. }
  35. function isStringStart(c){
  36. return (c === "\"" || c === "'");
  37. }
  38. function isNumType(c){
  39. return ("$%".indexOf(c) >= 0 || isDigit(c));
  40. }
  41. function isDigit(c){
  42. return /[0-9]/i.test(c);
  43. }
  44. function isHex(c){
  45. return /[0-9a-fA-F]/i.test(c);
  46. }
  47. function isBinary(c){
  48. return ("01".indexOf(c) >= 0);
  49. }
  50. function isLabelStart(c){
  51. return /[a-fA-F_]/i.test(c);
  52. }
  53. function isLabel(c){
  54. return (isLabelStart(c) || isDigit(c));
  55. }
  56. function isPunctuation(c){
  57. return (",()".indexOf(c) >= 0);
  58. }
  59. function isOp(c){
  60. return ("=+-/*".indexOf(c) >= 0);
  61. }
  62. // ----------------------------------------------
  63. // Read Operations
  64. // ----------------------------------------------
  65. function skipComment(stream){
  66. readWhile(stream, (c)=>{return c != "\n";});
  67. stream.next();
  68. }
  69. function readHex(stream){
  70. stream.next();
  71. var str = readWhile(stream, isHex);
  72. return {type:'number', val: parseInt(str, 16)};
  73. }
  74. function readBinary(stream){
  75. stream.next();
  76. var str = readWhile(stream, isBinary);
  77. return {type:'number', val: parseInt(str, 2)};
  78. }
  79. function readNumber(stream){
  80. let c = stream.peek();
  81. if (c === "$")
  82. return readHex(stream);
  83. if (c === "%")
  84. return readBinary(stream);
  85. var dot = false;
  86. var str = readWhile(stream, (c)=>{
  87. if (c === "."){
  88. if (dot){return false;}
  89. dot = true;
  90. return true;
  91. }
  92. return isDigit(c);
  93. });
  94. return {type:'number', val:parseFloat(str)};
  95. }
  96. function readString(stream, end){
  97. var str = "";
  98. var escaped = false;
  99. stream.next();
  100. while (!stream.eof()){
  101. let c = stream.next();
  102. if (escaped){
  103. str += c;
  104. escaped = false;
  105. } else if (c === "\\"){
  106. escaped = true;
  107. } else if (c === end){
  108. break;
  109. } else {
  110. str += c;
  111. }
  112. }
  113. return {type: "string", val: str};
  114. }
  115. function readLabel(stream){
  116. str = readWhile(stream, isLabel);
  117. return {type:"label", val: str};
  118. }
  119. function readWhile(stream, validator){
  120. var str = "";
  121. while (!stream.eof() && validator(stream.peek()))
  122. str += stream.next();
  123. return str;
  124. }
  125. function nextToken(stream){
  126. readWhile(stream, isWhiteSpace);
  127. if (stream.eof()){return null;}
  128. let c = stream.peek();
  129. if (c === ";"){
  130. skipComment(stream);
  131. return nextToken(stream);
  132. } else if (isStringStart(c)){
  133. return readString(stream, c);
  134. } else if (isNumType(c)){
  135. return readNumber(stream);
  136. } else if (isLabelStart(c)){
  137. return readLabel(stream);
  138. } else if (isPunctuation(c)){
  139. return {type:"punctuation", val:stream.next()};
  140. } else if (isMathOp(c)){
  141. return {type:"op", val:stream.next()};
  142. }
  143. stream.die("Unable to process character '" + c + "'.");
  144. }
  145. function tokenize(input){
  146. var stream = GetTextStream(input);
  147. var tokens = [];
  148. var t = nextToken(stream);
  149. while (t !== null){
  150. tokens.push(t);
  151. t = nextToken(stream);
  152. }
  153. return tokens;
  154. }
  155. module.exports = tokenize;