{ }codelper
변수 만들기변수 범위목록 복사P0360

값을 넣기 전에 쓴 변수

작은 언어로 쓴 프로그램 n줄을 실행하지 않고 검사한다. 이 언어의 변수는 선언만 해서는 값이 없고, 값을 넣은 뒤에야 쓸 수 있다. 명령은 여덟 가지이고 한 줄에 하나씩 있다. - var x: 변수 x를 선언한다.
아직 값은 없다. - set x: x에 값을 넣는다. - use x: x의 값을 쓴다. - if: 블록을 연다.
이 블록은 실행될 수도 있고 건너뛸 수도 있다. - else: if 부분을 닫고 else 부분을 연다.
else가 있는 if는 두 부분 가운데 꼭 하나만 실행된다. - loop: 블록을 연다.
이 블록은 0번 이상 되풀이된다. - end: 가장 안쪽에 열려 있는 if나 loop를 닫는다. - stop: 프로그램을 그 자리에서 끝낸다. if 부분, else 부분, loop 블록 안에서 선언한 변수는 그 부분이 닫힐 때 사라진다. set x나 use x에서 x가 선언되어 있지 않으면 undeclared 오류다.
아직 선언하지 않았거나 이미 사라진 경우다. use x에서 x가 선언되어 있어도, 그 줄까지 올 수 있는 길 가운데 x를 선언한 뒤로 값을 한 번도 넣지 않고 오는 길이 하나라도 있으면 unassigned 오류다. 길을 따질 때 if와 loop의 조건은 모른다고 본다.
if는 어느 쪽으로든 갈 수 있고, loop는 몇 번이든 돌 수 있다. 오류가 난 줄은 아무 일도 하지 않은 것으로 보고 검사를 이어 간다. 오류를 위에서부터 차례로 출력하고, 마지막 줄에 오류 수를 출력하라.

입력
첫 줄에 n (1 ≤ n ≤ 1,000)이 주어진다.
다음 n줄에 명령이 하나씩 주어진다.
명령과 이름은 빈칸 하나로 띄운다.
이름은 영어 소문자 1 ~ 10글자이고 명령 낱말과 같지 않다.
if와 loop는 end와 짝이 맞는다.
else는 가장 안쪽에 열려 있는 것이 if 부분일 때만 나오고, if 하나에 많아야 하나다.
지금 선언되어 있는 이름을 다시 선언하지 않는다.
사라진 이름은 다시 선언할 수 있다.
stop은 if 부분이나 else 부분의 마지막 명령으로만 나오고, 한 if의 두 부분이 모두 stop으로 끝나지는 않는다
출력
오류마다 한 줄에 줄 번호, 오류 이름(undeclared 또는 unassigned), 변수 이름을 빈칸 하나씩 띄워 출력한다.
줄 번호는 명령만 세며 첫 명령이 1이다.
마지막 줄에 오류 수를 출력한다
예시 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
예시 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
입력(stdin)

실행하면 이 줄들이 차례로 input()·표준 입력으로 들어가요(실행 중에 묻지 않아요).

$ 준비됐어요

코드를 입력해 풀려면 PC에서 열어 주세요. 모바일에서는 문제와 풀이를 볼 수 있어요.

다른 언어로는?언어별 풀이 보기 · 8개 언어 풀이

풀이는 지금 보이는 변수마다 '어느 길로 와도 값이 있다'는 표시를 두고, 블록을 열 때 그 표시들의 사본을 떠 둬요.
함정 하나: else 없는 if 안에서 넣은 값은 블록 뒤에서 믿을 수 없어요.
건너뛸 수 있으니 블록을 열기 전의 표시로 되돌려요.
둘째 예시의 5번째 줄이 그래요.
함정 둘: if와 else가 있으면 두 쪽 모두에서 넣어야 해요.
첫 예시의 a는 if 부분에서만 넣어서 13번째 줄이 오류예요.
함정 셋: else 부분은 if 부분이 끝난 상태가 아니라 if를 만나기 전의 상태에서 시작해요.
첫 예시의 8번째 줄이 그래요.
함정 넷: loop는 한 번도 안 돌 수 있어요.
둘째 예시의 z는 loop 안에서 넣었지만 22번째 줄이 오류고, 첫 바퀴에는 아직 값이 없으니 19번째 줄도 오류예요.
함정 다섯: stop으로 끝난 쪽은 블록 뒤로 오지 못하니 빼고 따져요.
둘째 예시의 y는 if 부분에서만 넣었지만 else 부분이 stop으로 끝나서 16번째 줄은 오류가 아니에요.
함정 여섯: 사라진 변수와 이름이 같아도 새로 선언하면 값 없는 새 변수예요.
첫 예시의 11번째 줄이 그래요.
Java·C#·C 풀이는 배열 칸을 다시 쓰기 때문에 선언할 때 표시를 꼭 지워요.
함정 일곱: 블록이 닫히면 그 안의 변수를 지워야 해요.
지우지 않으면 첫 예시의 14번째 줄이 undeclared가 아니라 unassigned가 돼요.
함정 여덟: 표시는 사본으로 떠야 해요.
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 sys  def 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 1000 typedef 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]; // 두 길 모두에서 넣었어야 한다            }        }        블록종류.자리빼기(끝);        블록표시.자리빼기(끝);        블록전.자리빼기(끝);        블록첫부분.자리빼기(끝);        블록첫멈춤.자리빼기(끝);    }    멈춤 = 지금멈춤;}콘솔.쓰기(오류);