词法分析

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


posted on 2016-08-11 10:48  内脏坏了  阅读(1442)  评论(0编辑  收藏  举报