Обновление: грамматику, которая генерирует файлы лексера и парсера JS, можно найти здесь: https://github.com/bkiers/PCREParser/tree/js
Я не обновлял эту страницу некоторое время (я сделаю этоскоро, если найду время).Вот модифицированная грамматика, которая не использует глобальный возврат и работает (насколько я вижу) с JavaScript (протестировано с ANTLR v3.3):
grammar PCRE;
options {
output=AST;
language=JavaScript;
}
tokens {
REGEX;
ATOM;
DOT;
OR;
CHAR_CLASS;
NEG_CHAR_CLASS;
RANGE;
QUOTATION;
INT;
QUANTIFIER;
GREEDY;
RELUCTANT;
POSSESSIVE;
BACK_REFERENCE;
CAPTURE_GROUP;
FLAG_GROUP;
ATOMIC_GROUP;
NON_CAPTURE_GROUP;
POSITIVE_LOOK_AHEAD;
NEGATIVE_LOOK_AHEAD;
POSITIVE_LOOK_BEHIND;
NEGATIVE_LOOK_BEHIND;
FLAGS;
ENABLE;
DISABLE;
DOT;
ATOM;
ATOMS;
}
// parser rules
parse
: regexAtoms EOF -> ^(REGEX regexAtoms)
;
regexAtoms
: atoms (Or^ atoms)*
;
atoms
: regexAtom* -> ^(ATOMS regexAtom*)
;
regexAtom
: unit quantifier? -> ^(ATOM unit quantifier?)
;
unit
: charClass
| singleChar
| boundaryMatch
| quotation
| backReference
| group
| ShorthandCharacterClass
| PosixCharacterClass
| Dot
;
quantifier
: (greedy -> ^(GREEDY greedy))
('+' -> ^(POSSESSIVE greedy)
|'?' -> ^(RELUCTANT greedy)
)?
;
greedy
: '+' -> INT["1"] INT["2147483647"]
| '*' -> INT["0"] INT["2147483647"]
| '?' -> INT["0"] INT["1"]
| '{' (a=integer -> INT[$a.text] INT[$a.text])
(
(',' -> INT[$a.text] INT["2147483647"])
(b=integer -> INT[$a.text] INT[$b.text])?
)?
'}'
;
charClass
: '[' (('^')=> '^' charClassAtom+ ']' -> ^(NEG_CHAR_CLASS charClassAtom+)
| charClassAtom+ ']' -> ^(CHAR_CLASS charClassAtom+)
)
;
charClassAtom
: (charClassSingleChar '-' charClassSingleChar)=>
charClassSingleChar '-' charClassSingleChar -> ^(RANGE charClassSingleChar charClassSingleChar)
| quotation
| ShorthandCharacterClass
| BoundaryMatch
| PosixCharacterClass
| charClassSingleChar
;
charClassSingleChar
: charClassEscape
| EscapeSequence
| OctalNumber
| SmallHexNumber
| UnicodeChar
| Or
| Caret
| Hyphen
| Colon
| Dollar
| SquareBracketStart
| RoundBracketStart
| RoundBracketEnd
| CurlyBracketStart
| CurlyBracketEnd
| Equals
| LessThan
| GreaterThan
| ExclamationMark
| Comma
| Plus
| Star
| QuestionMark
| Dot
| Digit
| OtherChar
;
charClassEscape
: '\\' ('\\' | '^' | ']' | '-')
;
singleChar
: regexEscape
| EscapeSequence
| OctalNumber
| SmallHexNumber
| UnicodeChar
| Hyphen
| Colon
| SquareBracketEnd
| CurlyBracketEnd
| Equals
| LessThan
| GreaterThan
| ExclamationMark
| Comma
| Digit
| OtherChar
;
regexEscape
: '\\' ('\\' | '|' | '^' | '$' | '[' | '(' | ')' | '{' | '}' | '+' | '*' | '?' | '.')
;
boundaryMatch
: Caret
| Dollar
| BoundaryMatch
;
backReference
: '\\' integer -> ^(BACK_REFERENCE integer)
;
group
: '('
( '?' ( (flags -> ^(FLAG_GROUP flags)
)
(':' regexAtoms -> ^(NON_CAPTURE_GROUP flags regexAtoms)
)?
| '>' regexAtoms -> ^(ATOMIC_GROUP regexAtoms)
| '!' regexAtoms -> ^(NEGATIVE_LOOK_AHEAD regexAtoms)
| '=' regexAtoms -> ^(POSITIVE_LOOK_AHEAD regexAtoms)
| '<' ( '!' regexAtoms -> ^(NEGATIVE_LOOK_BEHIND regexAtoms)
| '=' regexAtoms -> ^(POSITIVE_LOOK_BEHIND regexAtoms)
)
)
| regexAtoms -> ^(CAPTURE_GROUP regexAtoms)
)
')'
;
flags
: (a=singleFlags -> ^(FLAGS ^(ENABLE $a)))
('-' b=singleFlags -> ^(FLAGS ^(ENABLE $a) ^(DISABLE $b))
)?
;
singleFlags
: OtherChar*
;
quotation
: QuotationStart innerQuotation QuotationEnd -> ^(QUOTATION innerQuotation)
;
innerQuotation
: (~QuotationEnd)*
;
integer
: (options{greedy=true;}: Digit)+
;
// lexer rules
QuotationStart
: '\\Q'
;
QuotationEnd
: '\\E'
;
PosixCharacterClass
: '\\p{' ('Lower' | 'Upper' | 'ASCII' | 'Alpha' | 'Digit' | 'Alnum' | 'Punct' | 'Graph' | 'Print' | 'Blank' | 'Cntrl' | 'XDigit' | 'Space') '}'
;
ShorthandCharacterClass
: Escape ('d' | 'D' | 's' | 'S' | 'w' | 'W')
;
BoundaryMatch
: Escape ('b' | 'B' | 'A' | 'Z' | 'z' | 'G')
;
OctalNumber
: Escape '0' ( OctDigit? OctDigit
| '0'..'3' OctDigit OctDigit
)
;
SmallHexNumber
: Escape 'x' HexDigit HexDigit
;
U nicodeChar
: Escape 'u' HexDigit HexDigit HexDigit HexDigit
;
EscapeSequence
: Escape ('t' | 'n' | 'r' | 'f' | 'a' | 'e' | ~('a'..'z' | 'A'..'Z' | '0'..'9'))
;
Escape : '\\';
Or : '|';
Hyphen : '-';
Caret : '^';
Colon : ':';
Dollar : '$';
SquareBracketStart : '[';
SquareBracketEnd : ']';
RoundBracketStart : '(';
RoundBracketEnd : ')';
CurlyBracketStart : '{';
CurlyBracketEnd : '}';
Equals : '=';
LessThan : '<';
GreaterThan : '>';
ExclamationMark : '!';
Comma : ',';
Plus : '+';
Star : '*';
QuestionMark : '?';
Dot : '.';
Digit : '0'..'9';
OtherChar : . ;
// fragments
fragment OctDigit : '0'..'7';
fragment HexDigit : ('0'..'9' | 'a'..'f' | 'A'..'F');
Она содержит рядом с целевым кодом нет,Единственное, что я сделал, это использовал пару строковых литералов для перезаписи AST (см. Квантификатор) и несколько вызовов .text
, но почти все цели ANTLR принимают строковые литералы в двойных кавычках и .text
, так что с Java вы должны быть в порядке, Python, C и JavaScript.Для C # я думаю, вам нужно изменить вызовы .text
на .Text
.
Вы можете проверить это с помощью следующего HTML-файла:
<html>
<head>
<script type="text/javascript" src="antlr3-all-min.js"></script>
<script type="text/javascript" src="PCRELexer.js"></script>
<script type="text/javascript" src="PCREParser.js"></script>
<style type="text/css">
#tree {
padding: 20px;
font-family: Monospace;
}
.leaf {
font-weight: bold;
font-size: 130%;
}
</style>
<script type="text/javascript">
function init() {
document.getElementById("parse").onclick = parseRegex;
}
function parseRegex() {
document.getElementById("tree").innerHTML = "";
var regex = document.getElementById("regex").value;
if(regex) {
var lexer = new PCRELexer(new org.antlr.runtime.ANTLRStringStream(regex));
var parser = new PCREParser(new org.antlr.runtime.CommonTokenStream(lexer));
var root = parser.parse().getTree();
printTree(root, 0);
}
else {
document.getElementById("regex").value = "enter a regex here first";
}
}
function printTree(root, indent) {
if(!root) return;
for(var i = 0; i < indent; i++) {
document.getElementById("tree").innerHTML += ". ";
}
var n = root.getChildCount();
if(n == 0) {
document.getElementById("tree").innerHTML += "<span class=\"leaf\">" + root + "</span><br />";
}
else {
document.getElementById("tree").innerHTML += root + "<br />";
}
for(i = 0; i < n; i++) {
printTree(root.getChild(i), indent + 1);
}
}
</script>
</head>
<body onload="init()">
<input id="regex" type="text" size="50" />
<button id="parse">parse</button>
<div id="tree"></div>
</body>
</html>
(я редко использую JavaScript, поэтому не обращайте внимания на беспорядок, размещенный выше!)
Если вы сейчас проанализируете регулярное выражение:
[^-234-7]|(?=[ab\]@]++$).|^$|\1\.\(
с помощью приведенного выше HTML-файла, вы увидите на экране следующее:
REGEX
. |
. . |
. . . |
. . . . ATOMS
. . . . . ATOM
. . . . . . NEG_CHAR_CLASS
. . . . . . . -
. . . . . . . 2
. . . . . . . 3
. . . . . . . RANGE
. . . . . . . . 4
. . . . . . . . 7
. . . . ATOMS
. . . . . ATOM
. . . . . . POSITIVE_LOOK_AHEAD
. . . . . . . ATOMS
. . . . . . . . ATOM
. . . . . . . . . CHAR_CLASS
. . . . . . . . . . a
. . . . . . . . . . b
. . . . . . . . . . \]
. . . . . . . . . . @
. . . . . . . . . POSSESSIVE
. . . . . . . . . . 1
. . . . . . . . . . 2147483647
. . . . . . . . ATOM
. . . . . . . . . $
. . . . . ATOM
. . . . . . .
. . . ATOMS
. . . . ATOM
. . . . . ^
. . . . ATOM
. . . . . $
. . ATOMS
. . . ATOM
. . . . BACK_REFERENCE
. . . . . 1
. . . ATOM
. . . . \.
. . . ATOM
. . . . \(
Beосторожно, я не проверял грамматику должным образом!Буду признателен, если вы сообщите мне, если найдете какие-либо ошибки.
РЕДАКТИРОВАТЬ
Если вы откомментируете строку language=JavaScript;
, создайте заново лексер и парсер и запуститеследующий класс:
import org.antlr.runtime.*;
import org.antlr.runtime.tree.*;
import org.antlr.stringtemplate.*;
public class Main {
public static void main(String[] args) throws Exception {
String src = "[^-234-7]|(?=[ab\\]@]++$).|^$|\\1\\.\\(|\\Q*+[\\E";
PCRELexer lexer = new PCRELexer(new ANTLRStringStream(src));
PCREParser parser = new PCREParser(new CommonTokenStream(lexer));
CommonTree tree = (CommonTree)parser.parse().getTree();
DOTTreeGenerator gen = new DOTTreeGenerator();
StringTemplate st = gen.toDOT(tree);
System.out.println(st);
}
}
вы увидите DOT-вывод, соответствующий следующему AST:
![enter image description here](https://i.stack.imgur.com/LPQiU.png)