Browse Source

Allow using variables as attribute values

master
Dylan Baker 6 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
 module.exports = class Compiler {
1
 module.exports = class Compiler {
2
-  constructor(tree) {
2
+  constructor(tree, context) {
3
     this.tree = tree
3
     this.tree = tree
4
+    this.context = context
4
     this.result = ''
5
     this.result = ''
5
   }
6
   }
6
 
7
 
7
   compile() {
8
   compile() {
8
     this.tree.forEach(node => {
9
     this.tree.forEach(node => {
9
       if (node.type === 'functionCall') {
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
         const content = compiler.compile()
13
         const content = compiler.compile()
13
         this.result += `
14
         this.result += `
14
           <${node.functionName}${attributes.length ? ' ' : ''}${attributes.join(' ')}>
15
           <${node.functionName}${attributes.length ? ' ' : ''}${attributes.join(' ')}>
15
             ${content}
16
             ${content}
16
           </${node.functionName}>
17
           </${node.functionName}>
17
         `
18
         `
18
-      } else if (node.type === 'text') {
19
+      } else if (node.type === 'string') {
19
         this.result += node.content
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
     return this.result
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
         elementNode.args.push(this.attribute())
28
         elementNode.args.push(this.attribute())
29
       } else if (this.tokenStream.peek().type === tokenTypes.OPAREN) {
29
       } else if (this.tokenStream.peek().type === tokenTypes.OPAREN) {
30
         elementNode.subtree.push(this.expr())
30
         elementNode.subtree.push(this.expr())
31
+      } else if (this.tokenStream.peek().type === tokenTypes.LITERAL) {
32
+        elementNode.subtree.push(this.identifier())
31
       } else if (this.tokenStream.peek().type === tokenTypes.QUOTE) {
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
   attribute() {
43
   attribute() {
45
     let attributeNode = new Node()
44
     let attributeNode = new Node()
46
     attributeNode.attributeName = this.tokenStream.eat(tokenTypes.ATTRIBUTE).value
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
     return attributeNode
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
   string() {
70
   string() {
52
     this.tokenStream.eat(tokenTypes.QUOTE)
71
     this.tokenStream.eat(tokenTypes.QUOTE)
53
     let stringValue = this.tokenStream.eat(tokenTypes.LITERAL).value
72
     let stringValue = this.tokenStream.eat(tokenTypes.LITERAL).value

+ 21
- 0
test/compiler.js View File

22
     '<div class="foobar"><p class="bazquux">Lorem ipsum dolor sit amet.</p></div>'
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
         args: [
24
         args: [
25
           new Node({
25
           new Node({
26
             attributeName: 'class',
26
             attributeName: 'class',
27
-            attributeValue: 'foobar',
27
+            attributeValue: new Node({type: 'string', content: 'foobar'}),
28
           })
28
           })
29
         ],
29
         ],
30
         subtree: [
30
         subtree: [
34
             args: [
34
             args: [
35
               new Node({
35
               new Node({
36
                 attributeName: 'class',
36
                 attributeName: 'class',
37
-                attributeValue: 'bazquux',
37
+                attributeValue: new Node({type: 'string', content: 'bazquux'}),
38
               })
38
               })
39
             ],
39
             ],
40
             subtree: [],
40
             subtree: [],

Loading…
Cancel
Save