看Java有一阵子了,除了开发Android程序外,自己又开始动手写一些乱七八糟的东西,这次为自己带来了一个手机端C语言解释器。
话不多说上图:
JavaCC(Java Compiler Compiler..囧)为Java平台广泛应用于词法和语法分析的工具,类似于C语言的lex和yacc,可以翻译为纯Java代码,是用Java实现编译器和解释器的一个很好的工具.同时java还有另一个此法语法分析工具是Antlr.
JavaCC项目网站: https://javacc.dev.java.net/
JavaCC原Sample中的Interpreter项目主要目的为了解释JJTree的语法树生成器.其中包含了一个类C语言(被称为SPL: Stupid Programming Language)的语法文件,从而实现其解释器。其中实现了很多简单的语句,简单到只有int和bool类型,赋值语句和条件和while循环语句,read和write关键字用来输出。例子可以从JavaCC项目中得到.
我借其语法文件一用,加入了float类型和C语言的一样的注释方式。改写一部分代码并在JDK1.6上运行。后来加了写界面移植到J2ME,家变成现在的样子。其中遇到很多头痛的问题下面会进行说明。
项目地址: http://code.google.com/p/c2me/
下面开始简要说明开发过程:
一、JavaCC Eclipse插件:http://eclipse-javacc.sourceforge.net/
可以在eclipse上自动解析并翻译.jj或者.jjt语法文件为java本地解析代码。(其中jj文件为词法分析的文件,而jjt为语法树分析文件.两个内容几乎相同,但是jjt可以建立语法树,这样才能方便我们的解释器工作)
借用其官方图片来说明新JavaCC项目建立过程:
建立JJTree项目以后
图示即为jjt语法文件.
经过我修改过的jtt文件如下(可在项目源码中找到)
options {
//STATIC = true;
MULTI = true;
NODE_EXTENDS="MyNode";
TRACK_TOKENS=true;
}
PARSER_BEGIN(SPLParser)
/** Stupid Programming Language parser. */
public class SPLParser {
/**
* Returns the root node of the AST.
* It only makes sense to call this after a successful parse.
* @return the root node
*/
public Node rootNode() {
return jjtree.rootNode();
}
}
PARSER_END(SPLParser)
SKIP : /* WHITE SPACE */
{
" "
| "/t"
| "/n"
| "/r"
| "/f"
| <"//" (~["/n","/r"])* ("/n" | "/r" | "/r/n")>
| <"/*" (~["*"])* "*" ("*" | ~["*","/"] (~["*"])* "*")* "/">
}
TOKEN : /* Types */
{
< INT: "int" >
|
< BOOL: "boolean" >
|
< FLOAT: "float" >
| < CHAR : "char" >
}
TOKEN : /* LITERALS */
{
< INTEGER_LITERAL: (<DIGIT>)+ >
| < FLOAT_LITERAL: (<DIGIT>)+ "." (<DIGIT>)* (<EXPONENT>)? (<FLOAT_SUFFIX>)? | "." (<DIGIT>)+ (<EXPONENT>)? (<FLOAT_SUFFIX>)? | (<DIGIT>)+ <EXPONENT> (<FLOAT_SUFFIX>)? | (<DIGIT>)+ (<EXPONENT>)? <FLOAT_SUFFIX> >
| < CHAR_LITERAL: "/'" (~["/'","//","/n","/r"] | "//" (["n","t","b","r","f","//","/'","/""] | ["0"-"7"] (["0"-"7"])? | ["0"-"3"] ["0"-"7"] ["0"-"7"])) "/'">
}
/*
* Program structuring syntax follows.
*/
/** Compilation unit. */
void CompilationUnit() :
{
//String name;
}
{
(
VarDeclaration() ";"
|
Statement()
)*
<EOF>
}
/** Variable declaration. */
void VarDeclaration() :
{ Token t; }
{
(
"boolean" { jjtThis.type = BOOL; }
|
"int" { jjtThis.type = INT; }
|
"float" { jjtThis.type = FLOAT; }
|
"char" { jjtThis.type = CHAR; }
)
t = <IDENTIFIER>
{ jjtThis.name = t.image; }
}
/*
* Expression syntax follows.
*/
/** Expression. */
void Expression() #void:
{}
{
LOOKAHEAD( PrimaryExpression() "=" )
Assignment()
|
ConditionalOrExpression()
}
/** Assignment. */
void Assignment() #Assignment(2) :
{}
{
PrimaryExpression() "=" Expression()
}
/** Conditional or expression. */
void ConditionalOrExpression() #void :
{}
{
ConditionalAndExpression()
( "||" ConditionalAndExpression() #OrNode(2) )*
}
/** Conditional and expression. */
void ConditionalAndExpression() #void :
{}
{
InclusiveOrExpression()
( "&&" InclusiveOrExpression() #AndNode(2) )*
}
/** Inclusive or expression. */
void InclusiveOrExpression() #void :
{}
{
ExclusiveOrExpression()
( "|" ExclusiveOrExpression() #BitwiseOrNode(2) )*
}
/** Exclusive or expression. */
void ExclusiveOrExpression() #void :
{}
{
AndExpression()
( "^" AndExpression() #BitwiseXorNode(2) )*
}
/** And expression. */
void AndExpression() #void :
{}
{
EqualityExpression()
( "&" EqualityExpression() #BitwiseAndNode(2) )*
}
/** Equality expression. */
void EqualityExpression() #void :
{}
{
RelationalExpression()
(
"==" RelationalExpression() #EQNode(2)
|
"!=" RelationalExpression() #NENode(2)
)*
}
/** Relational expression. */
void RelationalExpression() #void :
{}
{
AdditiveExpression()
(
"<" AdditiveExpression() #LTNode(2)
|
">" AdditiveExpression() #GTNode(2)
|
"<=" AdditiveExpression() #LENode(2)
|
">=" AdditiveExpression() #GENode(2)
)*
}
/** Additive expression. */
void AdditiveExpression() #void :
{}
{
MultiplicativeExpression()
(
"+" MultiplicativeExpression() #AddNode(2)
|
"-" MultiplicativeExpression() #SubtractNode(2)
)*
}
/** Multiplicative expression. */
void MultiplicativeExpression() #void :
{}
{
UnaryExpression()
(
"*" UnaryExpression() #MulNode(2)
|
"/" UnaryExpression() #DivNode(2)
|
"%" UnaryExpression() #ModNode(2)
)*
}
/** Unary expression. */
void UnaryExpression() #void :
{}
{
"~" UnaryExpression() #BitwiseComplNode(1)
|
"!" UnaryExpression() #NotNode(1)
|
PrimaryExpression()
}
/** Primary expression. */
void PrimaryExpression() #void :
{
//String name;
}
{
Literal()
|
Id()
|
"(" Expression() ")"
}
/** An Id. */
void Id() :
{
Token t;
}
{
t = <IDENTIFIER> { jjtThis.name = t.image; }
}
/** A literal. */
void Literal() #void :
{
Token t;
}
{
(
t=<INTEGER_LITERAL>
{
jjtThis.val = Integer.parseInt(t.image);
}
)#IntConstNode
|
BooleanLiteral()
|
(
t=<FLOAT_LITERAL>
{
jjtThis.val = Float.parseFloat(t.image);
}
)#FloatConstNode
| (
t = < CHAR_LITERAL >
{
jjtThis.val =
t.image.
replace("//n","/n").
replace("//r","/r").
replace("//t","/t").
replace("//0","/0").
charAt(1);
}
)#CharConstNode
}
/** A boolean literal. */
void BooleanLiteral() #void :
{}
{
"true" #TrueNode
|
"false" #FalseNode
}
/*
* Statement syntax follows.
*/
/** A statement. */
void Statement() #void :
{}
{
";"
|
LOOKAHEAD(2)
LabeledStatement()
|
Block()
|
StatementExpression()
|
IfStatement()
|
WhileStatement()
|
IOStatement()
}
/** A labeled statement. */
void LabeledStatement() #void :
{}
{
<IDENTIFIER> ":" Statement()
}
/** A block. */
void Block() :
{}
{
"{" ( Statement() )* "}"
}
/** A statement expression. */
void StatementExpression() :
/*
* The last expansion of this production accepts more than the legal
* SPL expansions for StatementExpression.
*/
{}
{
Assignment() ";"
}
/** An if statement. */
void IfStatement() :
/*
* The disambiguating algorithm of JavaCC automatically binds dangling
* else's to the innermost if statement. The LOOKAHEAD specification
* is to tell JavaCC that we know what we are doing.
*/
{}
{
"if" "(" Expression() ")" Statement() [ LOOKAHEAD(1) "else" Statement() ]
}
/** A while statement. */
void WhileStatement() :
{}
{
"while" "(" Expression() ")" Statement()
}
/** An IO statement. */
void IOStatement() #void :
{
//String name;
}
{
ReadStatement()
|
WriteStatement()
}
/** A read statement. */
void ReadStatement() :
{ Token t; }
{
"read" t = <IDENTIFIER>
{ jjtThis.name = t.image; }
}
/** A write statement. */
void WriteStatement() :
{ Token t; }
{
"write" t = <IDENTIFIER>
{ jjtThis.name = t.image; }
}
TOKEN : /* IDENTIFIERS */
{
< IDENTIFIER: <LETTER> (<LETTER>|<DIGIT>)* >
|
< #LETTER: [ "a"-"z", "A"-"Z" ] >
|
< #DIGIT: [ "0"-"9"] >
|
< #FLOAT_SUFFIX: ["f","F","d","D"] >
|
< #EXPONENT: ["e","E"] (["+","-"])? (<DIGIT>)+>
}
经过Eclipse和JavaCC插件编译后自动生成了jj和java文件.Eclipse会报告一些错误和警告,包括Package没有写,或者用到了Stack或者ArrayList容器但是没有声名所存储的模板类型,不用鸟他。运行就好了。
要说明生成的代码结构和各个文件所起到的作用不是很简单,请参考这些链接:
小型桌面计算器javacc的实现 http://mopishv0.blog.163.com/blog/static/54455932201081393726584/
还有一个链接是JavaEye的..悲剧的down掉了.. http://abruzzi.javaeye.com/
有了代码,我们run一下就可以得到一个命令行的解释器了。
输入像这样的类似C的代码就可以运行了
int n;
n = 0;
while(n < 10) { //没有for..
n=n+1; //没有自增运算
write n;
}
二、移植到J2ME平台
J2ME是J2SE的一个子集,去除了一些容器和很多平台相关内容,加入了J2ME专有的界面处理API和移动设备API。在不是很老的API中还是有float类型和经过包装的Float类的(让人欣慰..),同时让人欣慰的是java.util中包含了List和Stack容器类型.这样使我们移植过程中复杂度大大降低了.但是遇到一些实际的实现还是修改一些代码,比如这样的一个List容器移植到J2ME就要换种写法
private java.util.List<int[]> jj_expentries = new java.util.ArrayList<int[]>();
jj_expentries.add(jj_expentry);
jj_expentries.size()
jj_expentries.get(i)
jj_expentries.clear();
private ArrayList marks = new ArrayList();
//我奇怪为啥不用Stack...里面的操作无外乎就push pop size..xxx的
public Node popNode() {
if (--sp < mk) {
mk = marks.remove(marks.size()-1);
}
//这边也很囧
return nodes.remove(nodes.size()-1);
}
要修改成
private java.util.List<int[]> jj_expentries = new java.util.ArrayList<int[]>();
jj_expentries.add(jj_expentry);
jj_expentries.size()
jj_expentries.elementAt(i)
jj_expentries.removeAllElements();
private Stack marks = new Stack();
//我改成了Stack,J2ME有..嘿嘿
public Node popNode() {
if (--sp < mk) {
mk = marks.pop();
}
return nodes.pop();
}
其中还有些问题,比如Stack等等容器最好是Object放进去,要修改响应数据,将其从源类型变为Object,在取出来的时候转换为所用类型,还有标准输出流OutputStream在J2ME里面没有了 只能替代成ByteArrayOutputStream并且转换到String再放到TextView中输出.其间历尽千辛万苦,终于修成正果。
最后加上了J2ME的界面,用到了TextView和buttons,加上事件,大功告成。
接下来就如项目中所说的TODO list一样了.准备加入for循环,更多的运算符,更多的常用数学函数,打造出更好的一个解释器拿来玩儿.