antlr4 代码 语法树_使用ANTLR4,用于代码镜像和基于Web的DSL的Primefaces扩展
antlr4 代碼 語(yǔ)法樹
DSL是很酷的東西,但我不清楚它們有什么用。
然后我意識(shí)到它們對(duì)以下方面有好處:
- 擺脫復(fù)雜的UI
意思是
- 更快的做事方式
而已。 當(dāng)我閱讀此博客時(shí),我得出了這個(gè)結(jié)論。
如果您的用戶是技術(shù)人員,并且不懼怕類似SQL的語(yǔ)法處理方式,則DSL特別適合
- 你有語(yǔ)法高亮
- 你有代碼補(bǔ)全
否則,DSL有點(diǎn)爛。
因此,我不得不向客戶提出一些概念證明。 他有模糊的要求,要準(zhǔn)確提取團(tuán)隊(duì)的需求并不容易(他們需要很多東西,而且他很忙),因此DSL可以在此過程中提供很多幫助,因?yàn)槿藗儽黄仍谒麄冃枰獣r(shí)才想清楚他們的需求面對(duì)一種語(yǔ)法(甚至很小的一種)。
因此,我采用了以下技術(shù):
- 用于代碼鏡像的JSF庫(kù)Primefaces擴(kuò)展
- ANTLR4 (與ANTLR3相比有很大的改進(jìn),而且本書很棒)
不幸的是,我無法在兩個(gè)工具中重復(fù)使用語(yǔ)法。 實(shí)際上,我找不到任何可以做到這一點(diǎn)的解決方案。 至少對(duì)于基于Web的JSF解決方案。 而且沒有時(shí)間學(xué)習(xí)。 所以我不得不稍微改一下。
首先,我們需要語(yǔ)法。 ANTLR4比ANTLR3更好,因?yàn)楝F(xiàn)在接線代碼是通過訪問者和偵聽器完成的。 語(yǔ)法內(nèi)沒有更多的Java代碼。 那很棒,而且更容易使用。
所以你可以有一個(gè)這樣的語(yǔ)法
grammar Grammar; options {language = Java; } @lexer::header {package parsers; }@parser::header {package parsers; } eval : expr EOF; expr : 'JOB' (jobName)? type 'TARGET' targetList ('START' startExpr)? startExpr : 'AT' cronTerm| 'AFTER' timeAmount timeUnits; timeAmount: INT; jobName: STRING; targetList: STRING (',' STRING)*; type : deleteUser| createUser;deleteUser: opDelete userName; createUser: opCreate userName; opDelete: 'DELETE'; opCreate: 'CREATE'; userName: STRING;cronTerm: '!'? (INT | '-' | '/' | '*' | '>' | '<')+;timeUnits: 'MINUTES'| 'HOURS'| 'DAYS'| 'WEEKS'| 'MONTHS';WS : [ \t\r\n]+ -> skip;STRING: '"' ( ESC_SEQ | ~('\\'|'"') )* '"';fragment HEX_DIGIT : ('0'..'9'|'a'..'f'|'A'..'F') ;fragment ESC_SEQ: '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\')| UNICODE_ESC| OCTAL_ESC;fragment OCTAL_ESC: '\\' ('0'..'3') ('0'..'7') ('0'..'7')| '\\' ('0'..'7') ('0'..'7')| '\\' ('0'..'7');fragment UNICODE_ESC: '\\' 'u' HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT;ID : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*;INT : '0'..'9'+;要編譯語(yǔ)法,請(qǐng)嘗試
public static void main(String[] args) {String[] arg0 = {"-visitor","/pathto/Grammar.g4"};org.antlr.v4.Tool.main(arg0);}然后,ANTLR將為您生成類。
在我們的例子中,我們想訪問解析樹并檢索我們想要的值。 我們這樣做擴(kuò)展了生成的抽象類。
import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.StringTokenizer;import org.antlr.v4.runtime.tree.ErrorNode;import bsh.EvalError; import bsh.Interpreter;public class MyLoader2 extends GrammarBaseVisitor<Void> {private String jobName,cronTerm,timeUnits,userName,jobType;private List<String> targetList;private boolean now,errorFound;private int timeAmount; private Interpreter bsh = new Interpreter();private String eval(String s) throws EvaluationException{try {if (!s.startsWith("\"")){return s;}bsh.eval("String s="+s);return (String)bsh.eval("s");} catch (EvalError e) {throw new EvaluationException(s);}}@Overridepublic Void visitTimeAmount(TimeAmountContext ctx) {try{this.timeAmount = Integer.parseInt(ctx.getText());}catch(java.lang.NumberFormatException nfe){throw new InvalidTimeAmountException(ctx.getText());}return super.visitTimeAmount(ctx);}@Overridepublic Void visitUserName(UserNameContext ctx) {this.userName = eval(ctx.getText());return super.visitUserName(ctx);}@Overridepublic Void visitCronTerm(CronTermContext ctx) {this.cronTerm = eval(ctx.getText());return super.visitCronTerm(ctx);}@Overridepublic Void visitTimeUnits(TimeUnitsContext ctx) {this.timeUnits = ctx.getText();return super.visitTimeUnits(ctx);}@Overridepublic Void visitTargetList(TargetListContext ctx) {this.targetList = toStringList(ctx.getText());return super.visitTargetList(ctx);}@Overridepublic Void visitJobName(JobNameContext ctx) {this.jobName = eval(ctx.getText());return super.visitJobName(ctx);}@Overridepublic Void visitOpCreate(OpCreateContext ctx) {this.jobType = ctx.getText();return super.visitOpCreate(ctx);}@Overridepublic Void visitOpDelete(OpDeleteContext ctx) {this.jobType = ctx.getText();return super.visitOpDelete(ctx);}private List<String> toStringList(String text) {List<String> l = new ArrayList<String>();StringTokenizer st = new StringTokenizer(text," ,");while(st.hasMoreElements()){l.add(eval(st.nextToken()));}return l;}private Map<String, String> toMapList(String text) throws InvalidItemsException, InvalidKeyvalException {Map<String, String> m = new HashMap<String, String>();if (text == null || text.trim().length() == 0){return m;}String[] items = text.split(",");if (items.length == 0){throw new InvalidItemsException();}for(String item:items){String[] keyval = item.split("=");if (keyval.length == 2){m.put(keyval[0], keyval[1]);}else{throw new InvalidKeyvalException(keyval.length);}}return m;}public String getJobName() {return jobName;}public String getCronTerm() {return cronTerm;}public String getTimeUnits() {return timeUnits;}public String getUserName() {return userName;}public String getJobType() {return jobType;}public List<String> getTargetList() {return targetList;}public boolean isNow() {return now;}public int getTimeAmount() {return timeAmount;}@Overridepublic Void visitOpNow(OpNowContext ctx) {this.now = ctx.getText().equals("NOW");return super.visitOpNow(ctx);}public boolean isErrorFound() {return errorFound;}@Overridepublic Void visitErrorNode(ErrorNode node) {this.errorFound = true;return super.visitErrorNode(node);} }請(qǐng)注意, beanshell解釋器用于將“ xyz”之類的字符串評(píng)估為xyz。 這對(duì)于其中包含轉(zhuǎn)義引號(hào)和字符的字符串特別有用。
因此,您有了語(yǔ)法和visiter / loader bean,然后我們可以對(duì)其進(jìn)行測(cè)試:
private static MyLoader getLoader(String str){ANTLRInputStream input = new ANTLRInputStream(str);GrammarLexer lexer = new GrammarLexer(input);CommonTokenStream tokens = new CommonTokenStream(lexer);GrammarParser parser = new GrammarParser(tokens);ParseTree tree = parser.eval();MyLoader loader = new MyLoader();loader.visit(tree);return loader; }public static void main(String[] args){MyLoader loader = getLoader("JOB \"jobName\" CREATE \"myuser\" TARGET \"site1\",\"site2\" START AFTER 1 DAY");System.out.println(loader.getJobName());System.out.println(loader.getJobType()); }大。 現(xiàn)在是黑客。 Code Mirror支持自定義語(yǔ)法,但JSF Primefaces擴(kuò)展中不存在 。 因此,我打開了resources-codemirror-1.2.0.jar,打開了/META-INF/resources/primefaces-extensions/codemirror/mode/modes.js文件,對(duì)其進(jìn)行了格式化 (以便我可以閱讀),然后我剛剛選擇了最簡(jiǎn)單的語(yǔ)言作為我的新自定義sintax熒光筆!
我改名了
(...) }, "xml"), CodeMirror.defineMIME("text/x-markdown", "markdown"), CodeMirror.defineMode("mylanguage", function (e) { (...)var t = e.indentUnit,n, i = r(["site", "type", "targetList"]),s = r(["AT","AFTER","CREATE","MINUTES","HOURS","TARGET","MONTHS","JOB","DAYS","DELETE","START","WEEKS" ]), (...) }), CodeMirror.defineMIME("text/x-mylanguage", "mylanguage"), CodeMirror.defineMode("ntriples", function () {(...)那些在“ s = r”中用大寫字母表示的標(biāo)記是將被突出顯示的標(biāo)記,而在“ i = r”中的那些標(biāo)記則是將被突出顯示的標(biāo)記。 為什么我們都想要? 因?yàn)榈诙N類型是“占位符”,我的意思是,我們將它們用于自動(dòng)填充內(nèi)容。
好,那么您的JSF xhtml頁(yè)面將如下所示
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"xmlns:f="http://java.sun.com/jsf/core"xmlns:h="http://java.sun.com/jsf/html"xmlns:ui="http://java.sun.com/jsf/facelets"xmlns:p="http://primefaces.org/ui"xmlns:pe="http://primefaces.org/ui/extensions"><h:body> <h:form id="form"> <pe:codeMirrorid="codeMirror"style="width:600px;" mode="myLanguage" widgetVar="myCodeMirror" theme="eclipse"value="#{myMB.script}"lineNumbers="true" completeMethod="#{myMB.complete}" extraKeys="{ 'Ctrl-Space': function(cm) { PF('myCodeMirror').complete(); }}"/> <p:commandButton value="Verify" action="#{myMB.verify}" /> (...)現(xiàn)在,我們需要自動(dòng)完成功能。 這是無聊的部分。 您必須手動(dòng)完成大部分的完成工作,因?yàn)闆]有上下文信息(請(qǐng)記住,我沒有時(shí)間學(xué)習(xí)……),所以快速而骯臟的方式是這樣的
in myMBpublic List<String> complete(final CompleteEvent event) {try {return this.myEJB.complete(event.getToken());} catch (Exception e) {jsfUtilEJB.addErrorMessage(e,"Could not complete");return null;}} in myEJBprivate static final String SITE = "site_"; public List<String> complete(String token) throws Exception {if (token == null || token.trim().length() == 0){return null;}else{List<String> suggestions = new ArrayList<String>();switch(token){//first search variablescase "targetlist":for(String v:TARGETS){suggestions.add(v); }break;case "site":List<Site> allSites = this.baseService.getSiteDAO().getAll();for(Site s:allSites){suggestions.add("\""+SITE+s.getName()+"\"");}break;case "type":suggestions.add("DELETE \"userName\"");suggestions.add("CREATE \"userName\"");break;case "AT":suggestions.add("AT \"cronExpression\"");suggestions.add("AT \"0 * * * * * * *\"");break;case "AFTER":for(int a:AMOUNTS){for(String u:UNITS){if (a == 1){suggestions.add("AFTER"+" "+a+" "+u);}else{suggestions.add("AFTER"+" "+a+" "+u+"S");}}}break;case "TARGET":for(String v:TARGETS){suggestions.add("TARGET "+v+""); }break;case "JOB":suggestions.add("JOB \"jobName\" \ntype \nTARGET targetlist \nSTART");break;case "START":suggestions.add("START AT \"cronExpression\"");suggestions.add("START AT \"0 * * * * * * *\"");for(int a:AMOUNTS){for(String u:UNITS){if (a == 1){suggestions.add("START AFTER"+" "+a+" "+u);}else{suggestions.add("START AFTER"+" "+a+" "+u+"S");}}}suggestions.add("START NOW");break;case "DELETE":suggestions.add("DELETE \"userName\"");break;case "CREATE":suggestions.add("CREATE \"userName\"");break;default:if (token.startsWith(SITE)){List<Site> matchedSites = this.baseService.getSiteDAO().getByPattern(token.substring(SITE.length())+"*");for(Site s:matchedSites){suggestions.add("\""+SITE+s.getName()+"\"");}}else{//then search substringsfor(String kw:KEYWORDS){if (kw.toLowerCase().startsWith(token.toLowerCase())){suggestions.add(kw);}}}}//end switch//remove dups and sortSet<String> ts = new TreeSet<String>(suggestions); return new ArrayList<String>(ts);} }private static final int[] AMOUNTS = {1,5,10}; private static final String[] UNITS = {"MINUTE","HOUR","DAY","WEEK","MONTH"}; private static final String[] TARGETS = {"site"};/** KEYWORDS are basic suggestions*/ private static final String[] KEYWORDS = {"AT","AFTER","CREATE","MINUTES","HOURS","TARGET","MONTHS","JOB","DAYS","DELETE","START","WEEKS"};因此,關(guān)鍵字的自動(dòng)填充內(nèi)容將僅向您顯示字段和更多關(guān)鍵字,而“占位符”(還記得jar中的codemirror javascript中的小寫關(guān)鍵字嗎?)是從數(shù)據(jù)庫(kù)中檢索到的動(dòng)態(tài)值(用于實(shí)際值)完成的。 另外,您可以使用部分字符串來檢索以子字符串開頭的那些字符串,如下所示:
當(dāng)然,在JPA中類似模式的搜索可以這樣執(zhí)行:
public abstract class GenericDAO<E> {protected EntityManager entityManager;private Class<E> clazz;private EntityType<E> pClass;@SuppressWarnings("unchecked")public GenericDAO(EntityManager entityManager) {this.entityManager = entityManager;ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass();this.clazz = (Class<E>) genericSuperclass.getActualTypeArguments()[0];EntityManagerFactory emf = this.entityManager.getEntityManagerFactory();Metamodel metamodel = emf.getMetamodel();this.pClass = metamodel.entity(clazz);}public List<E> getByPattern(String pattern) {pattern = pattern.replace("?", "_").replace("*", "%");CriteriaBuilder cb = entityManager.getCriteriaBuilder();CriteriaQuery<E> q = cb.createQuery(clazz);Root<E> entity = q.from(clazz);SingularAttribute<E, String> singularAttribute = (SingularAttribute<E, String>) pClass.getDeclaredSingularAttribute(getNameableField(clazz));Path<String> path = entity.get(singularAttribute);q.where(cb.like(path, pattern));q.select(entity);TypedQuery<E> tq = entityManager.createQuery(q);List<E> all = tq.getResultList();return all;}private String getNameableField(Class<E> clazz) {for(Field f : clazz.getDeclaredFields()) {for(Annotation a : f.getAnnotations()) {if(a.annotationType() == Nameable.class) {return f.getName();}}}return null;} (...)其中Nameable是您的實(shí)體類的注釋:
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD}) public @interface Nameable { }用它來注釋實(shí)體類中的單個(gè)列,即String。 像這樣:
@Entity @Table(uniqueConstraints=@UniqueConstraint(columnNames={"name"})) public class Site implements Serializable {/****/private static final long serialVersionUID = 8008732613898597654L;@Id@GeneratedValue(strategy = GenerationType.AUTO)private Long id;@Nameable@Column(nullable=false)private String name; (...) 當(dāng)然,“驗(yàn)證”按鈕只是獲取您的腳本并將其推入加載器。
翻譯自: https://www.javacodegeeks.com/2014/01/playing-with-antlr4-primefaces-extensions-for-code-mirror-and-web-based-dsls.html
antlr4 代碼 語(yǔ)法樹
總結(jié)
以上是生活随笔為你收集整理的antlr4 代码 语法树_使用ANTLR4,用于代码镜像和基于Web的DSL的Primefaces扩展的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电脑显卡供电电流是多少瓦(电脑显卡供电多
- 下一篇: 撤回上一步的快捷键(撤回下一步快捷键)