Browse Source

Allow using mixins inside mixins

master
Dylan Baker 3 years ago
parent
commit
e415b5b7fd
2 changed files with 53 additions and 9 deletions
  1. 51
    7
      src/ast/mixin.ts
  2. 2
    2
      src/env.ts

+ 51
- 7
src/ast/mixin.ts View File

@@ -3,8 +3,8 @@ import Env, { EnvError } from '../env';
3 3
 import Token from '../token';
4 4
 
5 5
 interface Expansion {
6
-  rules: (String | EnvError)[];
7
-  children: (String | EnvError)[];
6
+  rules: (string | EnvError)[];
7
+  children: (string | EnvError)[];
8 8
 }
9 9
 
10 10
 export class Mixin {
@@ -42,24 +42,68 @@ export class Mixin {
42 42
       );
43 43
     }
44 44
 
45
+    const expandedArgs: AST.Node[] = [];
46
+    for (const arg of args) {
47
+      if (arg instanceof AST.Identifier) {
48
+        const lookup = env.get(arg.name);
49
+        if (lookup instanceof EnvError) return lookup;
50
+        expandedArgs.push(lookup);
51
+      }
52
+
53
+      expandedArgs.push(arg);
54
+    }
55
+
45 56
     const mixinEnv = new Env(env);
46 57
     this.parameters.forEach((param, index) => {
47
-      mixinEnv.set(param.name.value, args[index]);
58
+      mixinEnv.set(param.name.value, expandedArgs[index]);
48 59
     });
49 60
 
50 61
     const rules = this.children
51 62
       .filter((child: AST.Node): child is AST.Rule => child instanceof AST.Rule)
52
-      .map((child: AST.Rule) => child.compile(mixinEnv, opts));
63
+      .map((rule: AST.Rule) => rule.compile(mixinEnv, opts));
53 64
 
54 65
     const children = this.children
55 66
       .filter(
56 67
         (child: AST.Node): child is AST.RuleSet => child instanceof AST.RuleSet
57 68
       )
58
-      .map((child: AST.RuleSet) => {
59
-        child.selectors.forEach((sel) => (sel.parents = parents));
60
-        return child.compile(mixinEnv, opts);
69
+      .map((ruleSet: AST.RuleSet) => {
70
+        ruleSet.selectors.forEach((sel) => (sel.parents = parents));
71
+        return ruleSet.compile(mixinEnv, opts);
61 72
       });
62 73
 
74
+    const applicationExpansions: (Expansion | EnvError)[] = this.children
75
+      .filter(
76
+        (child: AST.Node): child is AST.Application =>
77
+          child instanceof AST.Application
78
+      )
79
+      .map((application: AST.Application) => {
80
+        const mixin = mixinEnv.get(application.name.value);
81
+        if (mixin instanceof EnvError) {
82
+          return mixin;
83
+        } else if (mixin instanceof AST.Mixin) {
84
+          return mixin.expand(mixinEnv, opts, parents, application.arguments);
85
+        } else {
86
+          return new EnvError(1, 'Expected a mixin');
87
+        }
88
+      });
89
+
90
+    const expansionError = applicationExpansions.find(
91
+      (el) => el instanceof EnvError
92
+    );
93
+    if (expansionError) {
94
+      return expansionError;
95
+    }
96
+
97
+    applicationExpansions.forEach((el) => {
98
+      if (!(el instanceof EnvError)) {
99
+        el.rules.forEach((rule) => {
100
+          if (!(rule instanceof EnvError)) {
101
+            rules.push(rule);
102
+          }
103
+        });
104
+      }
105
+    });
106
+
63 107
     return { rules, children };
64 108
   }
65 109
 }

+ 2
- 2
src/env.ts View File

@@ -17,8 +17,8 @@ export class EnvError implements Error {
17 17
 export default class Env {
18 18
   public data: { [name: string]: AST.Node };
19 19
 
20
-  public constructor(parent = {}) {
21
-    this.data = Object.assign({}, parent);
20
+  public constructor(parent?: Env) {
21
+    this.data = parent ? Object.assign({}, parent.data) : {};
22 22
   }
23 23
 
24 24
   public get(name: Token): AST.Node | EnvError {

Loading…
Cancel
Save