Browse Source

Allow commas in rule values

master
Dylan Baker 5 years ago
parent
commit
8958d4e0cc
4 changed files with 62 additions and 31 deletions
  1. 10
    6
      src/ast/rule.ts
  2. 10
    1
      src/parser.ts
  3. 2
    2
      src/tests/compiler.test.ts
  4. 40
    22
      src/tests/parser.test.ts

+ 10
- 6
src/ast/rule.ts View File

3
 
3
 
4
 export class Rule {
4
 export class Rule {
5
   public property: AST.Property;
5
   public property: AST.Property;
6
-  public value: AST.Value;
6
+  public values: AST.Value[];
7
 
7
 
8
   public constructor(
8
   public constructor(
9
     property: AST.Property,
9
     property: AST.Property,
10
-    value: AST.Value
10
+    values: AST.Value[]
11
   ) {
11
   ) {
12
     this.property = property;
12
     this.property = property;
13
-    this.value = value;
13
+    this.values = values;
14
   }
14
   }
15
 
15
 
16
   public compile(env: Env, opts: AST.Opts): string | EnvError {
16
   public compile(env: Env, opts: AST.Opts): string | EnvError {
18
     const indentSpacer = opts.prettyPrint ? '  ' + Array(opts.depth).fill(' ').join('') : '';
18
     const indentSpacer = opts.prettyPrint ? '  ' + Array(opts.depth).fill(' ').join('') : '';
19
     const property = this.property.compile(env, opts);
19
     const property = this.property.compile(env, opts);
20
     if (property instanceof EnvError) return property;
20
     if (property instanceof EnvError) return property;
21
-    const value = this.value.compile(env, opts);
22
-    if (value instanceof EnvError) return value;
23
-    return `${indentSpacer}${property}:${wordSpacer}${value};`;
21
+    const values: string[] = [];
22
+    for (const value of this.values) {
23
+      const compiledValue = value.compile(env, opts);
24
+      if (compiledValue instanceof EnvError) return compiledValue;
25
+      values.push(compiledValue);
26
+    }
27
+    return `${indentSpacer}${property}:${wordSpacer}${values.join(',')};`;
24
   }
28
   }
25
 }
29
 }

+ 10
- 1
src/parser.ts View File

311
       const property = this.property();
311
       const property = this.property();
312
       if (property instanceof ParserError) return property;
312
       if (property instanceof ParserError) return property;
313
 
313
 
314
+      const values = [];
314
       const value = this.value();
315
       const value = this.value();
315
       if (value instanceof ParserError) return value;
316
       if (value instanceof ParserError) return value;
317
+      values.push(value);
316
 
318
 
317
-      return new AST.Rule(property, value);
319
+      while (this.currentToken().type === TokenTypes.COMMA) {
320
+        this.eat(TokenTypes.COMMA);
321
+        const value = this.value();
322
+        if (value instanceof ParserError) return value;
323
+        values.push(value);
324
+      }
325
+
326
+      return new AST.Rule(property, values);
318
     } else if (this.currentToken().type === TokenTypes.LPAREN) {
327
     } else if (this.currentToken().type === TokenTypes.LPAREN) {
319
       const next = this.nextToken();
328
       const next = this.nextToken();
320
       if (next.type === TokenTypes.LITERAL) {
329
       if (next.type === TokenTypes.LITERAL) {

+ 2
- 2
src/tests/compiler.test.ts View File

160
     [
160
     [
161
       new AST.Rule(
161
       new AST.Rule(
162
         new AST.Property(new Token(TokenTypes.PROPERTY, 'max-width', 1)),
162
         new AST.Property(new Token(TokenTypes.PROPERTY, 'max-width', 1)),
163
-        new AST.Identifier(new Token(TokenTypes.IDENTIFIER, 'width', 1)),
163
+        [new AST.Identifier(new Token(TokenTypes.IDENTIFIER, 'width', 1))],
164
       ),
164
       ),
165
       new AST.Rule(
165
       new AST.Rule(
166
         new AST.Property(new Token(TokenTypes.PROPERTY, 'margin', 1)),
166
         new AST.Property(new Token(TokenTypes.PROPERTY, 'margin', 1)),
167
-        new AST.Literal(new Token(TokenTypes.LITERAL, 'auto', 1)),
167
+        [new AST.Literal(new Token(TokenTypes.LITERAL, 'auto', 1))],
168
       )
168
       )
169
     ]
169
     ]
170
   ));
170
   ));

+ 40
- 22
src/tests/parser.test.ts View File

71
     t.deepEqual(result.tree, [
71
     t.deepEqual(result.tree, [
72
       new AST.RuleSet(
72
       new AST.RuleSet(
73
         [new AST.Selector(literalToken('.para'))],
73
         [new AST.Selector(literalToken('.para'))],
74
-        [new AST.Rule(property('color'), literalNode('black'))]
74
+        [new AST.Rule(property('color'), [literalNode('black')])]
75
       ),
75
       ),
76
     ]);
76
     ]);
77
   }
77
   }
88
           new AST.Selector(literalToken('.header')),
88
           new AST.Selector(literalToken('.header')),
89
           new AST.Selector(literalToken('.footer')),
89
           new AST.Selector(literalToken('.footer')),
90
         ],
90
         ],
91
-        [new AST.Rule(property('color'), literalNode('black'))]
91
+        [new AST.Rule(property('color'), [literalNode('black')])]
92
       ),
92
       ),
93
     ]);
93
     ]);
94
   }
94
   }
105
       new AST.RuleSet(
105
       new AST.RuleSet(
106
         [new AST.Selector(literalToken('body'))],
106
         [new AST.Selector(literalToken('body'))],
107
         [
107
         [
108
-          new AST.Rule(property('color'), literalNode('black')),
108
+          new AST.Rule(property('color'), [literalNode('black')]),
109
           new AST.RuleSet(
109
           new AST.RuleSet(
110
             [
110
             [
111
               new AST.Selector(literalToken('div'), [
111
               new AST.Selector(literalToken('div'), [
113
               ]),
113
               ]),
114
             ],
114
             ],
115
             [
115
             [
116
-              new AST.Rule(property('color'), literalNode('blue')),
116
+              new AST.Rule(property('color'), [literalNode('blue')]),
117
               new AST.RuleSet(
117
               new AST.RuleSet(
118
                 [
118
                 [
119
                   new AST.Selector(literalToken('span'), [
119
                   new AST.Selector(literalToken('span'), [
122
                     ]),
122
                     ]),
123
                   ]),
123
                   ]),
124
                 ],
124
                 ],
125
-                [new AST.Rule(property('color'), literalNode('red'))]
125
+                [new AST.Rule(property('color'), [literalNode('red')])]
126
               ),
126
               ),
127
             ]
127
             ]
128
           ),
128
           ),
166
           new AST.RuleSet(
166
           new AST.RuleSet(
167
             [new AST.Selector(literalToken('div'))],
167
             [new AST.Selector(literalToken('div'))],
168
             [
168
             [
169
-              new AST.Rule(property('background'), identifierNode('blue')),
170
-              new AST.Rule(property('color'), identifierNode('red')),
169
+              new AST.Rule(property('background'), [identifierNode('blue')]),
170
+              new AST.Rule(property('color'), [identifierNode('red')]),
171
             ]
171
             ]
172
           ),
172
           ),
173
         ]
173
         ]
192
         [
192
         [
193
           new AST.RuleSet(
193
           new AST.RuleSet(
194
             [new AST.Selector(literalToken('div'))],
194
             [new AST.Selector(literalToken('div'))],
195
-            [new AST.Rule(property('background'), identifierNode('blue'))]
195
+            [new AST.Rule(property('background'), [identifierNode('blue')])]
196
           ),
196
           ),
197
           new AST.RuleSet(
197
           new AST.RuleSet(
198
             [new AST.Selector(literalToken('span'))],
198
             [new AST.Selector(literalToken('span'))],
199
-            [new AST.Rule(property('color'), identifierNode('red'))]
199
+            [new AST.Rule(property('color'), [identifierNode('red')])]
200
           ),
200
           ),
201
         ]
201
         ]
202
       ),
202
       ),
218
         [
218
         [
219
           new AST.RuleSet(
219
           new AST.RuleSet(
220
             [new AST.Selector(literalToken('div'))],
220
             [new AST.Selector(literalToken('div'))],
221
-            [new AST.Rule(property('flex-direction'), literalNode('row'))]
221
+            [new AST.Rule(property('flex-direction'), [literalNode('row')])]
222
           ),
222
           ),
223
         ]
223
         ]
224
       ),
224
       ),
235
       new AST.Keyframes(literalNode('fade'), [
235
       new AST.Keyframes(literalNode('fade'), [
236
         new AST.RuleSet(
236
         new AST.RuleSet(
237
           [new AST.Selector(literalToken('0%'))],
237
           [new AST.Selector(literalToken('0%'))],
238
-          [new AST.Rule(property('opacity'), literalNode('1'))]
238
+          [new AST.Rule(property('opacity'), [literalNode('1')])]
239
         ),
239
         ),
240
         new AST.RuleSet(
240
         new AST.RuleSet(
241
           [new AST.Selector(literalToken('100%'))],
241
           [new AST.Selector(literalToken('100%'))],
242
-          [new AST.Rule(property('opacity'), literalNode('0'))]
242
+          [new AST.Rule(property('opacity'), [literalNode('0')])]
243
         ),
243
         ),
244
       ]),
244
       ]),
245
     ]);
245
     ]);
276
         [
276
         [
277
           new AST.Rule(
277
           new AST.Rule(
278
             new AST.Property(propertyToken('max-width')),
278
             new AST.Property(propertyToken('max-width')),
279
-            identifierNode('width')
279
+            [identifierNode('width')]
280
           ),
280
           ),
281
           new AST.Rule(
281
           new AST.Rule(
282
             new AST.Property(propertyToken('margin')),
282
             new AST.Property(propertyToken('margin')),
283
-            literalNode('auto')
283
+            [literalNode('auto')]
284
           ),
284
           ),
285
         ]
285
         ]
286
       ),
286
       ),
304
                 new AST.Selector(literalToken('div')),
304
                 new AST.Selector(literalToken('div')),
305
               ]),
305
               ]),
306
             ],
306
             ],
307
-            [new AST.Rule(property('color'), literalNode('blue'))]
307
+            [new AST.Rule(property('color'), [literalNode('blue')])]
308
           ),
308
           ),
309
-          new AST.Rule(property('color'), literalNode('green')),
309
+          new AST.Rule(property('color'), [literalNode('green')]),
310
         ]
310
         ]
311
       )
311
       )
312
     );
312
     );
323
       new AST.RuleSet(
323
       new AST.RuleSet(
324
         [new AST.Selector(literalToken('div'))],
324
         [new AST.Selector(literalToken('div'))],
325
         [
325
         [
326
-          new AST.Rule(property('color'), literalNode('green')),
326
+          new AST.Rule(property('color'), [literalNode('green')]),
327
           new AST.RuleSet(
327
           new AST.RuleSet(
328
             [
328
             [
329
               new AST.Selector(literalToken('span'), [
329
               new AST.Selector(literalToken('span'), [
330
                 new AST.Selector(literalToken('div')),
330
                 new AST.Selector(literalToken('div')),
331
               ]),
331
               ]),
332
             ],
332
             ],
333
-            [new AST.Rule(property('color'), literalNode('blue'))]
333
+            [new AST.Rule(property('color'), [literalNode('blue')])]
334
           ),
334
           ),
335
         ]
335
         ]
336
       )
336
       )
366
         [
366
         [
367
           new AST.RuleSet(
367
           new AST.RuleSet(
368
             [new AST.Selector(literalToken('div'))],
368
             [new AST.Selector(literalToken('div'))],
369
-            [new AST.Rule(property('color'), literalNode('blue'))]
369
+            [new AST.Rule(property('color'), [literalNode('blue')])]
370
           ),
370
           ),
371
         ]
371
         ]
372
       )
372
       )
392
             [
392
             [
393
               new AST.RuleSet(
393
               new AST.RuleSet(
394
                 [new AST.Selector(literalToken('&'))],
394
                 [new AST.Selector(literalToken('&'))],
395
-                [new AST.Rule(property('color'), literalNode('blue'))]
395
+                [new AST.Rule(property('color'), [literalNode('blue')])]
396
               ),
396
               ),
397
             ]
397
             ]
398
           ),
398
           ),
418
             [
418
             [
419
               new AST.RuleSet(
419
               new AST.RuleSet(
420
                 [new AST.Selector(literalToken('div'))],
420
                 [new AST.Selector(literalToken('div'))],
421
-                [new AST.Rule(property('color'), literalNode('blue'))]
421
+                [new AST.Rule(property('color'), [literalNode('blue')])]
422
               ),
422
               ),
423
             ]
423
             ]
424
           )
424
           )
445
             [
445
             [
446
               new AST.RuleSet(
446
               new AST.RuleSet(
447
                 [new AST.Selector(literalToken('div'))],
447
                 [new AST.Selector(literalToken('div'))],
448
-                [new AST.Rule(property('color'), identifierNode('y'))]
448
+                [new AST.Rule(property('color'), [identifierNode('y')])]
449
               ),
449
               ),
450
             ]
450
             ]
451
           ),
451
           ),
454
     );
454
     );
455
   }
455
   }
456
 });
456
 });
457
+
458
+test('allow multiple values for a rule', (t) => {
459
+  t.plan(2);
460
+  const result = parse('(p :font-family Times, serif)');
461
+  t.false(result instanceof ParserError);
462
+  if (!(result instanceof LexerError) && !(result instanceof ParserError)) {
463
+    t.deepEqual(
464
+      result.tree[0],
465
+      new AST.RuleSet(
466
+        [new AST.Selector(literalToken('p'))],
467
+        [new AST.Rule(
468
+          property('font-family'),
469
+          [literalNode('Times'), literalNode('serif')]
470
+        )]
471
+      )
472
+    )
473
+  }
474
+});

Loading…
Cancel
Save