В этом случае вам нужно будет использовать целевой спецификатор c кода внутри предиката в вашей грамматике.
Что вам нужно сделать, это так: всякий раз, когда Лексер натыкается на ^
, ему придется посмотреть на 2 символа впереди в потоке. Если эти 2 символа являются словом и не словом, он создаст токен Controlchar
, включая Alpha
, следующий за ^
. Если нет, создайте Pointer
из ^
.
Для ANTLR3 с Java в качестве целевого языка, это может выглядеть так:
// Cannot be a fragment now, because we're changing the `type` in certain cases. And because it is
// no fragment any more, it has to come before the `ControlString` rule.
Controlchar
: '^' ( // Execute the predicate, which looks ahead 2 chars and passes if
// these 2 chars are a word and a non-word
{((char)input.LA(1) + "" + (char)input.LA(2)).matches("\\w\\W")}?=>
// If the predicate is true, match a single `Alpha`
Alpha
| // If the predicate failed, change the type of this token to a `Pointer`
{$type=Pointer;}
)
| '#' Digitseq
| '#' '$' Hexdigitseq
;
ControlString
: Controlchar+
;
Я быстро запустил test:
import org.antlr.runtime.*;
public class Main {
public static void main(String[] args) throws Exception {
String[] tests = {
"^Foo",
"^F oo",
"^ F oo",
"Foo^M",
"Foo ^ M",
"Foo ^M",
"Foo^ M",
"'Text'^M",
"'Text' ^M",
"'Text' ^ M",
"^M'Text'",
"^M 'Text'",
"^ M'Text'",
"^Q^E^D"
};
for (String test : tests) {
TLexer lexer = new TLexer(new ANTLRStringStream(test));
CommonTokenStream tokenStream = new CommonTokenStream(lexer);
tokenStream.fill();
System.out.printf("\ntest: %-15s tokens: ", test);
for (Token t : tokenStream.getTokens()) {
if (t.getType() != -1) {
System.out.printf(" %s", TParser.tokenNames[t.getType()]);
}
}
}
}
}
, который напечатал:
test: ^Foo tokens: Pointer TkIdentifier
test: ^F oo tokens: Controlchar TkIdentifier
test: ^ F oo tokens: Pointer TkIdentifier TkIdentifier
test: Foo^M tokens: TkIdentifier Controlchar
test: Foo ^ M tokens: TkIdentifier Pointer TkIdentifier
test: Foo ^M tokens: TkIdentifier Controlchar
test: Foo^ M tokens: TkIdentifier Pointer TkIdentifier
test: 'Text'^M tokens: QuotedString Controlchar
test: 'Text' ^M tokens: QuotedString Controlchar
test: 'Text' ^ M tokens: QuotedString Pointer TkIdentifier
test: ^M'Text' tokens: Controlchar QuotedString
test: ^M 'Text' tokens: Controlchar QuotedString
test: ^ M'Text' tokens: Pointer TkIdentifier QuotedString
test: ^Q^E^D tokens: ControlString
Обратите внимание, что вы также можете сохранить свою грамматику (немного) чище, переместив встроенный код в раздел lexer::members
:
// Place this in the top of your grammar definition
@lexer::members {
private boolean isControlchar() {
// TODO
// - check if there are actually 2 chars ahead and not an EOF
// - perhaps something else than a regex match here
return ((char)input.LA(1) + "" + (char)input.LA(2)).matches("\\w\\W");
}
}
...
Controlchar
: '^' ( {isControlchar()}?=> Alpha
| {$type=Pointer;}
)
| '#' Digitseq
| '#' '$' Hexdigitseq
;
Поскольку type
токена изменяется программно, правило лексера, такое как Controlstring : Controlchar+;
, также будет соответствовать ^^^
(три Pointer
токена). Если возможно, вы можете создать вместо этого правило парсера:
controlstring
: Controlchar+
;