在上一篇面向语言编程LOP(Language Oriented Programming)方法介绍中介绍了LOP以及一种LOP的实现方法MPS,本篇主要讲解一下使用MPS1.5的一个示例,以便大家能更好的理解是如何使用MPS实现LOP项目的。
需求
计算一个Java/PHP开发人员的收入,输入在Java和PHP项目投入的时间,计算器自动计算收入值,界面表示如下:
目标
通过写以下4行代码来生成这个计算器(10和5是常量)
1 |
calculator MySalary |
2 |
input PHP Hours |
3 |
input Java Hours |
4 |
output Java Hours* 10 + PHP Hours* 5 |
主要步骤
- 生成前面目标中提到的语法的一种语言来实现计算器。语言定义基本的逻辑概念、组成规范、关系以及各自的行为
- 生成一个生成器,定义构建swing应用程序的规则
- 使用我们创建的语言来实现一个计算器
下面主要讲解一下语言定义部分,代码生成器部分请参考MPS1.5的示例
生成项目
生成一个新的项目,项目名为:calculator
按Next后,生成一种新的语言,设定语言命名空间为: jetbrains.mps.tutorial.calculator(命名空间一般为companyName.meaningful.name)
再按Next,生成一种新的方案(方案是一套特定语言的模型):
理想情况下,语言和使用语言的方案是分离的两个项目,但是建议在生成语言时把这个项目放在一起,便于测试新的语言。
按Finish后进入项目视图,默认展开语言项目jetbrains.mps.tutorial.calculator()节点:
语言项目下有多个节点,这些节点前面都带有M图标,分别代表模型的不同方面:
- structure:描述语言语法
- editor:描述编写语言时的编辑器
- constraints:描述一些约束
- type system:描述如何计算节点类型
jetbrains.mps.tutorial.calculator.sandbox()表示我们的测试方案节点,Modules Pool() 包含一系列可以参考使用的语言和方案,后面会再介绍这些内容。
下面我们开始来建立Calculator语言
Calculator概念(concept)
首先我们需要生成一个新的概念:Calculator。设置instance can be root为true,实现INamedConcepts接口以便具有Name属性,
提示:按Tab/Shift+Tab可以导航编辑焦点<> ;按Ctrl+Space可以打开自动完成提示列表
生成一个编辑器
MPS编辑器看上去是文本编辑器,但是它是一个直接使用语法树的一个结构化编辑器。编辑器使用单元格(cells)来表示概念节点,这里有以下几种节点类型:
- 属性单元格: 编辑节点属性
- 常量单元格:显示常量值
- 集合单元格:在内部嵌套其他单元格
前面定义了概念calculator,它的编辑器应该为:calculator name
选择Calculator概念的【Editor】页签,新增一个概念编辑器,
- 添加一个缩进集合类型编辑器:[-
- 输入常量:calculator
- 建立一个属性单元格编辑器
在‘calculator’常量单元格编辑器后按Enter键,通过‘{’选择属性name:{name}
测试一下概念编辑器
在语言项目中,点击右键选择 Generate Language 来生成语言。生成之后,我们在方案项目新建一个模型:
在 Model name中输入类型 jetbrains.mps.tutorial.calculator.sandbox
把jetbrains.mps.tutorial.calculator语言加入到Model Properties对话框的Used Languages中,这样才能使用这个语言来编写应用:
在测试模型sandbox中新建一个Calculator概念测试应用,显示如下:
到现在我们已经简单建立了第一条语言,下面建立输入字段概念
给Calculator加上子概念InputField
新建一个InputField概念,设置instance can be root为false,实现INamedConcepts接口以便具有Name属性,然后再生成编辑器:[- input { name } -]
为了使得计算器包含输入字段,需要把InputField类型作为Calculator概念子对象:
选择在Calculator编辑器中的属性name,按Alt+Enter弹出意图列表,选择Add New Line
为了每个输入字段显示一行,我们可以对子对象InputField选择Add newline for children意图:
按Ctrl+F9重新生成语言后,再次进入应用界面:
输出概念:OutputField
生成一个默认的输出概念:OutputField,编辑器如下:
把OutputField添加到calculator概念子对象中:
Calculator编辑器更改如下:
现在我们能够在outfield中输入任何表达式:
添加表达式支持
MPS中有基础语言,新的语言使用它们需要让自己的语言扩展基础语言。现在我们需要使用表达式功能,打开语言属性对话框:
在Dependencies 页签中的Extended Languages 列表选择jetbrains.mps.baseLanguage:
BaseLanguage包含一个Expression概念,它表示一个表达式,格式如"2", "2+3", "abc+abc"等。这个就是我们这里示例中output需要的。
现在给OutputField添加expression子类型:
更改编辑器:
现在可以输入表达式,但是还不能通过input计算,应用界面如下:
扩展表达式概念
为了支持通过input来计算,我们需要生成自己的表达式类型。建立概念InputFieldReference,从Expression继承下来,添加InputField为引用概念field
生成InputFieldReference编辑器:%field%-> {name}表示引用概念属性
应用界面如下:MPS的智能引用会在当前上下文决定哪些引用可以使用,这里自动完成列表会列出width
定义生成器
MPS生成器语言使用两种参数引用:property macros (marked with $
) andnode macros ($$
)。
我更关注的是前面语言定义部分,这部分还没有具体看,刚兴趣的可以参考MPS1.5的示例
MPS网站
Download MPS
A Language Workbench in Action - MPS
欢迎转载,转载请注明:转载自周金根 [ http://zhoujg.cnblogs.com/ ]