All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sonar.iac.docker.parser.grammar.DockerGrammar Maven / Gradle / Ivy

The newest version!
/*
 * SonarQube IaC Plugin
 * Copyright (C) 2021-2023 SonarSource SA
 * mailto:info AT sonarsource DOT com
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package org.sonar.iac.docker.parser.grammar;

import com.sonar.sslr.api.typed.GrammarBuilder;
import java.util.List;
import org.sonar.iac.common.parser.grammar.Punctuator;
import org.sonar.iac.docker.parser.TreeFactory;
import org.sonar.iac.docker.tree.api.AddInstruction;
import org.sonar.iac.docker.tree.api.Alias;
import org.sonar.iac.docker.tree.api.ArgInstruction;
import org.sonar.iac.docker.tree.api.Argument;
import org.sonar.iac.docker.tree.api.Body;
import org.sonar.iac.docker.tree.api.CmdInstruction;
import org.sonar.iac.docker.tree.api.CopyInstruction;
import org.sonar.iac.docker.tree.api.DockerImage;
import org.sonar.iac.docker.tree.api.EntrypointInstruction;
import org.sonar.iac.docker.tree.api.EnvInstruction;
import org.sonar.iac.docker.tree.api.ExecForm;
import org.sonar.iac.docker.tree.api.ExpandableStringCharacters;
import org.sonar.iac.docker.tree.api.ExpandableStringLiteral;
import org.sonar.iac.docker.tree.api.ExposeInstruction;
import org.sonar.iac.docker.tree.api.Expression;
import org.sonar.iac.docker.tree.api.File;
import org.sonar.iac.docker.tree.api.Flag;
import org.sonar.iac.docker.tree.api.FromInstruction;
import org.sonar.iac.docker.tree.api.HealthCheckInstruction;
import org.sonar.iac.docker.tree.api.HereDocument;
import org.sonar.iac.docker.tree.api.Instruction;
import org.sonar.iac.docker.tree.api.LabelInstruction;
import org.sonar.iac.docker.tree.api.Literal;
import org.sonar.iac.docker.tree.api.MaintainerInstruction;
import org.sonar.iac.docker.tree.api.KeyValuePair;
import org.sonar.iac.docker.tree.api.OnBuildInstruction;
import org.sonar.iac.docker.tree.api.RunInstruction;
import org.sonar.iac.docker.tree.api.ShellForm;
import org.sonar.iac.docker.tree.api.ShellInstruction;
import org.sonar.iac.docker.tree.api.StopSignalInstruction;
import org.sonar.iac.docker.tree.api.SyntaxToken;
import org.sonar.iac.docker.tree.api.UserInstruction;
import org.sonar.iac.docker.tree.api.VolumeInstruction;
import org.sonar.iac.docker.tree.api.WorkdirInstruction;

// Ignore uppercase method names warning
@SuppressWarnings("java:S100")
public class DockerGrammar {

  private final GrammarBuilder b;
  private final TreeFactory f;

  public DockerGrammar(GrammarBuilder b, TreeFactory f) {
    this.b = b;
    this.f = f;
  }

  public File FILE() {
    return b.nonterminal(DockerLexicalGrammar.FILE).is(
      f.file(
        BODY(),
        b.optional(b.token(DockerLexicalGrammar.SPACING)),
        b.token(DockerLexicalGrammar.EOF)));
  }

  public Body BODY() {
    return b.nonterminal(DockerLexicalGrammar.BODY).is(
      f.body(
        b.zeroOrMore(ARG()),
        b.oneOrMore(DOCKERIMAGE())));
  }

  public DockerImage DOCKERIMAGE() {
    return b.nonterminal(DockerLexicalGrammar.DOCKERIMAGE).is(
      f.dockerImage(
        FROM(),
        b.zeroOrMore(INSTRUCTION())));
  }

  public Instruction INSTRUCTION() {
    return b.nonterminal(DockerLexicalGrammar.INSTRUCTION).is(
      f.instruction(
        b.firstOf(
          ONBUILD(),
          MAINTAINER(),
          STOPSIGNAL(),
          WORKDIR(),
          EXPOSE(),
          LABEL(),
          ENV(),
          ARG(),
          CMD(),
          ENTRYPOINT(),
          RUN(),
          ADD(),
          COPY(),
          USER(),
          VOLUME(),
          SHELL(),
          HEALTHCHECK())));
  }

  public OnBuildInstruction ONBUILD() {
    return b.nonterminal(DockerLexicalGrammar.ONBUILD).is(
      f.onbuild(
        b.token(DockerKeyword.ONBUILD),
        INSTRUCTION()));
  }

  public FromInstruction FROM() {
    return b.nonterminal(DockerLexicalGrammar.FROM).is(
      f.from(
        b.token(DockerKeyword.FROM),
        b.optional(FLAG()),
        f.ignoreFirst(b.token(DockerLexicalGrammar.WHITESPACE), ARGUMENT()),
        b.optional(ALIAS())));
  }

  public Flag FLAG() {
    return b.nonterminal(DockerLexicalGrammar.FLAG).is(
      f.flag(
        b.token(DockerLexicalGrammar.FLAG_PREFIX),
        b.token(DockerLexicalGrammar.FLAG_NAME),
        b.optional(b.token(DockerLexicalGrammar.EQUALS_OPERATOR)),
        b.optional(ARGUMENT())));
  }

  public Alias ALIAS() {
    return b.nonterminal(DockerLexicalGrammar.ALIAS).is(
      f.alias(
        b.token(DockerLexicalGrammar.ALIAS_AS),
        b.token(DockerLexicalGrammar.IMAGE_ALIAS)));
  }

  public MaintainerInstruction MAINTAINER() {
    return b.nonterminal(DockerLexicalGrammar.MAINTAINER).is(
      f.maintainer(b.token(DockerKeyword.MAINTAINER), ARGUMENTS()));
  }

  // TODO get rid of this method or even rename the method and token grammar because it will only remain for MAINTAINER
  public List ARGUMENTS() {
    return b.>nonterminal(DockerLexicalGrammar.ARGUMENTS).is(
      b.oneOrMore(
        f.argument(b.token(DockerLexicalGrammar.STRING_LITERAL))));
  }

  public StopSignalInstruction STOPSIGNAL() {
    return b.nonterminal(DockerLexicalGrammar.STOPSIGNAL).is(
      f.stopSignal(
        b.token(DockerKeyword.STOPSIGNAL),
        b.token(DockerLexicalGrammar.WHITESPACE),
        ARGUMENT()));
  }

  public WorkdirInstruction WORKDIR() {
    return b.nonterminal(DockerLexicalGrammar.WORKDIR).is(
      f.workdir(
        b.token(DockerKeyword.WORKDIR),
        b.oneOrMore(f.ignoreFirst(
          b.token(DockerLexicalGrammar.WHITESPACE),
          ARGUMENT()))));
  }

  public ExposeInstruction EXPOSE() {
    return b.nonterminal(DockerLexicalGrammar.EXPOSE).is(
      f.expose(
        b.token(DockerKeyword.EXPOSE),
        b.oneOrMore(f.ignoreFirst(
          b.token(DockerLexicalGrammar.WHITESPACE),
          ARGUMENT()))));
  }

  public LabelInstruction LABEL() {
    return b.nonterminal(DockerLexicalGrammar.LABEL).is(
      b.firstOf(
        f.label(
          b.token(DockerKeyword.LABEL),
          b.oneOrMore(
            f.ignoreFirst(
              b.token(DockerLexicalGrammar.WHITESPACE),
              KEY_VALUE_PAIR_WITH_EQUAL()))),
        f.label(
          b.token(DockerKeyword.LABEL),
          f.ignoreFirst(
            b.token(DockerLexicalGrammar.WHITESPACE),
            KEY_VALUE_PAIR_WITHOUT_EQUAL()))));
  }

  public EnvInstruction ENV() {
    return b.nonterminal(DockerLexicalGrammar.ENV).is(
      f.env(
        b.token(DockerKeyword.ENV),
        b.oneOrMore(
          f.ignoreFirst(
            b.token(DockerLexicalGrammar.WHITESPACE),
            KEY_VALUE_PAIR()))));
  }

  public UserInstruction USER() {
    return b.nonterminal(DockerLexicalGrammar.USER).is(
      f.user(
        b.token(DockerKeyword.USER),
        b.oneOrMore(f.ignoreFirst(
          b.token(DockerLexicalGrammar.WHITESPACE),
          ARGUMENT()))));
  }

  public ArgInstruction ARG() {
    return b.nonterminal(DockerLexicalGrammar.ARG).is(
      f.arg(
        b.token(DockerKeyword.ARG),
        b.oneOrMore(
          f.ignoreFirst(
            b.token(DockerLexicalGrammar.WHITESPACE),
            b.firstOf(
              KEY_VALUE_PAIR_WITH_EQUAL(),
              KEY_VALUE_PAIR_KEY_ONLY())))));
  }

  public AddInstruction ADD() {
    return b.nonterminal(DockerLexicalGrammar.ADD).is(
      f.add(
        b.token(DockerKeyword.ADD),
        b.zeroOrMore(
          FLAG()),
        b.firstOf(
          EXEC_FORM(),
          SHELL_FORM())));
  }

  public CopyInstruction COPY() {
    return b.nonterminal(DockerLexicalGrammar.COPY).is(
      f.copy(
        b.token(DockerKeyword.COPY),
        b.zeroOrMore(
          FLAG()),
        b.firstOf(
          HEREDOC_FORM(),
          EXEC_FORM(),
          SHELL_FORM())));
  }

  public CmdInstruction CMD() {
    return b.nonterminal(DockerLexicalGrammar.CMD).is(
      f.cmd(
        b.token(DockerKeyword.CMD),
        b.optional(
          b.firstOf(
            EXEC_FORM(),
            SHELL_FORM_GENERIC()))));
  }

  public EntrypointInstruction ENTRYPOINT() {
    return b.nonterminal(DockerLexicalGrammar.ENTRYPOINT).is(
      f.entrypoint(
        b.token(DockerKeyword.ENTRYPOINT),
        b.optional(
          b.firstOf(
            EXEC_FORM(),
            SHELL_FORM_GENERIC()))));
  }

  public RunInstruction RUN() {
    return b.nonterminal(DockerLexicalGrammar.RUN).is(
      f.run(
        b.token(DockerKeyword.RUN),
        b.zeroOrMore(
          FLAG()),
        b.optional(
          b.firstOf(
            HEREDOC_FORM(),
            EXEC_FORM(),
            SHELL_FORM_GENERIC()))));
  }

  public HealthCheckInstruction HEALTHCHECK() {
    return b.nonterminal(DockerLexicalGrammar.HEALTHCHECK).is(
      f.healthcheck(
        b.token(DockerKeyword.HEALTHCHECK),
        b.zeroOrMore(FLAG()),
        b.firstOf(
          b.token(DockerLexicalGrammar.HEALTHCHECK_NONE),
          CMD())));
  }

  public ShellInstruction SHELL() {
    return b.nonterminal(DockerLexicalGrammar.SHELL).is(
      f.shell(
        b.token(DockerKeyword.SHELL),
        EXEC_FORM()));
  }

  /**
   * Exec Form is something like this:
   * {@code ["executable","param1","param2"]}
   * what is used by different instructions like CMD, ENTRYPOINT, RUN, SHELL
   */
  public ExecForm EXEC_FORM() {
    return b.nonterminal(DockerLexicalGrammar.EXEC_FORM).is(
      f.execForm(
        b.token(Punctuator.LBRACKET),
        b.optional(
          f.ignoreFirst(b.optional(b.token(DockerLexicalGrammar.WHITESPACE)),
            f.singleExpressionArgument(EXPANDABLE_STRING_LITERAL()))),
        b.zeroOrMore(
          f.tuple(
            b.token(Punctuator.COMMA),
            f.ignoreFirst(b.optional(b.token(DockerLexicalGrammar.WHITESPACE)),
              f.singleExpressionArgument(EXPANDABLE_STRING_LITERAL())))),
        b.token(Punctuator.RBRACKET)));
  }

  /**
   * Shell Form is a way to define some executable command fo different instructions like CMD, ENTRYPOINT, RUN
   */
  public ShellForm SHELL_FORM() {
    return b.nonterminal(DockerLexicalGrammar.SHELL_FORM).is(
      f.shellForm(
        b.oneOrMore(
          f.ignoreFirst(b.token(DockerLexicalGrammar.WHITESPACE), ARGUMENT()))));
  }

  /**
   * Generic version of Shell Form, which should be used to parse non-docker-only syntax for shell content.
   */
  public ShellForm SHELL_FORM_GENERIC() {
    return b.nonterminal(DockerLexicalGrammar.SHELL_FORM_GENERIC).is(
      f.shellForm(
        b.oneOrMore(
          f.ignoreFirst(b.token(DockerLexicalGrammar.WHITESPACE), ARGUMENT_GENERIC()))));
  }

  public HereDocument HEREDOC_FORM() {
    return b.nonterminal(DockerLexicalGrammar.HEREDOC_FORM).is(
      f.hereDocument(
        b.token(DockerLexicalGrammar.HEREDOC_EXPRESSION)));
  }

  public HereDocument HEREDOC_FORM_CONTENT() {
    return b.nonterminal(DockerLexicalGrammar.HEREDOC_FORM_CONTENT).is(
      f.hereDocumentContent(
        HEREDOC_ELEMENT(),
        b.zeroOrMore(
          f.ignoreFirst(b.token(DockerLexicalGrammar.WHITESPACE_OR_LINE_BREAK), HEREDOC_ELEMENT()))));
  }

  public Argument HEREDOC_ELEMENT() {
    return b.nonterminal().is(
      b.firstOf(
        HEREDOC_NAME(),
        ARGUMENT()));
  }

  public Argument HEREDOC_NAME() {
    return b.nonterminal().is(
      f.asArgument(
        f.regularStringLiteral(b.token(DockerLexicalGrammar.HEREDOC_NAME))));
  }

  public VolumeInstruction VOLUME() {
    return b.nonterminal(DockerLexicalGrammar.VOLUME).is(
      f.volume(
        b.token(DockerKeyword.VOLUME),
        b.firstOf(
          EXEC_FORM(),
          SHELL_FORM())));
  }

  public Argument ARGUMENT() {
    return b.nonterminal(DockerLexicalGrammar.ARGUMENT).is(
      f.newArgument(
        b.oneOrMore(
          b.firstOf(
            STRING_LITERAL(),
            VARIABLE()))));
  }

  public Argument ARGUMENT_GENERIC() {
    return b.nonterminal(DockerLexicalGrammar.ARGUMENT_GENERIC).is(
      f.newArgument(
        b.oneOrMore(
          b.firstOf(
            STRING_LITERAL_GENERIC(),
            VARIABLE_GENERIC()))));
  }

  public Argument KEY_ARGUMENT() {
    return b.nonterminal().is(
      f.newArgument(
        b.oneOrMore(
          b.firstOf(
            f.regularStringLiteral(b.token(DockerLexicalGrammar.QUOTED_STRING_LITERAL)),
            f.regularStringLiteral(b.token(DockerLexicalGrammar.UNQUOTED_KEY_LITERAL)),
            EXPANDABLE_STRING_LITERAL(),
            VARIABLE()))));
  }

  public Expression STRING_LITERAL() {
    return b.nonterminal().is(
      b.firstOf(
        REGULAR_STRING_LITERAL(),
        EXPANDABLE_STRING_LITERAL()));
  }

  public Expression STRING_LITERAL_GENERIC() {
    return b.nonterminal().is(
      b.firstOf(
        REGULAR_STRING_LITERAL(),
        EXPANDABLE_STRING_LITERAL_GENERIC()));
  }

  public KeyValuePair KEY_VALUE_PAIR() {
    return b.nonterminal(DockerLexicalGrammar.KEY_VALUE_PAIR).is(
      b.firstOf(
        KEY_VALUE_PAIR_WITH_EQUAL(),
        KEY_VALUE_PAIR_WITHOUT_EQUAL(),
        KEY_VALUE_PAIR_KEY_ONLY()));
  }

  public KeyValuePair KEY_VALUE_PAIR_WITH_EQUAL() {
    return b.nonterminal().is(
      f.keyValuePair(
        KEY_ARGUMENT(),
        b.token(DockerLexicalGrammar.EQUALS_OPERATOR),
        ARGUMENT()));
  }

  public KeyValuePair KEY_VALUE_PAIR_WITHOUT_EQUAL() {
    return b.nonterminal().is(
      f.keyValuePair(
        KEY_ARGUMENT(),
        f.ignoreFirst(
          b.token(DockerLexicalGrammar.WHITESPACE),
          ARGUMENT()),
        b.zeroOrMore(
          f.tuple(
            b.token(DockerLexicalGrammar.WHITESPACE),
            ARGUMENT()))));
  }

  public KeyValuePair KEY_VALUE_PAIR_KEY_ONLY() {
    return b.nonterminal().is(
      f.keyValuePair(
        KEY_ARGUMENT(),
        b.optional(b.token(DockerLexicalGrammar.EQUALS_OPERATOR))));
  }

  public Literal REGULAR_STRING_LITERAL() {
    return b.nonterminal(DockerLexicalGrammar.REGULAR_STRING_LITERAL).is(
      b.firstOf(
        f.regularStringLiteral(b.token(DockerLexicalGrammar.UNQUOTED_STRING_LITERAL)),
        f.regularStringLiteral(b.token(DockerLexicalGrammar.QUOTED_STRING_LITERAL))));
  }

  public ExpandableStringLiteral EXPANDABLE_STRING_LITERAL() {
    return b.nonterminal(DockerLexicalGrammar.EXPANDABLE_STRING_LITERAL).is(
      f.expandableStringLiteral(
        b.token(Punctuator.DOUBLE_QUOTE),
        b.oneOrMore(
          b.firstOf(
            EXPANDABLE_STRING_CHARACTERS(),
            VARIABLE())),
        b.token(Punctuator.DOUBLE_QUOTE)));
  }

  public ExpandableStringLiteral EXPANDABLE_STRING_LITERAL_GENERIC() {
    return b.nonterminal(DockerLexicalGrammar.EXPANDABLE_STRING_LITERAL_GENERIC).is(
      f.expandableStringLiteral(
        b.token(Punctuator.DOUBLE_QUOTE),
        b.oneOrMore(
          b.firstOf(
            EXPANDABLE_STRING_CHARACTERS(),
            VARIABLE_GENERIC())),
        b.token(Punctuator.DOUBLE_QUOTE)));
  }

  public Expression EXPANDABLE_STRING_CHARACTERS() {
    return b.nonterminal().is(
      f.expandableStringCharacters(b.token(DockerLexicalGrammar.STRING_WITH_ENCAPS_VAR_CHARACTERS)));
  }

  public Expression VARIABLE() {
    return b.nonterminal().is(
      b.firstOf(
        REGULAR_VARIABLE(),
        ENCAPS_VARIABLE()));
  }

  public Expression VARIABLE_GENERIC() {
    return b.nonterminal().is(
      b.firstOf(
        REGULAR_VARIABLE(),
        ENCAPS_VARIABLE_GENERIC()));
  }

  public Expression REGULAR_VARIABLE() {
    return b.nonterminal(DockerLexicalGrammar.REGULAR_VARIABLE).is(
      f.regularVariable(
        b.token(Punctuator.DOLLAR),
        b.token(DockerLexicalGrammar.REGULAR_VAR_IDENTIFIER)));
  }

  public Expression ENCAPS_VARIABLE() {
    return b.nonterminal(DockerLexicalGrammar.ENCAPSULATED_VARIABLE).is(
      f.encapsulatedVariable(
        b.token(Punctuator.DOLLAR_LCURLY),
        b.token(DockerLexicalGrammar.REGULAR_VAR_IDENTIFIER),
        b.optional(
          f.tuple(
            b.token(DockerLexicalGrammar.ENCAPS_VAR_MODIFIER_SEPARATOR),
            ENCAPS_VARIABLE_MODIFIER())),
        b.token(Punctuator.RCURLYBRACE)));
  }

  public Expression ENCAPS_VARIABLE_GENERIC() {
    return b.nonterminal(DockerLexicalGrammar.ENCAPSULATED_VARIABLE_GENERIC).is(
      f.encapsulatedVariableGeneric(
        b.token(Punctuator.DOLLAR_LCURLY),
        b.token(DockerLexicalGrammar.REGULAR_VAR_IDENTIFIER),
        b.optional(
          b.token(DockerLexicalGrammar.ENCAPS_VAR_MODIFIER_GENERIC)),
        b.token(Punctuator.RCURLYBRACE)));
  }

  public Argument ENCAPS_VARIABLE_MODIFIER() {
    return b.nonterminal().is(
      f.newArgument(
        b.oneOrMore(
          b.firstOf(
            EXPANDABLE_STRING_LITERAL(),
            VARIABLE(),
            f.regularStringLiteral(b.token(DockerLexicalGrammar.UNQUOTED_VARIABLE_MODIFIER)),
            f.regularStringLiteral(b.token(DockerLexicalGrammar.QUOTED_STRING_LITERAL))))));
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy