编译原理-词法分析
发布日期:2021-05-20 04:47:44 浏览次数:12 分类:精选文章

本文共 4589 字,大约阅读时间需要 15 分钟。

词法分析实验——Decaf语言的语法解析

词法分析的核心任务

词法分析是编程语言解析的第一步,主要通过从左到右读取源程序的字符流,识别出有意义的词法单元。这些词法单元包括保留字、标识符、常量、运算符、界符等。在识别出每个单词的同时,词法分析程序还需要验证词法的正确性。

识别出的词法单元会被记录为单词记录,该记录通常包含两个部分信息:

  • 单词符号 (Token):对应具体意义的词法单元类型,如标识符、整数等。
  • 属性值 (Attribute):记录单词的相关属性值,如标识符的名称、常量的值等。
  • 在大多数编程语言中,词法单元的类别可以归纳为以下几类:

    • 保留字:如 ifwhilestruct 等,这些是编程语言的关键操作符。
    • 标识符:用于表示变量名、函数名、常量名等,如 variableNameconstantNamefunctionName 等。
    • 常量:如整数 25、浮点数 3.1415、字符串常量 "ABC" 等。
    • 运算符:如 +-*/= 等。
    • 界符:如逗号 ","、分号 ";"、括号 () 等。

    某些编程语言的构词规则如下:

  • 无符号整数<数字> ::= <数字> { <数字> }
  • 标识符<标识符> ::= <字母> { <字母> | <数字> }
  • 保留字<保留字> ::= const | var | procedure | begin | end | odd | if | then | call | while | do | read | write
  • 运算符<运算符> ::= + | - | * | / | = | # | < | <= | > | >= | :=
  • 界符<界符> ::= ( | ) | , | ; | { | }
  • 实验内容

    对于给定的Decaf程序片段:

    class Main {    static void main() {        Print("hello world");    }}

    设计一个词法分析器,实现上述语法规则。词法分析结果如下:

    保留字    class分隔符 { 分隔符 } 保留字    static分隔符 void 分隔符 main 分隔符 ( 分隔符 )分隔符 { 分隔符 } 分隔符 static分隔符 void 分隔符 main 分隔符 (分隔符 )分隔符 Print 分隔符 ( 分隔符 "hello world" 分隔符 )分隔符 ;

    代码实现

    #include 
    #include
    #include
    #include
    #include
    #include
    using namespace std;// 存储标识符可用字符vector
    chars;vector
    digits;char tmp;vector
    delimiters = { "(", ")", ",",";", "{","}" };// 初始化字符集合void Initialize() { char tmp = 'A'; for (int i = 0; i < 26; ++i, tmp++) { chars.push_back(tmp); } char tmp = 'a'; for (int i = 0; i < 26; ++i, tmp++) { chars.push_back(tmp); }}// 判断是否为保留字bool IsKeyword(string s, const vector
    & keywords) { for (const string& word : keywords) { if (s == word) { return true; } } return false;}// 判断是否为运算符bool IsOperator(string s) { return find(operators.begin(), operators.end(), s) != operators.end();}// 判断是否为分隔符bool IsDelimiter(string s) { return find(delimiters.begin(), delimiters.end(), s) != delimiters.end();}// 判断是否是标识符bool IsIdentifier(string s) { if (s.empty()) { return false; } if (!IsKeyword(s, keywords)) { if (s[0] in chars && s.size() < 1 + (s.size() - 1)) { // 检查是否有分隔符 for (char c : s) { if (IsDelimiter(string(1, c))) { return false; } } } bool allLettersOrDigits = true; for (char c : s) { if (find(chars.begin(), chars.end(), c) == chars.end() && !IsKeyword(string(1, c), keywords)) { return false; } } return true; } return false;}class Tokenizer {public: Tokenizer(const vector
    & reservedWords, const vector
    & operators, const vector
    & delimiters); void Analyze(const string& input); vector
    > GetResults() const { return res; }private: vector
    > res; const vector
    & keywords; const vector
    & operators; const vector
    & delimiters; // 判断是否字符串是保留字 bool IsKeyword(const string& s) const { return IsKeyword(s, keywords); } bool IsOperator(const string& s) const { return IsOperator(s); } bool IsDelimiter(const string& s) const { return IsDelimiter(s); } bool IsIdentifier(const string& s) const { return IsIdentifier(s); }};Tokenizer::Tokenizer(const vector
    & reservedWords, const vector
    & operators, const vector
    & delimiters): keywords(reservedWords), operators(operators), delimiters(delimiters) {}void Tokenizer::Analyze(const string& input) { string currentToken; int i = 0; int start = 0; bool inString = false; bool inStringFirst = false; for (; i < input.size(); ++i) { char c = input[i]; if (c == '"' && !inString) { inString = true; inStringFirst = true; start = i; } else if (c == '"' && inString) { inString = false; } if (inString) { currentToken += c; continue; } // 处理空格分隔 if (c == ' ' && !inString) { if (!currentToken.empty()) { AnalyzeToken(currentToken); currentToken.clear(); if (c == ' ' && start == i - 1) { // 处理前导空格 } } } } // 处理剩余的当前Token if (!currentToken.empty()) { AnalyzeToken(currentToken); currentToken.clear(); }}void Tokenizer::AnalyzeToken(const string& s) { if (IsKeyword(s)) { res.push_back({"保留字", s}); return; } if (IsOperator(s)) { res.push_back({"运算符", s}); return; } if (IsDelimiter(s)) { res.push_back({"分隔符", s}); return; } if (IsIdentifier(s)) { res.push_back({"标识符", s}); return; } // 处理保留字前后内容}int main() { // 初始化字符集合 Initialize(); // 初始化文件流 ifstream infile; infile.open("exp1.txt", ios::in); if (!infile.is_open()) { cout << "无法打开文件" << endl; return 1; } // 读取输入 string buffer = ""; string line; while (getline(infile, line)) { buffer += line; } Tokenizer tokenizerください您提供保留字、运算符和分隔符的列表; tokenizer.Analyze(buffer); // 生成输出 ofstream output("out.txt"); for (const auto& p : tokenizer.GetResults()) { output << p.first << " " << p.second << endl; } output.close(); infile.close(); return 0;}

    ##-reference如果想要了解更多关于词法分析的实现细节,可以参考以下资料:

  • 《计算机编程语言--语法与词法分析》
  • 《编程语言技术》
  • 词法分析是语言解析的基础,理解其原理对于编写高效且灵活的解析器至关重要。

    上一篇:HTML5从零开始
    下一篇:Intent和IntentFilter

    发表评论

    最新留言

    路过按个爪印,很不错,赞一个!
    [***.219.124.196]2025年05月07日 15时25分36秒