A. 解释器

内存限制:512 MiB 时间限制:1000 ms 标准输入输出
题目类型:传统 评测方式:文本比较

题目描述

20xx年,没书读的 Bardisk 考了本教资当起了小学老师。

今天他要给同学们关于科普图灵机的常识,为形象生动起见,他搬来了一个「图灵机教具」。

实际的图灵机的行为很难被同学们直观理解,因此这一个教具并不是一个严格意义上的图灵机,而只是一个与之相似的模型。

具体来说,该模型具备两条可以滚动、且嵌在纸带筒里的纸带(注意不是纸环,不能无限滚动下去,也不会循环)。每个纸带筒上有一个固定不动的指针,指向纸带筒的中间位置(以及目前停留在纸带筒中间位置的那段纸带)。因此,随着纸带的滚动,指针指向的纸带位置也会不断改变。同时,每条纸带被分成 512 个长度一样的格子,而在初始状态时,纸带筒上的指针指向纸带最左侧的一格。

不过,这两条纸带上的内容并不一样——其中一条纸带叫做「program」,即程序纸带。另一条纸带叫做「data」,即数据纸带。程序纸带的每一格上都画着一个符号,而数据纸带的每一格上都写着两位的十六进制数。起初,程序纸带由用户写好,而数据纸带上全部都是 0。

模型还有一个可供写入的纸板,和一个纸板输出口,以及一个模式转换开关,对应「数字模式」和「字符模式」,初始时模型在数字模式。

接下来,为了描述简便,我们将当前数据纸带筒指针指向的数字称为「当前数字」,并将当前程序纸带筒指针指向的符号称为「当前符号」,将目前模式转换开关状态对应的模式称为「当前模式」。

当模型开始运行时,模型会首先检查当前符号,根据其类型进行操作:

  • +:模型会计算出当前数字 的结果,然后将这个数覆写到纸带的对应位置上。
  • -:模型会计算出当前数字 的结果,然后将这个数覆写到纸带的对应位置上。
  • <:模型会将数据纸带顺时针(指针相对纸带向左移动)转动一格。
  • >:模型会将数据纸带逆时针(指针相对纸带向右移动)转动一格。
  • [:模型会检查当前数字。如果当前数字非零,那么什么也不做;否则,模型会一直逆时针滚动程序纸带,直到碰到程序纸带上与这个 [ 相匹配的 ] 才停止(注意此处的匹配指的是滚动到此处时,指针遇见的 [ 和 ] 一样多)。
  • ]:模型会检查当前数字。如果当前数字为零,那么什么也不做;否则,模型会一直顺时针滚动程序纸带,直到碰到程序纸带上与这个 ] 相匹配的 [ 才停止(注意此处的匹配指的是滚动到此处时,指针遇见的 [ 和 ] 一样多)。
  • .:如果当前模式是字符模式,那么模型会根据 ASCII 码表把当前数字编码为 ASCII 字符后写到一个纸板上,并从纸板出口排出;如果当前模式是数字模式,模型会把当前数字直接写在纸板上排出。
  • ,:如果当前模式是字符模式,那么模型会首先通知用户在输入纸板处写下一个字符,此后模型将写下的字符按 ASCII 解码为数字覆写到当前数字对应的纸带位置上;如果当前模式是数字模式,那么模型会通知用户写下一个两位的十六进制数字,然后将它覆写到当前数字对应的纸带位置上。
  • #:模型会拨动模式转换开关,改变当前的模式。
  • !:终止运行并复位。

在按照当前符号做完上述操作以后,模型会逆时针滚动一次程序纸带,使得指针相对纸带向右移动一格。

当然,模型在运行时会由于程序纸带没有被妥善编写而出现错误,在出现错误以后,模型会直接通知用户发生了错误和发生的错误类型,随后停止运行并复位纸带。可能的错误类型有:

  • DATA_OUT_OF_BOUND:数据纸带无法继续滚动(在指针抵达边界的情况下向边界方向继续移动)。
  • PROGRAM_OUT_OF_BOUND:程序纸带无法继续滚动(在指针抵达边界的情况下向边界方向继续移动)。
  • INVALID_USER_INPUT:根据当前模式,用户的输入是非法的。
  • UNEXPECTED_END_OF_INPUT:请求输入,但用户不再给出输入。
  • NOT_PRINTABLE:在字符模式下,输出解码得到的字符不是一个 ASCII 可打印字符(\n 除外)。

Bardisk 不擅长编程,尤其是面对这种神秘的「编程语言」,更是无所适从,因此他首先希望你编写一个解释器,能够在给定程序纸带内容时,模拟模型的运行并给出模型在停机或报错前的输出。

输入格式

首先输入从最左侧开始程序纸带的内容,保证以 ! 结尾,注意可能出现仅供可读性的空格和回车,你需要将它们忽略。

在 ! 出现的下一行开始,给出的是用户对模型的输入(模型也可以没有任何输入)。

如果当前是字符模式,你可以直接从输入流中读取一个字符作为输入。

如果当前是数字模式,你可以从输入流中读取两个字符作为输入的十六进制数,模型只允许输入两位十六进制数(允许前导零),而且字母必须是大写。

注意,你在不可以任意多读一个字符,哪怕是空白符,因为这有可能是对模型的输入。

保证输入的程序纸带长度不会超限。

保证 ! 后直到下一行为止没有非空白符。

输出格式

要求记录下模拟的模型的(报错前的)输出和报错,并输出。

具体来说,当模型处于字符模式时,原样记录下模型的输出;当模型处于数字模式时,将模型输出的数字转换为二位十六进制数字(不足补零)输出,字母必须是大写。

在每次输出换行符之前,请多输出一个 $ 来标记行末。

如果模型报错,另起一行(即使这一行还什么也没有,此时也要输出 $ 表示上一行的末尾)输出模型的报错类型,详见题目描述。

多余的输入可以被忽略。

在程序正常结束以后,输出一个 $ 表示输出已经结束。

样例

样例输入 1

>,>,>,>,>,
>,>,>,>,>,
>,>,>,>,>,
>,>,>,>,>,
>,>,>,>,>,
#[.<]!
21676E69766C6F53206D656C626F7250202C657962646F6F47

样例输出 1

Goodbye, Problem Solving!$

样例输入 2

>,>,[<+>-]<<

>.<!
2F3D

样例输出 2

6C$

样例输入 3

,#.,.,#.,.!
20ab

样例输出 3

 a62$
UNEXPECTED_END_OF_INPUT

数据范围与提示

总共有 10 个数据点,其中:

第 1 ~ 4 个测试点的程序都是计算两个数之和,纯数字模式,可能出现非法输入异常或程序纸带越界异常。

第 5 ~ 6 个测试点的程序是判断两个整数是否相同,可能出现输入不足异常。

第 7 ~ 8 个测试点的程序是计算两个数之积,可能出现无法打印异常。

第 9 个测试点是一个只有 +、>、# 和 . 的测试输出的程序。

第 10 个测试点测试数据纸带越界异常。

对于全部数据,保证输入的总长度不超过 ,且在模拟正确的情况下,程序能够停机,在整个模拟过程中,程序纸带筒滚动的格数不超过 。