58 — 컴파일러: 작은 바이트코드 실행기
가이드 29는 소스 텍스트를 Python으로 번역해 실행했고, 49는 줄을 토큰으로 나누어 실행을 보냈습니다. 다음 단계는 바이트코드입니다: 이미 작은 데이터 단계로 컴파일된 명령. 실행기 — 작은 가상 머신 — 은 프로그램 카운터로 그 단계들을 걸어갑니다. Python이 자기 코드를 실행하는 방식입니다.
단계
컴파일된 프로그램은 데이터입니다: 명령들의 목록. 각 명령은 목록이며 첫 요소가 연산, 나머지가 그 인자입니다:
실행해 보기 →program = [ ["set", "x", "0"], ["add", "x", "2"], ["add", "x", "3"], ["show", "x"], ]set x 0는x라는 변수에 0을 저장하고,add x 2는 2를 더합니다. 아직 아무 줄도 실행되지 않습니다 — 이것은 단계들의 설명입니다.실행기가
pc(프로그램 카운터)와varsdict(기계의 메모리)로 한 걸음씩 진행합니다. 반복 한 바퀴마다pc의 명령을 가져오고, 실행하고,pc를 앞으로 움직입니다.jnz는 변수가 0이 아닌 동안 다른pc로 점프합니다 — 이것이 바이트코드 반복이 만들어지는 방식입니다. 전체 실행기,bytecode.ko.nme로 저장:
실행해 보기 →# bytecode.ko.nme — 작은 바이트코드 실행기, 미니 가상 머신. # 실행: nme 실행 bytecode.ko # 각 명령은 목록이며, run()이 프로그램 카운터로 한 걸음씩 실행합니다. def run(program): vars = {} pc = 0 step = 0 while pc < len(program): instr = program[pc] op = instr[0] step = step + 1 if op == "set": vars[instr[1]] = int(instr[2]) elif op == "add": vars[instr[1]] = vars[instr[1]] + int(instr[2]) elif op == "sub": vars[instr[1]] = vars[instr[1]] - int(instr[2]) elif op == "show": 말해 f"step {step} pc {pc}: {instr[1]} = {vars[instr[1]]}" elif op == "jnz": if vars[instr[1]] != 0: pc = int(instr[2]) continue pc = pc + 1 말해 f"{step}단계 만에 프로그램이 끝났습니다" countdown = [ ["set", "x", "0"], ["add", "x", "2"], ["add", "x", "3"], ["show", "x"], ] 말해 "첫 프로그램:" run(countdown) 말해 "점프로 만든 반복:" loop = [ ["set", "n", "3"], ["show", "n"], ["sub", "n", "1"], ["jnz", "n", "1"], ] run(loop) 말해 "완료"step은 가져온 명령마다 하나씩 세고,show는 현재pc를 알려 줍니다. 반복에서pc는n이 0이 아닌 동안 1로 돌아가고,n이 0이 되면 끝을 지나 떨어져 나갑니다.실행하세요:
nme 실행 bytecode.ko첫 프로그램: step 4 pc 3: x = 5 4단계 만에 프로그램이 끝났습니다 점프로 만든 반복: step 2 pc 1: n = 3 step 5 pc 1: n = 2 step 8 pc 1: n = 1 10단계 만에 프로그램이 끝났습니다 완료첫 프로그램은 네 단계를 실행했습니다: set, add, add, show. 반복은 열 단계를 실행했습니다:
jnz가 기계를pc1로 세 번 되돌려 보냈고, 그다음 떨어져 나가게 했습니다.
직접 해보기
cmp(비교) 명령을 추가해 1이나 0을 저장하고, 변수가 0일 때 점프하는 jz를
추가해 보세요.
배운 것
- 바이트코드는 이미 작은 데이터 단계의 목록으로 컴파일된 소스입니다.
- 프로그램 카운터(
pc)가 기계가 다음에 실행할 명령을 말해 줍니다. vars, 즉 dict가 기계의 메모리입니다. 각 연산이 그것을 읽고 씁니다.jnz는pc를 바꿔 점프하며, 이것이 가상 머신 안에서 반복이 동작하는 방식입니다.