首先声明一下以下文章是跟据我用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)变量:本脚本只有全局变量,我们使用一个结构符号表(另外这个表还保存着关键字等)来保存变量名、类型(说明是变量、关键字等)和值。以下是此符号表的定义:

publicstruct

{

publicstring

publicstring

publicint

}

Symtable[] m_table=new Symtable[max_size];

max_size的大小说明了本脚本可以定义的变量数有多少。

2、词法分析:

在这个阶段解释器把读取的字符分为以下类型:

(1)、变量

(2)、数字

(3)、字符串

(4)、+、-、*、/等符号

(5)、if、while等关键字

以下是此过程的代码:

 

private int lexan(StreamReader
        {
            m_tmpvalue = "";
            char
            while (true)
            {
                t = (char)sr.Read();
                if
                {
                    return
                }
                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
                }
                else if
                {
                    while
                    {
                        m_tmpvalue += t;
                        t = (char)sr.Peek();
                        if
                        {
                            sr.Read();
                        }
                    }
                    int
                    if
                    {
                        p = InertTable(m_tmpvalue, m_idtype);
                    }
                    return
                }
                else if
                {
                    return (int)t;
                }
                else if
                {
                    char tmpc = (char)sr.Peek();
                    if (tmpc == '=')
                    {
                        sr.Read();
                        if (t == '>')
                        {
                            return
                        }
                        else if (t == '<')
                        {
                            return
                        }
                        else if (t == '!')
                        {
                            return
                        }
                        else if (t == '=')
                        {
                            return
                        }
                        else
                        {
                            CException.ScriptError("格式错误!");
                        }
                    }
                    return (int)t;
                }
                else if (t == '/"')
                {
                    t=(char)sr.Read();
                    while (t != '/"')
                    {
                        m_tmpvalue += t;
                        t = (char)sr.Read();
                    }
                    return
                }
                else
                {
                    CException.ScriptError("错误格式");
                    return
                }
            }
      }

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);
            }
        }