Можно ли создать грамматику с встроенной грамматикой (со своим собственным лексером) внутри?
Если вы имеете в виду, возможно ли определить два языка в одной грамматике (используя отдельные лексеры), тогда ответ: нет, это невозможно.
Однако, если вопрос заключается в том, возможно ли анализировать два языка в одном AST, тогда ответ: да, это возможно.
Вам просто нужно:
- определить оба языка в их собственной грамматике;
- создать правило лексера в основной грамматике, которое фиксирует весь ввод встроенного языка;
- использует правило перезаписи, которое вызывает пользовательский метод, который анализирует внешний AST и вставляет его в основной AST, используя
{
... }
(см. Правило expr
в основной грамматике (MyLanguage.g
) ).
MyLanguage.g
grammar MyLanguage;
options {
output=AST;
ASTLabelType=CommonTree;
}
tokens {
ROOT;
}
@members {
private CommonTree parseSQL(String sqlSrc) {
try {
MiniSQLLexer lexer = new MiniSQLLexer(new ANTLRStringStream(sqlSrc));
MiniSQLParser parser = new MiniSQLParser(new CommonTokenStream(lexer));
return (CommonTree)parser.parse().getTree();
} catch(Exception e) {
return new CommonTree(new CommonToken(-1, e.getMessage()));
}
}
}
parse
: assignment+ EOF -> ^(ROOT assignment+)
;
assignment
: Var Id '=' expr ';' -> ^('=' Id expr)
;
expr
: Num
| SQL -> {parseSQL($SQL.text)}
;
Var : 'var';
Id : ('a'..'z' | 'A'..'Z')+;
Num : '0'..'9'+;
SQL : '[' ~']'* ']';
Space : ' ' {skip();};
MiniSQL.g
grammar MiniSQL;
options {
output=AST;
ASTLabelType=CommonTree;
}
parse
: '[' statement ']' EOF -> statement
;
statement
: select
;
select
: Select '*' From ID -> ^(Select '*' From ID)
;
Select : 'select';
From : 'from';
ID : ('a'..'z' | 'A'..'Z')+;
Space : ' ' {skip();};
Main.java
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 = "var Query = [select * from table]; var x = 42;";
MyLanguageLexer lexer = new MyLanguageLexer(new ANTLRStringStream(src));
MyLanguageParser parser = new MyLanguageParser(new CommonTokenStream(lexer));
CommonTree tree = (CommonTree)parser.parse().getTree();
DOTTreeGenerator gen = new DOTTreeGenerator();
StringTemplate st = gen.toDOT(tree);
System.out.println(st);
}
}
Запустить демо
java -cp antlr-3.3.jar org.antlr.Tool MiniSQL.g
java -cp antlr-3.3.jar org.antlr.Tool MyLanguage.g
javac -cp antlr-3.3.jar *.java
java -cp .:antlr-3.3.jar Main
С учетом ввода:
var Query = [select * from table]; var x = 42;
Выход класса Main
соответствует следующему AST:
И если вы хотите разрешить строковые литералы внутри вашего SQL (которые могут содержать ]
), и комментарии (которые могут содержать '
и ]
), вы можете использовать следующее правило SQL
внутри вашего SQL. основная грамматика:
SQL
: '[' ( ~(']' | '\'' | '-')
| '-' ~'-'
| COMMENT
| STR
)*
']'
;
fragment STR
: '\'' (~('\'' | '\r' | '\n') | '\'\'')+ '\''
| '\'\''
;
fragment COMMENT
: '--' ~('\r' | '\n')*
;
, который правильно проанализировал бы следующие входные данные в одном токене:
[
select a,b,c
from table
where a='A''B]C'
and b='' -- some ] comment ] here'
]
Просто знайте, что попытка создать грамматику для всего диалекта SQL (или даже большого подмножества) не является тривиальной задачей! Возможно, вы захотите поискать существующие парсеры SQL или посмотрите на вики ANTLR пример грамматики.