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.

compiler.js 2.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. const util = require('util')
  2. module.exports = class Compiler {
  3. constructor(tree, context) {
  4. this.tree = tree
  5. this.context = context
  6. this.pos = 0
  7. this.result = ''
  8. this.selfClosingTags = [
  9. 'area',
  10. 'base',
  11. 'br',
  12. 'col',
  13. 'command',
  14. 'embed',
  15. 'hr',
  16. 'img',
  17. 'input',
  18. 'keygen',
  19. 'link',
  20. 'menuitem',
  21. 'meta',
  22. 'param',
  23. 'source',
  24. 'track',
  25. 'wbr',
  26. ]
  27. this.standardLibrary = {
  28. cond: function(predicate, left, right) {
  29. if (predicate) {
  30. let compiler = new Compiler([left], this.context)
  31. return compiler.compile()
  32. } else {
  33. let compiler = new Compiler([right], this.context)
  34. return compiler.compile()
  35. }
  36. },
  37. }
  38. }
  39. compile() {
  40. this.tree.forEach(node => {
  41. switch (node.constructor.name) {
  42. case 'Application':
  43. this.result += this.application(node)
  44. break;
  45. case 'String':
  46. this.result += node.value;
  47. break;
  48. case 'Identifier':
  49. this.result += this.lookup(node)
  50. break;
  51. }
  52. })
  53. return this.result
  54. }
  55. application(node) {
  56. if (this.standardLibrary[node.functionName.name]) {
  57. return this.standardLibrary[node.functionName.name](...node.args)
  58. }
  59. return this.htmlElement(node)
  60. }
  61. htmlElement(node) {
  62. let result = `<${node.functionName.name}`
  63. node.args.filter(arg => arg.constructor.name === 'Attribute').forEach(arg => {
  64. result += ` ${arg.name}=`
  65. let compiler = new Compiler([arg.value], this.context)
  66. result += `"${compiler.compile()}"`
  67. })
  68. result += '>'
  69. node.args.filter(arg => arg.constructor.name !== 'Attribute').forEach(arg => {
  70. let compiler = new Compiler([arg], this.context)
  71. result += compiler.compile()
  72. })
  73. if (!this.selfClosingTags.includes(node.functionName.name)) {
  74. result += `</${node.functionName.name}>`
  75. }
  76. return result
  77. }
  78. lookup(identifier) {
  79. let result = this.context[identifier.name]
  80. if (!result) {
  81. throw `Undefined variable '${identifier.name}'`
  82. }
  83. return result
  84. }
  85. currentNode() {
  86. return this.tree[this.pos]
  87. }
  88. }