ANTLR是否可以在内部使用嵌入语法制作语法?

时间:2011-10-13 07:50:42

标签: antlr grammar

ANTLR:是否可以在内部使用嵌入式语法(使用自己的词法分析器)制作语法?

例如,在我的语言中,我有能力使用嵌入式SQL语言:

var Query = [select * from table];
with Query do something ....;

是否可以使用ANTLR?

2 个答案:

答案 0 :(得分:9)

  

是否可以在内部使用嵌入语法(使用自己的词法分析器)制作语法?

如果你的意思是是否可以在一个语法中定义两种语言(使用单独的词法分析器),那么答案是:不,这是不可能的。

但是,如果问题是是否可以将两种语言解析为单个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:

enter image description here

如果你想在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 wiki以获取示例语法。

答案 1 :(得分:2)

是的,使用AntLR,它被称为岛语法。 您可以在island-grammar文件夹中的v3 examples中获得一个工作示例:它显示了使用语法来解析java代码中的javadoc注释。

您还可以在文档Island Grammars Under Parser ControlAnother one中找到一些线索。