123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109 |
- import * as AST from '../ast';
- import Env, { EnvError } from '../env';
- import Token from '../token';
-
- interface Expansion {
- rules: (string | EnvError)[];
- children: (string | EnvError)[];
- }
-
- export class Mixin {
- public name: Token;
- public parameters: AST.Identifier[];
- public children: AST.Child[];
-
- public constructor(
- name: Token,
- parameters: AST.Identifier[],
- children: AST.Child[]
- ) {
- this.name = name;
- this.parameters = parameters;
- this.children = children;
- }
-
- public compile(env: Env, _opts: AST.Opts) {
- env.set(this.name.value, this);
- return '';
- }
-
- public expand(
- env: Env,
- opts: AST.Opts,
- parents: AST.Selector[],
- args: AST.Node[]
- ): Expansion | EnvError {
- if (this.parameters.length !== args.length) {
- return new EnvError(
- this.name.line,
- `Expected ${this.parameters.length} arguments but received ${
- args.length
- }`
- );
- }
-
- const expandedArgs: AST.Node[] = [];
- for (const arg of args) {
- if (arg instanceof AST.Identifier) {
- const lookup = env.get(arg.name);
- if (lookup instanceof EnvError) return lookup;
- expandedArgs.push(lookup);
- }
-
- expandedArgs.push(arg);
- }
-
- const mixinEnv = new Env(env);
- this.parameters.forEach((param, index) => {
- mixinEnv.set(param.name.value, expandedArgs[index]);
- });
-
- const rules = this.children
- .filter((child: AST.Node): child is AST.Rule => child instanceof AST.Rule)
- .map((rule: AST.Rule) => rule.compile(mixinEnv, opts));
-
- const children = this.children
- .filter(
- (child: AST.Node): child is AST.RuleSet => child instanceof AST.RuleSet
- )
- .map((ruleSet: AST.RuleSet) => {
- ruleSet.selectors.forEach((sel) => (sel.parents = parents));
- return ruleSet.compile(mixinEnv, opts);
- });
-
- const applicationExpansions: (Expansion | EnvError)[] = this.children
- .filter(
- (child: AST.Node): child is AST.Application =>
- child instanceof AST.Application
- )
- .map((application: AST.Application) => {
- const mixin = mixinEnv.get(application.name.value);
- if (mixin instanceof EnvError) {
- return mixin;
- } else if (mixin instanceof AST.Mixin) {
- return mixin.expand(mixinEnv, opts, parents, application.arguments);
- } else {
- return new EnvError(1, 'Expected a mixin');
- }
- });
-
- const expansionError = applicationExpansions.find(
- (el) => el instanceof EnvError
- );
- if (expansionError) {
- return expansionError;
- }
-
- applicationExpansions.forEach((el) => {
- if (!(el instanceof EnvError)) {
- el.rules.forEach((rule) => {
- if (!(rule instanceof EnvError)) {
- rules.push(rule);
- }
- });
- }
- });
-
- return { rules, children };
- }
- }
|