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 4.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. const AST = require('./ast')
  2. const OsloError = require('./OsloError')
  3. const tokenTypes = require('./tokenTypes')
  4. module.exports = class Parser {
  5. parse(tokenStream) {
  6. this.tokenStream = tokenStream
  7. let tree = []
  8. while (this.tokenStream.peek().type !== tokenTypes.EOF) {
  9. let expr = this.expr()
  10. tree.push(expr)
  11. if (this.tokenStream.error) {
  12. return {
  13. error: this.tokenStream.error,
  14. }
  15. }
  16. }
  17. return tree
  18. }
  19. expr() {
  20. let token = this.tokenStream.peek()
  21. switch (token.type) {
  22. case tokenTypes.ATTRIBUTE:
  23. return this.attribute()
  24. case tokenTypes.BOOLEAN:
  25. return this.bool()
  26. case tokenTypes.IDENTIFIER:
  27. return this.identifier()
  28. case tokenTypes.NUMBER:
  29. return this.number()
  30. case tokenTypes.QUOTE:
  31. return this.string()
  32. case tokenTypes.SYMBOL:
  33. return this.symbol()
  34. case tokenTypes.OPAREN:
  35. return this.form()
  36. default:
  37. this.tokenStream.error = `Unexpected ${token.type} on line ${
  38. token.line
  39. }`
  40. break
  41. }
  42. }
  43. form() {
  44. this.tokenStream.eat(tokenTypes.OPAREN)
  45. let node
  46. if (this.tokenStream.peek().value === 'lambda') {
  47. this.tokenStream.eat(tokenTypes.IDENTIFIER)
  48. node = this.lambda()
  49. } else if (this.tokenStream.peek().value === 'define') {
  50. this.tokenStream.eat(tokenTypes.IDENTIFIER)
  51. node = this.definition()
  52. } else if (this.tokenStream.peek().value === 'if') {
  53. this.tokenStream.eat(tokenTypes.IDENTIFIER)
  54. node = this.conditional()
  55. } else if (this.tokenStream.peek().value === 'let') {
  56. this.tokenStream.eat(tokenTypes.IDENTIFIER)
  57. node = this.let()
  58. } else {
  59. node = new AST.Application()
  60. node.function = this.expr()
  61. node.args = []
  62. while (this.tokenStream.peek().type !== tokenTypes.CPAREN) {
  63. node.args.push(this.expr())
  64. }
  65. }
  66. this.tokenStream.eat(tokenTypes.CPAREN)
  67. return node
  68. }
  69. attribute() {
  70. return new AST.Attribute({
  71. name: this.tokenStream.eat(tokenTypes.ATTRIBUTE).value,
  72. value: this.expr(),
  73. })
  74. }
  75. bool() {
  76. return new AST.Boolean({
  77. value: this.tokenStream.eat(tokenTypes.BOOLEAN).value,
  78. })
  79. }
  80. conditional() {
  81. return new AST.Conditional({
  82. condition: this.expr(),
  83. ifCase: this.expr(),
  84. elseCase: this.expr(),
  85. })
  86. }
  87. definition() {
  88. return new AST.Definition({
  89. symbol: this.identifier(),
  90. value: this.expr(),
  91. })
  92. }
  93. identifier() {
  94. let token = this.tokenStream.eat(tokenTypes.IDENTIFIER)
  95. return new AST.Identifier({
  96. name: token.value,
  97. line: token.line,
  98. })
  99. }
  100. lambda() {
  101. let parameters = []
  102. this.tokenStream.eat(tokenTypes.OPAREN)
  103. while (this.tokenStream.peek().type !== tokenTypes.CPAREN) {
  104. parameters.push(
  105. new AST.Identifier({
  106. name: this.tokenStream.eat(tokenTypes.IDENTIFIER).value,
  107. }),
  108. )
  109. }
  110. this.tokenStream.eat(tokenTypes.CPAREN)
  111. return new AST.Lambda({
  112. parameters: parameters,
  113. body: this.form(),
  114. })
  115. }
  116. let() {
  117. let bindings = []
  118. this.tokenStream.eat(tokenTypes.OPAREN)
  119. while (this.tokenStream.peek().type !== tokenTypes.CPAREN) {
  120. this.tokenStream.eat(tokenTypes.OPAREN)
  121. bindings.push({
  122. key: new AST.Identifier({
  123. name: this.tokenStream.eat(tokenTypes.IDENTIFIER).value,
  124. }),
  125. value: this.expr(),
  126. })
  127. this.tokenStream.eat(tokenTypes.CPAREN)
  128. }
  129. this.tokenStream.eat(tokenTypes.CPAREN)
  130. return new AST.LetBinding({
  131. bindings: bindings,
  132. body: this.expr(),
  133. })
  134. }
  135. number() {
  136. return new AST.Number({
  137. value: this.tokenStream.eat(tokenTypes.NUMBER).value,
  138. })
  139. }
  140. string() {
  141. this.tokenStream.eat(tokenTypes.QUOTE)
  142. let value = ''
  143. if (this.tokenStream.peek().type === tokenTypes.LITERAL) {
  144. value = this.tokenStream.eat(tokenTypes.LITERAL).value
  145. }
  146. let node = new AST.String({
  147. value: value,
  148. })
  149. this.tokenStream.eat(tokenTypes.QUOTE)
  150. return node
  151. }
  152. symbol() {
  153. return new AST.Symbol({
  154. value: this.tokenStream.eat(tokenTypes.SYMBOL).value,
  155. })
  156. }
  157. }