소스 검색

Implement mapping

master
Dylan Baker 7 년 전
부모
커밋
db64e79472
6개의 변경된 파일92개의 추가작업 그리고 12개의 파일을 삭제
  1. 12
    2
      bin/oslo.js
  2. 17
    3
      src/compiler.js
  3. 25
    7
      src/lexer.js
  4. 18
    0
      src/parser.js
  5. 1
    0
      src/tokenTypes.js
  6. 19
    0
      test/compiler.js

+ 12
- 2
bin/oslo.js 파일 보기

8
 
8
 
9
 class OsloCLI {
9
 class OsloCLI {
10
   constructor(opts) {
10
   constructor(opts) {
11
+    this.context = {}
12
+
13
+    if (opts.l) {
14
+      this.context = JSON.parse(fs.readFileSync(this.absolutePath(opts.l)))
15
+    }
16
+
11
     let output
17
     let output
12
 
18
 
13
     if (opts.f && opts.f !== true) {
19
     if (opts.f && opts.f !== true) {
62
 
68
 
63
   file(file) {
69
   file(file) {
64
     const contents = fs.readFileSync(file).toString()
70
     const contents = fs.readFileSync(file).toString()
65
-    return oslo(contents)
71
+    return oslo(contents, this.context)
66
   }
72
   }
67
 
73
 
68
   directory(directory) {
74
   directory(directory) {
80
   }
86
   }
81
 
87
 
82
   inline(source) {
88
   inline(source) {
83
-    return oslo(source)
89
+    return oslo(source, this.context)
84
   }
90
   }
85
 
91
 
86
   absolutePath(file, root = null) {
92
   absolutePath(file, root = null) {
107
 
113
 
108
       oslo -f index.oslo
114
       oslo -f index.oslo
109
 
115
 
116
+  -l The path to a json file to use as context for compilation
117
+
118
+      oslo -f index.oslo -l data.json
119
+
110
   -o Where to direct the output. If no path is provided, output will be directed
120
   -o Where to direct the output. If no path is provided, output will be directed
111
      to stdout. When used with -f, the argument should be the path to a file.
121
      to stdout. When used with -f, the argument should be the path to a file.
112
      When used with -d, the argument should be the path to a directory, which
122
      When used with -d, the argument should be the path to a directory, which

+ 17
- 3
src/compiler.js 파일 보기

16
         )
16
         )
17
         const compiler = new Compiler(node.subtree, this.context)
17
         const compiler = new Compiler(node.subtree, this.context)
18
         const content = compiler.compile()
18
         const content = compiler.compile()
19
-        this.result += `<${node.functionName}${
20
-          attributes.length ? ' ' : ''
21
-        }${attributes.join(' ')}>${content}</${node.functionName}>`
19
+
20
+        if (node.functionName) {
21
+          this.result += `<${node.functionName}${
22
+            attributes.length ? ' ' : ''
23
+          }${attributes.join(' ')}>${content}</${node.functionName}>`
24
+        } else {
25
+          this.result += content
26
+        }
22
       } else if (node.type === 'string') {
27
       } else if (node.type === 'string') {
23
         this.result += node.content
28
         this.result += node.content
24
       } else if (node.type === 'identifier') {
29
       } else if (node.type === 'identifier') {
25
         this.result += this.lookup(node.name)
30
         this.result += this.lookup(node.name)
31
+      } else if (node.type === 'map') {
32
+        const symbol = node.symbol.value
33
+        const subject = this.lookup(node.subject.name)
34
+        subject.forEach(item => {
35
+          let context = {}
36
+          context[symbol] = item
37
+          const compiler = new Compiler([node.body], context)
38
+          this.result += compiler.compile()
39
+        })
26
       }
40
       }
27
     })
41
     })
28
 
42
 

+ 25
- 7
src/lexer.js 파일 보기

21
           line: line,
21
           line: line,
22
         })
22
         })
23
         pos++
23
         pos++
24
-      } else if (source[pos].match(/['"]/)) {
24
+      } else if (source[pos].match(/["]/)) {
25
         allowSpecialCharactersInLiterals = !allowSpecialCharactersInLiterals
25
         allowSpecialCharactersInLiterals = !allowSpecialCharactersInLiterals
26
         tokenStream.tokens.push({
26
         tokenStream.tokens.push({
27
           type: tokenTypes.QUOTE,
27
           type: tokenTypes.QUOTE,
36
           value: value,
36
           value: value,
37
         })
37
         })
38
         pos += value.length + 1 // the +1 is to account for the colon
38
         pos += value.length + 1 // the +1 is to account for the colon
39
+      } else if (source[pos].match(/\'/)) {
40
+        let value = /'([^()"\s]+)/.exec(source.slice(pos))[1].trim()
41
+        tokenStream.tokens.push({
42
+          type: tokenTypes.SYMBOL,
43
+          line: line,
44
+          value: value,
45
+        })
46
+        pos += value.length + 1 // the +1 is to account for the apostrophe
39
       } else if (source[pos].match(/\n/)) {
47
       } else if (source[pos].match(/\n/)) {
40
         line++
48
         line++
41
         pos++
49
         pos++
48
           endPattern = /[^"']+/
56
           endPattern = /[^"']+/
49
         }
57
         }
50
 
58
 
51
-        let value = endPattern.exec(source.slice(pos))[0]
52
-        tokenStream.tokens.push({
53
-          type: tokenTypes.LITERAL,
54
-          line: line,
55
-          value: value.trim(),
56
-        })
59
+        let value = endPattern.exec(source.slice(pos))[0].trim()
60
+
61
+        if (['if', 'map'].includes(value)) {
62
+          tokenStream.tokens.push({
63
+            type: tokenTypes.KEYWORD,
64
+            line: line,
65
+            value: value,
66
+          })
67
+        } else {
68
+          tokenStream.tokens.push({
69
+            type: tokenTypes.LITERAL,
70
+            line: line,
71
+            value: value.trim(),
72
+          })
73
+        }
74
+
57
         pos += value.length
75
         pos += value.length
58
       }
76
       }
59
     }
77
     }

+ 18
- 0
src/parser.js 파일 보기

35
         elementNode.subtree.push(this.identifier())
35
         elementNode.subtree.push(this.identifier())
36
       } else if (this.tokenStream.peek().type === tokenTypes.QUOTE) {
36
       } else if (this.tokenStream.peek().type === tokenTypes.QUOTE) {
37
         elementNode.subtree.push(this.quotedString())
37
         elementNode.subtree.push(this.quotedString())
38
+      } else if (this.tokenStream.peek().type === tokenTypes.KEYWORD) {
39
+        elementNode.subtree.push(this.keyword())
38
       }
40
       }
39
     }
41
     }
40
 
42
 
72
     })
74
     })
73
   }
75
   }
74
 
76
 
77
+  keyword() {
78
+    const keyword = this.tokenStream.eat(tokenTypes.KEYWORD)
79
+
80
+    if (keyword.value === 'map') {
81
+      const symbol = this.tokenStream.eat(tokenTypes.SYMBOL)
82
+      const body = this.expr()
83
+      const subject = this.identifier()
84
+      return new Node({
85
+        type: 'map',
86
+        symbol: symbol,
87
+        body: body,
88
+        subject: subject,
89
+      })
90
+    }
91
+  }
92
+
75
   string() {
93
   string() {
76
     this.tokenStream.eat(tokenTypes.QUOTE)
94
     this.tokenStream.eat(tokenTypes.QUOTE)
77
     let stringValue = this.tokenStream.eat(tokenTypes.LITERAL).value
95
     let stringValue = this.tokenStream.eat(tokenTypes.LITERAL).value

+ 1
- 0
src/tokenTypes.js 파일 보기

5
   QUOTE: '"',
5
   QUOTE: '"',
6
   LITERAL: 'literal',
6
   LITERAL: 'literal',
7
   ATTRIBUTE: 'attribute',
7
   ATTRIBUTE: 'attribute',
8
+  SYMBOL: 'symbol',
8
   EOF: 'eof',
9
   EOF: 'eof',
9
 }
10
 }

+ 19
- 0
test/compiler.js 파일 보기

43
     '<div class="foobar"><p class="bazquux">Lorem ipsum dolor sit amet.</p></div>',
43
     '<div class="foobar"><p class="bazquux">Lorem ipsum dolor sit amet.</p></div>',
44
   )
44
   )
45
 })
45
 })
46
+
47
+test('compiles map operations', function(t) {
48
+  t.plan(1)
49
+  const lexer = new Lexer()
50
+  const tokenStream = lexer.scan(`
51
+    (ul
52
+      (map 'item (li item) items))
53
+  `)
54
+  const parser = new Parser(tokenStream)
55
+  const tree = parser.parse()
56
+  const compiler = new Compiler(tree, {
57
+    items: ['one', 'two', 'three'],
58
+  })
59
+  const result = compiler.compile()
60
+  t.deepEqual(
61
+    result.replace(/\n/g, '').replace(/  +/g, ''),
62
+    '<ul><li>one</li><li>two</li><li>three</li></ul>',
63
+  )
64
+})

Loading…
취소
저장