在实现模型驱动开发中,我们可以解释模型直接运行在领域框架之上,也可以把模型通过代码生成技术转换成代码之后编译运行在框架之上。这两种方式都有利弊,可以搭配使用,在OpenExpessApp中将采用这两种方法,类库通过代码生成,UI等元模型通过框架解释执行。由于代码生成是MDD中很重要的一项技术,所以本篇我将介绍一下代码生成相关的知识。
模型驱动中的代码生成
在EMF中,Metamodel为EMF元模型,Model为EMF模型;在MS DSL Tools中,MetaModel为DSL元模型,模板为T4模板
代码生成技术
-
Antlr3
Antlr是一个非常著名的开源语言生成工具,官方网站是这么介绍的:
ANTLR, ANother Tool for Language Recognition, is a languagetool that provides a framework for constructing recognizers,interpreters, compilers, and translators from grammatical descriptionscontaining actions in a variety of targetlanguages. ANTLR provides excellent support for treeconstruction, tree walking, translation, error recovery, and errorreporting. There are currently about 5,000 ANTLR sourcedownloads a month.
研究过一阵子Antlr,用它来写过一个.Net下的表达式引擎,参考使用Antlr实现表达式引擎。
使用Antlr的一般经过如下步骤:
- 写语法
- 写StringTemplate模板(可选)
- 在AntlrWorks调试语法
- 从语法生成类
- 使用生成的类来写应用程序
作者写了一本介绍Antlr的专著 The Definitive Antlr Reference,我只是几年前大概看过一遍,觉得作者很牛。不过作者又出的另外一本书Language Implementation Patterns,有人说这本书更好,它将ANTLR内部的设计思想也讲得很清楚了。相比而言,之前那本书只能算是ANTLR的用户手册,而新书算是ANTLR的设计思想。
-
Irony - .NET Language Implementation KitIrony是在Codeplex网站上的一个开源语言实现工具,官方介绍如下:
Irony is a development kit for implementing languages on .NET platform. It uses the flexibility and power of c# language and .NET Framework 3.5 to implement a completely new and streamlined technology of compiler construction. Unlike most existing yacc/lex-style solutions Irony does not employ any scanner or parser code generation from grammar specifications written in a specialized meta-language. In Irony the target language grammar is coded directly in c# using operator overloading to express grammar constructs. Irony's scanner and parser modules use the grammar encoded as c# class to control the parsing process. See the expression grammar sample for an example of grammar definition in c# class, and using it in a working parser.
......
代码生成定义语言
在特定领域建模 DSM(Domain Specific)介绍中介绍了DSM的不同工具的比较,其中代码生成定义语言也可以从表中看到,下面将会介绍一下DSL tools的T4和MetaEdit+的MERL(MetaEdit+ Reporting Language):
它的一个Report示例:
02 |
foreach .State [Watch]
|
08 |
do ~From>()~To.State [Watch]
|
它的语法定义如下:
001 |
STRING : "'" CHAR* "'"
|
002 |
where CHAR is any character; a ' character must be doubled
|
004 |
NAMECHAR : "a" .. "z" | "0" .. "9" | " " | {_+-[]?} | INTLNAMECHAR | ESCAPECHAR
|
005 |
INTLNAMECHAR : {äëïöü} | {áéíóú} | {àèìòù} | {âêîôû} | {ñãœçÿ} | {߀} | {¿¡«»} |
006 |
ESCAPECHAR : "\" ECHAR |
007 |
where ECHAR is anything that is not a letter number or underscore
|
009 |
If NAME contains a space, the whole name should have a ";" after it, or one of ".>~#" forming the start of the next element in a chainClause
|
010 |
WILDNAME : [ "^" ] (NAMECHAR | "*" | "#" )+
|
011 |
If WILDNAME contains a space, the whole name should have a ";" after it.
|
012 |
# Design element access and output commands (5.3.2) |
013 |
chainOutputClause : (propClauseWithLevel | propClause | (graphEltClause+ propClause)) [ ";" ] [translatorNames] [ ";" ];
|
014 |
translatorNames : ( "%" <NAMECHAR>+)+;
|
015 |
propClauseWithLevel : propClause levelNumber [ ";" ];
|
016 |
levelNumber : [ ";" ] " " * [ "-" ] <NUMBER>+;
|
017 |
propClause : ":" (<NAME> | "()" );
|
018 |
graphEltClause : (objClause | relClause | roleClause | portClause) [ ";" ]
|
019 |
objClause : "." typeChoiceClause
|
020 |
relClause : ">" typeChoiceClause
|
021 |
roleClause : "~" typeChoiceClause
|
022 |
portClause : "#" typeChoiceClause
|
023 |
typeChoiceClause : NAME |
025 |
| "( " WILDNAME { " | " WILDNAME}* ")"
|
026 |
# General commands (5.3.3) |
027 |
report : oldreport | newreport; |
028 |
oldreport : "report" <STRING> clause* "endreport" ;
|
029 |
newreport: [newheadersection] clause*; |
030 |
newheadersection : <NAME> [ "(" [<NAME> ( "," <NAME>)*] ")" ];
|
047 |
basicClause : atomicClause | iterableClause; |
048 |
atomicClause : newlineClause | separatorClause | literal | variableRef | simpleClause; |
049 |
newlineClause : "newline" [ ";" ];
|
050 |
separatorClause : "sep" [ ";" ];
|
051 |
literal : <STRING> [translatorNames] [ ";" ];
|
052 |
variableRef : "$" <NAME> [translatorNames] [ ";" ];
|
053 |
simpleClause : ( "id" | "type" | "metatype" | "oid" | "projectid" | "objectid" | "project" ) [levelNumber] [ ";" ] [translatorNames] [ ";" ]
|
055 |
( "x" | "y" | "left" | "right" | "top" | "bottom" | "centerX" | "centerY" | "width" | "height" | "area" ) [levelNumber] [ ";" ] [translatorNames] [ ";" ];
|
056 |
iterableClause : ( "decompositions" | "explosions" | "containers" | "contents" | "stack" | "graphs" ) [ ";" ];
|
057 |
# Control and navigation commands (5.3.4) |
058 |
ifClause : "if" [condition]
|
059 |
"then" [ ";" ] (clause* | ";" )
|
060 |
[ "else" [ ";" ] clause*]
|
062 |
condition : ( "not" condition)
|
063 |
| (condition "and" condition)
|
064 |
| (condition "or" condition)
|
065 |
| ( "(" condition ")" )
|
067 |
expression : comparison | unary; |
068 |
unary : comparableClause; |
069 |
comparison : comparableClause comp comparableClause |
071 |
comparableClause : atomicClause | chainClause; |
072 |
comp : "<" | ">" | "<=" | ">=" | "=" | "<>" | "=~" | "=/" ;
|
073 |
loop : ( "do" | "dowhile" )
|
074 |
(chainClause | atomicClause) |
075 |
[whereClause] [filterClause] |
076 |
"{" clause* "}" [ ";" ]
|
080 |
[whereClause] [filterClause] |
081 |
"{" clause* "}" [ ";" ];
|
082 |
chainClause : (chainElementClause [levelNumber] [ ";" ])+;
|
083 |
chainElementClause : graphEltClause | propClause | iterableClause; |
084 |
whereClause : "where" condition;
|
085 |
filterClause : orderbyClause [uniqueClause] |
087 |
orderbyClause : "orderby" orderCriterion ( "," orderCriterion)*;
|
088 |
uniqueClause : "unique" [clause+ ( "," clause+)*];
|
089 |
orderCriterion : clause+ [ "num" ] [ "asc" | "desc" ];
|
090 |
subreportClause : ( "subreport" | "subgenerator" ) [ ";" ] clause* "run" [ ";" ];
|
091 |
# External I/O commands (5.3.5) |
092 |
fileClause : outputFileClause | filenameReadClause | filenamePrintClause; |
093 |
outputFileClause : "filename" [ ";" ] clause*
|
094 |
[ "encoding" [ ";" ] clause+]
|
095 |
[ "md5start" [ ";" ] clause+]
|
096 |
[ "md5stop" [ ";" ] clause+]
|
099 |
modeClause : ( "write" | "merge" | "append" ) [ ";" ];
|
100 |
filenameReadClause : "filename" [ ";" ] clause*
|
101 |
[ "encoding" [ ";" ] clause+]
|
103 |
filenamePrintClause : "filename" [ ";" ] clause* "print" [ ";" ];
|
104 |
md5Clause : "md5id" [ ";" ] clause* "md5Block" [ ";" ]
|
107 |
executeClause : ( "external" | "internal" ) [ ";" ]
|
109 |
( "execute" | "executeBlocking" ) [ ";" ];
|
110 |
promptAskClause : "prompt" [ ";" ] clause* "ask" [ ";" ];
|
111 |
# String and number commands (5.3.6) |
112 |
variableClause : variableReadClause | variableWriteClause; |
113 |
variableReadClause : "variable" [ ";" ] clause+ "read" [ ";" ];
|
114 |
variableWriteClause : "variable" [ ";" ] clause+
|
115 |
variableModeClause clause* [ ";" ]
|
117 |
variableModeClause : ( "write" | "append" ) [ ";" ];
|
118 |
variableAssign : "$" <NAME> "=" [ ";" ] (variableAssign | basicClause | chainOutputClause);
|
119 |
translationClause : "to" [ ";" ] clause*
|
120 |
[ "translate" [ ";" ] clause*]
|
122 |
mathClause : "math" [ ";" ] clause* "evaluate" [ ";" ];
|
书籍
What's Inside:
- Code generation basics
- CG techniques and best practices
- Patterns of CG design
- How to deploy generators
- Many example generators
Includes generators for:
- Database access
- RPC
- Unit tests
- Documentation
- Business logic
- Data translation
LOP中的代码生成
LOP中的代码生成有三种主要的方法,我们将结合使用它们来定义模型转换;第一种是遍历方式,你枚举源模型中所有节点,检视每一个,并基于检视到的信息生成目标模型中的一些目标节点;第二种方式是使用模板和宏来定义如何生成目标语言;第三种方式是使用模式匹配来查找在源模型中的哪些节点上应用转换。
Code Generation by Model Transformation - A Case Study in Transformation Modularity
本文转自 jingen_zhou 51CTO博客,原文链接:http://blog.51cto.com/zhoujg/403113,如需转载请自行联系原作者