文章目录
  1. 1. 语言工作台要素
  2. 2. 模式定义语言和元模型
  3. 3. 源码编辑和投射编辑
    1. 3.1. 多种表现形式
  4. 4. 说明性编程
  5. 5. 工具之旅
  6. 6. 语言工作台和CASE工具
  7. 7. 我们该使用语言工作台么

语言工作台是一种工具,它可以帮助我们打造自己的DSL,并为这种DSL提供现代IDE风格的工具支持。其基本想法是,这类工具不仅要提供一个创建DSL的IDE,还要为编辑这些DSL构建新的IDE。

语言工作台是一个年轻的领域,本章主要介绍一些不易变的核心原则。

语言工作台要素

  • “语义模型”模式一般用元模型定义出语义模型的数据结构及静态语意;
  • DSL编程环境为人们编写DSL脚本定义丰富的编辑体验,可能是直接编辑源码,也可能是进行投射编辑(projectional editing);
  • 语义模型行为定义出构建好语义模型后DSL脚本能够做些什么,通常采用的技术是代码生成。

语言工作台以语义模型为系统核心,提供工具辅助模型定义。语言工作台采用一种特殊的元模型建模结构,这种结构要用运行时工具处理模型。

这样一来,模式和行为得以分离。语义模型模式本质是一种数据模型,没有任何行为。代码生成是运行语义模型最常用的方式。

在语言工作台中有两个通用原则:模式定义(schema definition)和投射编辑

模式定义语言和元模型

语言工作台提供一个环境,用于定义模型的模式,也就是数据结构,通常会用到一种专用的DSL–模式定义语言。至于行为语义,则是要单独定义的,一般用代码生成来做。

模式定义语言也有自己的语义模型,其本身也是模型。模型定义语言本身的语义模型就是DSL语义模型的元模型

对于上面的状态图,我们可以用java代码实现:创建三个类State、Event和Transition。

class Transition {
    State source, target;
    Event trigger;
    ......
}

我们也可以使用类图来实现:

我们上面例子中的模式是Java类的定义,也可以把模式定义成一组Java对象,而不仅仅是类。这样就可以在运行时操作这个模式。用三个Java类就可以给出这一想法的策略实现:类、字段和对象。

class MClass {
    private String name;
    private Map<String, MField> fields;
}

class MField {
    private String name;
    private MClass target;    
}

class MObject {
    private String name;
    private MClass mclass;
    private Map<String, MObject> fields;
}

通过这个环境,就可以创建状态和转换的模式了。

private MClass state, event, transition;
private void buildTwoStateSchema() {
    state = new MClass("State");
    event = new MClass("Event");

    transition = new MClass("Transition");
    transition.addField(new MField("source", state));
    transition.addField(new MField("target", state));
    transition.addField(new MField("trigger", event));
}

接下来,就可以用这个模式定义简单状态模型了。

private MObject active, waitingForDrawer, transitionInstance, lightOn;
private void buildTwoStateModel() {
    active = new MObject(state, "active");
    waitingForDrawer = new MObject(state, "waiting For Drawer");
    lightOn = new MObject(event, "light On");
    transitionInstance = new MObject(transition);
    transitionInstance.set("source", active);
    transitionInstance.set("target", lightOn);
    transitionInstance.set("trigger", waitingForDrawer);
}

元模型是一个模型,其实例定义另一个模型的模式。元模型只是另一种语义模型,所以,我们可以很容易定义一个DSL组装这个模型,就像我们为基本模型所做的一样—-这样的DSL,成为模式定义语言。

对元模型的反对声音是,语义模型丧失了成为恰当OO领域模型的能力。

大多数语言工作台都是用元模型。同其他模型一样,元模型也要有自己的模式定义其结构。前面的例子中描述的MClass、MField和MObject就是模式。模式定义语言本身只不过是语言工作台的有一种DSL而已。

许多语言工作台采用“自举工作台”方式,即能够定义自己。

模式定义语言同文法的差别,简单回答就是,文法定义某种(文本)语言的具体语法,而模式定义语言定义语义模型模式的结构。

当定义模式时,要考虑的是数据结构:类和字段。但是,还有一个元素要考虑:模式的结构性约束。通常,结构性约束是一些校验规则。

源码编辑和投射编辑

基于源码的编辑系统用一种可编辑的表现形式定义程序,通过工具将其代入运行时系统。

投射编辑系统,程序的核心表现形式则是一种鱼所用工具相关的格式。这个格式就是工具使用的语义模型的一种持久化表现形式。桌面数据库工具(如Microsoft Access)就是一个很好的投射编辑系统的工具。

相比基于文本的方法,投射编辑可以给我们带来许多好处:

  • 可以通过不同的表现形式进行编辑;
  • 可以更好地控制编辑体验,输入正确信息,避免犯错;
  • 可以使用多个投射,可以同时,可以作为主要投射的备选方案。

源文件也有一些实用上的优势。发邮件,解释某件事用文本片段会很容易;使用一些文本处理工具,某些转换可以很好自动化。

多种表现形式

源码扮演两个角色:编辑形式(编辑时程序的表现形式)和存储形式(进行持久化存储时的表现形式)。

投射性编辑,表现形式可以由不同的安排

说明性编程

投射编辑最引人入胜的一个发展是,它对成为说明性编程的支持。世界上最流行的程序设计环境是电子表格。大多数电子表格程序员都是外行程序员。

使用电子表格,最常见的东西是,对一组数字的说明性计算。程序隐藏在公式栏之后,一次只能看到一个单元格。电子表格将程序的执行和定义融合一体。

当试图理清概念时,考虑一下边界条件是一个不错的做法。一种边界情况是,编辑时会用到这样的程序信息投射,比如,在IDE中会给出类的层次结构。从某个方面看,与说明性编程很像,因为层次结构会随着我们的编辑不断地更新,但是二者的区别死,IDE的层次结构是从程序的静态信息中得到的,而说明性编程所需的信息则源自程序实际的运行。

说明性编程也全是好的,在使用电子表格和GUI设计器时,它们弱化了程序的结构。但是复杂的电子表格和UI面板都非常难以理解和修改。它们毫无节制的使用复制和粘贴在编程中。

工具之旅

介绍一些设计的语言工作台:

  • Intentional Software的Intentional WorkBench
  • MetaCase
  • JetBrain的Meta-Programming System(MPS)
  • Xtext
  • Microsoft的SQL Server Modeling

语言工作台和CASE工具

CASE(Compter-Aided Software Engineering)工具让我们可以使用各种图形符号表现软件设计,然后生成软件。

表面上,二者有很多相似之处。以模型为核心、使用元模型定义、图形投射。

关键技术差异是,CASE工具无法让我们定义自己的语言。或许,最重要的差异还是在文化上,在CASE领域里,许多人认为程序设计无关紧要。而语言工作台只是为程序员创建一个更有效率的环境。

我们该使用语言工作台么

应该谨慎使用,这类工具目前的绑定(lock-in)问题很严重。用一个语言工作台所写的代码,几乎不能导入到另外一个。

规避风险的一个方法是,把语言工作台当做解析器,而非完整的DSL环境。

文章目录
  1. 1. 语言工作台要素
  2. 2. 模式定义语言和元模型
  3. 3. 源码编辑和投射编辑
    1. 3.1. 多种表现形式
  4. 4. 说明性编程
  5. 5. 工具之旅
  6. 6. 语言工作台和CASE工具
  7. 7. 我们该使用语言工作台么