使用 RASCAL 拆分声明和初始化
Splitting declaration and initialization using RASCAL
我是 Rascal 的新手,正在尝试其 transformation/term 重写能力。
我想编写一个脚本来拆分如下声明:
整数 x = 5;
变成declaration/initializations喜欢:
int x;
x = 5;
我该怎么做?假设我要转换的语言是 Java.
感谢您的帮助。
解决方案草图
好问题。有几种方法可以做到这一点,我将展示最简单的一种。请注意,您的示例不是最简单的示例,因为它需要从单个语句转换为语句列表(即,它不是类型保留)。
这里不多说一个完整的例子,解释如下。
module Decl
import IO;
import ParseTree;
// Decl, a trivial language, to demo simple program trafo
lexical Id = [a-z][a-z0-9]* !>> [a-z0-9] \ Reserved;
lexical Natural = [0-9]+ ;
lexical String = "\"" ![\"]* "\"";
layout Layout = WhitespaceAndComment* !>> [\ \t\n];
lexical WhitespaceAndComment
= [\ \t\n\r]
;
keyword Reserved = "int" | "str" | "if" | "then" | "else" | "fi" | "while" | "do" | "od";
start syntax Program
= {Statement ";"}* body
;
syntax Type
= "int"
| "str"
;
syntax Statement
= Type tp Id var
| Type tp Id var ":=" Expression exp
| Id var ":=" Expression val
| "if" Expression cond "then" {Statement ";"}* thenPart "else" {Statement ";"}* elsePart "fi"
| "while" Expression cond "do" {Statement ";"}* body "od"
;
syntax Expression
= Id name
| String string
| Natural natcon
| bracket "(" Expression e ")"
> left ( Expression lhs "+" Expression rhs
| Expression lhs "-" Expression rhs
)
;
str trafo1 () {
p = parse(#start[Program], "int x := 1").top;
newBody = "";
for(stat <- p.body){
if((Statement) `<Type tp> <Id var> := <Expression exp>` := stat){
newBody += "<tp> <var>; <var> := <exp>";
} else {
newBody += "<stat>";
}
}
return newBody;
}
主要部分是简单语言的完整语法。实际转换由 trafo1
完成,它执行以下操作:
- 解析示例。
- 引入并初始化
newBody
(用于构建结果)。
- 迭代给定正文中的语句。
- 测试每个语句是否符合要求的形式。
如果为真,则附加转换后的语句。请注意,此处使用 字符串模板 来构建转换后的语句。
如果为假,则附加原始语句。
- Return 结果字符串。
讨论
解决方案的风格主要取决于您的目标。这里我们只是构建一个字符串。如果需要,您可以 return 解析后的字符串作为结果。
备选方案:
- 转换为具体的解析树(不是那么容易,因为仍然缺乏一些功能,但我们的目标是使其成为首选解决方案)。
- 首先转换为抽象语法树(AST),并在AST上进行转换。
希望这可以帮助您入门。
我这里还有一些代码示例,它们使用具体的语法匹配和替换来达到您想要的效果:
module JavaMatch
import lang::java::\syntax::Java15;
// this just replaces exactly these specific kinds of declarations, as the only statement in a block:
CompilationUnit splitInitializersSimple(CompilationUnit u) = visit(u) {
case (Block) `{ int i = 0; }` => (Block) `{int i; i = 0;}`
};
// the next generalizes over any type, variable name or expression, but still one statement in a block:
CompilationUnit splitInitializersSingle(CompilationUnit u) = visit(u) {
case (Block) `{ <Type t> <Id i> = <Expr e>; }`
=> (Block) `{<Type t> <Id i>; <Id i> = <Expr e>;}`
};
// Now we allow more statements around the declaration, and we simply leave them where they are
CompilationUnit splitInitializersInContext(CompilationUnit u) = visit(u) {
case (Block) `{ <BlockStm* pre>
' <Type t> <Id i> = <Expr e>;
' <BlockStm* post>
'}`
=> (Block) `{ <BlockStm* pre>
' <Type t> <Id i>;
' <Id i> = <Expr e>;
' <BlockStm* post>
'}`
};
// But there could be more initializers in the same decl as well, as in int i, j = 0, k; :
CompilationUnit splitInitializersInContext2(CompilationUnit u) = visit(u) {
case (Block) `{ <BlockStm* pre>
' <Type t> <{VarDec ","}+ a>, <Id i>= <Expr e>, <{VarDec ","}+ b>;
' <BlockStm* post>
'}`
=> (Block) `{ <BlockStm* pre>
' <Type t> <{VarDec ","}+ a>, <Id i>, <{VarDec ","}+ b>;
' <Id i> = <Expr e>;
' <BlockStm* post>
'}`
};
// and now we add `innermost` such that not only the first but all occurrences are replaced:
CompilationUnit splitInitializersInContext2(CompilationUnit u) = innermost visit(u) {
case (Block) `{ <BlockStm* pre>
' <Type t> <{VarDec ","}+ a>, <Id i>= <Expr e>, <{VarDec ","}+ b>;
' <BlockStm* post>
'}`
=> (Block) `{ <BlockStm* pre>
' <Type t> <{VarDec ","}+ a>, <Id i>, <{VarDec ","}+ b>;
' <Id i> = <Expr e>;
' <BlockStm* post>
'}`
};
void doIt(loc file) {
start[CompilationUnit] unit = parse(#start[CompilationUnit], file);
unit.top = splitInitializersInContext(unit.top);
writeFile(file, "<unit>");
}
最后的评论,因为这仍然不完全笼统:
- 注意修饰符和数组类型;这只会添加更多变量以匹配并携带到右侧
- 初始化器现在将以与语句相反的顺序出现,如果它们之间存在数据依赖性,这将中断
- 请注意,规则的形状很大程度上取决于 Java 语法,因为我们在这里使用具体的语法匹配。它有助于在您创建此类代码时浏览语法。
- 此代码在许多地方保留注释,但不是全部,特别是在重写的声明之间和在重写的 vardec 之间,使用此代码将丢失注释。
我是 Rascal 的新手,正在尝试其 transformation/term 重写能力。
我想编写一个脚本来拆分如下声明:
整数 x = 5;
变成declaration/initializations喜欢:
int x; x = 5;
我该怎么做?假设我要转换的语言是 Java.
感谢您的帮助。
解决方案草图
好问题。有几种方法可以做到这一点,我将展示最简单的一种。请注意,您的示例不是最简单的示例,因为它需要从单个语句转换为语句列表(即,它不是类型保留)。
这里不多说一个完整的例子,解释如下。
module Decl
import IO;
import ParseTree;
// Decl, a trivial language, to demo simple program trafo
lexical Id = [a-z][a-z0-9]* !>> [a-z0-9] \ Reserved;
lexical Natural = [0-9]+ ;
lexical String = "\"" ![\"]* "\"";
layout Layout = WhitespaceAndComment* !>> [\ \t\n];
lexical WhitespaceAndComment
= [\ \t\n\r]
;
keyword Reserved = "int" | "str" | "if" | "then" | "else" | "fi" | "while" | "do" | "od";
start syntax Program
= {Statement ";"}* body
;
syntax Type
= "int"
| "str"
;
syntax Statement
= Type tp Id var
| Type tp Id var ":=" Expression exp
| Id var ":=" Expression val
| "if" Expression cond "then" {Statement ";"}* thenPart "else" {Statement ";"}* elsePart "fi"
| "while" Expression cond "do" {Statement ";"}* body "od"
;
syntax Expression
= Id name
| String string
| Natural natcon
| bracket "(" Expression e ")"
> left ( Expression lhs "+" Expression rhs
| Expression lhs "-" Expression rhs
)
;
str trafo1 () {
p = parse(#start[Program], "int x := 1").top;
newBody = "";
for(stat <- p.body){
if((Statement) `<Type tp> <Id var> := <Expression exp>` := stat){
newBody += "<tp> <var>; <var> := <exp>";
} else {
newBody += "<stat>";
}
}
return newBody;
}
主要部分是简单语言的完整语法。实际转换由 trafo1
完成,它执行以下操作:
- 解析示例。
- 引入并初始化
newBody
(用于构建结果)。 - 迭代给定正文中的语句。
- 测试每个语句是否符合要求的形式。 如果为真,则附加转换后的语句。请注意,此处使用 字符串模板 来构建转换后的语句。 如果为假,则附加原始语句。
- Return 结果字符串。
讨论
解决方案的风格主要取决于您的目标。这里我们只是构建一个字符串。如果需要,您可以 return 解析后的字符串作为结果。
备选方案:
- 转换为具体的解析树(不是那么容易,因为仍然缺乏一些功能,但我们的目标是使其成为首选解决方案)。
- 首先转换为抽象语法树(AST),并在AST上进行转换。
希望这可以帮助您入门。
我这里还有一些代码示例,它们使用具体的语法匹配和替换来达到您想要的效果:
module JavaMatch
import lang::java::\syntax::Java15;
// this just replaces exactly these specific kinds of declarations, as the only statement in a block:
CompilationUnit splitInitializersSimple(CompilationUnit u) = visit(u) {
case (Block) `{ int i = 0; }` => (Block) `{int i; i = 0;}`
};
// the next generalizes over any type, variable name or expression, but still one statement in a block:
CompilationUnit splitInitializersSingle(CompilationUnit u) = visit(u) {
case (Block) `{ <Type t> <Id i> = <Expr e>; }`
=> (Block) `{<Type t> <Id i>; <Id i> = <Expr e>;}`
};
// Now we allow more statements around the declaration, and we simply leave them where they are
CompilationUnit splitInitializersInContext(CompilationUnit u) = visit(u) {
case (Block) `{ <BlockStm* pre>
' <Type t> <Id i> = <Expr e>;
' <BlockStm* post>
'}`
=> (Block) `{ <BlockStm* pre>
' <Type t> <Id i>;
' <Id i> = <Expr e>;
' <BlockStm* post>
'}`
};
// But there could be more initializers in the same decl as well, as in int i, j = 0, k; :
CompilationUnit splitInitializersInContext2(CompilationUnit u) = visit(u) {
case (Block) `{ <BlockStm* pre>
' <Type t> <{VarDec ","}+ a>, <Id i>= <Expr e>, <{VarDec ","}+ b>;
' <BlockStm* post>
'}`
=> (Block) `{ <BlockStm* pre>
' <Type t> <{VarDec ","}+ a>, <Id i>, <{VarDec ","}+ b>;
' <Id i> = <Expr e>;
' <BlockStm* post>
'}`
};
// and now we add `innermost` such that not only the first but all occurrences are replaced:
CompilationUnit splitInitializersInContext2(CompilationUnit u) = innermost visit(u) {
case (Block) `{ <BlockStm* pre>
' <Type t> <{VarDec ","}+ a>, <Id i>= <Expr e>, <{VarDec ","}+ b>;
' <BlockStm* post>
'}`
=> (Block) `{ <BlockStm* pre>
' <Type t> <{VarDec ","}+ a>, <Id i>, <{VarDec ","}+ b>;
' <Id i> = <Expr e>;
' <BlockStm* post>
'}`
};
void doIt(loc file) {
start[CompilationUnit] unit = parse(#start[CompilationUnit], file);
unit.top = splitInitializersInContext(unit.top);
writeFile(file, "<unit>");
}
最后的评论,因为这仍然不完全笼统:
- 注意修饰符和数组类型;这只会添加更多变量以匹配并携带到右侧
- 初始化器现在将以与语句相反的顺序出现,如果它们之间存在数据依赖性,这将中断
- 请注意,规则的形状很大程度上取决于 Java 语法,因为我们在这里使用具体的语法匹配。它有助于在您创建此类代码时浏览语法。
- 此代码在许多地方保留注释,但不是全部,特别是在重写的声明之间和在重写的 vardec 之间,使用此代码将丢失注释。