Przeglądaj źródła

Split last into separate files

master
Dylan Baker 5 lat temu
rodzic
commit
4983044c07

+ 44
- 341
src/ast.ts Wyświetl plik

@@ -1,342 +1,45 @@
1
-import Env, { EnvError } from './env';
2
-import Token from './token';
3
-
4
-export namespace AST {
5
-  export type Child = Rule | RuleSet | Application;
6
-
7
-  export interface Opts {
8
-    depth: number;
9
-    prettyPrint: boolean;
10
-  }
11
-
12
-  const spaces = (depth: number): string => {
13
-    return Array(depth)
14
-      .fill(' ')
15
-      .join('');
16
-  };
17
-
18
-  export class Node {
19
-    public compile(_env: Env, _opts: Opts): string | EnvError {
20
-      return new EnvError(1, "shouldn't call directly");
21
-    }
22
-  }
23
-
24
-  export class Literal extends Node {
25
-    public value: Token;
26
-
27
-    public constructor(value: Token) {
28
-      super();
29
-      this.value = value;
30
-    }
31
-
32
-    public compile(_env: Env, _opts: Opts) {
33
-      return this.value.value;
34
-    }
35
-  }
36
-
37
-  export class Identifier extends Node {
38
-    public name: Token;
39
-
40
-    public constructor(name: Token) {
41
-      super();
42
-      this.name = name;
43
-    }
44
-
45
-    public compile(env: Env, opts: Opts): string | EnvError {
46
-      const value = env.get(this.name);
47
-      if (value instanceof EnvError) return value;
48
-      return value.compile(env, opts);
49
-    }
50
-  }
51
-
52
-  export class Selector extends Node {
53
-    public name: Token;
54
-    public parents: Selector[];
55
-
56
-    public constructor(name: Token, parents: Selector[] = []) {
57
-      super();
58
-      this.name = name;
59
-      this.parents = parents;
60
-    }
61
-
62
-    public getLineages(): string[] {
63
-      if (this.parents.length === 0) return [this.name.value];
64
-      return Array.prototype.concat(
65
-        ...this.parents.map((parent) => {
66
-          return parent.getLineages().map((lineage) => {
67
-            if (this.name.value.match(/^&/)) {
68
-              return lineage + this.name.value.slice(1);
69
-            }
70
-            return lineage + ' ' + this.name.value;
71
-          });
72
-        })
73
-      );
74
-    }
75
-  }
76
-
77
-  export class Property extends Node {
78
-    public name: Token;
79
-
80
-    public constructor(name: Token) {
81
-      super();
82
-      this.name = name;
83
-    }
84
-
85
-    public compile(_env: Env, _opts: Opts): string | EnvError {
86
-      return this.name.value;
87
-    }
88
-  }
89
-
90
-  export class Binding extends Node {
91
-    public identifier: Identifier;
92
-    public value: Node;
93
-
94
-    public constructor(identifier: Identifier, value: Node) {
95
-      super();
96
-      this.identifier = identifier;
97
-      this.value = value;
98
-    }
99
-  }
100
-
101
-  export class Let extends Node {
102
-    public bindings: Binding[];
103
-    public children: Node[];
104
-
105
-    public constructor(bindings: Binding[], children: Node[]) {
106
-      super();
107
-      this.bindings = bindings;
108
-      this.children = children;
109
-    }
110
-
111
-    public compile(env: Env, opts: Opts): string | EnvError {
112
-      this.bindings.forEach((binding) => {
113
-        env.set(binding.identifier.name.value, binding.value);
114
-      });
115
-
116
-      const children = this.children.map((child) => child.compile(env, opts));
117
-      const childrenError = children.find((node) => node instanceof EnvError);
118
-      if (childrenError instanceof EnvError) return childrenError;
119
-
120
-      return children.join(opts.prettyPrint ? '\n' : '');
121
-    }
122
-  }
123
-
124
-  export class MediaQueryPredicate extends Node {
125
-    public property: Property;
126
-    public value: Literal | Identifier | Application;
127
-
128
-    public constructor(
129
-      property: Property,
130
-      value: Literal | Identifier | Application
131
-    ) {
132
-      super();
133
-      this.property = property;
134
-      this.value = value;
135
-    }
136
-
137
-    public compile(env: Env, opts: Opts) {
138
-      const wordSpacer = opts.prettyPrint ? ' ' : '';
139
-      const property = this.property.compile(env, opts);
140
-      const value = this.value.compile(env, opts);
141
-      return `${property}:${wordSpacer}${value}`;
142
-    }
143
-  }
144
-
145
-  export class MediaQuery extends Node {
146
-    public predicate: MediaQueryPredicate;
147
-    public children: Node[];
148
-
149
-    public constructor(predicate: MediaQueryPredicate, children: Node[]) {
150
-      super();
151
-      this.predicate = predicate;
152
-      this.children = children;
153
-    }
154
-
155
-    public compile(env: Env, opts: Opts): string | EnvError {
156
-      const lineSpacer = opts.prettyPrint ? '\n' : '';
157
-      const wordSpacer = opts.prettyPrint ? ' ' : '';
158
-      const predicate = this.predicate.compile(env, opts);
159
-
160
-      const children = this.children.map((child) =>
161
-        child.compile(env, { ...opts, depth: opts.depth + 2 })
162
-      );
163
-      const childrenError = children.find((node) => node instanceof EnvError);
164
-      if (childrenError instanceof EnvError) return childrenError;
165
-
166
-      return `@media(${predicate})${wordSpacer}{${lineSpacer}${children.join(
167
-        lineSpacer
168
-      )}${lineSpacer}}`;
169
-    }
170
-  }
171
-
172
-  export class Keyframes extends Node {
173
-    public name: Literal;
174
-    public children: Node[];
175
-
176
-    public constructor(name: Literal, children: Node[]) {
177
-      super();
178
-      this.name = name;
179
-      this.children = children;
180
-    }
181
-
182
-    public compile(env: Env, opts: Opts) {
183
-      const lineSpacer = opts.prettyPrint ? '\n' : '';
184
-      const wordSpacer = opts.prettyPrint ? ' ' : '';
185
-      const name = this.name.compile(env, opts);
186
-
187
-      const children = this.children.map((child) =>
188
-        child.compile(env, { ...opts, depth: opts.depth + 2 })
189
-      );
190
-      const childrenError = children.find((node) => node instanceof EnvError);
191
-      if (childrenError instanceof EnvError) return childrenError;
192
-
193
-      return `@keyframes ${name}${wordSpacer}{${lineSpacer}${children.join(
194
-        lineSpacer
195
-      )}${lineSpacer}}`;
196
-    }
197
-  }
198
-
199
-  export class Application extends Node {
200
-    public name: Literal;
201
-    public arguments: Node[];
202
-
203
-    public constructor(name: Literal, args: Node[]) {
204
-      super();
205
-      this.name = name;
206
-      this.arguments = args;
207
-    }
208
-
209
-    public compile(env: Env, opts: Opts) {
210
-      const wordSpacer = opts.prettyPrint ? ' ' : '';
211
-      const [lParen, rParen, comma] = ['(', ')', ','];
212
-      const compiledArguments = this.arguments
213
-        .map((arg) => arg.compile(env, opts))
214
-        .join(`${comma}${wordSpacer}`);
215
-      return `${this.name.value.value}${lParen}${compiledArguments}${rParen}`;
216
-    }
217
-  }
218
-
219
-  export class Mixin extends Node {
220
-    public name: Token;
221
-    public parameters: Identifier[];
222
-    public children: Child[];
223
-
224
-    public constructor(
225
-      name: Token,
226
-      parameters: Identifier[],
227
-      children: Child[]
228
-    ) {
229
-      super();
230
-      this.name = name;
231
-      this.parameters = parameters;
232
-      this.children = children;
233
-    }
234
-    public compile(env: Env, _opts: Opts) {
235
-      env.set(this.name.value, this);
236
-      return '';
237
-    }
238
-  }
239
-
240
-  export class Rule extends Node {
241
-    public property: Property;
242
-    public value: Literal | Identifier | Application;
243
-
244
-    public constructor(
245
-      property: Property,
246
-      value: Literal | Identifier | Application
247
-    ) {
248
-      super();
249
-      this.property = property;
250
-      this.value = value;
251
-    }
252
-
253
-    public compile(env: Env, opts: Opts): string | EnvError {
254
-      const wordSpacer = opts.prettyPrint ? ' ' : '';
255
-      const indentSpacer = opts.prettyPrint ? '  ' + spaces(opts.depth) : '';
256
-      const property = this.property.compile(env, opts);
257
-      if (property instanceof EnvError) return property;
258
-      const value = this.value.compile(env, opts);
259
-      if (value instanceof EnvError) return value;
260
-      return `${indentSpacer}${property}:${wordSpacer}${value};`;
261
-    }
262
-  }
263
-
264
-  export class RuleSet extends Node {
265
-    public selectors: Selector[];
266
-    public children: Child[];
267
-
268
-    public constructor(selectors: Selector[], children: Child[] = []) {
269
-      super();
270
-      this.selectors = selectors;
271
-      this.children = children;
272
-    }
273
-
274
-    public compile(env: Env, opts: Opts): string | EnvError {
275
-      const lineSpacer = opts.prettyPrint ? '\n' : '';
276
-      const wordSpacer = opts.prettyPrint ? ' ' : '';
277
-      const indentSpacer = opts.prettyPrint ? spaces(opts.depth) : '';
278
-      const [lBrace, rBrace] = ['{', '}'];
279
-
280
-      const lineages = ([] as string[]).concat(
281
-        ...this.selectors.map((sel) => sel.getLineages())
282
-      );
283
-
284
-      const rules: (String | EnvError)[] = [];
285
-      const children: (String | EnvError)[] = [];
286
-
287
-      for (const child of this.children) {
288
-        if (child instanceof Rule) {
289
-          rules.push(child.compile(env, opts));
290
-        } else if (child instanceof RuleSet) {
291
-          children.push(child.compile(env, opts));
292
-        } else if (child instanceof Application) {
293
-          const mixin = env.get(child.name.value);
294
-          if (mixin instanceof EnvError) return mixin;
295
-          if (mixin instanceof AST.Mixin) {
296
-            if (mixin.parameters.length !== child.arguments.length) {
297
-              return new EnvError(
298
-                child.name.value.line,
299
-                `Expected ${mixin.parameters.length} arguments but received ${
300
-                  child.arguments.length
301
-                }`
302
-              );
303
-            }
304
-            const mixinEnv = new Env(env);
305
-            mixin.parameters.forEach((param, index) => {
306
-              mixinEnv.set(param.name.value, child.arguments[index]);
307
-            });
308
-            mixin.children.forEach((mixinChild) => {
309
-              if (mixinChild instanceof Rule) {
310
-                rules.push(mixinChild.compile(mixinEnv, opts));
311
-              } else if (mixinChild instanceof RuleSet) {
312
-                mixinChild.selectors.map((sel) => {
313
-                  sel.parents = this.selectors;
314
-                });
315
-                children.push(mixinChild.compile(mixinEnv, opts));
316
-              } else {
317
-                console.log(mixinChild);
318
-              }
319
-            });
320
-          }
321
-        }
322
-      }
323
-
324
-      const rulesError = rules.find((node) => node instanceof EnvError);
325
-      if (rulesError instanceof EnvError) return rulesError;
326
-      const childrenError = children.find((node) => node instanceof EnvError);
327
-      if (childrenError instanceof EnvError) return childrenError;
328
-
329
-      const compiledRules = rules.join(lineSpacer);
330
-      const compiledChildren = children.join(lineSpacer);
331
-
332
-      const lineage = lineages.join(`,${wordSpacer}`);
333
-      const frontMatter = `${indentSpacer}${lineage}${wordSpacer}${lBrace}${lineSpacer}`;
334
-      const backMatter = `${lineSpacer}${indentSpacer}${rBrace}${
335
-        compiledChildren.length ? lineSpacer : ''
336
-      }`;
337
-      return rules.length
338
-        ? `${frontMatter}${compiledRules}${backMatter}${compiledChildren}`
339
-        : compiledChildren;
340
-    }
341
-  }
1
+import { Application } from './ast/application';
2
+import { Identifier } from './ast/identifier';
3
+import { Keyframes } from './ast/keyframes';
4
+import { Let } from './ast/let';
5
+import { Literal } from './ast/literal';
6
+import { MediaQuery } from './ast/mediaQuery';
7
+import { MediaQueryPredicate } from './ast/mediaQueryPredicate';
8
+import { Mixin } from './ast/mixin';
9
+import { Property } from './ast/property';
10
+import { Rule } from './ast/rule';
11
+import { RuleSet } from './ast/ruleSet';
12
+
13
+export type Child = Rule | RuleSet | Application;
14
+export type Value = Literal | Identifier | Application;
15
+export type Node
16
+  = Application
17
+  | Identifier
18
+  | Keyframes
19
+  | Let
20
+  | Literal
21
+  | MediaQuery
22
+  | MediaQueryPredicate
23
+  | Mixin
24
+  | Property
25
+  | Rule
26
+  | RuleSet
27
+
28
+export * from './ast/application';
29
+export * from './ast/binding';
30
+export * from './ast/identifier';
31
+export * from './ast/keyframes';
32
+export * from './ast/let';
33
+export * from './ast/literal';
34
+export * from './ast/mediaQuery';
35
+export * from './ast/mediaQueryPredicate';
36
+export * from './ast/mixin';
37
+export * from'./ast/property';
38
+export * from './ast/rule';
39
+export * from'./ast/ruleSet';
40
+export * from'./ast/selector';
41
+
42
+export interface Opts {
43
+  depth: number;
44
+  prettyPrint: boolean;
342 45
 }

+ 21
- 0
src/ast/application.ts Wyświetl plik

@@ -0,0 +1,21 @@
1
+import * as AST from '../ast';
2
+import Env, { EnvError } from '../env';
3
+
4
+export class Application {
5
+  public name: AST.Literal;
6
+  public arguments: AST.Node[];
7
+
8
+  public constructor(name: AST.Literal, args: AST.Node[]) {
9
+    this.name = name;
10
+    this.arguments = args;
11
+  }
12
+
13
+  public compile(env: Env, opts: AST.Opts): string | EnvError {
14
+    const wordSpacer = opts.prettyPrint ? ' ' : '';
15
+    const [lParen, rParen, comma] = ['(', ')', ','];
16
+    const compiledArguments = this.arguments
17
+      .map((arg) => arg.compile(env, opts))
18
+      .join(`${comma}${wordSpacer}`);
19
+    return `${this.name.value.value}${lParen}${compiledArguments}${rParen}`;
20
+  }
21
+}

+ 11
- 0
src/ast/binding.ts Wyświetl plik

@@ -0,0 +1,11 @@
1
+import * as AST from '../ast';
2
+
3
+export class Binding {
4
+  public identifier: AST.Identifier;
5
+  public value: AST.Node;
6
+
7
+  public constructor(identifier: AST.Identifier, value: AST.Node) {
8
+    this.identifier = identifier;
9
+    this.value = value;
10
+  }
11
+}

+ 17
- 0
src/ast/identifier.ts Wyświetl plik

@@ -0,0 +1,17 @@
1
+import * as AST from '../ast';
2
+import Env, { EnvError } from '../env';
3
+import Token from '../token';
4
+
5
+export class Identifier {
6
+  public name: Token;
7
+
8
+  public constructor(name: Token) {
9
+    this.name = name;
10
+  }
11
+
12
+  public compile(env: Env, opts: AST.Opts): string | EnvError {
13
+    const value = env.get(this.name);
14
+    if (value instanceof EnvError) return value;
15
+    return value.compile(env, opts);
16
+  }
17
+}

+ 28
- 0
src/ast/keyframes.ts Wyświetl plik

@@ -0,0 +1,28 @@
1
+import * as AST from '../ast';
2
+import Env, { EnvError } from '../env';
3
+
4
+export class Keyframes {
5
+  public name: AST.Literal;
6
+  public children: AST.Node[];
7
+
8
+  public constructor(name: AST.Literal, children: AST.Node[]) {
9
+    this.name = name;
10
+    this.children = children;
11
+  }
12
+
13
+  public compile(env: Env, opts: AST.Opts): string | EnvError {
14
+    const lineSpacer = opts.prettyPrint ? '\n' : '';
15
+    const wordSpacer = opts.prettyPrint ? ' ' : '';
16
+    const name = this.name.compile(env, opts);
17
+
18
+    const children = this.children.map((child) =>
19
+      child.compile(env, { ...opts, depth: opts.depth + 2 })
20
+    );
21
+    const childrenError = children.find((node) => node instanceof EnvError);
22
+    if (childrenError instanceof EnvError) return childrenError;
23
+
24
+    return `@keyframes ${name}${wordSpacer}{${lineSpacer}${children.join(
25
+      lineSpacer
26
+    )}${lineSpacer}}`;
27
+  }
28
+}

+ 24
- 0
src/ast/let.ts Wyświetl plik

@@ -0,0 +1,24 @@
1
+import * as AST from '../ast';
2
+import Env, { EnvError } from '../env';
3
+
4
+export class Let {
5
+  public bindings: AST.Binding[];
6
+  public children: AST.Node[];
7
+
8
+  public constructor(bindings: AST.Binding[], children: AST.Node[]) {
9
+    this.bindings = bindings;
10
+    this.children = children;
11
+  }
12
+
13
+  public compile(env: Env, opts: AST.Opts): string | EnvError {
14
+    this.bindings.forEach((binding) => {
15
+      env.set(binding.identifier.name.value, binding.value);
16
+    });
17
+
18
+    const children = this.children.map((child) => child.compile(env, opts));
19
+    const childrenError = children.find((node) => node instanceof EnvError);
20
+    if (childrenError instanceof EnvError) return childrenError;
21
+
22
+    return children.join(opts.prettyPrint ? '\n' : '');
23
+  }
24
+}

+ 15
- 0
src/ast/literal.ts Wyświetl plik

@@ -0,0 +1,15 @@
1
+import * as AST from '../ast';
2
+import Env from '../env';
3
+import Token from '../token';
4
+
5
+export class Literal {
6
+  public value: Token;
7
+
8
+  public constructor(value: Token) {
9
+    this.value = value;
10
+  }
11
+
12
+  public compile(_env: Env, _opts: AST.Opts) {
13
+    return this.value.value;
14
+  }
15
+}

+ 28
- 0
src/ast/mediaQuery.ts Wyświetl plik

@@ -0,0 +1,28 @@
1
+import * as AST from '../ast';
2
+import Env, { EnvError } from '../env';
3
+
4
+export class MediaQuery {
5
+  public predicate: AST.MediaQueryPredicate;
6
+  public children: AST.Node[];
7
+
8
+  public constructor(predicate: AST.MediaQueryPredicate, children: AST.Node[]) {
9
+    this.predicate = predicate;
10
+    this.children = children;
11
+  }
12
+
13
+  public compile(env: Env, opts: AST.Opts): string | EnvError {
14
+    const lineSpacer = opts.prettyPrint ? '\n' : '';
15
+    const wordSpacer = opts.prettyPrint ? ' ' : '';
16
+    const predicate = this.predicate.compile(env, opts);
17
+
18
+    const children = this.children.map((child) =>
19
+      child.compile(env, { ...opts, depth: opts.depth + 2 })
20
+    );
21
+    const childrenError = children.find((node) => node instanceof EnvError);
22
+    if (childrenError instanceof EnvError) return childrenError;
23
+
24
+    return `@media(${predicate})${wordSpacer}{${lineSpacer}${children.join(
25
+      lineSpacer
26
+    )}${lineSpacer}}`;
27
+  }
28
+}

+ 22
- 0
src/ast/mediaQueryPredicate.ts Wyświetl plik

@@ -0,0 +1,22 @@
1
+import * as AST from '../ast';
2
+import Env from '../env';
3
+
4
+export class MediaQueryPredicate {
5
+  public property: AST.Property;
6
+  public value: AST.Value;
7
+
8
+  public constructor(
9
+    property: AST.Property,
10
+    value: AST.Value
11
+  ) {
12
+    this.property = property;
13
+    this.value = value;
14
+  }
15
+
16
+  public compile(env: Env, opts: AST.Opts) {
17
+    const wordSpacer = opts.prettyPrint ? ' ' : '';
18
+    const property = this.property.compile(env, opts);
19
+    const value = this.value.compile(env, opts);
20
+    return `${property}:${wordSpacer}${value}`;
21
+  }
22
+}

+ 23
- 0
src/ast/mixin.ts Wyświetl plik

@@ -0,0 +1,23 @@
1
+import * as AST from '../ast';
2
+import Env from '../env';
3
+import Token from '../token';
4
+
5
+export class Mixin {
6
+  public name: Token;
7
+  public parameters: AST.Identifier[];
8
+  public children: AST.Child[];
9
+
10
+  public constructor(
11
+    name: Token,
12
+    parameters: AST.Identifier[],
13
+    children: AST.Child[]
14
+  ) {
15
+    this.name = name;
16
+    this.parameters = parameters;
17
+    this.children = children;
18
+  }
19
+  public compile(env: Env, _opts: AST.Opts) {
20
+    env.set(this.name.value, this);
21
+    return '';
22
+  }
23
+}

+ 15
- 0
src/ast/property.ts Wyświetl plik

@@ -0,0 +1,15 @@
1
+import * as AST from '../ast';
2
+import Env, { EnvError } from '../env';
3
+import Token from '../token';
4
+
5
+export class Property {
6
+  public name: Token;
7
+
8
+  public constructor(name: Token) {
9
+    this.name = name;
10
+  }
11
+
12
+  public compile(_env: Env, _opts: AST.Opts): string | EnvError {
13
+    return this.name.value;
14
+  }
15
+}

+ 25
- 0
src/ast/rule.ts Wyświetl plik

@@ -0,0 +1,25 @@
1
+import * as AST from '../ast';
2
+import Env, { EnvError } from '../env';
3
+
4
+export class Rule {
5
+  public property: AST.Property;
6
+  public value: AST.Value;
7
+
8
+  public constructor(
9
+    property: AST.Property,
10
+    value: AST.Value
11
+  ) {
12
+    this.property = property;
13
+    this.value = value;
14
+  }
15
+
16
+  public compile(env: Env, opts: AST.Opts): string | EnvError {
17
+    const wordSpacer = opts.prettyPrint ? ' ' : '';
18
+    const indentSpacer = opts.prettyPrint ? '  ' + Array(opts.depth).fill(' ').join('') : '';
19
+    const property = this.property.compile(env, opts);
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};`;
24
+  }
25
+}

+ 80
- 0
src/ast/ruleSet.ts Wyświetl plik

@@ -0,0 +1,80 @@
1
+import * as AST from '../ast';
2
+import Env, { EnvError } from '../env';
3
+
4
+export class RuleSet {
5
+  public selectors: AST.Selector[];
6
+  public children: AST.Child[];
7
+
8
+  public constructor(selectors: AST.Selector[], children: AST.Child[] = []) {
9
+    this.selectors = selectors;
10
+    this.children = children;
11
+  }
12
+
13
+  public compile(env: Env, opts: AST.Opts): string | EnvError {
14
+    const lineSpacer = opts.prettyPrint ? '\n' : '';
15
+    const wordSpacer = opts.prettyPrint ? ' ' : '';
16
+    const indentSpacer = opts.prettyPrint ? Array(opts.depth).fill(' ').join('') : '';
17
+    const [lBrace, rBrace] = ['{', '}'];
18
+
19
+    const lineages = ([] as string[]).concat(
20
+      ...this.selectors.map((sel) => sel.getLineages())
21
+    );
22
+
23
+    const rules: (String | EnvError)[] = [];
24
+    const children: (String | EnvError)[] = [];
25
+
26
+    for (const child of this.children) {
27
+      if (child instanceof AST.Rule) {
28
+        rules.push(child.compile(env, opts));
29
+      } else if (child instanceof RuleSet) {
30
+        children.push(child.compile(env, opts));
31
+      } else if (child instanceof AST.Application) {
32
+        const mixin = env.get(child.name.value);
33
+        if (mixin instanceof EnvError) return mixin;
34
+        if (mixin instanceof AST.Mixin) {
35
+          if (mixin.parameters.length !== child.arguments.length) {
36
+            return new EnvError(
37
+              child.name.value.line,
38
+              `Expected ${mixin.parameters.length} arguments but received ${
39
+                child.arguments.length
40
+              }`
41
+            );
42
+          }
43
+          const mixinEnv = new Env(env);
44
+          mixin.parameters.forEach((param, index) => {
45
+            mixinEnv.set(param.name.value, child.arguments[index]);
46
+          });
47
+          mixin.children.forEach((mixinChild) => {
48
+            if (mixinChild instanceof AST.Rule) {
49
+              rules.push(mixinChild.compile(mixinEnv, opts));
50
+            } else if (mixinChild instanceof RuleSet) {
51
+              mixinChild.selectors.map((sel) => {
52
+                sel.parents = this.selectors;
53
+              });
54
+              children.push(mixinChild.compile(mixinEnv, opts));
55
+            } else {
56
+              console.log(mixinChild);
57
+            }
58
+          });
59
+        }
60
+      }
61
+    }
62
+
63
+    const rulesError = rules.find((node) => node instanceof EnvError);
64
+    if (rulesError instanceof EnvError) return rulesError;
65
+    const childrenError = children.find((node) => node instanceof EnvError);
66
+    if (childrenError instanceof EnvError) return childrenError;
67
+
68
+    const compiledRules = rules.join(lineSpacer);
69
+    const compiledChildren = children.join(lineSpacer);
70
+
71
+    const lineage = lineages.join(`,${wordSpacer}`);
72
+    const frontMatter = `${indentSpacer}${lineage}${wordSpacer}${lBrace}${lineSpacer}`;
73
+    const backMatter = `${lineSpacer}${indentSpacer}${rBrace}${
74
+      compiledChildren.length ? lineSpacer : ''
75
+    }`;
76
+    return rules.length
77
+      ? `${frontMatter}${compiledRules}${backMatter}${compiledChildren}`
78
+      : compiledChildren;
79
+  }
80
+}

+ 25
- 0
src/ast/selector.ts Wyświetl plik

@@ -0,0 +1,25 @@
1
+import Token from '../token';
2
+
3
+export class Selector {
4
+  public name: Token;
5
+  public parents: Selector[];
6
+
7
+  public constructor(name: Token, parents: Selector[] = []) {
8
+    this.name = name;
9
+    this.parents = parents;
10
+  }
11
+
12
+  public getLineages(): string[] {
13
+    if (this.parents.length === 0) return [this.name.value];
14
+    return Array.prototype.concat(
15
+      ...this.parents.map((parent) => {
16
+        return parent.getLineages().map((lineage) => {
17
+          if (this.name.value.match(/^&/)) {
18
+            return lineage + this.name.value.slice(1);
19
+          }
20
+          return lineage + ' ' + this.name.value;
21
+        });
22
+      })
23
+    );
24
+  }
25
+}

+ 1
- 1
src/compiler.ts Wyświetl plik

@@ -1,4 +1,4 @@
1
-import { AST } from './ast';
1
+import * as AST from './ast';
2 2
 import Env, { EnvError } from './env';
3 3
 
4 4
 export interface CompilerOpts {

+ 1
- 1
src/env.ts Wyświetl plik

@@ -1,4 +1,4 @@
1
-import { AST } from './ast';
1
+import * as AST from './ast';
2 2
 import { Error } from './error';
3 3
 import Token from './token';
4 4
 

+ 1
- 1
src/parser.ts Wyświetl plik

@@ -1,4 +1,4 @@
1
-import { AST } from './ast';
1
+import * as AST from './ast';
2 2
 import { Error } from './error';
3 3
 import Token, { TokenTypes } from './token';
4 4
 

+ 1
- 1
src/tests/compiler.test.ts Wyświetl plik

@@ -1,6 +1,6 @@
1 1
 import * as test from 'tape';
2 2
 
3
-import { AST } from '../ast';
3
+import * as AST from '../ast';
4 4
 import Compiler, { CompilerOpts } from '../compiler';
5 5
 import Env, { EnvError } from '../env';
6 6
 import Lexer, { LexerError, LexerResult } from '../lexer';

+ 1
- 1
src/tests/parser.test.ts Wyświetl plik

@@ -1,6 +1,6 @@
1 1
 import * as test from 'tape';
2 2
 
3
-import { AST } from '../ast';
3
+import * as AST from '../ast';
4 4
 import Lexer, { LexerError, LexerResult } from '../lexer';
5 5
 import Parser, { ParserError, ParserResult } from '../parser';
6 6
 import Token, { TokenTypes } from '../token';

Ładowanie…
Anuluj
Zapisz