Lex 半复杂正则表达式

Lex semi-complex regex expression

所以,我正在为学校做一个项目,我们必须制作一个 shell,到目前为止我一直做得很好。我需要弄清楚如何扩展 "words" 的正则表达式,以便识别特殊字符。

基本上,我需要能够通过某些特殊字符分隔单词,但如果它们被转义则不需要。

例如:echo sometext>file.txt 将被标记为 WORD WORD GREAT WORD。但是,echo sometext\>file.txt 将被标记为 WORD WORD,其中 "sometext>file.txt" 是一个词。

我无法找出可以处理此问题的正则表达式。这是我目前拥有的: [^ \t\n][^ \t\n<>&|]*。这适用于挑选 echo sometext>file.txt 之类的东西,但我不确定如何扩展它以查找除空格或特殊字符以外的任何字符,除非该特殊字符被转义。

感谢任何帮助。

怎么样:

(?:\.|[^ \t\n<>&|])*

\t\n<>& 和 [=18] 上的“断言” =](即 [^ \t\n<>&|] 中的内容),除非已转义。

演示:https://regex101.com/r/lEkNRk/1

或者:

(?:\.|[^ \t\n<>&|])+|[ \t\n<>&|]

这也单独匹配 > 等等,如果没有转义。也许它更适合您建议的 WORD WORD GREAT WORD 模式。

演示:https://regex101.com/r/rkh8lC/1

如果您正在使用 (f)lex,您可能需要这样的东西。它基于 Posix 2016 中的语法摘要,为了好玩而加入了一些 bashisms,我尝试使用与该文档相同的标记名称。

这里省略了很多重要的功能:

  • 正确处理报价需要做更多的工作。我什至没有尝试过。 (最烦人的部分是你可以在带引号的字符串中包含命令扩展 $(...),这是一个独立的词法上下文。)

  • 我没有包含将 yytext 的副本保存到 yylval 的代码,这需要为 WORD 和保留字(但不是元字符或以元字符开头的符号),因为保留字并不总是保留的。这个事实也造成了一些语法问题。

  • 我没有区分NAMEWORD(规则5和8)。

  • 虽然用(f)lex 的trailing-context 算子straight-forward,但我懒得认IO_NUMBER。 (仅当紧跟在数字后面的字符是 <>.

  • 时,数字才是特殊的
  • 我也没有尝试识别ASSIGNMENT_WORD(见2.10.1)。赋值在句法上并不重要,但在语义上很重要,在识别此类词时设置标志很有用。可以在 WORD 模式之前添加识别 ASSIGNMENT_WORDs 并在 yylval 中设置标志的模式(请参阅下面关于订购模式的注释)。

下面有关于这些模式排序的注释。当多个模式匹配同一个标记时,(f)lex 具有定义的匹配顺序这一事实通常大大简化了正则表达式的构造,但这也意味着在某些情况下必须仔细排序规则。

[|&;()<>\n]   return *yytext;   /* Metacharacters, including newline */
[[:space:]]   ;                 /* Ignore other whitespace */
"||"          return OR_IF;     /* Multi-metacharacter sequences */
"&&"          return AND_IF;
";;"          return DSEMI;
"<<"          return DLESS;
"<<-"         return DLESSDASH;
"<<<"         return TLESS;     /* Bash here strings */
">>"          return DGREAT;
"<&"          return LESSAND;
">&"          return GREATAND;
"<>"          return LESSGREAT;
">|"          return CLOBBER;

"(("          return DLparen;   /* Bash arithmetic conditional */
"))"          return DRparen;

"if"          return If;        /* reserved words, only matched when   */
"then"        return Then;      /* they are a complete word (and often */
"else"        return Else;      /* treated as regular words even then).*/
"elif"        return Elif;
"fi"          return Fi;
"do"          return Do;
"done"        return Done;
"case"        return Case;
"esac"        return Esac;
"while"       return While;
"until"       return Until;
"for"         return For;
"in"          return In;
"time"        return Time;      /* In bash, this is reserved */

"{"           return Rbrace;
"}"           return Lbrace;
"!"           return Bang;

"[["          return DLbracket; /* Bash conditional */
"]]"          return DRbracket;

"#".*         ;                 /* Comments. Only if # would start a word. */

([^[:space:]|&;()<>]|\.)+ return WORD;

此模式集合经过仔细排序,以便生成正确的标记;这些模式并不相互排斥。特别是:

  • 换行符将匹配前两个规则。因为它实际上在语法上很重要,所以忽略规则放在第二位。

  • 元字符永远不是标记的一部分(除非转义),因此 WORD 规则不允许使用它们。另一方面,{}! 不是元字符,尽管它们在某些上下文中具有语法意义;它们必须首先被识别为单词才能使用。从这个意义上讲,它们类似于 iffor 等关键字;它们的模式需要出现在 WORD 规则之前,以便在它们形成一个完整的单词时被正确识别。

  • 类似地,# 字符仅当它是单词中的第一个字符时才开始注释。否则就是一个普通的字字符。同样,识别(和忽略)注释需要匹配规则出现在 WORD 之前。请注意,该规则 not 匹配终止注释的换行符;该换行符实际上在语法上很重要,就像任何其他换行符一样,因此必须将其返回给解析器。