summaryrefslogtreecommitdiffstats
path: root/llparse-frontend/src/container/index.ts
blob: a62aac8774cb87572b6c1ff77a2f193b72a33cce (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import * as assert from 'assert';

import { ICodeImplementation } from '../implementation/code';
import { IImplementation } from '../implementation/full';
import { INodeImplementation } from '../implementation/node';
import { ITransformImplementation } from '../implementation/transform';
import { IWrap } from '../wrap';
import { ContainerWrap } from './wrap';

export { ContainerWrap };

export class Container {
  private readonly map: Map<string, IImplementation> = new Map();

  public add(key: string, impl: IImplementation): void {
    assert(!this.map.has(key), `Duplicate implementation key: "${key}"`);
    this.map.set(key, impl);
  }

  public build(): IImplementation {
    return {
      code: this.buildCode(),
      node: this.buildNode(),
      transform: this.buildTransform(),
    };
  }

  public buildCode(): ICodeImplementation {
    return {
      And: this.combine((impl) => impl.code.And),
      IsEqual: this.combine((impl) => impl.code.IsEqual),
      Load: this.combine((impl) => impl.code.Load),
      Match: this.combine((impl) => impl.code.Match),
      MulAdd: this.combine((impl) => impl.code.MulAdd),
      Or: this.combine((impl) => impl.code.Or),
      Span: this.combine((impl) => impl.code.Span),
      Store: this.combine((impl) => impl.code.Store),
      Test: this.combine((impl) => impl.code.Test),
      Update: this.combine((impl) => impl.code.Update),
      Value: this.combine((impl) => impl.code.Value),
    };
  }

  public buildNode(): INodeImplementation {
    return {
      Consume: this.combine((impl) => impl.node.Consume),
      Empty: this.combine((impl) => impl.node.Empty),
      Error: this.combine((impl) => impl.node.Error),
      Invoke: this.combine((impl) => impl.node.Invoke),
      Pause: this.combine((impl) => impl.node.Pause),
      Sequence: this.combine((impl) => impl.node.Sequence),
      Single: this.combine((impl) => impl.node.Single),
      SpanEnd: this.combine((impl) => impl.node.SpanEnd),
      SpanStart: this.combine((impl) => impl.node.SpanStart),
      TableLookup: this.combine((impl) => impl.node.TableLookup),
    };
  }

  public buildTransform(): ITransformImplementation {
    return {
      ID: this.combine((impl) => impl.transform.ID),
      ToLower: this.combine((impl) => impl.transform.ToLower),
      ToLowerUnsafe: this.combine((impl) => impl.transform.ToLowerUnsafe),
    };
  }

  private combine<T>(gather: (impl: IImplementation) => new(n: T) => IWrap<T>)
    : new(n: T) => ContainerWrap<T> {
    const wraps: Map<string, new(n: T) => IWrap<T>> = new Map();
    for (const [ key, impl ] of this.map) {
      wraps.set(key, gather(impl));
    }

    return class ContainerWrapSingle extends ContainerWrap<T> {
      constructor(ref: T) {
        super(ref);

        for (const [ key, impl ] of wraps) {
          this.map.set(key, new impl(ref));
        }
      }
    };
  }
}