Check an n-line program written in a small language, without running it.
In this language a variable has no value when it is only declared. It can be used only after a value has been put in.
There are eight commands, one per line.
- var x: declare the variable x. It has no value yet.
- set x: put a value into x.
- use x: use the value of x.
- if: open a block. This block may run or may be skipped.
- else: close the if part and open the else part. For an if with an else, exactly one of the two parts runs.
- loop: open a block. This block repeats 0 or more times.
- end: close the innermost open if or loop.
- stop: end the program right there.
A variable declared inside an if part, an else part or a loop block disappears when that part closes.
If x is not declared at set x or use x, it is an undeclared error. It has either not been declared yet or has already disappeared.
Even if x is declared at use x, it is an unassigned error when at least one of the paths that can reach that line arrives without ever putting a value into x since it was declared.
When looking at paths, treat the conditions of if and loop as unknown. An if can go either way, and a loop can run any number of times.
A line with an error is treated as having done nothing, and the check goes on.
Print the errors from the top in order, and print the number of errors on the last line.
Input
The first line has n (1 ≤ n ≤ 1,000). Each of the next n lines has one command. A command and its name are separated by one space. A name is 1 to 10 lowercase English letters and is not a command word. Every if and loop is matched with an end. An else appears only when the innermost open part is an if part, and an if has at most one else. A name that is currently declared is never declared again. A name that has disappeared may be declared again. stop appears only as the last command of an if part or an else part, and the two parts of one if never both end with stop
Output
For each error print one line with the line number, the error name (undeclared or unassigned) and the variable name, separated by single spaces. Line numbers count only commands, and the first command is 1. Print the number of errors on the last line
Example 1 · stdin
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
Example 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
Error List00F8 ↓ · ⇧F8 ↑
Input (stdin)
When you run, these lines are fed to input()/stdin in order (the program doesn't stop to ask).
$ Ready
Open this on a computer to write and run code. On mobile you can read the problem and the solutions.
In other languagesShow solutions by language · 8 languages
The solutions keep, for every visible variable, a mark saying "it has a value on every path", and save a copy of those marks when a block opens. Trap one: a value put in inside an if without else cannot be trusted after the block. The block may be skipped, so go back to the marks from before it opened. Line 5 of the second example shows this. Trap two: with if and else, the value must be put in on both sides. In the first example a is set only in the if part, so line 13 is an error. Trap three: the else part starts from the state before the if, not from where the if part ended. Line 8 of the first example shows this. Trap four: a loop may run zero times. In the second example z is set inside the loop, yet line 22 is an error, and since there is no value yet on the first pass, line 19 is an error too. Trap five: a side that ends with stop never reaches the lines after the block, so leave it out. In the second example y is set only in the if part, but the else part ends with stop, so line 16 is not an error. Trap six: declaring a name again after it disappeared makes a new variable with no value. Line 11 of the first example shows this. The Java, C# and C solutions reuse array slots, so they always clear the mark when declaring. Trap seven: when a block closes, its variables must be removed. Without that, line 14 of the first example becomes unassigned instead of undeclared. Trap eight: the marks must be saved as a copy. In Python, JS and Craykon, storing with = points at the same list, so changes made inside the block show up in the saved one too. Copy with Python filled[:], JS/TS slice(), Java Arrays.copyOf, C# Array.Copy, C memcpy, Craykon GetRange. A C++ vector is copied whole by = alone. This check is what real compilers do. Java gives the error variable x might not have been initialized when x is used after int x; if (c) { x = 1; }, and the same with while in place of if. C# gives error CS0165 and TS gives TS2454. Java and C# accept the code when both sides set the variable or when the else side ends with return. C and C++ compile the same code without an error, and warn only with an option such as -Wall. The variable holds an indeterminate value, and reading it is undefined behaviour in most cases. In JS, using x after let x; gives undefined. Python has no separate declaration, and using a variable in a function after its if was skipped raises UnboundLocalError at run time. Craykon has no separate declaration either: the line that first puts a value in is the declaration, so a variable without a value cannot exist. Using a name outside the block where it was first set is reported before the program runs, as a name that hasn't been created yet. Declaring a visible name again in an inner block is a compile error in Java and C#. That matches the promise in this problem. C allows it, and the inner one hides the outer one.
Pythonmain.py · 56 lines
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 lines
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 lines
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 lines
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 lines
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 lines
#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 lines
#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 lines
// 목록을 통째로 베낀다. 그냥 = 로 넣으면 같은 목록을 가리킨다베끼기(원본){ return 원본.GetRange(0, 원본.Count);}// 목록을 앞에서 '남길수'개만 남긴다줄이기(목록, 남길수){ while (목록.Count > 남길수) { 목록.RemoveAt(목록.Count - 1); }}n = Console.ReadLine().ToNumber();이름들 = NewList(0, ""); // 지금 보이는 변수 이름 (선언한 차례대로)채움 = NewList(0, false); // 이름들과 짝: 어느 길로 와도 값이 들어 있는가// 열려 있는 블록마다 한 칸씩블록종류 = NewList(0, "");블록표시 = NewList(0, 0); // 블록을 열 때 보이던 변수 수블록전 = NewList(0, 채움); // 블록을 열 때의 상태 사본블록첫부분 = NewList(0, 채움); // if 부분이 끝났을 때의 상태블록첫멈춤 = NewList(0, false);멈춤 = false; // 바로 앞 명령이 stop인가오류 = 0;for (i = 1; i <= n; i++){ 조각 = Console.ReadLine().Trim().Split(" "); 명령 = 조각[0]; 지금멈춤 = false; if (명령 == "var") { 이름들.Add(조각[1]); 채움.Add(false); // 선언만으로는 값이 없다 } else if (명령 == "set" || 명령 == "use") { x = 조각[1]; 자리 = 이름들.IndexOf(x); if (자리 == -1) { Console.WriteLine(i + " undeclared " + x); 오류 = 오류 + 1; } else if (명령 == "set") { 채움[자리] = true; } else if (!채움[자리]) { Console.WriteLine(i + " unassigned " + x); 오류 = 오류 + 1; } } else if (명령 == "if" || 명령 == "loop") { 블록종류.Add(명령); 블록표시.Add(이름들.Count); 블록전.Add(베끼기(채움)); 블록첫부분.Add(채움); 블록첫멈춤.Add(false); } else if (명령 == "stop") { 지금멈춤 = true; } else if (명령 == "else") { 끝 = 블록종류.Count - 1; 줄이기(이름들, 블록표시[끝]); // if 부분에서 선언한 변수는 여기서 사라진다 줄이기(채움, 블록표시[끝]); 블록종류[끝] = "else"; 블록첫부분[끝] = 채움; 블록첫멈춤[끝] = 멈춤; 채움 = 베끼기(블록전[끝]); // else 부분은 if를 만나기 전의 상태에서 시작한다 } else { 끝 = 블록종류.Count - 1; 줄이기(이름들, 블록표시[끝]); 줄이기(채움, 블록표시[끝]); if (블록종류[끝] != "else") { 채움 = 블록전[끝]; // 한 번도 안 돌 수 있으니 블록 전의 상태로 } else if (멈춤) { 채움 = 블록첫부분[끝]; // else 부분은 여기까지 오지 못한다 } else if (!블록첫멈춤[끝]) { 첫부분 = 블록첫부분[끝]; for (k = 0; k < 채움.Count; k++) { 채움[k] = 채움[k] && 첫부분[k]; // 두 길 모두에서 넣었어야 한다 } } 블록종류.RemoveAt(끝); 블록표시.RemoveAt(끝); 블록전.RemoveAt(끝); 블록첫부분.RemoveAt(끝); 블록첫멈춤.RemoveAt(끝); } 멈춤 = 지금멈춤;}Console.WriteLine(오류);