首先声明一下以下文章是跟据我用C#写的脚本解释器的经验之谈,如不认可也请不要找本人。
一般写个脚本解释器需要以下的步骤:
源程序-词法分析-语法分析-生成中间代码-解释中间代码
一、我写的脚本解释器就是跟据上面的过程写的,下面说明一下本脚本解释器的语法。
1、语法规则:
(1)script_begin代表语句开始
(2)script_end代表语句结束
(3)条件语句:if 表达式 语句 endif
(4)循环语句:while 表达式 语句 endwhile
(5)赋值语句:变量=表达式
(6)表达式:
(为方便我们这里使用产生式来说明)
expr->expr+term|expr-term|term|term>=term|term<=term|term==term|term!=term|term>term|term<term|term
term->term*factor|term/factor|factor
factor->(expr)|数字|变量|字符串|!变量
(注意:产生式中的->代表前面的值可以是后面中的任何一个值,|代表着或的意思)
(7)变量:本脚本只有全局变量,我们使用一个结构符号表(另外这个表还保存着关键字等)来保存变量名、类型(说明是变量、关键字等)和值。以下是此符号表的定义:
public struct Symtable
{
public string value;
public string valuestr;
public int toketype;
}
Symtable[] m_table=new Symtable[max_size];
max_size的大小说明了本脚本可以定义的变量数有多少。
2、词法分析:
在这个阶段解释器把读取的字符分为以下类型:
(1)、变量
(2)、数字
(3)、字符串
(4)、+、-、*、/等符号
(5)、if、while等关键字
以下是此过程的代码:
private int lexan(StreamReader sr)
{
m_tmpvalue = "";
char t;
while (true)
{
t = (char)sr.Read();
if (sr.EndOfStream)
{
return -1;
}
if (t == ' ')
{
}
else if (t == '/r')
{
t = (char)sr.Read();
if (t == '/n')
{
error_line_num++;
}
}
else if (Char.IsDigit(t))
{
while (Char.IsDigit(t))
{
m_tmpvalue += t;
t = (char)sr.Peek();
if (Char.IsDigit(t))
{
sr.Read();
}
}
return m_numtype;
}
else if (isStrword(t))
{
while (isStrword(t))
{
m_tmpvalue += t;
t = (char)sr.Peek();
if (isStrword(t))
{
sr.Read();
}
}
int p = LookUp(m_tmpvalue);
if (p == -1)
{
p = InertTable(m_tmpvalue, m_idtype);
}
return m_symtable[p].toketype;
}
else if (isSingleword(t))
{
return (int)t;
}
else if (isDoubleword(t))
{
char tmpc = (char)sr.Peek();
if (tmpc == '=')
{
sr.Read();
if (t == '>')
{
return m_largede;
}
else if (t == '<')
{
return m_smallde;
}
else if (t == '!')
{
return m_bude;
}
else if (t == '=')
{
return m_dede;
}
else
{
CException.ScriptError("格式错误!");
}
}
return (int)t;
}
else if (t == '/"')
{
t=(char)sr.Read();
while (t != '/"')
{
m_tmpvalue += t;
t = (char)sr.Read();
}
return m_strtype;
}
else
{
CException.ScriptError("错误格式");
return -1;
}
}
}
2、语法分析:
在这个过程中对读取的字符按产生式的进行分阶段处理;表达式从中缀变成后缀,中缀就像3+4+5的后缀就是34+5+并面在后缀中是不需要括号的。这个阶段还要生成中间代码。
代码如下:
private void match(int t, StreamReader sr)
{
if (t == m_headtype)
{
m_headtype = lexan(sr);
}
else
{
CException.ScriptError("错误格式");
}
}
private void WriteToMemory(string s)
{
m_zhongJingdaima += s;
}
public int stmtExplain(StreamReader sr)
{
if (m_headtype == m_begintype)
{
match(m_headtype, sr);
int ret = stmtExplain(sr);
while (ret != 0)
{
ret = stmtExplain(sr);
}
}
else if (m_headtype == m_endtype)
{
match(m_headtype, sr);
return 0;
}
else if (m_headtype == m_iftype)
{
WriteToMemory("label:" + label_line + "/n");
label_line++;
match(m_headtype, sr);
expr(sr);
WriteToMemory("gofalse label:" + label_line + " /n");
stmtExplain(sr);
match(m_endif, sr);
WriteToMemory("label:" + label_line + "/n");
label_line++;
}
else if (m_headtype == m_whiletype)
{
int tmplabel=label_line;
WriteToMemory("label:" + label_line + "/n");
label_line++;
match(m_headtype, sr);
expr(sr);
WriteToMemory("gofalse label:" + label_line + " /n");
stmtExplain(sr);
match(m_endwhile, sr);
WriteToMemory("goto label:" + tmplabel + " /n");
WriteToMemory("label:" + label_line + "/n");
label_line++;
}
else if (m_headtype == m_funtiontype)
{
WriteToMemory("function ");
WriteToMemory(m_tmpvalue);
WriteToMemory("/n");
match(m_headtype, sr);
}
else if (m_headtype == m_idtype)
{
int index = LookUp(m_tmpvalue);
WriteToMemory("lvalue " + m_tmpvalue + "/n");
match(m_headtype, sr);
match('=', sr);
expr(sr);
WriteToMemory(":=/n");
}
return 1;
}
private void expr(StreamReader sr)
{
term(sr);
while (true)
{
switch (m_headtype)
{
case '+':
{
match(m_headtype, sr);
term(sr);
WriteToMemory("+/n");
continue;
}
case '-':
{
match(m_headtype, sr);
term(sr);
WriteToMemory("-/n");
continue;
}
case m_largede:
{
match(m_headtype, sr);
term(sr);
WriteToMemory(">=/n");
continue;
}
case m_smallde:
{
match(m_headtype, sr);
term(sr);
WriteToMemory("<=/n");
continue;
}
case m_bude:
{
match(m_headtype, sr);
term(sr);
WriteToMemory("!=/n");
continue;
}
case m_dede:
{
match(m_headtype, sr);
term(sr);
WriteToMemory("==/n");
continue;
}
case '>':
{
match(m_headtype, sr);
term(sr);
WriteToMemory(">/n");
continue;
}
case '<':
{
match(m_headtype, sr);
term(sr);
WriteToMemory("</n");
continue;
}
default:
return;
}
}
}
private void term(StreamReader sr)
{
factor(sr);
while (true)
{
switch (m_headtype)
{
case '*':
{
match(m_headtype, sr);
factor(sr);
WriteToMemory("*/n");
continue;
}
case '/':
{
match(m_headtype, sr);
factor(sr);
WriteToMemory("//n");
continue;
}
default:
return;
}
}
}
private void factor(StreamReader sr)
{
switch (m_headtype)
{
case '(':
{
match('(', sr);
expr(sr);
match(')', sr);
break;
}
case m_numtype:
{
WriteToMemory("push " + m_tmpvalue + "/n");
match(m_numtype, sr);
break;
}
case m_idtype:
{
WriteToMemory("rvalue "+m_tmpvalue+"/n");
match(m_idtype, sr);
break;
}
case m_strtype:
{
WriteToMemory("push " + m_tmpvalue + "/n");
match(m_headtype, sr);
break;
}
case '!':
{
match(m_headtype, sr);
match(m_headtype, sr);
WriteToMemory("oppvalue "+m_tmpvalue+"/n");
break;
}
case m_funtiontype:
{
WriteToMemory("function ");
WriteToMemory(m_tmpvalue);
WriteToMemory("/n");
match(m_headtype, sr);
break;
}
default:
CException.ScriptError("错误格式!");
break;
}
}
private string ReadStrLine(string s)
{
int tmpindex = s.IndexOf("/n",m_excuteline_index);
string tmpstr;
if (tmpindex != -1)
{
int readlength=tmpindex-m_excuteline_index;
tmpstr = s.Substring(m_excuteline_index,readlength);
m_excuteline_index = tmpindex+1;
}
else
{
return null;
}
return tmpstr;
}
二、现在我们的已把你的难以解释的脚本语言生成容易解释的中间代码,现在开始解释中间代码,为完成这个目标我们先看下中间语言的语法规则:
中间语法:
Push / 把/压栈
Lvalue /取值压栈
+ 弹出栈顶两个值相加后压栈
function 函数
等等
private void ExectStr()
{
string retstr;
retstr = ReadStrLine(m_zhongJingdaima);
while (retstr != null)
{
if (retstr.StartsWith("lvalue"))
{
string[] split = retstr.Split(new char[] { ' ' });
int index=LookUp(split[1]);
m_stack.push(Convert.ToString(index));
}
else if (retstr.StartsWith("function"))
{
string tmpvalue = retstr.Substring(9);
string pushvalue = null;
int paramstart = tmpvalue.IndexOf('(') + 1;
int paramend = tmpvalue.LastIndexOf(')');
int paramlength = paramend - paramstart;
string subparam = tmpvalue.Substring(paramstart, paramlength);
ArrayList split=new ArrayList();
while (true)
{
string paramstr="";
int startindex=0;
if (subparam.StartsWith("/""))
{
startindex = subparam.IndexOf("/"", 1);
if (startindex == -1)
{
break;
}
paramstr = subparam.Substring(1, startindex - 1);
startindex = subparam.IndexOf(",", startindex);
}
else
{
startindex = subparam.IndexOf(",", startindex);
if (startindex == -1)
{
paramstr = subparam;
}
else
{
paramstr = subparam.Substring(0, startindex);
}
int tmplookindex = LookUp(paramstr);
if (tmplookindex != -1)
{
paramstr = m_symtable[tmplookindex].value;
}
}
split.Add(paramstr);
if (startindex == -1)
{
break;
}
startindex++;
subparam = subparam.Substring(startindex);
}
if (tmpvalue.StartsWith("Strcat"))
{
pushvalue = m_globalFunction.Strcat(split[0].ToString(), split[1].ToString());
}
else if (tmpvalue.StartsWith("StartSplit"))
{
pushvalue = m_globalFunction.StartSplit(split[0].ToString(), split[1].ToString());
}
else if (tmpvalue.StartsWith("NextSplit"))
{
pushvalue = m_globalFunction.NextSplit(split[0].ToString(), split[1].ToString());
}
else if (tmpvalue.StartsWith("GetSplit"))
{
pushvalue = m_globalFunction.GetSplit(split[0].ToString(), split[1].ToString(), Convert.ToInt32(split[2]));
}
else
{
CException.ScriptError("错误格式!");
}
if (pushvalue != null)
{
m_stack.push(pushvalue);
}
}
else if (retstr.StartsWith("push"))
{
//int index=retstr.IndexOf(' ', 0);
string tmpstr = retstr.Substring(5);
m_stack.push(tmpstr);
}
else if (retstr.StartsWith("oppvalue"))
{
string[] split = retstr.Split(new char[] { ' ' });
int index = LookUp(split[1]);
bool tmpbool = !Convert.ToBoolean(m_symtable[index].value);
m_stack.push(Convert.ToString(tmpbool));
}
else if (retstr.StartsWith("rvalue"))
{
string[] split = retstr.Split(new char[] { ' ' });
int index = LookUp(split[1]);
m_stack.push(m_symtable[index].value);
}
else if (retstr.StartsWith(":="))
{
string rtmpvalue = m_stack.pop();
string ltmpvalue = m_stack.pop();
m_symtable[Convert.ToInt32(ltmpvalue)].value = rtmpvalue;
}
else if (retstr.StartsWith("+"))
{
string ltmpvalue = m_stack.pop();
string rtmpvalue = m_stack.pop();
string totalvalue = Convert.ToString(Convert.ToInt32(ltmpvalue) + Convert.ToInt32(rtmpvalue));
m_stack.push(totalvalue);
}
else if (retstr.StartsWith("*"))
{
string rtmpvalue = m_stack.pop();
string ltmpvalue = m_stack.pop();
string totalvalue = Convert.ToString(Convert.ToInt32(ltmpvalue) * Convert.ToInt32(rtmpvalue));
m_stack.push(totalvalue);
}
else if (retstr.StartsWith("/"))
{
string rtmpvalue = m_stack.pop();
string ltmpvalue = m_stack.pop();
string totalvalue = Convert.ToString(Convert.ToInt32(ltmpvalue) / Convert.ToInt32(rtmpvalue));
m_stack.push(totalvalue);
}
else if (retstr.StartsWith("-"))
{
string rtmpvalue = m_stack.pop();
string ltmpvalue = m_stack.pop();
string totalvalue = Convert.ToString(Convert.ToInt32(ltmpvalue) - Convert.ToInt32(rtmpvalue));
m_stack.push(totalvalue);
}
else if (retstr.StartsWith(">="))
{
string rtmpvalue = m_stack.pop();
string ltmpvalue = m_stack.pop();
if (Convert.ToInt32(ltmpvalue) >= Convert.ToInt32(rtmpvalue))
{
m_stack.push("true");
}
else
{
m_stack.push("false");
}
}
else if (retstr.StartsWith("!="))
{
string rtmpvalue = m_stack.pop();
string ltmpvalue = m_stack.pop();
if (Convert.ToInt32(ltmpvalue) != Convert.ToInt32(rtmpvalue))
{
m_stack.push("true");
}
else
{
m_stack.push("false");
}
}
else if (retstr.StartsWith("<="))
{
string rtmpvalue = m_stack.pop();
string ltmpvalue = m_stack.pop();
if (Convert.ToInt32(ltmpvalue) <= Convert.ToInt32(rtmpvalue))
{
m_stack.push("true");
}
else
{
m_stack.push("false");
}
}
else if (retstr.StartsWith(">"))
{
string rtmpvalue = m_stack.pop();
string ltmpvalue = m_stack.pop();
if (Convert.ToInt32(ltmpvalue) > Convert.ToInt32(rtmpvalue))
{
m_stack.push("true");
}
else
{
m_stack.push("false");
}
}
else if (retstr.StartsWith("<"))
{
string rtmpvalue = m_stack.pop();
string ltmpvalue = m_stack.pop();
if (Convert.ToInt32(ltmpvalue) < Convert.ToInt32(rtmpvalue))
{
m_stack.push("true");
}
else
{
m_stack.push("false");
}
}
else if (retstr.StartsWith("=="))
{
string rtmpvalue = m_stack.pop();
string ltmpvalue = m_stack.pop();
if (ltmpvalue == rtmpvalue)
{
m_stack.push("true");
}
else
{
m_stack.push("false");
}
}
else if (retstr.StartsWith("gofalse"))
{
string rtmpvalue = m_stack.pop();
if (!Convert.ToBoolean(rtmpvalue))
{
string[] split = retstr.Split(new char[] { ' ' });
string strtofind = split[1] + "/n";
int tmppos = m_zhongJingdaima.IndexOf(strtofind);
m_excuteline_index = tmppos;
}
}
else if (retstr.StartsWith("gotrue"))
{
string rtmpvalue = m_stack.pop();
if (Convert.ToBoolean(rtmpvalue))
{
string[] split = retstr.Split(new char[] { ' ' });
string strtofind = split[1] + "/n";
int tmppos = m_zhongJingdaima.IndexOf(strtofind);
m_excuteline_index = tmppos;
}
}
else if (retstr.StartsWith("goto"))
{
string[] split = retstr.Split(new char[] { ' ' });
string strtofind = split[1] + "/n";
int tmppos = m_zhongJingdaima.IndexOf(strtofind);
m_excuteline_index = tmppos;
}
retstr = ReadStrLine(m_zhongJingdaima);
}
}