javacc中的哈希映射



我想创建一种具有多个函数和一个主函数的编程语言。对于该语言的解释器,我使用的是哈希映射,但我不知道如何在哈希映射中存储中间值。一个有效程序的例子包括:

DEF MAIN { ADDITION(4) } ;
DEF ADDITION x { x+3 } ;

这就是我目前所拥有的:

HashMap<String, Function> Program() : {
HashMap<String, Function> symbolTable = new HashTable<>();
}
{
(FunctionDefinition(symbolTable))*
<EOF>
{return symbolTable;}
}
void FunctionDefinition(SymbolTable table)
{
Function f;
String name;
}
{
<DEF> <SPACE>
(
(name = <MAIN>)
|   (name = <FUNC> (<SPACE> <PARAM> ))
<SPACE>
f = FunctionBody()
";"
{
if (table.hashKey(name)) { System.out.println("do something");}
else { table.add(name, f); }
})
}
void FunctionBody() : {}
{
<LEFT> <SPACE>
Expression()
<SPACE> <RIGHT>
}
void Expression() : {}
{
AdditiveExpression()
}
void AdditiveExpression() : {
}
{
MultiplicativeExpression() (<PLUS> MultiplicativeExpression()
{
try {
int a = s.pop();
int b = s.pop();
stack.push(a+b);
}
catch (ClassCastException ex) {
System.out.println("Only numbers can be used for arithmetic operations.");
throw new ParseException();
}
})*
}
void MultiplicativeExpression() : {
}
{
UnaryExpression() (<MUL> UnaryExpression()
{
try {
int a = s.pop();
int b = s.pop();
stack.push(a*b);
}
catch (ClassCastException ex) {
System.out.println("Only numbers can be used for arithmetic operations");
throw new ParseException();
}
})*
}
void UnaryExpression() : {
Token x;
}
{
(x = <NUMBER> | x = <PARAM>)
{s.push(x.image);}
}

任何帮助都将不胜感激。

正如@Theodore所说,在解析程序时无法执行程序。我想我应该在这里加两分钱。

解析的结果可能是函数表,您将有一个主要的方法来做这样的事情:

Parser parser = new Parser(System.in, "UTF-8");
Map<String, Function> table = parser.program();
Function main = table.get("MAIN");
if (main == null) {
System.out.println("Function MAIN doesn't exist");
} else {
int r = main.invoke(table);
System.out.println("Result: " + r);
}

我在你的代码中看到了一些奇怪的东西:

与其在每个令牌之间使用令牌<SPACE>,不如使用SKIP指令:

SKIP : { " " | "r" | "n" | "t" }

您正在为同一事物使用三个不同的令牌:<MAIN><FUNC><PARAM>。它们都是名称,您可以定义一个令牌<NAME>如下:

TOKEN: {
< NAME: <LETTER>(<LETTER>|<DIGIT>)* >
| < #LETTER: ["a"-"z", "A"-"Z", "_"] >
| < #DIGIT: ["0"-"9"] >
}

函数定义的规则将变为:

<DEF> <NAME> ( <NAME> )* functionBody() ";"

注意,一个函数可以具有零个或多个参数;函数CCD_ 6将具有零。

在您的示例中,函数MAIN包含对函数ADDITION的前向引用,这意味着在解析函数MAIN的主体时,您将发现对函数ADDITION的调用尚未在符号表中。对于编程语言来说,使用前向引用的能力是一个不错的特性,但它会使实现稍微复杂一些。

如果你正在做一个解释器,你基本上有两个选项来处理前向引用:1(在解析后进行第二次传递到";"修复";正向引用,2(在运行时执行名称解析。第二种选择更简单,但速度较慢。

请注意,函数的参数总是在使用之前定义的。它们只能在函数体中访问。您可以在解析定义的头时为参数构建一个表,然后将该表传递给解析函数体的方法。

示例:

void functionDefinition(Map<String, Function> table): {
Expression body;
Function f;
String name;
String param;
int paramCount = 0;
Map<String,Parameter> params = new HashMap<String,Parameter>();
}
{
<DEF> name=<NAME>.image (
param=<NAME>.image {params.put(param, new Parameter(paramCount++));}
)*
body=functionBody(params)
{ f = new Function(paramCount, body); }
";"
{
if (table.containsKey(name)) {
System.out.println("Function " + name + "already exists");
} else {
table.put(name, f);
}
}
}

要评估函数体中的表达式,可以使用解释器模式,其中表达式的元素都是Expression接口的实现:

public interface Expression {
public int evaluate(Map<String,Function> table, int... parms);
}

这里parms是传递给正在执行的函数的实际参数。

以下文件是基于您的问题的一种非常简单的语言的非常简单、有效的实现的来源。它可以执行您的示例:

DEF MAIN { ADDITION(4) } ;
DEF ADDITION x { x+3 } ;

它还可以执行这样的操作:

DEF MAIN { ADDITION3(4) } ;
DEF ADDITION3 x { ADDITION(x,3) } ;
DEF ADDITION x y { x+y } ;

我希望这会有所帮助。

JavaCC文件,parser.jj:

options {
STATIC = false;
IGNORE_CASE = false;
}
PARSER_BEGIN(Parser)
package org.tastefuljava.minilang;
import java.io.InputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
public class Parser {
public static void main(String[] args) {
try {
Parser parser = new Parser(System.in, "UTF-8");
Map<String, Function> table = parser.program();
int r = Expression.call("MAIN").evaluate(table);
System.out.println("Result: " + r);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
PARSER_END(Parser)
SKIP : { " " | "r" | "n" | "t" }
TOKEN: {
< DEF: "DEF" >
| < NAME: <LETTER>(<LETTER>|<DIGIT>)* >
| < #LETTER: ["a"-"z", "A"-"Z", "_"] >
| < #DIGIT: ["0"-"9"] >
| < PLUS: "+" >
| < MUL: "*" >
| < LEFT: "{" >
| < RIGHT: "}" >
| < NUMBER: (<DIGIT>)+ >
}
Map<String, Function> program(): {
Map<String, Function> symbolTable = new HashMap<>();
}
{
(functionDefinition(symbolTable))*
{return symbolTable;}
}
void functionDefinition(Map<String, Function> table): {
Expression body;
Function f;
String name;
String param;
int paramCount = 0;
Map<String,Parameter> params = new HashMap<String,Parameter>();
}
{
<DEF> name=<NAME>.image (
param=<NAME>.image {params.put(param, new Parameter(paramCount++));}
)*
body=functionBody(params)
{ f = new Function(paramCount, body); }
";"
{
if (table.containsKey(name)) {
System.out.println("Function " + name + "already exists");
} else {
table.put(name, f);
}
}
}
Expression functionBody(Map<String,Parameter> params): {
Expression e;
}
{
<LEFT>
e=expression(params)
<RIGHT>
{
return e;
}
}
Expression expression(Map<String,Parameter> params): {
Expression e;
}
{
e=additiveExpression(params)
{
return e;
}
}
Expression additiveExpression(Map<String,Parameter> params): {
Expression e, f;
}
{
e=multiplicativeExpression(params)
(<PLUS> f=multiplicativeExpression(params) {
e = Expression.add(e,f);
})*
{
return e;
}
}
Expression multiplicativeExpression(Map<String,Parameter> params): {
Expression e, f;
}
{
e=unaryExpression(params) (<MUL> f=unaryExpression(params) {
e = Expression.mul(e,f);
})*
{
return e;
}
}
Expression unaryExpression(Map<String,Parameter> params): {
Expression e;
String s;
int n;
Expression[] parms;
}
{
(
n=number() {
e = Expression.number(n);
}
| s=<NAME>.image ( parms=parameterList(params) {
e = Expression.call(s, parms);
} | {
Parameter p = params.get(s);
if (p != null) {
e = Expression.param(p.index);
} else {
// no parameter found: assume it's a parameterless function                    
e = Expression.call(s);
}
})
)
{
return e;
}
}
int number(): {
String s;
}
{
s=<NUMBER>.image
{
return Integer.parseInt(s);
}
}
Expression[] parameterList(Map<String,Parameter> params): {
List<Expression> parms = new ArrayList<Expression>();
Expression p;
}
{
"("
(
p=expression(params) { parms.add(p); }
( "," p=expression(params){ parms.add(p); } )*
)?
")"
{
return parms.toArray(new Expression[parms.size()]);
}
}

函数类,Function.java:

package org.tastefuljava.minilang;
public class Function {
public final int paramCount;
public final Expression body;
public Function(int paramCount, Expression body) {
this.paramCount = paramCount;
this.body = body;
}
}

参数类,Parameter.java:

package org.tastefuljava.minilang;
public class Parameter {
public final int index;
public Parameter(int index) {
this.index = index;
}
}

表达式接口,Expression.java:

package org.tastefuljava.minilang;
import java.util.Map;
public interface Expression {
public int evaluate(Map<String,Function> table, int... parms);
public static Expression number(int x) {
return (table, parms) -> x;
}
public static Expression add(Expression a, Expression b) {
return (table, parms) ->
a.evaluate(table, parms) + b.evaluate(table, parms);
}
public static Expression mul(Expression a, Expression b) {
return (table, parms) ->
a.evaluate(table, parms) * b.evaluate(table, parms);
}
public static Expression call(String name, Expression... parms) {
return (table, oldParms) -> {
Function f = table.get(name);
if (f == null) {
System.out.println("Unknown function " + name);
return 0;
} else if (f.paramCount != parms.length) {
System.out.println("Wrong number of parameters for function "
+ name + ": expected " + f.paramCount
+ ", actual " + parms.length);
return 0;
}
int[] newParms = new int[parms.length];
for (int i = 0; i < parms.length; ++i) {
newParms[i] = parms[i].evaluate(table, oldParms);
}
return f.body.evaluate(table, newParms);
};
}
public static Expression param(int index) {
return (table, parms) -> parms[index];
}
}

您需要FunctionBody非终结符来返回函数的中间表示。

问题是你没有一个中间表示。您试图进行直接解释,即在解析程序的同时执行程序。这对于像这个古老教程中的那种简单的交互式计算器来说很好。然而,一旦您开始处理循环和函数,您就真正需要生成(并在以后执行(中间代码。请参阅此常见问题解答了解原因。

如果您正在参加一门课程,请询问您的讲师,他或她会为中级代码推荐什么。

如果你没有讲师,我的建议是为堆栈机器生成机器代码,因为你已经在使用堆栈执行了。例如,请参阅我关于在递归下降解析器中为堆栈机器生成机器代码的注释,尤其是第14至20页;我没有为此使用解析器生成器,但这并不重要。(我用^表示序列串联,所以,例如,m := m^div只是意味着在中间表示的末尾添加一条div指令。(

任何一本关于编译器的书都会涵盖这类内容。

Tom Copeland的书可能有更多的信息,并且是JavaCC特有的。

最新更新