AntLR4:编写 Dockerfile 的语法规则

3
我正在尝试编写一个语法,可以识别dockerfile(上下文无关文法)。 简而言之,dockerfiles是由命令组成的文本文件。 一个命令可以是单行或多行,并通过其名称进行标识。
最简单的dockerfile命令示例: FROM anImageNameThatCanContainsPrettyMuchAnythingButWS EXPOSE aNumber 还有一些更复杂的命令,例如: ADD aPath anotherPath COPY aPath anotherPath ENV aKey=aValue 这里最复杂的命令是RUN命令。 RUN命令基本上可以是任何shell命令,因此可以是任何内容。我要做的唯一一件事就是通过“&&”来“拆分”RUN命令。
到目前为止,我已经完成了以下工作:
grammar Dockerfile;

dockerfile: ((COMMENT | command))+ EOF;

COMMENT
    :   ( '#' ~[\r\n]* '\r'? '\n'
        | '/*' .*? '*/'
        ) -> skip
    ;

command: one_line | run;
one_line: (from | env | entrypoint | maintainer | workdir | add | copy | expose) (NEWLINE)*;

from: FROM ANYKEYS;
maintainer: MAINTAINER ANYKEYS ANYKEYS;

env: ENV ANYKEYS '=' ANYKEYS;

entrypoint: ENTRYPOINT ANYKEYS;

workdir: WORKDIR ANYKEYS;

add: ADD .*?;

copy: COPY src dest;
src: ANYKEYS | '.';
dest: ANYKEYS | '.';

expose: EXPOSE NUMBER;

run: RUN body NEWLINE;
body: shellCmd (SHELLAND shellCmd)* ;
shellCmd: ANYKEYS+;

SHARP: '#';
FROM: [fF][rR][oO][mM];
ENV: [eE][nN][vV];
RUN: [rR][uU][nN];
ENTRYPOINT: [eE][nN][tT][rR][yY][pP][oO][iI][nN][tT];
MAINTAINER: [mM][aA][iI][nN][tT][aA][iI][nN][eE][rR];
WORKDIR: [wW][oO][rR][kK][dD][iI][rR];
SHELLAND: '&&' | ('\\' NEWLINE '&&');
ADD: [aA][dD][dD];
COPY: [cC][oO][pP][yY];
EXPOSE: [eE][xX][pP][oO][sS][eE];

NUMBER: [0-9]+;
LETTER: [a-zA-Z];
ANYKEYS: (LETTER | NUMBER | ':' | '_' | '-' | '/' | '|' | '"' | '=' | '*' | '\\' | '\'' | '+' | ']' | '[' | '{' | '}' | ';' | '!' | '~' | '.' | '–' | '$' | '<' | '>' | '@' | ',')+;

NEWLINE: ('\n' | '\r')+;
WS : ((' ' | '\t')+) -> skip;

那么有什么问题呢? 首先,ANYKEYS规则很不好看,但我找不到更好的方法来解决。 接下来,RUN exit 9000无法工作,会产生"extraneous input '9000' expecting {SHELLAND,ANYKEYS,NEWLINE}"的错误,我不理解,因为ANYKEYS应该与ANYKEY规则中的NUMBER匹配。
我有点迷惑了,不明白为什么它不能匹配这样的输入,并且也不知道用更好的方法怎么做。
谢谢你的帮助和建议!

1
你陷入了词法分析器陷阱。无论如何,你可能应该使用词法分析器模式,以防命令恰好具有与你的关键字之一匹配的参数。 - Lucas Trzesniewski
谢谢你的回答!但是将 ANYKEYS 移到语法的结尾不会改变任何事情,我错了吗?所以我现在不知道该怎么做才能解决这个问题?使用模式能解决这个问题吗? 如果我正确理解了词法分析器模式,它可以用来在语法中引入一种上下文,避免类似于 RUNRUN 主体内部被解释为运行命令的歧义情况。 - ttben
Antlr4 会尝试首先匹配可能的最长标记。 - RandomEli
@LucasTrzesniewski 我阅读了有关模式的文档和示例,但在我的情况下,我不知道如何从运行模式中退出,因为没有标记可以离开运行模式。当你读取其他命令之后,在新行后,你会离开运行模式。 - ttben
@LucasTrzesniewski,你是对的。这种语言没有深度,没有括号,因此可以通过正则表达式来处理(我已经做到了)。当然,它也可以通过AntLR语法来处理,但正如你所说,这有点过度杀伤力了。感谢你的帮助! - ttben
显示剩余2条评论
1个回答

0

我不熟悉AntLR4,但对于你的语法来说,包含某些指令的多种形式是很重要的。

以下指令有两种形式:

RUN
ADD
COPY
ENTRYPOINT
HEALTHCHECK

以下指令有三种形式:

CMD

运行

RUN指令有两种形式:

RUN <command>                          # (shell form, the command is run in a shell
                                       #  which by default is /bin/sh -c on Linux
                                       #  or cmd /S /C on Windows)

RUN ["executable", "param1", "param2"] # (exec form)

ADD

ADD指令有两种形式:

ADD <src>... <dest>
ADD ["<src>",... "<dest>"] # (this form is required for paths containing whitespace)

复制

COPY指令有两种形式:

COPY <src>... <dest>
COPY ["<src>",... "<dest>"] # (this form is required for paths containing whitespace)

ENTRYPOINT

ENTRYPOINT指令有两种形式:

ENTRYPOINT ["executable", "param1", "param2"] # (exec form, preferred)
ENTRYPOINT command param1 param2              # (shell form)

健康检查

HEALTHCHECK指令有两种形式:

HEALTHCHECK [OPTIONS] CMD command # (check container health by running a command inside the container)
HEALTHCHECK NONE                  # (disable any healthcheck inherited from the base image)

CMD

CMD指令有三种形式:

CMD ["executable","param1","param2"] # (exec form, this is the preferred form)
CMD ["param1","param2"]              # (as default parameters to ENTRYPOINT)
CMD command param1 param2            # (shell form)

欲了解更多信息和每个命令的示例,请参见:https://docs.docker.com/engine/reference/builder/


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接