Skip to content

Commit ea54eab

Browse files
committed
Start fleshing out AST for component values
1 parent aa1c444 commit ea54eab

File tree

3 files changed

+175
-32
lines changed

3 files changed

+175
-32
lines changed

packages/cascada/index.js

+86-30
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
const inspect = require('util').inspect;
2-
const css = require('../parse-css');
1+
// var inspect = require('util').inspect;
2+
var css = require('../parse-css');
3+
4+
module.exports.types = require('./types');
35

46
module.exports.parse = function parse(source) {
57
return acceptStylesheet(
@@ -21,7 +23,7 @@ function acceptRules(nodes) {
2123
function acceptRule(node) {
2224
switch (node.type) {
2325
case 'QUALIFIED-RULE': return acceptStyleRule(node);
24-
case 'AT-RULE': return acceptAtRule(node);
26+
case 'AT-RULE': return acceptAtRule(node);
2527
default: throw new Error("Unknown rule type: " + node.type);
2628
}
2729
}
@@ -53,7 +55,7 @@ function acceptDeclarations(tokens, acceptor) {
5355
function acceptStyleDeclaration(node) {
5456
switch (node.type) {
5557
case 'DECLARATION': return acceptStylePropertyDeclaration(node);
56-
case 'AT-RULE': return acceptAtRule(node);
58+
case 'AT-RULE': return acceptAtRule(node);
5759
default: throw new Error("Unknown style declaration type: " + node.type);
5860
}
5961
}
@@ -63,39 +65,97 @@ function acceptStylePropertyDeclaration(node) {
6365
type: 'StylePropertyDeclaration',
6466
important: node.important,
6567
name: node.name,
66-
value: acceptContextualValue(node.name, node.value)
68+
values: acceptComponentValues(node.value)
6769
};
6870
}
6971

70-
function acceptContextualValue(name, parts) {
71-
parts = trimWhitespaceTokens(parts);
72+
function acceptComponentValues(parts) {
73+
return trimWhitespaceTokens(parts).map(acceptComponentValue);
74+
}
7275

73-
if (name[0] === '-' && name[1] === '-') {
74-
return acceptCustomPropertyValue(name, parts);
75-
} else {
76-
return acceptStyleValue(name, parts);
76+
function acceptComponentValue(node) {
77+
switch (node.tokenType) {
78+
case 'WHITESPACE': return acceptToken(node);
79+
case 'DELIM': return acceptToken(node);
80+
case ',': return acceptToken(node);
81+
case 'IDENT': return acceptIdent(node);
82+
case 'HASH': return acceptHash(node);
83+
case 'STRING': return acceptString(node);
84+
case 'NUMBER': return acceptNumber(node);
85+
case 'DIMENSION': return acceptDimension(node);
86+
case 'PERCENTAGE': return acceptPercentage(node);
7787
}
78-
}
7988

80-
function acceptCustomPropertyValue(name, parts) {
81-
if (parts[0].type === 'BLOCK') {
82-
return acceptMixin(parts);
83-
} else {
84-
return acceptStyleValue(name, parts);
89+
switch (node.type) {
90+
// case 'integer': return acceptInteger(node);
91+
case 'FUNCTION': return acceptFunction(node);
8592
}
93+
94+
throw new Error("Unknown component value: <" + node.tokenType + ", " + node.type + ">");
95+
}
96+
97+
function acceptToken(node) {
98+
return {
99+
type: "Token",
100+
source: node.toSource()
101+
};
86102
}
87103

88-
function acceptMixin(parts) {
89-
var block = parts[0];
104+
function acceptIdent(node) {
105+
return {
106+
type: "Ident",
107+
name: node.value,
108+
source: node.toSource()
109+
};
110+
}
90111

112+
function acceptHash(node) {
91113
return {
92-
type: 'Mixin',
93-
body: acceptDeclarations(block.value, acceptMixinStyleDeclaration)
114+
type: "Hash",
115+
value: node.value,
116+
source: node.toSource()
94117
};
95118
}
96119

97-
function acceptStyleValue(name, parts) {
98-
return printValue(parts);
120+
function acceptString(node) {
121+
return {
122+
type: "String",
123+
value: node.value,
124+
source: node.toSource()
125+
};
126+
}
127+
128+
function acceptNumber(node) {
129+
return {
130+
type: "Number",
131+
value: node.value,
132+
source: node.toSource()
133+
};
134+
}
135+
136+
function acceptDimension(node) {
137+
return {
138+
type: "Dimension",
139+
value: node.value,
140+
unit: node.unit,
141+
source: node.toSource()
142+
};
143+
}
144+
145+
function acceptPercentage(node) {
146+
return {
147+
type: "Percentage",
148+
value: node.value,
149+
source: node.toSource()
150+
};
151+
}
152+
153+
function acceptFunction(node) {
154+
return {
155+
type: "Function",
156+
name: node.name,
157+
args: acceptComponentValues(node.value)
158+
};
99159
}
100160

101161
function printValue(parts) {
@@ -110,11 +170,11 @@ function printValue(parts) {
110170
}).join('');
111171
}
112172

113-
function acceptStyleValueParts(name, parts) {
114-
return parts.map(acceptStyleValuePart);
173+
function acceptComponentValueParts(name, parts) {
174+
return parts.map(acceptComponentValuePart);
115175
}
116176

117-
function acceptStyleValuePart(node) {
177+
function acceptComponentValuePart(node) {
118178
if (node.tokenType) {
119179
return node;
120180
}
@@ -125,10 +185,6 @@ function acceptStyleValuePart(node) {
125185
}
126186
}
127187

128-
function acceptFunction(node) {
129-
throw new Error("Functions in property values (e.g. calc and color) are not yet supported.");
130-
}
131-
132188
function acceptMixinStyleDeclaration(node) {
133189
switch (node.type) {
134190
case 'DECLARATION': return acceptStylePropertyDeclaration(node);

packages/cascada/types.js

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
const inspect = require('util').inspect;
2+
const css = require('../parse-css');
3+
4+
module.exports.tok = tok;
5+
module.exports.ws = ws;
6+
module.exports.comma = comma;
7+
8+
module.exports.number = number;
9+
module.exports.ident = ident;
10+
module.exports.func = func;
11+
12+
function tok(source) {
13+
return {
14+
type: 'Token',
15+
source: source
16+
}
17+
};
18+
19+
function number(value) {
20+
return {
21+
type: 'Number',
22+
value: value,
23+
source: value,
24+
}
25+
};
26+
27+
function ident(name) {
28+
return {
29+
type: 'Ident',
30+
name: name,
31+
source: name
32+
};
33+
};
34+
35+
function func(name, args) {
36+
return {
37+
type: 'Function',
38+
name: name,
39+
args: args
40+
};
41+
};
42+
43+
function ws() {
44+
return tok(' ');
45+
};
46+
47+
function comma() {
48+
return tok(',');
49+
};

test/ast.js

+40-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
var assert = require('assert');
2-
var parse = require('../packages/cascada').parse;
2+
var cascada = require('../packages/cascada');
3+
var parse = cascada.parse;
4+
var t = cascada.types;
35

46
describe("Cascada", function() {
57
it("accepts at-rules", function() {
@@ -26,7 +28,43 @@ describe("Cascada", function() {
2628
type: 'StylePropertyDeclaration',
2729
important: false,
2830
name: 'color',
29-
value: 'red'
31+
values: [ t.ident('red') ]
3032
});
3133
});
34+
35+
it("accepts multiple component values in a declaration", function() {
36+
var ss = parse("a { color: red blue; }");
37+
assert.strictEqual(ss.rules.length, 1);
38+
assert.strictEqual(ss.rules[0].type, 'StyleRule');
39+
assert.deepEqual(ss.rules[0].selectors, ['a']);
40+
assert.strictEqual(ss.rules[0].body.length, 1);
41+
assert.deepEqual(ss.rules[0].body[0], {
42+
type: 'StylePropertyDeclaration',
43+
important: false,
44+
name: 'color',
45+
values: [ t.ident('red'), t.ws(), t.ident('blue') ]
46+
});
47+
});
48+
49+
it("accepts nested function values in style rules", function() {
50+
var ss = parse("a { color: rgba(255, 123, 17, var(--alpha)); }");
51+
assert.strictEqual(ss.rules.length, 1);
52+
assert.strictEqual(ss.rules[0].type, 'StyleRule');
53+
assert.deepEqual(ss.rules[0].selectors, ['a']);
54+
assert.strictEqual(ss.rules[0].body.length, 1);
55+
assert.deepEqual(ss.rules[0].body[0], {
56+
type: 'StylePropertyDeclaration',
57+
important: false,
58+
name: 'color',
59+
values: [
60+
t.func('rgba', [
61+
t.number(255), t.comma(), t.ws(),
62+
t.number(123), t.comma(), t.ws(),
63+
t.number(17), t.comma(), t.ws(),
64+
t.func('var', [ t.ident('--alpha') ]),
65+
])
66+
]
67+
});
68+
});
69+
3270
});

0 commit comments

Comments
 (0)