我正在尝试编写一个软件,该软件应仅使用这些功能执行基本编程语言的指令:
它应该一次一步显示“减少”或“简化”的代码,并让我展示示例输出的示例:
迭代1:
a=3;
b=2;
c=true;
if(c && (a < 3 * (5 -2) ) || b >= 3 * (5 -2))){
System.out.println("going through if");
}else{
System.out.println("going through else");
}
迭代2:
if(true && (a < 3 * (5 -2) ) || b >= 3 * (5 -2))){
System.out.println("going through if");
}else{
System.out.println("going through else");
}
迭代3:
if(true && (3 < 3 * (5 -2) ) || 2 >= 3 * (5 -2))){
System.out.println("going through if");
}else{
System.out.println("going through else");
}
迭代4:
if(true && (3 < 9 ) || 2 >= 3 * (5 -2))){
System.out.println("going through if");
}else{
System.out.println("going through else");
}
迭代5:
if(true && (3 < 9 ) || 2 >= 3 * 3)){
System.out.println("going through if");
}else{
System.out.println("going through else");
}
迭代6:
if(true && (3 < 9 ) || 2 >= 9)){
System.out.println("going through if");
}else{
System.out.println("going through else");
}
迭代7:
if(true && true || 2 >= 9)){
System.out.println("going through if");
}else{
System.out.println("going through else");
}
迭代8:
if(true && (true || false)){
System.out.println("going through if");
}else{
System.out.println("going through else");
}
迭代9:
if(true && false){
System.out.println("going through if");
}else{
System.out.println("going through else");
}
迭代10:
if(false){
System.out.println("going through if");
}else{
System.out.println("going through else");
}
迭代11:
System.out.println("going through else");
因此它应该解析输入代码并在每次迭代时同时执行它,只进行一次基本操作,替换操作结果并在不再需要简化步骤时完成循环。任何人都知道如何做到这一点,例如使用ANTLR工具?我一直在检查ANTLR的Tree Listener功能,因为它似乎要走了,但我不清楚如何实现它。
另一个原因是最佳的是它是用Javascript实现的,以便能够在Web浏览器中执行,但Java代码就足够了(即作为Java applet执行)。
语法:
program
: (variable | function)*
statement*
;
variable
: IDENT ('=' expression)? ';'
;
type
: 'int'
| 'boolean'
| 'String'
| 'char'
;
statement
: assignmentStatement
| ifStatement
;
ifStatement
: 'if' '(' expression ')' '{' statement+ '}'
('else if' '(' expression ')' '{' statement+)* '}'
('else' '{' statement+)? '}'
;
assignmentStatement
: IDENT '=' expression ';'
;
returnStatement
: 'return' expression ';'
;
function
: 'function' IDENT '(' parameters? ')' '{'
(statement|returnStatement)*
'}'
;
parameters
: IDENT (',' IDENT)*
;
term
: IDENT
| '(' expression ')'
| INTEGER
;
negation
: '-' -> NEGATION
;
unary
: ('+'! | negation^)* term
;
mult
: unary (('*' | '/' | 'mod') unary)*
;
add
: mult (('+' | '-') mult)*
;
relation
: add (('=' | '/=' | '<' | '<=' | '>=' | '>') add)*
;
expression
: relation (('and' | 'or') relation)*
;
fragment LETTER : ('a'..'z' | 'A'..'Z') ;
fragment DIGIT : '0'..'9';
INTEGER : DIGIT+ ;
IDENT : LETTER (LETTER | DIGIT)*;
WS : (' ' | '\t' | '\n' | '\r' | '\f')+ {$channel = HIDDEN;};
COMMENT : '//' .* ('\n'|'\r') {$channel = HIDDEN;};
答案 0 :(得分:6)
[OP:...(或任何其他工具)]
您使用短语term rewriting,然后展示一个有趣的示例,通过替换值并执行constant folding来生成最终的程序答案,逐步处理程序的源代码。
抽象地说,你想从术语重写系统中得到的是能够从一组术语重写开始的能力。规则,实质上指定
if you see this, replace it by that
e.g。
" if (true) then ... " ==> " ... "
并以有组织的方式反复应用这些规则,直到达到一些停止条件(通常是&#34;不再适用规则&#34;)。
有两种方法可以实现术语重写,既可以从术语开始(在你的情况下是程序的AST),也可以产生相同的结果。不同之处在于重写规则的实际实施方式。
程序树重写
第一种方式在程序上指定重写步骤。也就是说,构建一个访问者来遍历AST,它在程序上检查节点类型,以及有趣节点类型的子树(&#34;匹配&#34;),并且在找到匹配的地方,根据以下内容修改树。规则的预期效果。
对于&#34; if&#34;上面的例子,访问者会发现&#34; if&#34;语句子树根,检查左/条件子树,看它是否是&#34; true&#34;子树,如果是的话,替换&#34; if&#34;由右子树生成修改树的子树。
常量折叠有点特殊:访问者检查操作员节点,检查其所有子节点是否为常量值,在这些常量上计算运算符的结果,然后用包含计算结果的节点替换运算符。
要使用一组规则实现重写,您必须首先记下所有抽象规则,然后对此访问者进行编码,并结合所有规则中的所有测试。这可能会产生一个非常混乱的代码,它会检查节点类型并在子树上上下走动以进行进一步检查。 (你也可以按规则实现这一个访问者,这使得它们更容易编写,但现在你有大量的访问者,你必须在树上重复运行它们......这最终会非常慢)。
访问者有点聪明:你不希望它在他们的时间和#34;之前处理子树#34;考虑这个代码,由重写器处理:
if (x) then y = 3/ 0; else y = 3;
你不想经常弃牌&#34; 3/0&#34; 之前的已经过评估。
您可以从任何解析器生成器(包括ANTLR)开始执行ASTs的过程重写;写访客只是汗流。背。也许很多。
由于将所有规则匹配组合到访问者中的问题,程序重写很难实现。如果你有几十个规则,这就变得难以管理,速度很快。 (如果您要使用规则来处理完整的计算机语言,那么每个语法位置至少会有一条规则;在这种情况下很容易获得数十条规则,如果不是数百条规则的话。
获得&#34;增量&#34; OP所需的显示方面,你必须在每个匹配/替换步骤后停止,然后重新打印AST,例如,从AST重新生成表面语法文本。修改访问者在每次树修改后调用prettyprint并不是很难。
生成AST的解析器生成器通常不会为执行漂亮的打印步骤提供很好的帮助。它比看起来漂亮印刷更难。细节太复杂了,不能放在这里;有关如何执行此操作的详细信息,请参阅我的SO answer on how to prettyprint。
下一个复杂问题:当遇到正在评估的程序中的变量时,应该在树中替换什么值?为此,需要一个符号表用于语言,并且必须使该符号表保持最新的变量值分配。
如果程序格式不正确,不会讨论会发生什么。它们将是: - {很可能rwrites需要很多&#34;错误检查&#34;防止无意义的计算(例如,&#34; x / y&#34;其中y是一个字符串)。
直接树重写
理想情况下,您想要的是直接接受明确的术语重写规则的引擎,并且可以应用它们。
Program Transformation Systems (PTS)这样做。具体系统包括Mathematica(现称为#34; Wolfram语言&#34;?),DMS,TXL,Stratego / XT。 (OP正在寻找一个用Java实现的:我认为Stratego有一个Java版本,其他的绝对没有。)
这些工具接受使用目标语言的表面语法编写的重写规则,将规则本质上转换为模式树对(a&#34;匹配&#34;带有可变占位符的树)和&#34;替换&#34;具有(相同)可变占位符的树。这些工具中的重写引擎将采用指定规则的任意子集,并将它们应用于树,通过比较&#34;匹配&#34;来检查所有匹配。当找到匹配时,树替换目标树,并用匹配的占位符替换替换树。这是编写复杂的重写集的一个主要方便。 (如果你考虑一下,这仍然是程序上的重写...只是引擎正在做它而不是你,规则说明符。尽管方便。)
这样的PTS包括构建AST的解析器生成器(Mathematica没有)和完整的prettyprinter(或者至少允许你方便地定义一个)。
对于DMS,您可以编写如下规则:
rule fold_true_ifTE(s: statement, t:statement): statement->statement =
" if (true) then \s else \t " -> " \s ";
rule fold_false_ifTE(s: statement, t:statement): statement->statement =
" if (false) then \s else \t " -> " \t ";
rule fold_constants_add(x:NUMBER,y:NUMBER):sum -> sum =
" \x + \y " -> " \Add\(\x\,\y\)";
前两条规则实现了&#34; if&#34;我们之前草拟的声明重写;你还需要规则只是&#34; if-then&#34;声明。引号是 metaquotes ;它们将规则规范语言(RSL)的文本与规则操纵的语言文本分开。 metaescaped 字母(s,t,x,y)是元变量,表示规则匹配的子树。这些元变量必须具有规则参数列表指定的AST类型(例如, s:statement 表示s是&#34;任何语句节点&#34;)。
第三条规则为&#34;添加&#34;实现了常量折叠。该模式寻找&#34; +&#34;操作;只有当找到一个同时具有数字常量的子项时才会获得匹配。它的工作原理是调用外部程序&#34; \ Add&#34;对其运营商; Add返回一个包含总和的树,重写引擎将其拼接到位。
在DMS的情况下,在每次重写尝试(失败和成功)之后调用重写机制都有一个钩子来跟踪重写结果。这个钩子将是OP称为prettyprinter的地方,以显示每个步骤后树的变化情况。
有关如何编写规则以评估代数表达式的详细示例,请参阅how to implement Algebra with rewrite rules。
有关DMS重写规则如何工作的更详细说明,以及将它们应用于&#34;简化&#34;的示例。 (评估)Nikolas Wirth的编程语言Oberon,见DMS Rewrite Rules。
在任何一种情况下都不显示控制规则应用顺序的方法。因为排序约束可以是任意的,所以必须介入并引导重写引擎。如果需要,DMS提供规则排序的完整程序控制。通常可以将规则划分为不同的集合:可以不加选择地应用的规则,以及需要排序的规则(例如,if-then简化规则)。
PTS不会使符号表问题消失; OP仍然需要一个。大多数PTS都没有为此提供任何支持。 DMS为此提供了明确的支持(配置需要一些努力,但比没有任何东西时要少得多!)以及构建静态类型检查器以帮助在开始执行之前验证程序是否良好。实际上,有许多问题需要解决以准备执行程序(例如,可能想要构建标签的映射到源代码点以实现有效的GOTO模拟)。请参阅Life After Parsing。答案 1 :(得分:1)
在下面的Symja示例代码段中,您可以尝试内置的Java术语重写引擎。
术语重写和模式匹配引擎的主要部分在包中实现:GIT: org.matheclipse.core.patternmatching
通过实现IEvalStepListener接口,您可以看到引擎在内部运行的步骤:
package org.matheclipse.core.examples;
import org.matheclipse.core.eval.EvalEngine;
import org.matheclipse.core.eval.ExprEvaluator;
import org.matheclipse.core.interfaces.AbstractEvalStepListener;
import org.matheclipse.core.interfaces.IExpr;
import org.matheclipse.parser.client.SyntaxError;
import org.matheclipse.parser.client.math.MathException;
public class SO_StepListenerExample {
private static class StepListener extends AbstractEvalStepListener {
/** Listens to the evaluation step in the evaluation engine. */
@Override
public void add(IExpr inputExpr, IExpr resultExpr, int recursionDepth, long iterationCounter) {
System.out.println("Depth " + recursionDepth + " Iteration " + iterationCounter + ": " + inputExpr.toString() + " ==> "
+ resultExpr.toString());
}
}
public static void main(String[] args) {
try {
ExprEvaluator util = new ExprEvaluator( );
EvalEngine engine = util.getEvalEngine();
engine.setStepListener(new StepListener());
IExpr result = util.evaluate(
"a=3;b=2;c=True;If(c && (a < 3 * (5 -2) ) || ( b >= 3 * (5 -2)),"
+ "GOINGTHROUGHIF,"
+ "GOINGTHROUGHELSE )");
System.out.println("Result: " + result.toString());
// disable trace mode if the step listener isn't necessary anymore
engine.setTraceMode(false);
} catch (SyntaxError e) {
// catch Symja parser errors here
System.out.println(e.getMessage());
} catch (MathException me) {
// catch Symja math errors here
System.out.println(me.getMessage());
} catch (Exception e) {
e.printStackTrace();
}
}
}
该示例生成以下输出:
Depth 4 Iteration 0: a=3 ==> 3
Depth 4 Iteration 0: b=2 ==> 2
Depth 3 Iteration 0: a=3;b=2 ==> 2
Depth 3 Iteration 0: c=True ==> True
Depth 2 Iteration 0: a=3;b=2;c=True ==> True
Depth 5 Iteration 0: c ==> True
Depth 6 Iteration 0: a ==> 3
Depth 8 Iteration 0: (-1)*2 ==> -2
Depth 7 Iteration 0: -2+5 ==> -2+5
Depth 7 Iteration 1: 5-2 ==> 3
Depth 6 Iteration 0: 3*(-2+5) ==> 3*3
Depth 6 Iteration 1: 3*3 ==> 9
Depth 5 Iteration 0: a<3*(-2+5) ==> 3<9
Depth 5 Iteration 1: 3<9 ==> True
Depth 4 Iteration 0: c&&a<3*(-2+5) ==> True
Depth 3 Iteration 0: c&&a<3*(-2+5)||b>=3*(-2+5) ==> True
Depth 2 Iteration 0: If(c&&a<3*(-2+5)||b>=3*(-2+5),goingthroughif,goingthroughelse) ==> goingthroughif
Depth 1 Iteration 0: a=3;b=2;c=True;If(c&&a<3*(-2+5)||b>=3*(-2+5),goingthroughif,goingthroughelse) ==> goingthroughif
Result: goingthroughif