<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          使用 Antlr 實(shí)現(xiàn)簡(jiǎn)單的 DSL

          共 504字,需瀏覽 2分鐘

           ·

          2019-05-24 04:15

          為什么要使用DSL

          DSL是領(lǐng)域?qū)S谜Z言,常見的DSL有SQL,CSS,Shell等等,這些DSL語言有別于其他通用語言如:C++,Java,C#,DSL常在特殊的場(chǎng)景或領(lǐng)域中使用。如下圖:

          Clipboard Image.png

          領(lǐng)域?qū)S谜Z言通常是被領(lǐng)域?qū)<沂褂茫I(lǐng)域?qū)<乙话悴皇煜ねㄓ镁幊陶Z言,但是他們一般對(duì)業(yè)務(wù)非常了解,程序員一般對(duì)通用語言比較熟悉,但是在做行業(yè)軟件的時(shí)候?qū)I(yè)務(wù)部了解。這就需要協(xié)作的過程,一種方式是領(lǐng)域?qū)<彝ㄟ^文檔或者教授的方式把業(yè)務(wù)邏輯傳遞給程序員讓程序員翻譯成業(yè)務(wù)邏輯,而另一種方法,程序員為領(lǐng)域?qū)<叶ㄖ艱SL,并編寫解釋DSL的環(huán)境嵌入在業(yè)務(wù)系統(tǒng)中。這樣在某塊功能的實(shí)現(xiàn)上,程序員可以不用去關(guān)系具體實(shí)現(xiàn)和業(yè)務(wù),而領(lǐng)域?qū)<乙膊挥眠^多的理解程序背后的事情。

          這種需求常常出現(xiàn)在OA系統(tǒng)或ERP系統(tǒng)的工作流中。比如說部門申請(qǐng)單的審批,如果是OA產(chǎn)品,那么這個(gè)審批流程將面對(duì)不同企業(yè)各式各樣的審批的條件,一個(gè)企業(yè)中不同的部門審批的條件也不一樣。如果全靠程序在后臺(tái)死寫,那么不可能窮盡用戶的想法,那么遇見這類對(duì)性能要求不高,又需要很強(qiáng)的靈活性的需求,通常會(huì)用到DSL,讓用戶輸入類似的業(yè)務(wù)邏輯:[部門]=”人事部” AND [金額] <= 1000 通過。

          在舉個(gè)例子,在車聯(lián)網(wǎng)系統(tǒng)中,我們需要判斷一輛車是否在經(jīng)濟(jì)區(qū)中運(yùn)行,這個(gè)業(yè)務(wù)邏輯判斷的因素比較多,常常不是程序員或者產(chǎn)品經(jīng)理可以寫出來的,需要交給車輛專家來編寫。也許會(huì)寫成這樣: ( [天氣]!=”下雨” AND 50< [車速] <= 80 ) OR ( [道路] ==”高速” AND 60< [車速] <= 110 )。這同樣需要我們把他翻譯成我們系統(tǒng)實(shí)現(xiàn)的代碼。

          如果上述的功能比較簡(jiǎn)單,DSL也不會(huì)很復(fù)雜,那么我們只需要簡(jiǎn)單的解釋器模式就可以解決。但是如果遇見的業(yè)務(wù)比較復(fù)雜且變化比較多,那么使用工具來解析DSL將是必然的選擇。

          常見的語法分析器代碼生成工具有yacc,lexer,antlr,T4等等。yacc采用的是LALR(1),而antlr采用LL(k)的解析方法。對(duì)詞法分析,語言分析,AST或者編譯原理有了解的話,有助于這些工具的使用。

          Antlr的安裝

          Antlr可以生成C#,Java'和其他一些語言的解析工具代碼。我這里使用C#做例子,可以在NuGet(Java就是在Maven)中下載最新版本Antlr的DLL,Antlr,Antlr4.Runtime.并且下載Antlr在VisioStudio的項(xiàng)目模板(在VS中Tools->Extensions and Updates)。如果你使用的VS項(xiàng)目模板那么你可以在項(xiàng)目添加g4后綴的文件,antlr詞法和語言生成工具的文法文件都是使用g4為后綴。如下圖,對(duì)于小型項(xiàng)目我們一般使用Combined Grammar,詞法和語法都放在一起。

          可以參考如下地址:https://github.com/tunnelvisionlabs/antlr4cs

          Clipboard Image.png

          在新建的g4中編輯語法,保存并編譯,就會(huì)在項(xiàng)目路徑下的obj\Debug目錄下生成語法解析和詞法解析的基類代碼。

          ?

          Antlr的語法簡(jiǎn)介

          最新的g4版本的語言可以參看官方文檔:,如果需要更加系統(tǒng)的學(xué)習(xí)的話,需要下載最新的antlr4的官方書籍antlr book 4,免費(fèi)的電子書可以百度搜索”The Definitive ANTLR 4 Reference”。


          Antlr實(shí)例

          以在車聯(lián)網(wǎng)系統(tǒng)中,判定車輛是否超速為例子。每個(gè)用戶或者說是企業(yè)都需要管理自己所有的車輛,在業(yè)務(wù)系統(tǒng)中,也會(huì)對(duì)車輛是否超速給出一個(gè)定義。這個(gè)定義也許不會(huì)想[車速]>80這么簡(jiǎn)單,有時(shí)候還會(huì)出現(xiàn)如下的定義:”(([車速]*10+3)>(200)) && ([企業(yè)ID] == \"123\") && ([時(shí)間]>1200 && [時(shí)間]<1700)”。從這個(gè)例子中可以看出,判定超速的規(guī)則支持四則混合運(yùn)算,還有一些特定的變量如車速,企業(yè)ID,時(shí)間。這中類型的定義是我們系統(tǒng)期望的讓每個(gè)用戶定義的方式。因?yàn)檫@種方式足夠靈活。用戶可以隨意配置。為了實(shí)現(xiàn)這種方式,解釋器模式是一個(gè)可行的方案,但是如果我們使用DSL,則更加靈活和可擴(kuò)展。我們定義的這種DSL,不單單執(zhí)行上述的四則混合運(yùn)算,還必須支持變量。這些變量都是我們?cè)谡媸堑南到y(tǒng)運(yùn)行中需要去獲取(數(shù)據(jù)庫(kù)或者緩存)的,也就是說,我們的解析程序首先要獲取這些變量的值,然后再進(jìn)行運(yùn)算,最后得出一個(gè)是否超速的結(jié)果。當(dāng)然隨著我們DSL的解析越來越完善,算法越來越先進(jìn),支持的變量也許會(huì)更多,也許還會(huì)有道路等級(jí),天氣因素等算法因子的出現(xiàn)。

          要實(shí)現(xiàn)這個(gè)需求首先我們要定義文法,也就是g4文件的內(nèi)容。

          注意的是,在一些文法后面用”#”號(hào)定義了一個(gè)名稱,就會(huì)在用于訪問生成的抽象語法樹AST的訪問器中生成該方法,用于訪問當(dāng)這個(gè)規(guī)約被滿足時(shí)候的那個(gè)樹節(jié)點(diǎn)。


          grammar ISL;
          @header?
          {?
          ???? using System;?
          }
          @members?
          {????}/*?
          * Parser Rules?
          */?
          /*?
          * 表達(dá)式?
          */expression?
          ??? : NUMBER??????????? #Number????????????
          ??? | STRING??????????? #String????????????????
          ??? | VARIABLE??????????? #Variable?
          ??? | SUB expression??? #SubExpr?
          ??? | expression op=(MUL|DIV) expression??? #MulDiv?
          ??? | expression op=(ADD|SUB) expression??? #AddSub?
          ??? | LEFT_PAREN expression RIGHT_PAREN??????? #Paren?
          ;equality_expression?
          ??? : TRUE??????? #LogicalTrue?
          ??? | FALSE??????? #LogicalFalse?
          ??? | expression op=(GREATE_THAN | GREATE_EQUAL_THAN | LESS_THAN | LESS_EQUAL_THAN | EQUAL | NOT_EQUAL) expression??? #LogicalOp?
          ??? | equality_expression op=(LOGICAL_NOT | LOGICAL_AND | LOGICAL_OR | EQUAL | NOT_EQUAL) equality_expression??? #LogicalAndOrNot?
          ??? | LEFT_PAREN equality_expression RIGHT_PAREN??????? #Paren2?
          ;/*?
          * 返回語句?
          */?
          return_statement?
          ??????? : RETURN equality_expression SEMICOLON??? #Return?
          ;elseif_list?
          ??? : elseif+?
          ??? //| elseif_list elseif?
          ;elseif?
          ??? : ELSEIF LEFT_PAREN expression RIGHT_PAREN block?
          ;if_statement?
          ??? : IF LEFT_PAREN expression RIGHT_PAREN block?
          ??? | IF LEFT_PAREN expression RIGHT_PAREN block ELSE block?
          ??? | IF LEFT_PAREN expression RIGHT_PAREN block elseif_list?
          ??? | IF LEFT_PAREN expression RIGHT_PAREN block elseif_list ELSE block?
          ;statement?
          ??????? : expression SEMICOLON?
          ??????? | if_statement?
          ;block?
          ??? : LEFT_CURLY statement_list RIGHT_CURLY?
          ??? | LEFT_CURLY RIGHT_CURLY?
          ;statement_list?
          ??????? : statement+?
          ;/*?
          * Lexer Rules?
          */VARIABLE : '[車速]' | '[天氣]' | '[時(shí)間]' | '[企業(yè)ID]' | '[用戶ID]';???????????? // 數(shù)字變量?
          NUMBER : [1-9][0-9]*|[0]|([0-9]+[.][0-9]+) ;???? // 數(shù)字?
          STRING : '"' ('\\"'|.)*? '"' ; // 字符串WS : [ \t\r\n]+ -> skip ; // skip spaces, tabs, newlinesADD : '+' ;?
          SUB : '-' ;?
          MUL : '*' ;?
          DIV : '/' ;?
          MOD : '%' ;?
          GREATE_THAN : '>' ;?
          GREATE_EQUAL_THAN : '>=' ;?
          LESS_THAN : '<' ;?
          LESS_EQUAL_THAN : '<=' ;?
          EQUAL : '==' ;?
          TRUE : 'true' ;?
          FALSE : 'false' ;?
          NOT_EQUAL : '!=' ;?
          LOGICAL_AND : '&&' ;?
          LOGICAL_OR : '||' ;?
          LOGICAL_NOT : '!' ;?
          LEFT_PAREN : '(' ;?
          RIGHT_PAREN : ')' ;?
          LEFT_CURLY : '{' ;?
          RIGHT_CURLY : '}' ;?
          CR : '\n' ;?
          IF : 'if' ;?
          ELSE : 'else' ;?
          ELSEIF : 'else if' ;?
          SEMICOLON : ';' ;?
          DOUBLE_QUOTATION : '"' ;?
          RETURN : 'return' ;LINE_COMMENT : '//' .*? '\n' -> skip ;?
          COMMENT : '/*' .*? '*/' -> skip ;

          ?

          生成好代碼之后,我們使用Visitor訪問器(參看The Definitive ANTLR 4 Reference這本書)來實(shí)現(xiàn)語法樹的訪問。

          ?

          public class ISLVisitor2 : ISLBaseVisitor 
              { 
                  public override Result VisitNumber(ISLParser.NumberContext context) 
                  { 
                      Result r = new Result(); 
                      r.Value = double.Parse(context.NUMBER().GetText()); 
                      r.Text = context.NUMBER().GetText(); 
                      return r; 
                  }
          
                  public override Result VisitParen(ISLParser.ParenContext context) 
                  { 
                      Result o = Visit(context.expression()); 
                      o.Text = "(" + o.Text + ")"; 
                      return o; 
                  }
          
                  public override Result VisitParen2(ISLParser.Paren2Context context) 
                  { 
                      Result o = Visit(context.equality_expression()); 
                      o.Text = "(" + o.Text + ")"; 
                      return o; 
                  }
          
                  public override Result VisitMulDiv(ISLParser.MulDivContext context) 
                  { 
                      Result r = new Result();
          
                      double left = Convert.ToDouble(Visit(context.expression(0)).Value); 
                      double right = Convert.ToDouble(Visit(context.expression(1)).Value);
          
                      if (context.op.Type == ISLParser.MUL) 
                      { 
                          r.Value = left * right; 
                          r.Text = Visit(context.expression(0)).Text + " 乘以 " + Visit(context.expression(1)).Text; 
                      }
          
                      if (context.op.Type == ISLParser.DIV) 
                      { 
                          r.Value = left / right; 
                          r.Text = Visit(context.expression(0)).Text + " 除以 " + Visit(context.expression(1)).Text; 
                      }
          
                      return r; 
                  }
          
                  public override Result VisitAddSub(ISLParser.AddSubContext context) 
                  { 
                      Result r = new Result();
          
                      double left = (double)Visit(context.expression(0)).Value; 
                      double right = (double)Visit(context.expression(1)).Value;
          
                      if (context.op.Type == ISLParser.ADD) 
                      { 
                          r.Value = left + right; 
                          r.Text = Visit(context.expression(0)).Text + " 加上 " + Visit(context.expression(1)).Text; 
                      } 
                      else 
                      { 
                          r.Value = left - right; 
                          r.Text = Visit(context.expression(0)).Text + " 減去 " + Visit(context.expression(1)).Text; 
                      }
          
                      return r; 
                  }
          
                  public override Result VisitVariable(ISLParser.VariableContext context) 
                  { 
                      Result r = new Result(); 
                      if (context.GetText() == "[車速]") 
                      { 
                          r.Text = "車速"; 
                          r.Value = TestData.VehicleSpeed; 
                      } 
                      else if (context.GetText() == "[天氣]") 
                      { 
                          r.Text = "天氣"; 
                          r.Value = TestData.Weather; 
                      } 
                      else if (context.GetText() == "[時(shí)間]") 
                      { 
                          r.Text = "時(shí)間"; 
                          r.Value = TestData.Now; 
                      } 
                      else if (context.GetText() == "[企業(yè)ID]") 
                      { 
                          r.Text = "企業(yè)ID"; 
                          r.Value = TestData.EntId; 
                      } 
                      else if (context.GetText() == "[用戶ID]") 
                      { 
                          r.Text = "用戶ID"; 
                          r.Value = TestData.AccountId; 
                      }
          
                      return r; 
                  }
          
                  public override Result VisitLogicalFalse(ISLParser.LogicalFalseContext context) 
                  { 
                      Result r = new Result(); 
                      r.Value = false; 
                      return r; 
                  }
          
                  public override Result VisitLogicalTrue(ISLParser.LogicalTrueContext context) 
                  { 
                      Result r = new Result(); 
                      r.Value = true; 
                      return r; 
                  }
          
                  public override Result VisitLogicalAndOrNot(ISLParser.LogicalAndOrNotContext context) 
                  { 
                      Result r = new Result(); 
                      if (context.op.Type == ISLParser.LOGICAL_AND) 
                      { 
                          bool o1 = Convert.ToBoolean(Visit(context.equality_expression(0)).Value); 
                          bool o2 = Convert.ToBoolean(Visit(context.equality_expression(1)).Value);
          
                          r.Value = o1 && o2;
          
                          r.Text = Visit(context.equality_expression(0)).Text + " 并且 " + Visit(context.equality_expression(1)).Text; 
                      }
          
                      return r; 
                  }
          
                  public override Result VisitString(ISLParser.StringContext context) 
                  { 
                      Result r = new Result();
          
                      r.Value = context.GetText().Replace("\"", ""); 
                      r.Text = context.GetText().Replace("\"", ""); 
                      return r; 
                  }
          
                  public override Result VisitLogicalOp(ISLParser.LogicalOpContext context) 
                  { 
                      Result r = new Result(); 
                      object result = null;
          
                      if (context.op.Type == ISLParser.GREATE_THAN) 
                      { 
                          double left = Convert.ToDouble(Visit(context.expression(0)).Value); 
                          double right = Convert.ToDouble(Visit(context.expression(1)).Value);
          
                          if (left > right) 
                          { 
                              result = 1; 
                          } 
                          else 
                          { 
                              result = 0; 
                          }
          
                          r.Text = Visit(context.expression(0)).Text + " 大于 " + Visit(context.expression(1)).Text; 
                      }
          
                      if (context.op.Type == ISLParser.LESS_THAN) 
                      { 
                          double left = Convert.ToDouble(Visit(context.expression(0)).Value); 
                          double right = Convert.ToDouble(Visit(context.expression(1)).Value);
          
                          if (left < right) 
                          { 
                              result = 1;
          
                          } 
                          else 
                          { 
                              result = 0; 
                          }
          
                          r.Text = Visit(context.expression(0)).Text + " 小于 " + Visit(context.expression(1)).Text; 
                      }
          
                      if (context.op.Type == ISLParser.EQUAL) 
                      { 
                          object left = Visit(context.expression(0)).Value; 
                          object right = Visit(context.expression(1)).Value;
          
                          if (left is string) 
                          { 
                              result = left.ToString() == right.ToString(); 
                          } 
                          else 
                          { 
                              result = Visit(context.expression(0)).Value == Visit(context.expression(1)).Value; 
                          }
          
                          r.Text = Visit(context.expression(0)).Text + " 等于 " + Visit(context.expression(1)).Text; 
                      }
          
                      r.Value = result; 
                      return r; 
                  }
          
                  public override Result VisitReturn(ISLParser.ReturnContext context) 
                  { 
                      Result o = Visit(context.equality_expression()); 
                      return o; 
                  } 
              }
          
              public class Result 
              { 
                  public string Text { get; set; }
          
                  public object Value { get; set; } 
              }

          ?

          class Program
              {
                  static void Main(string[] args)
                  {
                      TestISL();
          
                      Console.ReadLine();
                  }
          
                  private static void TestISL()
                  {
                      string text = string.Empty;
                      ParseISL("");
                  }
          
                  private static void ParseISL(string input)
                  {
                      input = "return (([車速]*10+3)>(200)) && ([企業(yè)ID] == \"123\") && ([時(shí)間]>1200 && [時(shí)間]<1700);";
          
                      AntlrInputStream inputStream = new AntlrInputStream(input);
                      ISLLexer lexer = new ISLLexer(inputStream);
          
                      CommonTokenStream tokens = new CommonTokenStream(lexer);
                      ISLParser parser = new ISLParser(tokens);
          
                      IParseTree tree = parser.return_statement();
          
                      //ISLVisitor visitor = new ISLVisitor();
                      //object ret = visitor.Visit(tree);
          
                      ISLVisitor2 visitor = new ISLVisitor2();
                      Result ret = visitor.Visit(tree);
          
                      //Console.WriteLine(ret);
                      Console.WriteLine(ret.Value);
                      Console.WriteLine(ret.Text);
                      Console.ReadLine();
                  }
              }


          ?

          Clipboard Image.png

          ?

          最后,點(diǎn)擊這里下載示例。



          作者:nick hao

          原文鏈接:https://www.cnblogs.com/haoxinyue/p/4225006.html

          本文轉(zhuǎn)自博客園網(wǎng),版權(quán)歸原作者所有。

          瀏覽 82
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产xxxx视频 | 内射毛片在线免费看 | 91在线无码精品秘 入口楼乃 | 日韩在线 | 青娱乐极品视频盛宴 |