DSL的实现要点(2)

 

实现外部DSL

与上一篇中所提及内部DSL不同,使用者不是通过API调用来使用DSL,而是通过我们定义的特定语法的领域语言来使用DSL

1 XML形式的DSL

脚本文件

<process name="Auto-Door">

      <state name="Open">

            <transition event="time-out" next_state="Close"/>

      </state>

      <state name="Close">

            <transition event="people-closer" next_state="Open"/>

      </state>         

</process>

实现

public class XmlConfigParser {

      //followings are context variables

      private Machine currentMachine;

      private State currentState;

      class ElementHandler extends DefaultHandler{

            private String getAttributeValue(String elemName,String attributeName,Attributes attris){

                  String attrValue=attris.getValue(attributeName);

                  if (attrValue==null){

                        throw new XmlConfigParseException("Element "+elemName+" shoudle have the attribute:"+attributeName);

                  }

                  return attrValue;

            }

            @Override

            public void endElement(String arg0, String arg1, String elemName)

            throws SAXException {

                  if (elemName.equals("state")){

                        currentMachine.getStates().add(currentState);

                  }

            }

            @Override

            public void startElement(String arg0, String arg1, String elemName,

                        Attributes attris) throws SAXException{              

                  if (elemName.equals("process")){

                        String processName=getAttributeValue(elemName,"name",attris);

                        currentMachine=new Machine(processName);

                  }

                  if (elemName.equals("state")){

                        String stateName=getAttributeValue(elemName,"name",attris);

                        currentState=new State(stateName);

                  }

                  if (elemName.equals("transition")){

                        String eventName=getAttributeValue(elemName,"event",attris);

                        String nextState=getAttributeValue(elemName,"next_state",attris);

                        Transition transition=new Transition();

                        transition.setEvent(new Event(eventName));

                        transition.setNextState(nextState);

                        currentState.getTransitions().add(transition);

                  }

            }

      }

      public Machine parser(String fileName){

            SAXParserFactory spfactory =

                 SAXParserFactory.newInstance();                 

                       

            try{

              SAXParser saxParser =

                       spfactory.newSAXParser();

              XMLReader reader=saxParser.getXMLReader();

              reader.setContentHandler(new ElementHandler());

              reader.parse(fileName);                  

              return currentMachine;

            }catch(Exception e){           

              throw new XmlConfigParseException("parsing is failed",e);

            }    

      }

}

实现要点

上述实现是通过SAX来进行XML解析的。

1 将领域模型结构直接映射为XML元素的结构

我们用这种方式来设计我们的DSL,这样做的好处是DSL比较容易使用(更接近领域模型),同时解析程序也会相对简单,比较容易生成相应的语义模型。

2 使用上下文变量

如上面程序中的:

private Machine currentMachine;

private State currentState;

他们就是上下文变量,由于SAX是顺序解析的,所以必须保持正确的工作上下文,如把生产Transition对象加入到正确的State中。

 

2 自定义语言

脚本文件

Machine   (Auto-Door){

               State(Open){

                              Transition{

                                             event : time-out ,

                                             next-state : Close

                              }             

               }

               State (Close){

                              Transition{

                                             event : people-closer ,

                                             next-state : Open

                              }

               }

}

 

实现

自己设计语法并实现解析器,通常需要我们具备一定的编译原理知识并且借用一定的解析器生成工具来帮助我们生产解析器代码。

实现中本人使用了 Antlr

Antlr的语法描述文件:

grammar StateMachineG;

@header {

import org.ccsoft.statemachine.models.Machine;

import org.ccsoft.statemachine.models.State;

import org.ccsoft.statemachine.models.Transition;

import org.ccsoft.statemachine.models.Event;

}

 

@members {

 

               public void emitErrorMessage(String msg) {

                              throw new RuntimeException(msg);

                              //super.emitErrorMessage(msg);

               }

 

}

 

machine returns [Machine value]               :              'Machine''('NAME')''{'{$value=new Machine($NAME.text);} (e=state{$value.getStates().add($e.value);})+'}';

state returns [State value]             :              'State''('NAME')''{'{$value=new State($NAME.text);}(e=transition{$value.getTransitions().add($e.value);})+'}';

transition returns [Transition value]

               :              'Transition''{'{$value=new Transition();}e=event{$value.setEvent($e.value);}','f=nextState{$value.setNextState($f.value);}'}';

event     returns [Event value] :     'event'':'e=NAME{$value=new Event($NAME.text);};

nextState returns [String value]

               :              'next-state'':'e=NAME{$value=$NAME.text;};

NAME    :              ('a'..'z' |'A'..'Z'|'0'..'9')+   ;

WS : (' ' |'/t' |'/n' |'/r' )+ {skip();} ;

实现要点

1 采用Antlr的内嵌Action

对于DSL的通常应用即通过外部脚本生产相关部分语义模型对象,使用Antlr的内嵌Action比采用语法树方式简单得多。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值