• Token的设计(2)


    词法分析

    Token的几个种类

    前端的第一步就是词法分析, 这个过程通俗来讲就是将源代码转化为一串Tokens. 所以首先应该想到的是, 到底该有哪几种类型的Token ? 关于这个问题我已经想过了, 该语言将会有如下几种Token.

    enum Token_Type{
        INT,            //      0|([1-9][0-9]*)
        FLOAT,          //      (0|([1-9][0-9]*).[0-9]+)
        STRING,
        IDENTIFIER,      //      [a-zA-Z_][0-9a-zA-Z_]*
        KEYWORD,
        OPERATOR,       // + - * / += -= *= /= = == ! - && ||
        BRACKET,        // () {} [] 
    };	
    

    你可以看到, 该语言其实只有三种基本类型, 我个人不打算支持bool因为我感觉bool底层实现就是整形, 所以并没有支持的必要. 另一方面这里面的intfloat如果过长的话可能会导致编译出错, 我个人只打算使用C++中的longdouble来解析这两个类型, 所以其实他们的长度也是有限制的. 另外KEYWORD在分析阶段会归入IDENTIFIER中, 等到真正构造Token的时候才会划分开来, 这样可以降低词法分析的难度.暂时的话我就想到这几种, 如果不够的话再临时加上吧.

    Token的属性以及构造函数

    接下来我们可以整体看一下Token这个类 :

    #ifndef FRED_TOKEN_H
    #define FRED_TOKEN_H
    
    #include <string>
    
    class Token{
    public:
        enum Token_Type{
            INT,            //      0|([1-9][0-9]*)
            FLOAT,          //      (0|([1-9][0-9]*).?[0-9]+)
            STRING,
            IDENTIFIER,      //      [a-zA-Z_][0-9a-zA-Z_]*
            KEYWORD,
            OPERATOR,       // + - * / += -= *= /= = == ! - && ||
            BRACKET,        // () {} []
        };
    
    private:
        Token_Type type;
        union Value{
            long l;
            double d;
            std::string s;
        };
    
        Value value;
    
    public:
        Token(Token_Type type, const std::string string): type(type) {
            switch (type){
                case INT:{
                    value.l = parseInt(string);
                    break;
                }
                case FLOAT:{
                    value.d = parseFloat(string);
                    break;
                }
                case IDENTIFIER:{
                    if(isKeyword(string)){
                        type = IDENTIFIER;
                    }
                    //no break here, auto jump into default
                }
                default:{
                    value.s = string;
                }
            }
        }
    
        Token(const Token&) = delete;
        Token(const Token&&) = delete;
        Token& operator=(const Token& rhs) = delete;
        Token& operator=(const Token&& rhs) = delete;
        virtual ~Token() = default;
    
        Token_Type getType() const { return type; }
    
        const Value& getValue() const { return value; }
    
    private:
        long parseInt(const std::string&);
        long parseFloat(const std::string&);
        bool isKeyword(const std::string&);
    };
    
    #endif //FRED_TOKEN_H
    
    1. Token的值之所以使用了union类型, 是因为对于intfloat, 这里实际上只需要关注他们的真实值这里使用的是longdouble来表示, 但是对于其他类型则仍然选择用字符串表示. 我也考虑过在Token上衍生出两个子类的做法, 但是考虑到子类getValue返回值不统一的情况, 所以这里只能选择用union, 这里我 自己也感觉有点别扭, 所以如果有更好的设计方式或者想法欢迎留言.
    2. 另外你可以看到, 正如我前面所说的我是在构造函数中将KEYWORDIDENTIFIER中分离出来的思路, 这样可以简化分析.

    完成了这些之后另外三个工具函数很简单 :

    #include "Token.h"
    #include <cmath>
    
    long Token::parseHelper(const std::string& str, size_t& idx){
        long sum = 0;
        for(size_t i = 0; i != idx; ++i){
            if(str[i] == '.'){
                idx = i;
                return sum;
            }
            sum = sum * 10 + (str[i] - '0');
        }
        return sum;
    }
    
    long Token::parseInt(const std::string& str){
        size_t len = str.length();
        return parseHelper(str, len);
    }
    
    double Token::parseFloat(const std::string& str){
        double sum = 0;
        size_t len = str.length(), idx = len;
        sum += parseHelper(str, idx);
        len -= ++idx;
        sum += parseHelper(str.substr(idx), len) / pow(10, len);
        return sum;
    }
    
    inline bool Token::isKeyword(const std::string& str) {
        return  str == "int"     ||
                str == "float"   ||
                str == "string"  ||
                str == "print"   ||
                str == "while"   ||
                str == "for"     ||
                str == "class"   ||
                str == "return";
    }
    
    
    
  • 相关阅读:
    margin塌陷(collapse)
    this的值
    变量、函数声明提升
    Git与Svn的区别—笔记1
    ECMAScript 总结
    正则表达式
    i2c 通信
    player/stage 学习---安装
    各种分区类型对应的partition_Id
    ubuntu 映射网络驱动器到本地
  • 原文地址:https://www.cnblogs.com/nzhl/p/5760111.html
Copyright © 2020-2023  润新知