题解:P11738 [集训队互测 2015] 未来程序·改

· · 题解

解法构建

本题要求实现一种语法类似于 C++ 的编程语言。由于数据规模较小,最方便的方法之一是解释执行。

考虑按照以下步骤:

  1. 拆分,将代码划分为若干片段,称为 Token,如 intmain(>=123456。其中一种实现方式是同时标注类型,如 keywordnumber
  2. 语法解析,从前往后遍历拆分后的代码,通过分辨当前位置和邻近位置的 Token 类型判断属于哪种语法,自顶向下递归构建抽象语法树(AST)用于存储信息。
  3. 解析执行,自顶向下使用专用的执行器模拟执行不同类型的 AST 节点表示的逻辑。

功能分析

第一步中,考虑每次产生一个类型枚举和内容字符串组成的对。

第二步中,不妨将所有需要实现的功能分为语句和表达式,且前者包含后者。其中一种分类方式是:

鉴于 cincoutendl 都不是整形,不会参与到其他表达式中,不妨认为 cincout 是普通语句而不是表达式,endl 只需要在待输出列表中添加任意能区分于其他表达式的值,比如空指针。

再次将表达式分为前缀和中缀,其中前者可以由第一个 Token 确定类型,而后者需要通过不位于开头的某个 Token 确定类型。具体地:

解析表达式时,一种常见的写法是:指定一个优先级,先读一个前缀,再不停读中缀,直到后面没有中缀或运算优先级低于(右结合则为不高于)当前优先级,并根据中缀选择合适的语法类型,将当前前缀传递给对应解析器,并将返回值作为新的前缀。读取整个表达式时,应该指定最低的优先级。

第三步中,需要实现整形和数组两种类型。要解决的问题之一是左值的实现,变量(包括数组的某一位)均为可赋值的。

我选用的写法是:将整形显式分为左值和右值,前者存储指针,后者存储值;数组压成一维并存储指针,保存每一维下标加一的偏移量,所有维均完成偏移后返回整形左值。由于本题中变量的生命周期始终跟随作用域,故可以考虑将整形左值和数组均存储于虚拟栈中。

需要注意,作用域之间并不是独立的,而是内层作用域会基于外层作用域扩充内容。

另外,函数可以保存其在 AST 中的根节点和参数列表。鉴于函数不会被存为函数指针与其他变量混用,可以单独存一张表。

实现细节

参考代码

详见 https://www.luogu.com.cn/paste/4i8pzhla。