编写简单的脚本解释器

时间:2021-08-13 16:36:25

 

首先声明一下以下文章是跟据我用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)、ifwhile等关键字

以下是此过程的代码:

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