没有合适的资源?快使用搜索试试~ 我知道了~
理论计算机科学电子笔记264(5)(2011)103-121www.elsevier.com/locate/entcsDSL工具NaveneethaVasudevan1伯恩茅斯大学Poole,多塞特,英国,BH 12 5BBLaurence Tratt2米德尔塞克斯大学The Burroughs,Hendon,伦敦,NW4 4BT,英国摘要越来越多的基于不同方法的工具被用于实现领域特定语言(DSL),但对于哪种方法是,或者方法是,最适合任何特定的问题。我们认为这在很大程度上可以解释为DSL社区内部缺乏理解。在本文中,我们的目标是通过实施一个共同的DSL案例研究,以增加对四种方法的相对优势和劣势的理解。此外,我们提出了四种方法的比较研究保留字:领域特定语言,解析,程序转换。1介绍领域特定语言(DSL)是为特定领域量身定制的迷你语言,与Java等通用语言(GPL)相比,它具有显著的优势[5]。当在GPL中开发软件系统时,人们经常会遇到这样的情况,即问题在所选择的GPL中无法自然表达。传统上,人们会找到一个合适的工作区(在GPL提供的框架内)来编码解决方案。使用这种变通方法的缺点之一是程序可能会变得复杂,从而使其远不如开发人员所希望的那样容易理解。GPL中缺乏表达性的问题可以通过使用DSL来克服。DSL允许程序在应用领域的抽象层次上实现,这使得能够快速有效地开发软件系统。给定一个域和对DSL的需求1电子邮件:naveneetha@yahoo.com2电子邮件:laurie@tratt.net1571-0661 © 2011 Elsevier B. V.在CC BY-NC-ND许可下开放访问。doi:10.1016/j.entcs.2011.06.007104N. 瓦苏德万湖Tratt/Electronic Notes in Theoretical Computer Science 264(5)(2011)103存在许多实现DSL的工具和方法。传统的方法涉及使用Lex和YACC或ANTLR等编译器工具将DSL实现为这种方法为DSL作者提供了对DSL的完全控制,从语法到执行风格,但由于每个实现都倾向于从头开始设计,因此导致了高昂的开发成本[5]。与传统技术相比,还可以使用嵌入方法嵌入式方法允许DSL继承宿主语言的基础结构,从而促进软件构件(如语法、语义等)的重用。从而降低软件开发成本。嵌入方法可以是同构的,也可以是异构的[13]:在异构嵌入中,用于编译宿主语言的系统和用于实现嵌入的系统是不同的;而在同构系统中,系统是相同的,所有组件都是专门设计的。这种区别很重要,因为它使人们能够理解给定方法的局限性。异构嵌入方法的示例包括:通过程序转换来支持DSL实现的Aggo/XT [2];以及通过使用语言扩展来支持DSL实现的Silver [14],其中通过转换将新的语言构造(用于特定于领域的功能)转换为宿主语言中的语义等效构造在同构嵌入方法中:Lisp和Nemerle [12]通过使用宏支持嵌入式语言的开发;在纯嵌入方法中-不使用宏扩展器或生成器-使用宿主语言功能(如高阶函数和多态性)最近,出现了一类新的工具语言工作台,它们为构建DSL提供了丰富的环境。Meta编程系统(MPS)[6]和意向域搜索引擎(IDW)[16]是这类新工具的典型代表。工作台本质上提供了一个集成开发环境(IDE)和底层基础语言。例如,IDW带有CL 1语言; MPS带有三种基本语言:用于定义语言抽象语法的结构;用于定义语言具体语法的编辑器;以及用于定义语言语义的语义使用这些语言构建工具,可以开发和集成DSL以实现特定领域的应用程序。在本文中,我们评估了四种方法DSL的实现。与Czarnecki等人 [4]评估三种语言的编译时元编程能力的风格类似,我们使用案例研究来评估这些方法。我们的案例研究是状态机语言的一个小型但实际的DSL示例。尽管我们的工作涉及一个单一的案例研究,但为我们的案例研究实现的DSL表明已经实现了更广泛的DSLN. 瓦苏德万湖Tratt/Electronic Notes in Theoretical Computer Science 264(5)(2011)103105到目前为止。我们选择研究的四种方法代表了DSL实现谱上重要的、不同的点:ANTLR代表了DSL实现的传统独立方法; Ruby代表了Hudak关于特定领域嵌入式语言 的 愿 景 的 弱 化 形 式 我 们 每 个 例 子 的 代 码 都 可 以 从从http://navkrish.net/downloads/dsltools src.tar.gz.据我们所知,这是第一次对DSL实现的独立方法和三种本文其余部分的结构如下。第2节介绍了案例研究,然后分别在第4、5、6和7节中为我们在ANTLR、Ruby、Agendgo和Converge第8节根据选定的维度和指标,对四种DSL工具及其方法进行了比较分析。第9节介绍了我们对这四种DSL工具的相对优势和劣势的2案例研究:有限状态机本文中使用的案例研究是一个带有状态和转换的旋转栅门机的状态机(图1)。“transition”的语法我们在不同的方法中实现这个案例研究,在每个方法中创建一个可执行的状态机,我们可以在其上触发事件并检查其行为。对于每种方法,我们定义一种状态机语言(以适合该方法的语法),然后为旋转栅门机实现状态机语言硬币[credit +1 3]/credit = credit +1报警= 0锁定doorOpen/alarm = true违规信用= 0重置/警报=假,信用= 0硬币[credit +1 == 3]/credit = 0 doorClose解锁Fig. 1. 旋转栅门的状态机3会议和讨论为了评估我们的案例研究的四个实现,我们使用了一组维度和指标。在本文中,106N. 瓦苏德万湖Tratt/Electronic Notes in Theoretical Computer Science 264(5)(2011)103模板(Java)输入文本模板调用文本输出程序(DSL)解析(FSMParser)代码生成(StringTemplate)程序(爪哇)图二、在ANTLR中实施DSL所需的两个阶段“度量”指的是可以(并且因此数字数据可以从DSL实现中表示出来)的属性。我们使用并扩展了Czarnecki等人[4]确定的维度(表1),以呈现我们的比较分析。然后,我们确定并定义了两个指标(表2),我们用它们来扩展我们的分析。尺寸说明DSL工具支持的主要方法是什么?DSL工具在转换后的结构的语法和语义的格式良好性方面提供了什么保证?DSL实现的“用户定义”方面可以重用吗上下文相关变换DSL工具可以执行上下文敏感的转换吗?错误报告DSL工具能否报告DSL源(行号和列或数据集)方面的错误?表1维列表指标描述代码行对于一个给定的案例研究,需要多少行代码来表示特定于域的信息?要学习的方面对于一个给定的案例研究,需要学习多少方面来实现一个 DSL?表2指标4ANTLR中DSL的实现ANTLR [11]是一个解析器生成器工具,它提供了一个实现语言翻译器的框架。在ANTLR中,生成的解析器可以以两种形式之一实现为翻译器:它可以执行语义动作;或者它可以执行语义动作以使用模板生成目标程序为了本文的目的,我们讨论ANTLR作为一个翻译器,发出一个目标程序。我们选择在ANTLR中使用模板通过翻译(图2)实现DSL。翻译过程有两个阶段:解析阶段,输入程序被解析,解析后的数据作为模板调用的参数;代码生成阶段,这些模板调用被映射到目标语言的概念。对于解析阶段,ANTLR提供了必要的库来生成给定语法的词法分析器和解析器程序。为了生成目标程序,ANTLR支持使用StringTemplate-aN. 瓦苏德万湖Tratt/Electronic Notes in Theoretical Computer Science 264(5)(2011)103107模板引擎库,用于使用模板生成文本。模板本质上是一个带有模板规则的文本文档,其中每个规则都包含<虽然ANTLR只支持少数GPL的代码生成,但有一个开源社区可以为新的目标语言开发代码生成库 在我们的案例研究中,我们从DSL中翻译代码片段Java(目标语言)我们以“transition”结构(来自我们的DSL程序)为例解释两阶段翻译过程。下面是一个代码片段,显示了转换的域特定信息(来自我们的案例研究)从锁定到解锁的转换解锁:coin[ credit+ 1 == 3]/credit:=0上面的“transition”结构的相应解析器规则(transition和ttail)转换范围{字符串名称;...String event;Listactions;booleanisguard;}:’transition’ tname=ID...’:’->transition(tname={new StringTemplate($tname.text)},...event={new StringTemplate($tevent.text)});@init{$transition::actions=new ArrayList();$transition::isguard=false;}:g=守卫?actionstats?-> {$transition::isguard}?guardBlock(guardcond={$g.st},t_name={$transition::name},...t_event={$transition::event},actions={$transition::actions})-> guardBlock(guardcond={new StringTemplate(“true”)},t_name={$transition::name},...t_event={$transition::event},actions={$transition::actions});解析器规则(上面代码中的transition和ttail)的每个元素(在RHS上)后面都可以跟随一个操作。一个动作是一个源代码块,在目标语言中(括在花括号中),用于生成输出或构造树,或修改符号表。一个动作在前面的元素匹配后立即执行。 例如在上面的transition解析器规则,当元素ID(tname=IDconstruct)匹配时,它导致动作此操作初始化transtition规则的范围块中定义的属性名称在ANTLR中,基本上有两种类型的作用域:在任何规则之外定义的命名全局作用域;以及在规则内定义的规则作用域(未命名)。全局作用域被命名,因此任何规则都可以通过其名称访问它,而规则作用域仅可由当前规则及其调用的所有规则访问。108N. 瓦苏德万湖Tratt/Electronic Notes in Theoretical Computer Science 264(5)(2011)103在 解 析 器 规 则 之 间 更 改 数 据 。 例 如 , 在 处 理 来 自 转 换 规 则 的 操 作($transition::name=$tname.text;)时初始化的属性名称现在可以在ttail ( guardBlock ( guardcond=$g.st , t name=$transition : :name,.))用于生成目标语言构造的规则。在ANTLR中,为了生成目标语言结构,需要将解析器规则映射到模板规则。然后,给定一个包含模板规则定义的模板,解析器(在运行时)将调用模板引擎以生成目标语言中的必要结构。对于上述transition和ttail规则,模板规则- transition(.)和guardBlock(...)- 将被调用。模板规则(transition(...)和guardBlock(...))定义目标语言(Java)中的结构如下所示transition(tname,from,to,event)::=“this.transitions.add(new Transition(\”tname>\",...,\“事件>\”);”guardBlock(guardcond,t_name,t_from,t_to,t_event,actions)::=if(transition_name.equals(“t_name>”)&&...transition_event.equals(“”)){if(()&&. )的第一个字符。//这里<操作;分隔符="\n”>returntrue;}}>>5在Ruby中实现DSLRuby是一种动态类型的通用面向对象语言[7]。在Ruby中,DSL是使用诸如lambda抽象(代码块)、求值、动态类型、反射和灵活语法等特性的组合来实现的我们解释了如何结合这些功能来实现我们的DSL程序作为一个例子的“过渡”结构。在Ruby中,代码块是一个闭包,可用于编码域特定信息。代码块可以使用分隔花括号({|X| printx})或使用do和end关键字在多行上。对用于转变的域特定信息进行编码的码块如下:转型“充电”做|不|t.from_state t.to'locked'www.example.com _state ='locked' t.guard do|信用|if(credit+1)<3true结束结束...端在上面的代码中,最初看起来像描述转换的DSL关键字的转换构造本质上是对方法的调用- transition -后跟两个参数:字符串和接受参数的代码块(|不|). 在Ruby中,创建方法需要对象或类的实例形式的文本对于我们想要执行转换方法的示例,上下文由状态机类(FSM)的实例(@fsm = Fsm.new)提供。要为fsm实例执行transitionN. 瓦苏德万湖Tratt/Electronic Notes in Theoretical Computer Science 264(5)(2011)103109可以使用Ruby实例eval方法允许在类实例的上下文中计算字符串或代码块。因此,对于我们的案例研究,其中transition构造包含在文本文件中,我们可以将文件作为字符串读取,然后为fsm实例计算构造。下面是一个代码片段,显示了使用实例eval方法评估DSL程序的load方法的定义和transition方法FSM类#将文件(DSL程序)作为参数def load(fsm_load)instance_eval(File.read(fsm_config),fsm_config)...端def transition(name,aBlock)transition = Transition_class.new(name)transition.load_block(aBlock)...端...端在Ruby中,方法接受代码块作为最终参数。然而,如果一个方法是用一个块参数(一个以&开头的最终参数,形式为&aBlock)定义的,那么一个代码块(作为方法的参数提供)将被隐式转换为Proc对象。Proc对象本质上是一个Ruby对象,表示一个代码块,然后可以作为对象传递,并通过使用yield或调用其调用方法(传递给调用方法的任何参数将被分配给块参数)来执行。在上面的代码中,由于transition方法定义了一个块参数(&aBlock),因此与之关联的Proc对象作为参数传递给transition对象的load block方法。下面的代码片段显示了&aBlock对象最终是如何通过调用yield self(self是指上面代码片段中的transitionTransition_classdef from_state(from_state)@from_state = from_state端def to_state=(to_state)@to_state= to_state端def guard(guardBlock)@guard_block = guardBlock端defload_blockyield self端...端的 上述代码也示出的对应方法定义(fromstate(from state)和to state=(to state))用于转换属性(例如,from statet.to调用(和定义)方法的两种不同风格--有等号和没有等号--表明了Ruby作为DSL工具的一个重要方面:语法灵活性。除了代码块之外,Ruby还支持动态类型,这允许运行时系统实现诸如动态分派和鸭子类型之类的特性。例如,Object类通过定义110N. 瓦苏德万湖Tratt/Electronic Notes in Theoretical Computer Science 264(5)(2011)103解析表(DSL)解析表(OpenGo + DSL +Java)输入文本属性属性文本输出程序(DSL)解析(sglri)变换(fsm变换)Pretty-print(pp-java)程序(Java)图3.第三章。Tumego中的转换管道显示了实现DSL的各个阶段两种方法:回应?检查对象是否会响应消息;方法missing捕获对象没有显式处理程序的消息。 与Smalltalk类似,Ruby支持在运行时创建(或替换)方法,然后可以使用这些方法动态地操纵对象的行为。6一种DSL在Xplego/XT中的实现Rectgo/XT [2]是一个软件转换框架,由Stratego语言(用于通过项重写实现程序转换)和XT工具集(用于提供实现这些转换的基础设施)组成。Analogo/XT通过以抽象语法树的形式表示程序来实现程序转换,称为注释术语(Aterms);然后详尽地应用一组策略和术语重写规则。在NodeGo/XT中,DSL是使用转换管道(图3)实现的,该管道由三个阶段组成:解析阶段,实现DSL的解析器;转换阶段,使用NodeGo语言实现转换程序;以及漂亮的打印阶段,将对于解析和漂亮打印阶段,可以使用XT工具集中的工具(分别为sglri和pp-java)。为了达到本文的目的,我们把注意力集中在转换流水线的关键阶段--转换程序上。转换程序是使用一组术语重写规则和策略来实现的。一个term-rewrite规则定义了一个在ATtrons上的转换,其形式为L:p1->p2,其中L是规则名,p1和p2是term 模式 。 策略 是一 个程 序, 它 通过 定义 重写 术语 的顺 序来 支持规 则对ATTRAN的应用例如,为了对AST上的单个自顶向下遍历顺序地应用规则R1和R2,自顶向下策略<–反复应用这些规则对于单个自顶向下遍历,自顶向下策略可以应用重复策略<-由自顶向下(repeat(R1 + R2))表示-然后可以调用规则(R1和R2)直到不再应用规则。此外,内置策略可以组合以定义用户定义的策略;对于上面的示例,用户定义的策略“简单”可以定义为简单=自< 定义策略的能力在两个方面很有用:第一,它支持规则和策略的重用;第二,它通过屏蔽AST上的低级操作来实现抽象N. 瓦苏德万湖Tratt/Electronic Notes in Theoretical Computer Science 264(5)(2011)103111在stratego中,术语重写规则可以通过使用嵌套的ATerms或使用对象语言的具体语法来编写[15]。例如,表达式对变量的赋值可以使用嵌套的ATerms(Assign(Var(x),Expr(e)或使用对象语言的具体语法(|[x:=e]|). 为了在术语重写规则中使用具体的syntax,必须使用对象语言的语法定义来扩展Meta在我们的案例研究中,我们想要将代码片段从DSL转换为Java,这涉及到合并Sparkgo(由Sparkgo编译器提供),Java(由Java-front [15]提供)和我们的DSL的语法定义。此外,对象语言的语法定义可以用元变量(对应于语法元素的模式,例如对象语言的标识符、表达式和列表)来扩展,然后可以将元变量用作元变量中的拼接。转换规则中对象语言构造内的级别表达式。对于语法定义的精简版本,显示了TransitionTail和Guard元素的定义以及它们对应的元变量(ttail和guard)的定义如下:上下文无关语法...“transition”Id“from”Id“to”Id“:“Id TransitionTail -> Transition {cons(“Transition”)}TransitionTailG| TransitionTailGA| TransitionTailA -> TransitionTail {cons(“TransitionTail”)}Guard-> TransitionTailG {cons(“TransitionTailG”)}“[”Exp“]”-> Guard {cons(“Guard”)}变量“ttail”[0-9]-> TransitionTail联系我们“guard”[0-9]->保护联系我们...变量的上述定义允许我们用元变量替换转换规则中对应于TransitionTail和Guard元素的DSL构造。下面的代码片段显示了从锁定到解锁的转换解锁:coin[ credit+ 1 == 3]/credit:=0guard-init:|[transition x_t from x_a to x_b:x_e ttail1]|-->-|[if((transitionName.equals(“~x_t”)&&.){bstm_1}]|其中 ttail 1=> bstm_1trans-tail:trans-tail |[守卫1]|-->-|[if(e_1){ _guard =true; return_guard; }]|其中 guard 1 => e_1trans-tail:trans-tail |[行动1]|-->-|[if(true){bstm_1*_guard=true;return _guard; }]|其中 action 1=> bstm_1*...在转换规则中使用where子句使得规则的可编程例如,guard-init规则的where子句中的 ttail 1构造将调用trans-tail规则中的任一个,这取决于ttail 1在运行时的值。在ttail1ATTFORM上调用trans-tail规则返回的值被分配给bstm 1元变量,然后将其拼接回转换规则以生成目标语言结构(Java语言的语法定义将bstm 1定义为元变量)。112N. 瓦苏德万湖Tratt/Electronic Notes in Theoretical Computer Science 264(5)(2011)1037一种DSL在Converge中的实现Converge [13]是一种动态类型的命令式编程语言,具有编译时元编程(CTMP)和语法扩展功能。Converge是一种语法丰富的现代语言,统一了Python(缩进和数据库)和Template Haskell(CTMP)等语言的概念。DSL在Converge中使用其CTMP工具实现。CTMP可以被认为等同于宏,因为它为用户提供了一种与编译器交互的机制,允许用户代码构建任意程序Converge使用其编译时元编程特性(拼接、准引用和插入)来实现任意程序片段的这种构造[13]。 接头注释$<...> 计算表达式之间的一个-gled括号,并将拼接批注本身替换为其评价例如,splice注释$x>告诉编译器在编译时计算'x',准引号[|...|] 允许用户构建表示其中表达式的AST。例如,当Converge表达式2 + 3的计算结果为5时,准引号表达式[|2+ 3|]计算为add(int(2),int(3))形式的AST。插入${. }是放置在准引号内的拼接注释。准引号内的拼接与准引号外的拼接的计算方式不同。它们不会在编译时计算,而是按原样复制到准引号转换到的代码中。例如,准引号表达式[|$ + 2|]将导致沿着add(x,int(2))行的AST,其中x必须计算为有效的AST。Converge允许通过DSL块将任意DSL嵌入到正常源文件中。 DSL块是使用以下方法在聚合源文件中引入的splice语法$<>的变体,其中expr必须计算为DSL实现函数。然后在编译时调用此函数,使用与普通拼接相同的机制将DSL块转换为Converge AST。DSL块使用Converge的基于缩进的语法;当缩进级别下降时,DSL块完成。我们案例研究中的DSL模块及其相应的DSL实现函数如下所示TurnstileFSM:=$FSM_Translator::转_itree>>:...状态锁定从锁定到解锁的转换解锁:coin[credit+ 1== 3]/credit:=0func parse_itree(parse_block,src_infos):parse_tree:= parse(parse_block,src_infos)return SM_Translator.new().generate(parse_tree)在编译时调用DSL实现函数FSMTranslator::Stringitree,其中包含一个表示DSL块的字符串以及从Converge tokenizer获得的src信息(Src信息将在第8节后面介绍)。使用Con-verge Parser Kit(CPK)分析这个字符串以生成一个分析树.这个解析树包含标记及其相关的src信息,使用准引号和插入遍历并转换为AST。CPK提供了一个简单的框架- 给定解析树中的一个节点,self。 预购N. 瓦苏德万湖Tratt/Electronic Notes in Theoretical Computer Science 264(5)(2011)103113方法可以用来调用适当的t函数。对于我们的案例研究,我们实现了SM翻译器类(继承Traverser::Traverser类),它包含必要的t函数(t系统,t转换等)。generate函数通过调用tsystem函数(system是我们语法的顶层规则)来启动翻译过程。 一段代码显示了 SM Translator类和self的使用。preorder方法根据节点的值调用必要的t函数如下:class SM_Translator(Traverser::Traverser):func generate(self,node):返回self._前序(节点)func_t_system(self,node):sts:= []//状态tns:=[] //转换...whilei node.len():
下载后可阅读完整内容,剩余1页未读,立即下载
cpongm
- 粉丝: 4
- 资源: 2万+
上传资源 快速赚钱
- 我的内容管理 收起
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
会员权益专享
最新资源
- RTL8188FU-Linux-v5.7.4.2-36687.20200602.tar(20765).gz
- c++校园超市商品信息管理系统课程设计说明书(含源代码) (2).pdf
- 建筑供配电系统相关课件.pptx
- 企业管理规章制度及管理模式.doc
- vb打开摄像头.doc
- 云计算-可信计算中认证协议改进方案.pdf
- [详细完整版]单片机编程4.ppt
- c语言常用算法.pdf
- c++经典程序代码大全.pdf
- 单片机数字时钟资料.doc
- 11项目管理前沿1.0.pptx
- 基于ssm的“魅力”繁峙宣传网站的设计与实现论文.doc
- 智慧交通综合解决方案.pptx
- 建筑防潮设计-PowerPointPresentati.pptx
- SPC统计过程控制程序.pptx
- SPC统计方法基础知识.pptx
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功