青语言开发文档

第一步:字符串到Token

创建于 2023-04-26 / 最近更新于 2023-04-26 / 1849
字体: [默认] [大] [更大]

要执行一段代码,首先需要能够识别代码中的语法元素,所以我们需要对代码进行解析。

青语言代码的解析定义在 Lang/Parser.cs 文件中,首先定义了青语言中使用到的符号和关键字:

/*这里定义一般的符号*/
        public static List<string> Marks = new List<string>() { 
            ";;",";", ",", ",", "“", "”", "‘", "\"", "、"
        };

        /*定义语言使用的中文关键字*/
        public static List<string> KeyMarks = new List<string>() {
            "元", "空", "真", "假", "如果", "再则", "否则", "当",
            "执行", "直到", "返回", "跳出", "继续", "取反", 
            "异步", "等待", "尝试", "排查", "例行", "抛出", "遍历",
        };


        /*
         * 下面定义中缀运算符
         * 由于中缀运算符存在优先级差异
         * 分开定义
         */
        public static List<string> AddMarks = new List<string>() {
            "+=", "-=", "+",  "-", 
            "加等", "减等", "加", "减",
        };

        public static List<string> MulMarks = new List<string>() {
            "*=", "/=", "%=", "*", "/", "%",
            "乘等", "除等", "模等", "乘", "除以", "模",
        };

        public static List<string> CompareMarks = new List<string>() {
            ">=", "<=", "==", "!=", "<>",  ">", "<",
            "大于等于", "小于等于", "等于", "不等于", "大于", "小于",
        };

        public static List<string> LogicMarks = new List<string>() {
            "&&", "||", 
            "且", "或", 
        };

        public static List<string> SetMarks = new List<string>() {
            "=", ":", ":",
            "设为", "为",
        };

        /*定义集合类型的标记符号*/
        public static List<string> CollMarks = new List<string>() {
            "{", "}", "【", "】", "(", ")", "《", "》",
        };

由于我们使用中文进行编程,所以中文关键字和符号如果出现在变量名或函数名中,会出现歧义。从语法上我们要求,如果需要使用中文关键字和符号,那么其前后必须有空白字符或者其他符号作为分隔标记。
所以,中文关键字和符号我们不作为拆分代码的标记,而是使用上面定义的符号(包括中英文符号)外加任意空白字符,作为拆分代码的依据。

拆分代码字符串,讲其转化为Token序列,使用到的函数:

public List SliceText(string text, string src) {
List<Token> ret = new List<Token>(); //定义返回的Token列表
            int startIdx = 0; //当前片段的起点
            int idx = 0; // 当前遍历的序号
            //遍历
            while(idx < text.Length) {
           ……
}

这里我们遍历整个代码字符串,设置idx表示当前遍历的位置。
遍历的过程中,每次向后移动1个位置,然后判断当前位置是否以符号开头。如果当前位置以符号开头,那么就作为分隔标志,将符号之前的部分切割后放入新建的Token。

遍历的过程中,如果遇到字符串起始标记,由于字符串是特殊的语法元素,在切割字符串时,就直接作为一个整体,放到Token中,使用到的函数是:

public string TakeStr(string text, int idx) {
    ……
}

另外,整个遍历过程中,一旦发现换行,那么就对Parser对象中的line自增1,表示当前行号。每次生成新的Token时都记录其所处的行号,作为之后运行期间的异常提示信息。

最后,得到的Token序列还需要进行一个-号歧义的处理,因为-号既有可能表示减法,也有可能是负数的标记,所以在返回前我们进行一次处理,包括一些清理工作

for(int i=0; i<ret.Count; i++) {
                if (ret[i].Str == "-" && ((i == 0 && ret.Count > 1) || (i>0 && Marks.Contains(ret[i-1].Str) && i<ret.Count-1))) {
                    
                    ret[i].Str = ret[i].Str + ret[i+1].Str;
                    ret.Remove(ret[i+1]);
                    
                }else if (ret[i].Str == "," || ret[i].Str == ",") {
                    ret.Remove(ret[i]);
                    
                    if (i < ret.Count-1 && ret[i].Str == "-") {
                        ret[i].Str = ret[i].Str + ret[i+1].Str;
                        ret.Remove(ret[i+1]);
                    } else {
                        i--;
                    }
                }
            }

这里如果-号的前面也是符号或关键字,那么就认为应该是负数的标记,把它后一个Token合并。

另外,逗号在青语言中仅作为视觉分隔,本身不表示任何意义,所以这里把逗号都去除了。

到这里,我们就得到了字符串分割后的Token序列,每个Token中都保存对应的代码片段、所处行号和来源的文件。

0 人点赞过