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.4KB

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