A templating language that looks like Lisp and compiles to HTML
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.

parser.js 3.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. const AST = require("./ast");
  2. const Error = require("./Error");
  3. const tokenTypes = require("./tokenTypes");
  4. module.exports = class Parser {
  5. constructor(tokenStream) {
  6. this.tokenStream = tokenStream;
  7. }
  8. parse() {
  9. let tree = [];
  10. while (this.tokenStream.peek().type !== tokenTypes.EOF) {
  11. let expr = this.expr();
  12. tree.push(expr);
  13. if (this.tokenStream.error) {
  14. return {
  15. error: this.tokenStream.error
  16. };
  17. }
  18. }
  19. return tree;
  20. }
  21. expr() {
  22. let token = this.tokenStream.peek();
  23. switch (token.type) {
  24. case tokenTypes.ATTRIBUTE:
  25. return this.attribute();
  26. case tokenTypes.BOOLEAN:
  27. return this.bool();
  28. case tokenTypes.IDENTIFIER:
  29. return this.identifier();
  30. case tokenTypes.NUMBER:
  31. return this.number();
  32. case tokenTypes.QUOTE:
  33. return this.string();
  34. case tokenTypes.SYMBOL:
  35. return this.symbol();
  36. case tokenTypes.OPAREN:
  37. return this.form();
  38. default:
  39. this.tokenStream.error = `Unexpected ${token.type} on line ${
  40. token.line
  41. }`;
  42. break;
  43. }
  44. }
  45. form() {
  46. this.tokenStream.eat(tokenTypes.OPAREN);
  47. let node;
  48. if (this.tokenStream.peek().value === "define") {
  49. this.tokenStream.eat(tokenTypes.IDENTIFIER);
  50. node = this.functionDefinition();
  51. } else {
  52. node = new AST.Application();
  53. node.functionName = this.identifier();
  54. node.args = [];
  55. while (this.tokenStream.peek().type !== tokenTypes.CPAREN) {
  56. node.args.push(this.expr());
  57. }
  58. }
  59. this.tokenStream.eat(tokenTypes.CPAREN);
  60. return node;
  61. }
  62. attribute() {
  63. return new AST.Attribute({
  64. name: this.tokenStream.eat(tokenTypes.ATTRIBUTE).value,
  65. value: this.expr()
  66. });
  67. }
  68. bool() {
  69. return new AST.Boolean({
  70. value: this.tokenStream.eat(tokenTypes.BOOLEAN).value
  71. });
  72. }
  73. functionDefinition() {
  74. let name = new AST.Identifier({
  75. name: this.tokenStream.eat(tokenTypes.IDENTIFIER).value
  76. });
  77. let parameters = [];
  78. this.tokenStream.eat(tokenTypes.OPAREN);
  79. while (this.tokenStream.peek().type !== tokenTypes.CPAREN) {
  80. parameters.push(
  81. new AST.Identifier({
  82. name: this.tokenStream.eat(tokenTypes.IDENTIFIER).value
  83. })
  84. );
  85. if (this.tokenStream.peek().type === tokenTypes.COMMA) {
  86. this.tokenStream.eat(tokenTypes.COMMA);
  87. }
  88. }
  89. this.tokenStream.eat(tokenTypes.CPAREN);
  90. return new AST.FunctionDefinition({
  91. name: name,
  92. parameters: parameters,
  93. body: this.form()
  94. });
  95. }
  96. identifier() {
  97. return new AST.Identifier({
  98. name: this.tokenStream.eat(tokenTypes.IDENTIFIER).value
  99. });
  100. }
  101. number() {
  102. return new AST.Number({
  103. value: this.tokenStream.eat(tokenTypes.NUMBER).value
  104. });
  105. }
  106. string() {
  107. this.tokenStream.eat(tokenTypes.QUOTE);
  108. let value = "";
  109. if (this.tokenStream.peek().type === tokenTypes.LITERAL) {
  110. value = this.tokenStream.eat(tokenTypes.LITERAL).value;
  111. }
  112. let node = new AST.String({
  113. value: value
  114. });
  115. this.tokenStream.eat(tokenTypes.QUOTE);
  116. return node;
  117. }
  118. symbol() {
  119. return new AST.Symbol({
  120. value: this.tokenStream.eat(tokenTypes.SYMBOL).value
  121. });
  122. }
  123. };