c - C lexer, understanding documentation, preprocessing tokens
问题描述
My goal is to build a parser for a reasonable subset C and right now I'm at the start, implementing the lexer. Answers to a similar question on the same topic pointed towards the International Standard for C (700 pages of documentation) and the Yacc grammar webpage.
I would welcome any help with understanding the documentation: Is it true that the following picture from the documentation represents grammar rules, where the notation C -> (A, B)
means that all occurrences of AB
in that order get replaced by C
?
identifier -> identifier-nondigit | (identifier,identifier-nondigit) | (identifier,digit)
identifier-nondigit -> nondigit | universal-character-name | other
digit -> 0 | 1 | 2 | ... | 9
non-digit -> _ | a | b | ... | z | A | ... | Z
I think I am confused because the documentation introduces 'preprocessing tokens' which I thought would be just labels of sequences of characters in the source produced without backtracking.
I.e. something like:
"15647 \n \t abdsfg8rg \t" -> "DWLDLW"
// D .. digits, W ... whitespace, L ... letters
It seems like the lexer is doing the same thing as the parser (just building a tree). What is the reason for introducing the preprocessing tokens and tokens?
Does it mean that the processing should be done 'in two waves'?
I was expecting the lexer to just use some regular expressions and maybe a few rules. But it seems like the result of lexing is a sequence of trees that can have the roots keyword, identifier, constant, string-literal, punctuator
.
Thank you for any clarifications.
解决方案
我想我很困惑,因为文档引入了“预处理标记”,我认为这只是源中产生的字符序列的标签,没有回溯。
预处理标记是 C 预处理器的输入。在将 C 源代码转换为可执行文件的过程中,预处理标记和中间空格首先由预处理器操作,然后预处理标记和空格的结果流在转换为(标准的词;也许“重新解释为”更好地传达了这个想法)一个标记流。语言标准的第 5.1.1.2 节对所有这些进行了概述。
从预处理标记到标记的转换是一个非常简单的映射:
- 标识符-->标识符或枚举常量(选择是上下文相关的,但在实践中可以通过避免在语义分析之前进行区分来解决)。
- pp-number --> integer-constant或floating-constant,视情况而定(constant 的两种选择)
- character-constant --> character-constant (常量的替代方案之一)
- 字符串文字-->字符串文字
- 标点符号-->标点符号
- 删除预处理指令后剩余的任何其他内容 --> 一个或多个单字符标记
请注意,预处理标记是一种特殊情况:每种形式的标头名称与预处理标记的其他可能标记化header-name
之间存在歧义。最好避免将任何内容分析为标头名称,除非在指令的上下文中,然后您也不必担心将标头名称预处理标记转换为常规标记,因为它们都不会在删除预处理指令。#include
词汇分析的更多细节在标准的第 6.4 节及其小节中提供。
似乎词法分析器正在做与解析器相同的事情(只是构建一棵树)。
我不明白你是如何得出这个结论的,但词法分析和解析之间的区别确实是人为的。没有必要这样划分语言分析过程,但事实证明它通常很方便,无论是编码还是计算。
引入预处理令牌和令牌的原因是什么?
基本上,标准 C 实际上是两种语言合二为一:预处理语言和 C 语言本身。早期,它们由完全不同的程序处理,预处理是可选的。预处理器对它所操作的单元有一个视图,这与 C 语法的分类并不完全一致。 Preprocessing-tokens是预处理器的语法分析和数据的单位,而tokens是C 的语法分析的单位。
这是否意味着应该“分两波”进行处理?
从逻辑上讲,是的。在实践中,我认为大多数编译器将两个逻辑传递集成到一个通过源的物理传递中。
推荐阅读
- python - 我怎样才能让程序在我的习惯跟踪器上跟踪完成的天数?
- winforms - webbrowser 控件不能左对齐显示表格文本?
- c - For 循环随着计数器的增加而跳过
- c# - “xsi:type”属性改为“type” c# xml文档
- javascript - hasOwnProperty 未定义
- spring - 配置对象在测试类中为空
- django - 使用 django 中应用的令牌身份验证方法,我无法使用“POST”方法访问功能
- php - 使用数据表导入数据时出错
- reactjs - 反应 Form.Control 模型/值映射
- sql-server - 将格式化为 20200321 的 bigint 值转换为格式化为 '2020-03-21 00:00:00' 的日期时间