小さな言語で書かれた n 行のプログラムを、実行せずに検査する。 この言語の変数は宣言しただけでは値がなく、値を入れた後でなければ使えない。 命令は8種類で、1行に1つずつある。 - var x: 変数 x を宣言する。 まだ値はない。 - set x: x に値を入れる。 - use x: x の値を使う。 - if: ブロックを開く。 このブロックは実行されることも、飛ばされることもある。 - else: if 部分を閉じて else 部分を開く。 else のある if は、2つの部分のうちちょうど1つだけが実行される。 - loop: ブロックを開く。 このブロックは 0 回以上繰り返される。 - end: いちばん内側で開いている if または loop を閉じる。 - stop: プログラムをその場で終える。 if 部分、else 部分、loop ブロックの中で宣言した変数は、その部分が閉じるときに消える。 set x や use x で x が宣言されていなければ undeclared エラーである。 まだ宣言していないか、すでに消えた場合である。 use x で x が宣言されていても、その行まで来られる道のうち、x を宣言してから一度も値を入れずに来る道が1つでもあれば unassigned エラーである。 道を考えるとき、if と loop の条件は分からないものとする。 if はどちらにも進めて、loop は何回でも回れる。 エラーになった行は何もしなかったものとして、検査を続ける。 エラーを上から順に出力し、最後の行にエラーの数を出力せよ。
入力
1行目に n (1 ≤ n ≤ 1,000) が与えられる。 続く n 行に命令が1つずつ与えられる。 命令と名前は空白1つで区切る。 名前は英小文字 1 ~ 10 文字で、命令の単語とは異なる。 if と loop は end と対応がとれている。 else はいちばん内側で開いているものが if 部分のときだけ現れ、if 1つにつき多くても1つである。 今宣言されている名前をもう一度宣言することはない。 消えた名前はもう一度宣言できる。 stop は if 部分または else 部分の最後の命令としてだけ現れ、1つの if の2つの部分が両方とも stop で終わることはない
14
var a
var b
if
set a
var t
set t
else
use a
set b
var t
use t
end
use a
use t
stdout
8 unassigned a
11 unassigned t
13 unassigned a
14 undeclared t
4
例 2 · stdin
22
var c
if
set c
end
use c
var x
var y
if
set x
set y
else
set x
stop
end
use x
use y
var z
loop
use z
set z
end
use z
stdout
5 unassigned c
19 unassigned z
22 unassigned z
3
main.py
エラー一覧00F8 ↓ · ⇧F8 ↑
入力(stdin)
実行すると、これらの行が順に input()・標準入力に渡されます(実行中に入力を求めません)。
$ 準備完了
コードを入力して解くにはパソコンで開いてください。モバイルでは問題と解答を見られます。
ほかの言語では?言語別の解答を見る · 8 言語の解答
解答は、今見えている変数ごとに「どの道から来ても値がある」という印を持ち、ブロックを開くときにその印のコピーを取っておきます。 罠その1:else のない if の中で入れた値は、ブロックの後では信用できません。 飛ばされることがあるので、ブロックを開く前の印に戻します。 2つ目の例の5行目がそうです。 罠その2:if と else があるなら、両方で入れなければなりません。 1つ目の例の a は if 部分でしか入れていないので、13行目はエラーです。 罠その3:else 部分は、if 部分が終わった状態ではなく、if に出会う前の状態から始まります。 1つ目の例の8行目がそうです。 罠その4:loop は1回も回らないことがあります。 2つ目の例の z は loop の中で入れていますが22行目はエラーで、1周目にはまだ値がないので19行目もエラーです。 罠その5:stop で終わった側はブロックの後に来られないので、除いて考えます。 2つ目の例の y は if 部分でしか入れていませんが、else 部分が stop で終わるので16行目はエラーではありません。 罠その6:消えた変数と同じ名前でも、宣言し直せば値のない新しい変数です。 1つ目の例の11行目がそうです。 Java・C#・C の解答は配列の場所を使い回すので、宣言のときに必ず印を消します。 罠その7:ブロックが閉じたら、その中の変数を消さなければなりません。 消さないと、1つ目の例の14行目が undeclared ではなく unassigned になります。 罠その8:印はコピーで取っておく必要があります。 Python・JS・クレイコンで = で入れると同じリストを指すので、ブロックの中で変えた内容が取っておいた側にも見えます。 コピーは Python filled[:]、JS・TS slice()、Java Arrays.copyOf、C# Array.Copy、C memcpy、クレイコン 部分リスト で取ります。 C++ の vector は = だけで丸ごと複製されます。 この検査は本物のコンパイラがしていることです。 Java は int x; if (c) { x = 1; } の後で x を使うと variable x might not have been initialized というエラーを出し、if の代わりに while でも同じです。 C# は CS0165、TS は TS2454 のエラーを出します。 Java と C# は、両方で入れているか、else 側が return で終わっていれば通します。 C と C++ は同じコードをエラーなしでコンパイルし、-Wall などのオプションを付けたときだけ警告を出します。 その変数の値は決まっておらず、読むとたいていは未定義動作です。 JS は let x; の後で x を使うと undefined になります。 Python には宣言が別になく、関数の中で if が飛ばされた後にその変数を使うと、実行中に UnboundLocalError になります。 クレイコンにも宣言は別になく、最初に値を入れる行が宣言なので、値のない変数はできません。 ブロックの中で初めて入れた名前をブロックの外で使うと、実行する前に、まだ作っていない名前だと知らせてくれます。 見えている名前を内側のブロックでもう一度宣言するのは、Java と C# ではコンパイルエラーです。 この問題の約束と同じです。 C では許されていて、内側のものが外側のものを隠します。
Pythonmain.py · 56 行
import sysdef main(): lines = sys.stdin.read().split("\n") n = int(lines[0]) names = [] # 지금 보이는 변수 이름 (선언한 차례대로) filled = [] # names와 짝: 어느 길로 와도 값이 들어 있는가 frames = [] # 열려 있는 블록: [종류, 열 때의 변수 수, 열 때의 filled 사본, if 부분의 결과, if 부분이 stop으로 끝났나] stopped = False # 바로 앞 명령이 stop인가 out = [] for i in range(1, n + 1): parts = lines[i].split() cmd = parts[0] if cmd == "var": names.append(parts[1]) filled.append(False) # 선언만으로는 값이 없다 elif cmd == "set" or cmd == "use": x = parts[1] if x not in names: out.append(f"{i} undeclared {x}") elif cmd == "set": filled[names.index(x)] = True elif not filled[names.index(x)]: out.append(f"{i} unassigned {x}") elif cmd == "if" or cmd == "loop": # filled[:] 로 사본을 뜬다. 그냥 filled를 넣으면 같은 리스트를 가리킨다 frames.append([cmd, len(names), filled[:], None, False]) elif cmd == "stop": stopped = True continue # stopped를 아래에서 지우지 않도록 elif cmd == "else": f = frames[-1] del names[f[1]:] # if 부분에서 선언한 변수는 여기서 사라진다 del filled[f[1]:] f[0] = "else" f[3] = filled f[4] = stopped filled = f[2][:] # else 부분은 if를 만나기 전의 상태에서 시작한다 else: # end f = frames.pop() del names[f[1]:] del filled[f[1]:] if f[0] != "else": filled = f[2] # 한 번도 안 돌 수 있으니 블록 전의 상태로 elif stopped: filled = f[3] # else 부분은 여기까지 오지 못한다: if 부분만 본다 elif not f[4]: filled = [a and b for a, b in zip(f[3], filled)] # 두 길 모두에서 넣었어야 한다 # if 부분이 stop으로 끝났으면 else 부분의 결과를 그대로 쓴다 stopped = False out.append(str(len(out))) print("\n".join(out))main()
JavaScriptmain.js · 47 行
const lines = require("fs").readFileSync(0, "utf8").split("\n");const n = Number(lines[0]);const names = []; // 지금 보이는 변수 이름 (선언한 차례대로)let filled = []; // names와 짝: 어느 길로 와도 값이 들어 있는가const frames = []; // 열려 있는 블록let stopped = false; // 바로 앞 명령이 stop인가const out = [];for (let i = 1; i <= n; i++) { const [cmd, x] = lines[i].trim().split(" "); let nowStopped = false; if (cmd === "var") { names.push(x); filled.push(false); // 선언만으로는 값이 없다 } else if (cmd === "set" || cmd === "use") { const at = names.indexOf(x); if (at === -1) out.push(`${i} undeclared ${x}`); else if (cmd === "set") filled[at] = true; else if (!filled[at]) out.push(`${i} unassigned ${x}`); } else if (cmd === "if" || cmd === "loop") { // slice()로 사본을 뜬다. filled를 그대로 넣으면 같은 배열을 가리킨다 frames.push({ kind: cmd, mark: names.length, before: filled.slice(), first: null, firstStopped: false }); } else if (cmd === "stop") { nowStopped = true; } else if (cmd === "else") { const f = frames[frames.length - 1]; names.length = f.mark; // if 부분에서 선언한 변수는 여기서 사라진다 filled.length = f.mark; f.kind = "else"; f.first = filled; f.firstStopped = stopped; filled = f.before.slice(); // else 부분은 if를 만나기 전의 상태에서 시작한다 } else { const f = frames.pop(); names.length = f.mark; filled.length = f.mark; if (f.kind !== "else") { filled = f.before; // 한 번도 안 돌 수 있으니 블록 전의 상태로 } else if (stopped) { filled = f.first; // else 부분은 여기까지 오지 못한다: if 부분만 본다 } else if (!f.firstStopped) { filled = filled.map((v, k) => v && f.first[k]); // 두 길 모두에서 넣었어야 한다 } } stopped = nowStopped;}out.push(String(out.length));console.log(out.join("\n"));
TypeScriptmain.ts · 56 行
const lines: string[] = require("fs").readFileSync(0, "utf8").split("\n");const n: number = Number(lines[0]);interface Frame { kind: string; mark: number; // 블록을 열 때 보이던 변수 수 before: boolean[]; // 블록을 열 때의 상태 사본 first: boolean[]; // if 부분이 끝났을 때의 상태 firstStopped: boolean;}const names: string[] = []; // 지금 보이는 변수 이름 (선언한 차례대로)let filled: boolean[] = []; // names와 짝: 어느 길로 와도 값이 들어 있는가const frames: Frame[] = [];let stopped = false; // 바로 앞 명령이 stop인가const out: string[] = [];for (let i = 1; i <= n; i++) { const [cmd, x] = lines[i].trim().split(" "); let nowStopped = false; if (cmd === "var") { names.push(x); filled.push(false); // 선언만으로는 값이 없다 } else if (cmd === "set" || cmd === "use") { const at = names.indexOf(x); if (at === -1) out.push(`${i} undeclared ${x}`); else if (cmd === "set") filled[at] = true; else if (!filled[at]) out.push(`${i} unassigned ${x}`); } else if (cmd === "if" || cmd === "loop") { // slice()로 사본을 뜬다. filled를 그대로 넣으면 같은 배열을 가리킨다 frames.push({ kind: cmd, mark: names.length, before: filled.slice(), first: [], firstStopped: false }); } else if (cmd === "stop") { nowStopped = true; } else if (cmd === "else") { const f = frames[frames.length - 1]; names.length = f.mark; // if 부분에서 선언한 변수는 여기서 사라진다 filled.length = f.mark; f.kind = "else"; f.first = filled; f.firstStopped = stopped; filled = f.before.slice(); // else 부분은 if를 만나기 전의 상태에서 시작한다 } else { const f = frames.pop()!; names.length = f.mark; filled.length = f.mark; if (f.kind !== "else") { filled = f.before; // 한 번도 안 돌 수 있으니 블록 전의 상태로 } else if (stopped) { filled = f.first; // else 부분은 여기까지 오지 못한다: if 부분만 본다 } else if (!f.firstStopped) { filled = filled.map((v, k) => v && f.first[k]); // 두 길 모두에서 넣었어야 한다 } } stopped = nowStopped;}out.push(String(out.length));console.log(out.join("\n"));
JavaMain.java · 80 行
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.ArrayList;import java.util.Arrays;public class Main { static class Frame { String kind; int mark; // 블록을 열 때 보이던 변수 수 boolean[] before; // 블록을 열 때의 상태 사본 boolean[] first; // if 부분이 끝났을 때의 상태 boolean firstStopped; } public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); int n = Integer.parseInt(br.readLine().trim()); String[] names = new String[n]; // 지금 보이는 변수 이름 (선언한 차례대로) boolean[] filled = new boolean[n]; // names와 짝: 어느 길로 와도 값이 들어 있는가 int count = 0; // 지금 보이는 변수 수 ArrayList<Frame> frames = new ArrayList<>(); boolean stopped = false; // 바로 앞 명령이 stop인가 StringBuilder out = new StringBuilder(); int errors = 0; for (int i = 1; i <= n; i++) { String[] parts = br.readLine().trim().split(" "); String cmd = parts[0]; boolean nowStopped = false; if (cmd.equals("var")) { names[count] = parts[1]; filled[count] = false; // 선언만으로는 값이 없다. 전에 쓰던 칸일 수 있으니 꼭 지운다 count++; } else if (cmd.equals("set") || cmd.equals("use")) { String x = parts[1]; int at = -1; for (int k = 0; k < count; k++) { if (names[k].equals(x)) at = k; // 문자열은 ==가 아니라 equals로 견준다 } if (at == -1) { out.append(i).append(" undeclared ").append(x).append('\n'); errors++; } else if (cmd.equals("set")) { filled[at] = true; } else if (!filled[at]) { out.append(i).append(" unassigned ").append(x).append('\n'); errors++; } } else if (cmd.equals("if") || cmd.equals("loop")) { Frame f = new Frame(); f.kind = cmd; f.mark = count; f.before = Arrays.copyOf(filled, count); // 사본. filled를 그대로 넣으면 같은 배열을 가리킨다 frames.add(f); } else if (cmd.equals("stop")) { nowStopped = true; } else if (cmd.equals("else")) { Frame f = frames.get(frames.size() - 1); count = f.mark; // if 부분에서 선언한 변수는 여기서 사라진다 f.kind = "else"; f.first = Arrays.copyOf(filled, count); f.firstStopped = stopped; System.arraycopy(f.before, 0, filled, 0, count); // else 부분은 if를 만나기 전의 상태에서 시작한다 } else { Frame f = frames.remove(frames.size() - 1); count = f.mark; if (!f.kind.equals("else")) { System.arraycopy(f.before, 0, filled, 0, count); // 한 번도 안 돌 수 있으니 블록 전의 상태로 } else if (stopped) { System.arraycopy(f.first, 0, filled, 0, count); // else 부분은 여기까지 오지 못한다 } else if (!f.firstStopped) { for (int k = 0; k < count; k++) filled[k] = filled[k] && f.first[k]; // 두 길 모두에서 넣었어야 한다 } } stopped = nowStopped; } out.append(errors).append('\n'); System.out.print(out); }}
C#Main.cs · 98 行
using System;using System.Collections.Generic;using System.Text;class Frame{ public string Kind; public int Mark; // 블록을 열 때 보이던 변수 수 public bool[] Before; // 블록을 열 때의 상태 사본 public bool[] First; // if 부분이 끝났을 때의 상태 public bool FirstStopped;}class Program{ static void Main() { int n = int.Parse(Console.ReadLine()); string[] names = new string[n]; // 지금 보이는 변수 이름 (선언한 차례대로) bool[] filled = new bool[n]; // names와 짝: 어느 길로 와도 값이 들어 있는가 int count = 0; // 지금 보이는 변수 수 var frames = new Stack<Frame>(); bool stopped = false; // 바로 앞 명령이 stop인가 var sb = new StringBuilder(); int errors = 0; for (int i = 1; i <= n; i++) { string[] parts = Console.ReadLine().Trim().Split(' '); string cmd = parts[0]; bool nowStopped = false; if (cmd == "var") { names[count] = parts[1]; filled[count] = false; // 선언만으로는 값이 없다. 전에 쓰던 칸일 수 있으니 꼭 지운다 count++; } else if (cmd == "set" || cmd == "use") { string x = parts[1]; int at = Array.IndexOf(names, x, 0, count); if (at == -1) { sb.Append(i).Append(" undeclared ").Append(x).Append('\n'); errors++; } else if (cmd == "set") { filled[at] = true; } else if (!filled[at]) { sb.Append(i).Append(" unassigned ").Append(x).Append('\n'); errors++; } } else if (cmd == "if" || cmd == "loop") { var f = new Frame { Kind = cmd, Mark = count, Before = new bool[count] }; Array.Copy(filled, f.Before, count); // 사본. filled를 그대로 넣으면 같은 배열을 가리킨다 frames.Push(f); } else if (cmd == "stop") { nowStopped = true; } else if (cmd == "else") { Frame f = frames.Peek(); count = f.Mark; // if 부분에서 선언한 변수는 여기서 사라진다 f.Kind = "else"; f.First = new bool[count]; Array.Copy(filled, f.First, count); f.FirstStopped = stopped; Array.Copy(f.Before, filled, count); // else 부분은 if를 만나기 전의 상태에서 시작한다 } else { Frame f = frames.Pop(); count = f.Mark; if (f.Kind != "else") { Array.Copy(f.Before, filled, count); // 한 번도 안 돌 수 있으니 블록 전의 상태로 } else if (stopped) { Array.Copy(f.First, filled, count); // else 부분은 여기까지 오지 못한다 } else if (!f.FirstStopped) { for (int k = 0; k < count; k++) filled[k] = filled[k] && f.First[k]; // 두 길 모두에서 넣었어야 한다 } } stopped = nowStopped; } sb.Append(errors).Append('\n'); Console.Write(sb); }}
C++main.cpp · 74 行
#include <algorithm>#include <iostream>#include <string>#include <vector>using namespace std;struct Frame { string kind; size_t mark; // 블록을 열 때 보이던 변수 수 vector<bool> before; // 블록을 열 때의 상태 사본 vector<bool> first; // if 부분이 끝났을 때의 상태 bool firstStopped;};int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n; cin >> n; vector<string> names; // 지금 보이는 변수 이름 (선언한 차례대로) vector<bool> filled; // names와 짝: 어느 길로 와도 값이 들어 있는가 vector<Frame> frames; bool stopped = false; // 바로 앞 명령이 stop인가 int errors = 0; for (int i = 1; i <= n; i++) { string cmd, x; cin >> cmd; bool nowStopped = false; if (cmd == "var") { cin >> x; names.push_back(x); filled.push_back(false); // 선언만으로는 값이 없다 } else if (cmd == "set" || cmd == "use") { cin >> x; auto it = find(names.begin(), names.end(), x); if (it == names.end()) { cout << i << " undeclared " << x << '\n'; errors++; } else if (cmd == "set") { filled[it - names.begin()] = true; } else if (!filled[it - names.begin()]) { cout << i << " unassigned " << x << '\n'; errors++; } } else if (cmd == "if" || cmd == "loop") { frames.push_back({cmd, names.size(), filled, {}, false}); // vector는 = 로 넣으면 통째로 베껴진다 } else if (cmd == "stop") { nowStopped = true; } else if (cmd == "else") { Frame& f = frames.back(); names.resize(f.mark); // if 부분에서 선언한 변수는 여기서 사라진다 filled.resize(f.mark); f.kind = "else"; f.first = filled; f.firstStopped = stopped; filled = f.before; // else 부분은 if를 만나기 전의 상태에서 시작한다 } else { Frame f = frames.back(); frames.pop_back(); names.resize(f.mark); filled.resize(f.mark); if (f.kind != "else") { filled = f.before; // 한 번도 안 돌 수 있으니 블록 전의 상태로 } else if (stopped) { filled = f.first; // else 부분은 여기까지 오지 못한다 } else if (!f.firstStopped) { for (size_t k = 0; k < f.mark; k++) filled[k] = filled[k] && f.first[k]; // 두 길 모두에서 넣었어야 한다 } } stopped = nowStopped; } cout << errors << '\n'; return 0;}
Cmain.c · 90 行
#include <stdio.h>#include <stdlib.h>#include <string.h>#define MAXN 1000typedef struct { int is_else; /* else 부분까지 왔는가 */ int mark; /* 블록을 열 때 보이던 변수 수 */ char *before; /* 블록을 열 때의 상태 사본 */ char *first; /* if 부분이 끝났을 때의 상태 */ int first_stopped;} Frame;static char names[MAXN][12]; /* 지금 보이는 변수 이름 (선언한 차례대로) */static char filled[MAXN]; /* names와 짝: 어느 길로 와도 값이 들어 있는가 */static Frame frames[MAXN];/* 길이 len인 상태를 새 메모리에 베낀다. 배열은 = 로 베낄 수 없다 */static char *copy_of(const char *src, int len) { char *p = malloc((size_t)len + 1); if (p == NULL) exit(1); memcpy(p, src, (size_t)len); return p;}int main(void) { int n; if (scanf("%d", &n) != 1) return 0; int count = 0, depth = 0, stopped = 0, errors = 0; for (int i = 1; i <= n; i++) { char cmd[8], x[12] = ""; if (scanf("%7s", cmd) != 1) return 0; int now_stopped = 0; int is_var = strcmp(cmd, "var") == 0, is_set = strcmp(cmd, "set") == 0, is_use = strcmp(cmd, "use") == 0; if (is_var || is_set || is_use) { if (scanf("%11s", x) != 1) return 0; } if (is_var) { strcpy(names[count], x); filled[count] = 0; /* 선언만으로는 값이 없다. 전에 쓰던 칸일 수 있으니 꼭 지운다 */ count++; } else if (is_set || is_use) { int at = -1; for (int k = 0; k < count; k++) { if (strcmp(names[k], x) == 0) at = k; /* 문자열은 ==가 아니라 strcmp로 견준다 */ } if (at == -1) { printf("%d undeclared %s\n", i, x); errors++; } else if (is_set) { filled[at] = 1; } else if (!filled[at]) { printf("%d unassigned %s\n", i, x); errors++; } } else if (strcmp(cmd, "if") == 0 || strcmp(cmd, "loop") == 0) { Frame *f = &frames[depth++]; f->is_else = 0; f->mark = count; f->before = copy_of(filled, count); f->first = NULL; f->first_stopped = 0; } else if (strcmp(cmd, "stop") == 0) { now_stopped = 1; } else if (strcmp(cmd, "else") == 0) { Frame *f = &frames[depth - 1]; count = f->mark; /* if 부분에서 선언한 변수는 여기서 사라진다 */ f->is_else = 1; f->first = copy_of(filled, count); f->first_stopped = stopped; memcpy(filled, f->before, (size_t)count); /* else 부분은 if를 만나기 전의 상태에서 시작한다 */ } else { Frame *f = &frames[--depth]; count = f->mark; if (!f->is_else) { memcpy(filled, f->before, (size_t)count); /* 한 번도 안 돌 수 있으니 블록 전의 상태로 */ } else if (stopped) { memcpy(filled, f->first, (size_t)count); /* else 부분은 여기까지 오지 못한다 */ } else if (!f->first_stopped) { for (int k = 0; k < count; k++) filled[k] = filled[k] && f->first[k]; /* 두 길 모두에서 넣었어야 한다 */ } free(f->before); free(f->first); /* free(NULL)은 아무 일도 하지 않는다 */ } stopped = now_stopped; } printf("%d\n", errors); return 0;}
Craykonmain.cray · 107 行
// 목록을 통째로 베낀다. 그냥 = 로 넣으면 같은 목록을 가리킨다베끼기(원본){ 返す 원본.部分リスト(0, 원본.個数);}// 목록을 앞에서 '남길수'개만 남긴다줄이기(목록, 남길수){ 間 (목록.個数 > 남길수) { 목록.位置で取り除く(목록.個数 - 1); }}n = コンソール.行読み().数に();이름들 = 新リスト(0, ""); // 지금 보이는 변수 이름 (선언한 차례대로)채움 = 新リスト(0, 偽); // 이름들과 짝: 어느 길로 와도 값이 들어 있는가// 열려 있는 블록마다 한 칸씩블록종류 = 新リスト(0, "");블록표시 = 新リスト(0, 0); // 블록을 열 때 보이던 변수 수블록전 = 新リスト(0, 채움); // 블록을 열 때의 상태 사본블록첫부분 = 新リスト(0, 채움); // if 부분이 끝났을 때의 상태블록첫멈춤 = 新リスト(0, 偽);멈춤 = 偽; // 바로 앞 명령이 stop인가오류 = 0;繰り返す (i = 1; i <= n; i++){ 조각 = コンソール.行読み().空白除去().分ける(" "); 명령 = 조각[0]; 지금멈춤 = 偽; もし (명령 == "var") { 이름들.追加(조각[1]); 채움.追加(偽); // 선언만으로는 값이 없다 } でなければ もし (명령 == "set" || 명령 == "use") { x = 조각[1]; 자리 = 이름들.位置検索(x); もし (자리 == -1) { コンソール.書く(i + " undeclared " + x); 오류 = 오류 + 1; } でなければ もし (명령 == "set") { 채움[자리] = 真; } でなければ もし (!채움[자리]) { コンソール.書く(i + " unassigned " + x); 오류 = 오류 + 1; } } でなければ もし (명령 == "if" || 명령 == "loop") { 블록종류.追加(명령); 블록표시.追加(이름들.個数); 블록전.追加(베끼기(채움)); 블록첫부분.追加(채움); 블록첫멈춤.追加(偽); } でなければ もし (명령 == "stop") { 지금멈춤 = 真; } でなければ もし (명령 == "else") { 끝 = 블록종류.個数 - 1; 줄이기(이름들, 블록표시[끝]); // if 부분에서 선언한 변수는 여기서 사라진다 줄이기(채움, 블록표시[끝]); 블록종류[끝] = "else"; 블록첫부분[끝] = 채움; 블록첫멈춤[끝] = 멈춤; 채움 = 베끼기(블록전[끝]); // else 부분은 if를 만나기 전의 상태에서 시작한다 } でなければ { 끝 = 블록종류.個数 - 1; 줄이기(이름들, 블록표시[끝]); 줄이기(채움, 블록표시[끝]); もし (블록종류[끝] != "else") { 채움 = 블록전[끝]; // 한 번도 안 돌 수 있으니 블록 전의 상태로 } でなければ もし (멈춤) { 채움 = 블록첫부분[끝]; // else 부분은 여기까지 오지 못한다 } でなければ もし (!블록첫멈춤[끝]) { 첫부분 = 블록첫부분[끝]; 繰り返す (k = 0; k < 채움.個数; k++) { 채움[k] = 채움[k] && 첫부분[k]; // 두 길 모두에서 넣었어야 한다 } } 블록종류.位置で取り除く(끝); 블록표시.位置で取り除く(끝); 블록전.位置で取り除く(끝); 블록첫부분.位置で取り除く(끝); 블록첫멈춤.位置で取り除く(끝); } 멈춤 = 지금멈춤;}コンソール.書く(오류);