#P16420. pm_1956脚本语言
pm_1956脚本语言
题目背景
你正在为一门简单的脚本语言编写编译器中的静态检查模块。程序在真正执行之前,编译器需要分析代码的控制流和变量初始化情况,并给出可能存在问题的警告。
这门语言非常精简:每行恰好是一条语句,只包含参数声明、赋值、条件分支和返回语句。你的任务是找出永远无法执行的代码,以及可能在初始化之前就被使用的变量。
题目描述
给定一个语法正确的脚本函数,共有 行代码。代码行从 开始编号。
脚本语言中只有以下语句:
<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> 是行号。
ELSE 和 END IF 本身不会生成机器指令,因此即使它们位于不可达区域,也不输出不可达警告。
若某一行是不可达代码,则这一行只输出不可达警告,不再检查变量初始化问题。
2. 变量可能未初始化
若某一行使用了变量 <variable>,并且存在至少一条能够到达该行的执行路径,使该变量此前没有被初始化,则输出:
Line <line>: variable <variable> might not have been initialized
变量在以下两种情况下被视为已经初始化:
- 它出现在第一行的参数列表中;
- 它曾经作为赋值语句左侧的变量被赋值。
变量会在以下位置被“使用”:
IF条件中;- 赋值语句右侧;
RETURN后面。
赋值语句左侧的变量不算被使用。例如在 E = T 中,即使 T 未初始化,这一行执行后,E 仍被视为已经初始化;编译器只需要对 T 给出警告。
同一行中同一个变量即使出现多次,也只输出一次警告。
条件判断规则
进行静态分析时,必须认为每个 IF 条件都可能为真,也可能为假,并且不能利用之前执行过的赋值或条件来推断结果。
例如,即使此前执行了 A = 4,语句 IF A < 4 THEN 的真假两种分支仍都必须考虑。
输出顺序
所有警告首先按行号从小到大排序。
若同一行有多个变量可能未初始化,则按变量名的字典序从小到大输出。
输入格式
第一行包含一个整数 ,表示代码行数。
接下来 行,每行包含脚本函数的一行代码。
输入代码保证语法正确。每行没有行首或行尾空格,相邻记号之间恰好有一个空格。
输出格式
第一行输出一个整数 ,表示警告条数。
接下来 行,按照规定顺序输出所有警告。
若没有任何警告,只输出一行 0。
数据范围
- ;
- 第一行且仅第一行是
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 没有任何警告。