Browse Source

Allow using variables as attribute values

master
Dylan Baker 5 years ago
parent
commit
564b4808e9
4 changed files with 66 additions and 11 deletions
  1. 19
    4
      src/compiler.js
  2. 24
    5
      src/parser.js
  3. 21
    0
      test/compiler.js
  4. 2
    2
      test/parser.js

+ 19
- 4
src/compiler.js View File

@@ -1,25 +1,40 @@
1 1
 module.exports = class Compiler {
2
-  constructor(tree) {
2
+  constructor(tree, context) {
3 3
     this.tree = tree
4
+    this.context = context
4 5
     this.result = ''
5 6
   }
6 7
 
7 8
   compile() {
8 9
     this.tree.forEach(node => {
9 10
       if (node.type === 'functionCall') {
10
-        const attributes = node.args.map(arg => `${arg.attributeName}="${arg.attributeValue}"`)
11
-        const compiler = new Compiler(node.subtree)
11
+        const attributes = node.args.map(arg => `${arg.attributeName}="${this.compileAttribute(arg.attributeValue)}"`)
12
+        const compiler = new Compiler(node.subtree, this.context)
12 13
         const content = compiler.compile()
13 14
         this.result += `
14 15
           <${node.functionName}${attributes.length ? ' ' : ''}${attributes.join(' ')}>
15 16
             ${content}
16 17
           </${node.functionName}>
17 18
         `
18
-      } else if (node.type === 'text') {
19
+      } else if (node.type === 'string') {
19 20
         this.result += node.content
21
+      } else if (node.type === 'identifier') {
22
+        this.result += this.lookup(node.name)
20 23
       }
21 24
     })
22 25
 
23 26
     return this.result
24 27
   }
28
+
29
+  compileAttribute(attribute) {
30
+    if (attribute.type === 'string') {
31
+      return attribute.content
32
+    } else if (attribute.type === 'identifier') {
33
+      return this.lookup(attribute.name)
34
+    }
35
+  }
36
+
37
+  lookup(name) {
38
+    return this.context[name]
39
+  }
25 40
 }

+ 24
- 5
src/parser.js View File

@@ -28,11 +28,10 @@ module.exports = class Parser {
28 28
         elementNode.args.push(this.attribute())
29 29
       } else if (this.tokenStream.peek().type === tokenTypes.OPAREN) {
30 30
         elementNode.subtree.push(this.expr())
31
+      } else if (this.tokenStream.peek().type === tokenTypes.LITERAL) {
32
+        elementNode.subtree.push(this.identifier())
31 33
       } else if (this.tokenStream.peek().type === tokenTypes.QUOTE) {
32
-        elementNode.subtree.push(new Node({
33
-          type: 'text',
34
-          content: this.string()
35
-        }))
34
+        elementNode.subtree.push(this.quotedString())
36 35
       }
37 36
     }
38 37
 
@@ -44,10 +43,30 @@ module.exports = class Parser {
44 43
   attribute() {
45 44
     let attributeNode = new Node()
46 45
     attributeNode.attributeName = this.tokenStream.eat(tokenTypes.ATTRIBUTE).value
47
-    attributeNode.attributeValue = this.string()
46
+    if (this.tokenStream.peek().type === tokenTypes.QUOTE) {
47
+      attributeNode.attributeValue = this.quotedString()
48
+    } else if (this.tokenStream.peek().type === tokenTypes.LITERAL) {
49
+      attributeNode.attributeValue = this.identifier()
50
+    }
51
+
48 52
     return attributeNode
49 53
   }
50 54
 
55
+  identifier() {
56
+    const identifier = this.tokenStream.eat(tokenTypes.LITERAL)
57
+    return new Node({
58
+      type: 'identifier',
59
+      name: identifier.value
60
+    })
61
+  }
62
+
63
+  quotedString() {
64
+    return new Node({
65
+      type: 'string',
66
+      content: this.string()
67
+    })
68
+  }
69
+
51 70
   string() {
52 71
     this.tokenStream.eat(tokenTypes.QUOTE)
53 72
     let stringValue = this.tokenStream.eat(tokenTypes.LITERAL).value

+ 21
- 0
test/compiler.js View File

@@ -22,3 +22,24 @@ test("compiles a simple template", t => {
22 22
     '<div class="foobar"><p class="bazquux">Lorem ipsum dolor sit amet.</p></div>'
23 23
   )
24 24
 })
25
+
26
+test("renders variables according to passed-in context", t => {
27
+  t.plan(1)
28
+  const lexer = new Lexer()
29
+  const tokenStream = lexer.scan(`
30
+    (div :class classOne
31
+      (p :class classTwo bodyText))
32
+  `)
33
+  const parser = new Parser(tokenStream)
34
+  const tree = parser.parse()
35
+  const compiler = new Compiler(tree, {
36
+    classOne: 'foobar',
37
+    classTwo: 'bazquux',
38
+    bodyText: 'Lorem ipsum dolor sit amet.'
39
+  })
40
+  const result = compiler.compile()
41
+  t.deepEqual(
42
+    result.replace(/\n/g, '').replace(/  +/g, ''),
43
+    '<div class="foobar"><p class="bazquux">Lorem ipsum dolor sit amet.</p></div>'
44
+  )
45
+})

+ 2
- 2
test/parser.js View File

@@ -24,7 +24,7 @@ test('parses token stream into a tree', t => {
24 24
         args: [
25 25
           new Node({
26 26
             attributeName: 'class',
27
-            attributeValue: 'foobar',
27
+            attributeValue: new Node({type: 'string', content: 'foobar'}),
28 28
           })
29 29
         ],
30 30
         subtree: [
@@ -34,7 +34,7 @@ test('parses token stream into a tree', t => {
34 34
             args: [
35 35
               new Node({
36 36
                 attributeName: 'class',
37
-                attributeValue: 'bazquux',
37
+                attributeValue: new Node({type: 'string', content: 'bazquux'}),
38 38
               })
39 39
             ],
40 40
             subtree: [],

Loading…
Cancel
Save