스코프 문서의 검증 코드다. if __name__ == "__main__": 아래가 자체 테스트다.
# 검증: 스코프 문서의 예(가장 가까운 안쪽 스코프 규칙, 정적 스코프와 동적 스코프의 차이)
# 실행: python3 31_identifier-scope_verify.py
# 작은 언어: ("decl", 이름, 값), ("print", 이름), ("block", [문장...]), ("call", 함수 이름)
# 함수는 {이름: 몸통 문장 목록}. 전역 선언은 맨 바깥 문장 목록에 둔다.
def run(globals_, funcs, main, dynamic):
"""dynamic=False면 정적 스코프(함수 몸통은 전역만 바깥으로 본다), True면 동적 스코프(부른 쪽을 바깥으로 본다)."""
out = []
genv = {}
for _, name, val in globals_:
genv[name] = val
def lookup(chain, name):
for env in reversed(chain):
if name in env:
return env[name]
raise NameError(name)
def exec_block(stmts, chain):
chain = chain + [{}]
for s in stmts:
if s[0] == "decl":
chain[-1][s[1]] = s[2]
elif s[0] == "print":
out.append(lookup(chain, s[1]))
elif s[0] == "block":
exec_block(s[1], chain)
elif s[0] == "call":
exec_block(funcs[s[1]], chain if dynamic else [genv])
exec_block(main, [genv])
return out
if __name__ == "__main__":
# (1) 문서의 첫 코드: 전역 a = 2, 함수 안에서 a를 찍고, a = 3을 선언하고, 다시 찍는다 -> 2, 3
g = [("decl", "a", 2)]
foo = [("print", "a"), ("decl", "a", 3), ("print", "a")]
assert run(g, {"foo": foo}, [("call", "foo")], dynamic=False) == [2, 3]
# (2) 안쪽 블록 예: 안쪽 블록에서 같은 이름을 다시 선언하면 그 블록 안에서만 가린다
body = [("decl", "a", 2), ("block", [("print", "a"), ("decl", "a", 3), ("print", "a")]), ("print", "a")]
assert run([], {"foo": body}, [("call", "foo")], dynamic=False) == [2, 3, 2]
# (3) foo가 자기 안에서만 a를 선언하는 예: foo() { int a = 3; } ... int a = 2; foo(); print(a); 두 방식 모두 2
prog = [("decl", "a", 2), ("call", "foo"), ("print", "a")]
f = {"foo": [("decl", "a", 3)]}
assert run([], f, prog, dynamic=False) == [2]
assert run([], f, prog, dynamic=True) == [2]
# (4) 두 방식이 갈리는 예: 전역 a = 2, show()는 a를 찍고, foo()는 a = 3을 선언한 뒤 show()를 부른다
g = [("decl", "a", 2)]
f = {"show": [("print", "a")], "foo": [("decl", "a", 3), ("call", "show")]}
assert run(g, f, [("call", "foo")], dynamic=False) == [2]
assert run(g, f, [("call", "foo")], dynamic=True) == [3]
# 부르는 자리가 바뀌면 동적 스코프의 답만 바뀐다
assert run(g, f, [("call", "show")], dynamic=True) == [2]
assert run(g, f, [("call", "show")], dynamic=False) == [2]
# (5) 확인 문제 C2: 세 겹 블록에서 선언 전후로 찍히는 값
g = [("decl", "a", 1)]
f = {"f": [("print", "a"),
("block", [("decl", "a", 2), ("print", "a"),
("block", [("print", "a"), ("decl", "a", 3), ("print", "a")]),
("print", "a")]),
("print", "a")]}
assert run(g, f, [("call", "f")], dynamic=False) == [1, 2, 2, 3, 2, 1]
print("정적 스코프: 2, 동적 스코프: 3 (show를 foo가 부를 때)")
print("ALL CHECKS PASSED")