text: /*
   * Example Of using Grammar complex grammar inheritance to implement
   * 'Structured natural language' supporting multiple 'spoken languages' using grammar inheritance.
   *
   * 1. An "Abstract" Base Grammar with two concrete grammars extending it.
   * 2. Each concrete grammar has a different lexer
   * 3. This also shows an example of using Token inheritance
   */
  
  const { createToken, Lexer, CstParser } = require("chevrotain")
  
  // ----------------- lexer -----------------
  const RelationWord = createToken({ name: "RelationWord", pattern: Lexer.NA })
  
  // Token inheritance CONSUME(RelationWord) will work on any Token extending RelationWord
  const And = createToken({
    name: "And",
    pattern: /and/,
    categories: RelationWord
  })
  const Before = createToken({
    name: "Before",
    pattern: /before/,
    categories: RelationWord
  })
  const After = createToken({
    name: "After",
    pattern: /after/,
    categories: RelationWord
  })
  const Und = createToken({
    name: "Und",
    pattern: /und/,
    categories: RelationWord
  })
  const Vor = createToken({
    name: "Vor",
    pattern: /vor/,
    categories: RelationWord
  })
  const Nach = createToken({
    name: "Nach",
    pattern: /nach/,
    categories: RelationWord
  })
  
  /// English Tokens
  const Cook = createToken({ name: "Cook", pattern: /cooking|cook/ })
  const Some = createToken({ name: "Some", pattern: /some/ })
  const Sausages = createToken({ name: "Sausages", pattern: /sausages/ })
  const Clean = createToken({ name: "Clean", pattern: /clean/ })
  const The = createToken({ name: "The", pattern: /the/ })
  const Room = createToken({ name: "Room", pattern: /room/ })
  
  // German Tokens
  const Kochen = createToken({ name: "Kochen", pattern: /kochen/ })
  const Wurstchen = createToken({ name: "Wurstchen", pattern: /wurstchen/ })
  const Wurst = createToken({ name: "Wurst", pattern: /wurst/ })
  const Raum = createToken({ name: "Raum", pattern: /raum/ })
  const Auf = createToken({ name: "Auf", pattern: /auf/ })
  const Den = createToken({ name: "Den", pattern: /den/ })
  
  const WhiteSpace = createToken({
    name: "WhiteSpace",
    pattern: /\s+/,
    group: Lexer.SKIPPED
  })
  
  const abstractTokens = [RelationWord]
  
  const englishTokens = [
    WhiteSpace,
    RelationWord,
    And,
    Before,
    After,
    Cook,
    Some,
    Sausages,
    Clean,
    The,
    Room
  ]
  
  const germanTokens = [
    WhiteSpace,
    RelationWord,
    Und,
    Vor,
    Nach,
    Kochen,
    Wurstchen,
    Wurst,
    Raum,
    Auf,
    Den
  ]
  
  // We can define a different Lexer for each of the sub grammars.
  const EnglishLexer = new Lexer(englishTokens)
  const GermanLexer = new Lexer(germanTokens)
  
  // ----------------- parser -----------------
  
  // Extending the base chevrotain CstParser class
  class AbstractCommandsParser extends CstParser {
    constructor(tokenVocabulary) {
      // combining the token vocabularies of parent and child.
      super(abstractTokens.concat(tokenVocabulary))
  
      const $ = this
  
      $.RULE("commands", () => {
        $.SUBRULE($.command)
  
        $.MANY(() => {
          $.CONSUME(RelationWord)
          $.SUBRULE2($.command)
        })
      })
  
      $.RULE("command", () => {
        // The cook and clean commands must be implemented in each sub grammar
        $.OR([
          { ALT: () => $.SUBRULE($.cookCommand) },
          { ALT: () => $.SUBRULE($.cleanCommand) }
        ])
      })
  
      // this is an "abstract" base grammar it should not be instantiated directly
      // therefor it does not invoke "performSelfAnalysis"
    }
  }
  
  class EnglishCommandsParser extends AbstractCommandsParser {
    constructor() {
      super(englishTokens)
  
      const $ = this
  
      // implementing the 'cookCommand' referenced in the AbstractCommandsParser
      $.RULE("cookCommand", () => {
        $.CONSUME(Cook)
        $.OPTION(() => {
          $.CONSUME(Some)
        })
        $.CONSUME(Sausages)
      })
  
      // implementing the 'cleanCommand' referenced in the AbstractCommandsParser
      $.RULE("cleanCommand", () => {
        $.CONSUME(Clean)
        $.CONSUME(The)
        $.CONSUME(Room)
      })
  
      // very important to call this after all the rules have been defined.
      // otherwise the parser may not work correctly as it will lack information
      // derived during the self analysis phase.
      this.performSelfAnalysis()
    }
  }
  
  class GermanCommandsParser extends AbstractCommandsParser {
    constructor() {
      super(germanTokens)
  
      const $ = this
  
      // implementing the 'cookCommand' referenced in the AbstractCommandsParser
      $.RULE("cookCommand", () => {
        $.CONSUME(Kochen)
        $.OR([
          { ALT: () => $.CONSUME(Wurstchen) },
          { ALT: () => $.CONSUME(Wurst) }
        ])
      })
  
      // implementing the 'cleanCommand' referenced in the AbstractCommandsParser
      $.RULE("cleanCommand", () => {
        $.CONSUME(Raum)
        $.CONSUME(Den)
        $.CONSUME2(Raum)
        $.CONSUME(Auf)
      })
  
      // very important to call this after all the rules have been defined.
      // otherwise the parser may not work correctly as it will lack information
      // derived during the self analysis phase.
      this.performSelfAnalysis()
    }
  }
  
  // ----------------- wrapping it all together -----------------
  
  // reuse the same parser instances.
  const englishParser = new EnglishCommandsParser()
  const germanParser = new GermanCommandsParser()
  
  module.exports = function (text, language) {
    // lex
    let lexer
    // match language and lexer.
    switch (language) {
      case "english":
        lexer = EnglishLexer
        break
      case "german":
        lexer = GermanLexer
        break
      default:
        throw Error("no valid language chosen")
    }
  
    const lexResult = lexer.tokenize(text)
  
    // parse
    let parser
    // match language and parser.
    switch (language) {
      case "english":
        parser = englishParser
        break
      case "german":
        parser = germanParser
        break
      default:
        throw Error("no valid language chosen")
    }
  
    // setting a new input will RESET the parser instance's state.
    parser.input = lexResult.tokens
    // any top level rule may be used as an entry point
    const cst = parser.commands()
  
    return {
      cst: cst,
      lexErrors: lexResult.errors,
      parseErrors: parser.errors
    }
  }
  