import assign from "../../lodash-es/assign.js";
import pickBy from "../../lodash-es/pickBy.js";
import forEach from "../../lodash-es/forEach.js";
import map from "../../lodash-es/map.js";
import isString from "../../lodash-es/isString.js";
import isRegExp from "../../lodash-es/isRegExp.js";
function tokenLabel(tokType) {
  if (hasTokenLabel(tokType)) {
    return tokType.LABEL;
  } else {
    return tokType.name;
  }
}
function hasTokenLabel(obj) {
  return isString(obj.LABEL) && obj.LABEL !== "";
}
class AbstractProduction {
  get definition() {
    return this._definition;
  }
  set definition(value) {
    this._definition = value;
  }
  constructor(_definition) {
    this._definition = _definition;
  }
  accept(visitor) {
    visitor.visit(this);
    forEach(this.definition, (prod) => {
      prod.accept(visitor);
    });
  }
}
class NonTerminal extends AbstractProduction {
  constructor(options) {
    super([]);
    this.idx = 1;
    assign(this, pickBy(options, (v) => v !== void 0));
  }
  set definition(definition) {
  }
  get definition() {
    if (this.referencedRule !== void 0) {
      return this.referencedRule.definition;
    }
    return [];
  }
  accept(visitor) {
    visitor.visit(this);
  }
}
class Rule extends AbstractProduction {
  constructor(options) {
    super(options.definition);
    this.orgText = "";
    assign(this, pickBy(options, (v) => v !== void 0));
  }
}
class Alternative extends AbstractProduction {
  constructor(options) {
    super(options.definition);
    this.ignoreAmbiguities = false;
    assign(this, pickBy(options, (v) => v !== void 0));
  }
}
class Option extends AbstractProduction {
  constructor(options) {
    super(options.definition);
    this.idx = 1;
    assign(this, pickBy(options, (v) => v !== void 0));
  }
}
class RepetitionMandatory extends AbstractProduction {
  constructor(options) {
    super(options.definition);
    this.idx = 1;
    assign(this, pickBy(options, (v) => v !== void 0));
  }
}
class RepetitionMandatoryWithSeparator extends AbstractProduction {
  constructor(options) {
    super(options.definition);
    this.idx = 1;
    assign(this, pickBy(options, (v) => v !== void 0));
  }
}
class Repetition extends AbstractProduction {
  constructor(options) {
    super(options.definition);
    this.idx = 1;
    assign(this, pickBy(options, (v) => v !== void 0));
  }
}
class RepetitionWithSeparator extends AbstractProduction {
  constructor(options) {
    super(options.definition);
    this.idx = 1;
    assign(this, pickBy(options, (v) => v !== void 0));
  }
}
class Alternation extends AbstractProduction {
  get definition() {
    return this._definition;
  }
  set definition(value) {
    this._definition = value;
  }
  constructor(options) {
    super(options.definition);
    this.idx = 1;
    this.ignoreAmbiguities = false;
    this.hasPredicates = false;
    assign(this, pickBy(options, (v) => v !== void 0));
  }
}
class Terminal {
  constructor(options) {
    this.idx = 1;
    assign(this, pickBy(options, (v) => v !== void 0));
  }
  accept(visitor) {
    visitor.visit(this);
  }
}
function serializeGrammar(topRules) {
  return map(topRules, serializeProduction);
}
function serializeProduction(node) {
  function convertDefinition(definition) {
    return map(definition, serializeProduction);
  }
  if (node instanceof NonTerminal) {
    const serializedNonTerminal = {
      type: "NonTerminal",
      name: node.nonTerminalName,
      idx: node.idx
    };
    if (isString(node.label)) {
      serializedNonTerminal.label = node.label;
    }
    return serializedNonTerminal;
  } else if (node instanceof Alternative) {
    return {
      type: "Alternative",
      definition: convertDefinition(node.definition)
    };
  } else if (node instanceof Option) {
    return {
      type: "Option",
      idx: node.idx,
      definition: convertDefinition(node.definition)
    };
  } else if (node instanceof RepetitionMandatory) {
    return {
      type: "RepetitionMandatory",
      idx: node.idx,
      definition: convertDefinition(node.definition)
    };
  } else if (node instanceof RepetitionMandatoryWithSeparator) {
    return {
      type: "RepetitionMandatoryWithSeparator",
      idx: node.idx,
      separator: serializeProduction(new Terminal({ terminalType: node.separator })),
      definition: convertDefinition(node.definition)
    };
  } else if (node instanceof RepetitionWithSeparator) {
    return {
      type: "RepetitionWithSeparator",
      idx: node.idx,
      separator: serializeProduction(new Terminal({ terminalType: node.separator })),
      definition: convertDefinition(node.definition)
    };
  } else if (node instanceof Repetition) {
    return {
      type: "Repetition",
      idx: node.idx,
      definition: convertDefinition(node.definition)
    };
  } else if (node instanceof Alternation) {
    return {
      type: "Alternation",
      idx: node.idx,
      definition: convertDefinition(node.definition)
    };
  } else if (node instanceof Terminal) {
    const serializedTerminal = {
      type: "Terminal",
      name: node.terminalType.name,
      label: tokenLabel(node.terminalType),
      idx: node.idx
    };
    if (isString(node.label)) {
      serializedTerminal.terminalLabel = node.label;
    }
    const pattern = node.terminalType.PATTERN;
    if (node.terminalType.PATTERN) {
      serializedTerminal.pattern = isRegExp(pattern) ? pattern.source : pattern;
    }
    return serializedTerminal;
  } else if (node instanceof Rule) {
    return {
      type: "Rule",
      name: node.name,
      orgText: node.orgText,
      definition: convertDefinition(node.definition)
    };
  } else {
    throw Error("non exhaustive match");
  }
}
export {
  AbstractProduction,
  Alternation,
  Alternative,
  NonTerminal,
  Option,
  Repetition,
  RepetitionMandatory,
  RepetitionMandatoryWithSeparator,
  RepetitionWithSeparator,
  Rule,
  Terminal,
  serializeGrammar,
  serializeProduction
};
//# sourceMappingURL=lib-src-model.js.map
