#P16420. pm_1956脚本语言

pm_1956脚本语言

题目背景

你正在为一门简单的脚本语言编写编译器中的静态检查模块。程序在真正执行之前,编译器需要分析代码的控制流和变量初始化情况,并给出可能存在问题的警告。

这门语言非常精简:每行恰好是一条语句,只包含参数声明、赋值、条件分支和返回语句。你的任务是找出永远无法执行的代码,以及可能在初始化之前就被使用的变量。

题目描述

给定一个语法正确的脚本函数,共有 NN 行代码。代码行从 11 开始编号。

脚本语言中只有以下语句:

<line>       ::= <head> | <assignment> | <if> | ELSE | END IF | <return>
<head>       ::= PARAM <paramlist> | PARAM
<assignment> ::= <variable> = <rvalue>
<if>         ::= IF <variable> <relation> <value> THEN
<return>     ::= RETURN <value>
<paramlist>  ::= <variable> | <variable> <paramlist>
<rvalue>     ::= <value> | <value> <operator> <value>
<value>      ::= <variable> | <integer>
<operator>   ::= + | - | * | /
<relation>   ::= < | = | >
<variable>   ::= A | B | ... | Z
<integer>    ::= 一个没有多余前导零的 32 位有符号整数

每个变量名都是一个大写英文字母,并保存一个 32 位有符号整数。

第一行一定是函数头 PARAM

  • PARAM 后列出的变量是函数参数,进入函数时已经初始化;
  • PARAM 后也可以没有任何变量。

每个 IF 都有与之匹配的 END IF,中间可以有一个 ELSE。条件语句可以嵌套,并且一定合法匹配。

你需要生成以下两类警告。

1. 不可达代码

若某一行无论各个 IF 条件如何取值,都不可能被执行,则输出:

Line <line>: unreachable code

其中 <line> 是行号。

ELSEEND IF 本身不会生成机器指令,因此即使它们位于不可达区域,也不输出不可达警告。

若某一行是不可达代码,则这一行只输出不可达警告,不再检查变量初始化问题。

2. 变量可能未初始化

若某一行使用了变量 <variable>,并且存在至少一条能够到达该行的执行路径,使该变量此前没有被初始化,则输出:

Line <line>: variable <variable> might not have been initialized

变量在以下两种情况下被视为已经初始化:

  1. 它出现在第一行的参数列表中;
  2. 它曾经作为赋值语句左侧的变量被赋值。

变量会在以下位置被“使用”:

  • IF 条件中;
  • 赋值语句右侧;
  • RETURN 后面。

赋值语句左侧的变量不算被使用。例如在 E = T 中,即使 T 未初始化,这一行执行后,E 仍被视为已经初始化;编译器只需要对 T 给出警告。

同一行中同一个变量即使出现多次,也只输出一次警告。

条件判断规则

进行静态分析时,必须认为每个 IF 条件都可能为真,也可能为假,并且不能利用之前执行过的赋值或条件来推断结果。

例如,即使此前执行了 A = 4,语句 IF A < 4 THEN 的真假两种分支仍都必须考虑。

输出顺序

所有警告首先按行号从小到大排序。

若同一行有多个变量可能未初始化,则按变量名的字典序从小到大输出。

输入格式

第一行包含一个整数 NN,表示代码行数。

接下来 NN 行,每行包含脚本函数的一行代码。

输入代码保证语法正确。每行没有行首或行尾空格,相邻记号之间恰好有一个空格。

输出格式

第一行输出一个整数 MM,表示警告条数。

接下来 MM 行,按照规定顺序输出所有警告。

若没有任何警告,只输出一行 0

数据范围

  • 2N502 \le N \le 50
  • 第一行且仅第一行是 PARAM 语句;
  • 参数列表中的变量互不相同;
  • 最后一行一定是 RETURN 语句;
  • 每个 IF 都有匹配的 END IF,并且至多有一个匹配的 ELSE
  • IF 中比较的左侧一定是变量,右侧是整数或另一个不同的变量;
  • 所有整数均在 32 位有符号整数范围内,并且没有多余前导零。

样例 1

输入

10
PARAM A B
IF A > 5 THEN
C = B * A
END IF
D = B - C
Z = Y + X
E = T
F = E + E
V = G + G
RETURN F

输出

5
Line 5: variable C might not have been initialized
Line 6: variable X might not have been initialized
Line 6: variable Y might not have been initialized
Line 7: variable T might not have been initialized
Line 9: variable G might not have been initialized

样例 2

输入

4
PARAM G
RETURN G
B = K
RETURN C

输出

2
Line 3: unreachable code
Line 4: unreachable code

样例 3

输入

21
PARAM T C
B = T
A = 4
IF A < 4 THEN
IF B > 3 THEN
Q = 100 + F
ELSE
IF C = -1111111111 THEN
Q = T - A
IF Q = 0 THEN
V = V - 1
END IF
ELSE
RETURN I
E = A
END IF
END IF
ELSE
Q = 1
END IF
RETURN Q

输出

4
Line 6: variable F might not have been initialized
Line 11: variable V might not have been initialized
Line 14: variable I might not have been initialized
Line 15: unreachable code

样例 4

输入

16
PARAM
IF A > 0 THEN
ELSE
END IF
IF A > 0 THEN
END IF
IF A > 0 THEN
A = 2
ELSE
IF A > 0 THEN
END IF
A = 3
END IF
IF A < 0 THEN
END IF
RETURN A

输出

4
Line 2: variable A might not have been initialized
Line 5: variable A might not have been initialized
Line 7: variable A might not have been initialized
Line 10: variable A might not have been initialized

样例 5

输入

25
PARAM I J K L T
IF I > 10 THEN
IF I < 100 THEN
IF J > 10 THEN
IF J < 100 THEN
IF K > 10 THEN
IF K < 100 THEN
IF L > 10 THEN
IF L < 100 THEN
A = I + J
B = K + L
C = A + B
RETURN C
IF T > 4 THEN
ELSE
END IF
END IF
END IF
END IF
END IF
END IF
END IF
END IF
END IF
RETURN -1

输出

1
Line 14: unreachable code

样例 6

输入

6
PARAM A
A = A + A
A = A * A
A = A - A
A = A / A
RETURN A

输出

0

样例说明

  • 样例 1 中,若第 2 行条件为假,则 C 在第 5 行可能尚未初始化。同一行中的 G + G 只会产生一条关于 G 的警告。
  • 样例 2 中,第 2 行已经返回,因此第 3、4 行永远无法执行;不可达行不再输出未初始化变量警告。
  • 样例 3 中,即使第 3 行将 A 赋值为 4,仍必须同时考虑第 4 行条件为真和为假。
  • 样例 5 中,第 13 行无条件返回,因此它后面的第 14 行在所在分支内不可达。
  • 样例 6 没有任何警告。