Browse Source

First try at the compiler

No variables or conditionals or anything yet, just
the basic ability to turn s-expressions into HTML.
master
Dylan Baker 5 years ago
parent
commit
709986560e
4 changed files with 64 additions and 7 deletions
  1. 25
    0
      src/compiler.js
  2. 13
    7
      src/parser.js
  3. 24
    0
      test/compiler.js
  4. 2
    0
      test/parser.js

+ 25
- 0
src/compiler.js View File

@@ -0,0 +1,25 @@
1
+module.exports = class Compiler {
2
+  constructor(tree) {
3
+    this.tree = tree
4
+    this.result = ''
5
+  }
6
+
7
+  compile() {
8
+    this.tree.forEach(node => {
9
+      if (node.type === 'functionCall') {
10
+        const attributes = node.args.map(arg => `${arg.attributeName}="${arg.attributeValue}"`)
11
+        const compiler = new Compiler(node.subtree)
12
+        const content = compiler.compile()
13
+        this.result += `
14
+          <${node.functionName}${attributes.length ? ' ' : ''}${attributes.join(' ')}>
15
+            ${content}
16
+          </${node.functionName}>
17
+        `
18
+      } else if (node.type === 'text') {
19
+        this.result += node.content
20
+      }
21
+    })
22
+
23
+    return this.result
24
+  }
25
+}

+ 13
- 7
src/parser.js View File

@@ -17,22 +17,28 @@ module.exports = class Parser {
17 17
   expr() {
18 18
     this.tokenStream.eat(tokenTypes.OPAREN)
19 19
 
20
-    let functionCallNode = new Node()
21
-    functionCallNode.functionName = this.tokenStream.eat(tokenTypes.LITERAL).value
22
-    functionCallNode.args = []
23
-    functionCallNode.subtree = []
20
+    let elementNode = new Node()
21
+    elementNode.type = 'functionCall'
22
+    elementNode.functionName = this.tokenStream.eat(tokenTypes.LITERAL).value
23
+    elementNode.args = []
24
+    elementNode.subtree = []
24 25
 
25 26
     while (this.tokenStream.peek().type != tokenTypes.CPAREN && this.tokenStream.peek().type !== tokenTypes.EOF) {
26 27
       if (this.tokenStream.peek().type === tokenTypes.ATTRIBUTE) {
27
-        functionCallNode.args.push(this.attribute())
28
+        elementNode.args.push(this.attribute())
28 29
       } else if (this.tokenStream.peek().type === tokenTypes.OPAREN) {
29
-        functionCallNode.subtree.push(this.expr())
30
+        elementNode.subtree.push(this.expr())
31
+      } else if (this.tokenStream.peek().type === tokenTypes.QUOTE) {
32
+        elementNode.subtree.push(new Node({
33
+          type: 'text',
34
+          content: this.string()
35
+        }))
30 36
       }
31 37
     }
32 38
 
33 39
     this.tokenStream.eat(tokenTypes.CPAREN)
34 40
 
35
-    return functionCallNode
41
+    return elementNode
36 42
   }
37 43
 
38 44
   attribute() {

+ 24
- 0
test/compiler.js View File

@@ -0,0 +1,24 @@
1
+const test = require("tape")
2
+
3
+const Compiler = require("../src/compiler")
4
+const Lexer = require("../src/lexer")
5
+const Node = require("../src/node")
6
+const Parser = require("../src/parser")
7
+const tt = require("../src/tokenTypes")
8
+
9
+test("compiles a simple template", t => {
10
+  t.plan(1)
11
+  const lexer = new Lexer()
12
+  const tokenStream = lexer.scan(`
13
+    (div :class "foobar"
14
+      (p :class "bazquux" "Lorem ipsum dolor sit amet."))
15
+  `)
16
+  const parser = new Parser(tokenStream)
17
+  const tree = parser.parse()
18
+  const compiler = new Compiler(tree)
19
+  const result = compiler.compile()
20
+  t.deepEqual(
21
+    result.replace(/\n/g, '').replace(/  +/g, ''),
22
+    '<div class="foobar"><p class="bazquux">Lorem ipsum dolor sit amet.</p></div>'
23
+  )
24
+})

+ 2
- 0
test/parser.js View File

@@ -19,6 +19,7 @@ test('parses token stream into a tree', t => {
19 19
     tree,
20 20
     [
21 21
       new Node({
22
+        type: 'functionCall',
22 23
         functionName: 'div',
23 24
         args: [
24 25
           new Node({
@@ -28,6 +29,7 @@ test('parses token stream into a tree', t => {
28 29
         ],
29 30
         subtree: [
30 31
           new Node({
32
+            type: 'functionCall',
31 33
             functionName: 'p',
32 34
             args: [
33 35
               new Node({

Loading…
Cancel
Save