def insert(self, data):
insert_memo_sql = '''INSERT INTO minim(title, memo) VALUES(?, ?);'''
try:
cur = self.db_conn.cursor()
cur.execute(insert_memo_sql, (data[0], data[1]))
self.db_conn.commit()
print(cur.lastrowid)
return cur.lastrowid
except sqlite3.Error as e:
print(e)
return -1
[개선코드]
def insert_bigdata(self, big_data):
insert_memo_sql = '''INSERT INTO minim(title, memo) VALUES(?, ?);'''
try:
cur = self.db_conn.cursor()
cur.execute("BEGIN TRANSACTION")
for data in big_data:
cur.execute(insert_memo_sql, (data[0], data[1]))
self.db_conn.commit()
print(cur.lastrowid)
return cur.lastrowid
except sqlite3.Error as e:
print(e)
self.db_conn.rollback() # rollback the transaction if there's any error
return -1
from enum import Enum
class Command(Enum):
PUSH = 'PUSH'
POP = 'POP'
ADD = 'ADD'
SUB = 'SUB'
MUL = 'MUL'
DIV = 'DIV'
MOD = 'MOD'
DUP = 'DUP'
SWAP = 'SWAP'
JMP = 'JMP'
JG = 'JG'
JL = 'JL'
JZ = 'JZ'
JNZ = 'JNZ'
CMP = 'CMP'
MOV = 'MOV'
NOP = 'NOP'
HALT = 'HALT'
class StackMachine:
"""
PUSH <value>: Push a value onto the stack. e.g., PUSH 10
POP: Remove and return the top value from the stack.
ADD: Remove the top two values from the stack, calculate their sum, and push the result onto the stack.
SUB: Remove the top two values from the stack, calculate their difference, and push the result onto the stack.
MUL: Remove the top two values from the stack, calculate their product, and push the result onto the stack.
DIV: Remove the top two values from the stack, calculate their division, and push the result onto the stack.
MOD: Remove the top two values from the stack, calculate their remainder, and push the result onto the stack.
DUP: Copy the top value from the stack and push it onto the stack.
SWAP: Swap the top two values on the stack.
JMP <label>: Move program execution to the given label. e.g., JMP LOOP
JG <label>: If the top value on the stack is greater than 0, move program execution to the given label.
JL <label>: If the top value on the stack is less than 0, move program execution to the given label.
JZ <label>: If the top value on the stack is 0, move program execution to the given label.
JNZ <label>: If the top value on the stack is not 0, move program execution to the given label.
CMP: Compare the top two values on the stack and push the result onto the stack.
(-1: First value is smaller, 0: Both values are equal, 1: First value is larger)
MOV <register>: Move the top value from the stack to the specified register (AX, BX, CX, or DX).
NOP: Perform no operation. This command is ignored in the program.
HALT: Stop program execution.
"""
def __init__(self):
self.stack = []
self.program = []
self.labels = {}
self.registers = {'AX': 0, 'BX': 0, 'CX': 0, 'DX': 0}
self.instruction_pointer = 0
def load_program(self, program):
self.program = program
self.labels = {line[:-1]: i for i, line in enumerate(program) if line.endswith(':')}
def parse_command(self, command_string):
command, *args = command_string.split()
if command not in Command.__members__:
raise ValueError(f"Invalid command: {command}")
return Command[command], args
def execute_command(self, command, args):
if command == Command.PUSH:
value = int(args[0]) if 'X' not in args[0] else self.registers[args[0]]
self.stack.append(value)
elif command == Command.POP:
self.stack.pop()
elif command == Command.ADD:
self.stack.append(self.stack.pop() + self.stack.pop())
elif command == Command.SUB:
b, a = self.stack.pop(), self.stack.pop()
self.stack.append(a - b)
elif command == Command.MUL:
self.stack.append(self.stack.pop() * self.stack.pop())
elif command == Command.DIV:
b, a = self.stack.pop(), self.stack.pop()
self.stack.append(a // b)
elif command == Command.MOD:
b, a = self.stack.pop(), self.stack.pop()
self.stack.append(a % b)
elif command == Command.DUP:
self.stack.append(self.stack[-1])
elif command == Command.SWAP:
self.stack[-1], self.stack[-2] = self.stack[-2], self.stack[-1]
elif command == Command.JMP:
self.instruction_pointer = self.labels[args[0]]
return
elif command == Command.JG:
if self.stack.pop() == 1:
self.instruction_pointer = self.labels[args[0]]
return
elif command == Command.JL:
if self.stack.pop() == -1:
self.instruction_pointer = self.labels[args[0]]
return
elif command == Command.JZ:
if self.stack.pop() == 0:
self.instruction_pointer = self.labels[args[0]]
return
elif command == Command.JNZ:
if self.stack.pop() != 0:
self.instruction_pointer = self.labels[args[0]]
return
elif command == Command.CMP:
b, a = self.stack.pop(), self.stack.pop()
self.stack.append((a > b) - (a < b))
elif command == Command.MOV:
self.registers[args[0]] = self.stack[-1]
elif command == Command.NOP:
pass
elif command == Command.HALT:
return
else:
raise ValueError(f"Unknown command: {command}")
self.instruction_pointer += 1
def run(self):
for _ in range(20000):
if self.instruction_pointer >= len(self.program):
break
command_string = self.program[self.instruction_pointer]
command, args = self.parse_command(command_string)
self.execute_command(command, args)
생성된 코드를 돌려보면, 역시 안됩니다.
디버깅 하면, Label 처리가 안되었을 알 수 있습니다.
9. Label 처리를 위한 코드를 손으로 추가 하여 수정 합니다.
from enum import Enum
class Command(Enum):
PUSH = 'PUSH'
POP = 'POP'
ADD = 'ADD'
SUB = 'SUB'
MUL = 'MUL'
DIV = 'DIV'
MOD = 'MOD'
DUP = 'DUP'
SWAP = 'SWAP'
JMP = 'JMP'
JG = 'JG'
JL = 'JL'
JZ = 'JZ'
JNZ = 'JNZ'
CMP = 'CMP'
MOV = 'MOV'
NOP = 'NOP'
HALT = 'HALT'
LABEL = 'LABEL'
class StackMachine:
"""
PUSH <value>: Push a value onto the stack. e.g., PUSH 10
POP: Remove and return the top value from the stack.
ADD: Remove the top two values from the stack, calculate their sum, and push the result onto the stack.
SUB: Remove the top two values from the stack, calculate their difference, and push the result onto the stack.
MUL: Remove the top two values from the stack, calculate their product, and push the result onto the stack.
DIV: Remove the top two values from the stack, calculate their division, and push the result onto the stack.
MOD: Remove the top two values from the stack, calculate their remainder, and push the result onto the stack.
DUP: Copy the top value from the stack and push it onto the stack.
SWAP: Swap the top two values on the stack.
JMP <label>: Move program execution to the given label. e.g., JMP LOOP
JG <label>: If the top value on the stack is greater than 0, move program execution to the given label.
JL <label>: If the top value on the stack is less than 0, move program execution to the given label.
JZ <label>: If the top value on the stack is 0, move program execution to the given label.
JNZ <label>: If the top value on the stack is not 0, move program execution to the given label.
CMP: Compare the top two values on the stack and push the result onto the stack.
(-1: First value is smaller, 0: Both values are equal, 1: First value is larger)
MOV <register>: Move the top value from the stack to the specified register (AX, BX, CX, or DX).
NOP: Perform no operation. This command is ignored in the program.
HALT: Stop program execution.
"""
def __init__(self):
self.stack = []
self.program = []
self.labels = {}
self.registers = {'AX': 0, 'BX': 0, 'CX': 0, 'DX': 0}
self.instruction_pointer = 0
def load_program(self, program):
self.program = program
self.labels = {line[:-1]: i for i, line in enumerate(program) if line.endswith(':')}
def parse_command(self, command_string):
command, *args = command_string.split()
if command not in Command.__members__:
if ':' in command:
return Command['LABEL'], args
else:
raise ValueError(f"Unknown command: {command}")
return Command[command], args
def execute_command(self, command, args):
if command == Command.PUSH:
value = int(args[0]) if 'X' not in args[0] else self.registers[args[0]]
self.stack.append(value)
elif command == Command.POP:
self.stack.pop()
elif command == Command.ADD:
self.stack.append(self.stack.pop() + self.stack.pop())
elif command == Command.SUB:
b, a = self.stack.pop(), self.stack.pop()
self.stack.append(a - b)
elif command == Command.MUL:
self.stack.append(self.stack.pop() * self.stack.pop())
elif command == Command.DIV:
b, a = self.stack.pop(), self.stack.pop()
self.stack.append(a // b)
elif command == Command.MOD:
b, a = self.stack.pop(), self.stack.pop()
self.stack.append(a % b)
elif command == Command.DUP:
self.stack.append(self.stack[-1])
elif command == Command.SWAP:
self.stack[-1], self.stack[-2] = self.stack[-2], self.stack[-1]
elif command == Command.JMP:
self.instruction_pointer = self.labels[args[0]]
return
elif command == Command.JG:
if self.stack.pop() == 1:
self.instruction_pointer = self.labels[args[0]]
return
elif command == Command.JL:
if self.stack.pop() == -1:
self.instruction_pointer = self.labels[args[0]]
return
elif command == Command.JZ:
if self.stack.pop() == 0:
self.instruction_pointer = self.labels[args[0]]
return
elif command == Command.JNZ:
if self.stack.pop() != 0:
self.instruction_pointer = self.labels[args[0]]
return
elif command == Command.CMP:
b, a = self.stack.pop(), self.stack.pop()
self.stack.append((a > b) - (a < b))
elif command == Command.MOV:
self.registers[args[0]] = self.stack[-1]
elif command == Command.NOP:
pass
elif command == Command.HALT:
return
elif command == Command.LABEL:
pass
else:
raise ValueError(f"Unknown command: {command}")
self.instruction_pointer += 1
def run(self):
for _ in range(20000):
if self.instruction_pointer >= len(self.program):
break
command_string = self.program[self.instruction_pointer]
command, args = self.parse_command(command_string)
self.execute_command(command, args)
10. execute_command() 함수 안의 수 많은 if - else 조합을 없애기 위해서 약간 코드를 고칩니다.
def command_push(self, args):
value = int(args[0]) if 'X' not in args[0] else self.registers[args[0]]
self.stack.append(value)
def command_pop(self, args):
self.stack.pop()
위와 같이 함수를 2개 추가하고, 코드를 약간 고쳐 줍니다.
11. 수정 된 코드를 넣고, 리팩토링을 부탁합니다.
from enum import Enum
class Command(Enum):
PUSH = 'PUSH'
POP = 'POP'
ADD = 'ADD'
SUB = 'SUB'
MUL = 'MUL'
DIV = 'DIV'
MOD = 'MOD'
DUP = 'DUP'
SWAP = 'SWAP'
JMP = 'JMP'
JG = 'JG'
JL = 'JL'
JZ = 'JZ'
JNZ = 'JNZ'
CMP = 'CMP'
MOV = 'MOV'
NOP = 'NOP'
HALT = 'HALT'
LABEL = 'LABEL'
class StackMachine:
"""
PUSH <value>: Push a value onto the stack. e.g., PUSH 10
POP: Remove and return the top value from the stack.
ADD: Remove the top two values from the stack, calculate their sum, and push the result onto the stack.
SUB: Remove the top two values from the stack, calculate their difference, and push the result onto the stack.
MUL: Remove the top two values from the stack, calculate their product, and push the result onto the stack.
DIV: Remove the top two values from the stack, calculate their division, and push the result onto the stack.
MOD: Remove the top two values from the stack, calculate their remainder, and push the result onto the stack.
DUP: Copy the top value from the stack and push it onto the stack.
SWAP: Swap the top two values on the stack.
JMP <label>: Move program execution to the given label. e.g., JMP LOOP
JG <label>: If the top value on the stack is greater than 0, move program execution to the given label.
JL <label>: If the top value on the stack is less than 0, move program execution to the given label.
JZ <label>: If the top value on the stack is 0, move program execution to the given label.
JNZ <label>: If the top value on the stack is not 0, move program execution to the given label.
CMP: Compare the top two values on the stack and push the result onto the stack.
(-1: First value is smaller, 0: Both values are equal, 1: First value is larger)
MOV <register>: Move the top value from the stack to the specified register (AX, BX, CX, or DX).
NOP: Perform no operation. This command is ignored in the program.
HALT: Stop program execution.
"""
def __init__(self):
self.stack = []
self.program = []
self.labels = {}
self.registers = {'AX': 0, 'BX': 0, 'CX': 0, 'DX': 0}
self.command_list = {}
self.instruction_pointer = 0
self._init_command_list()
def _init_command_list(self):
self.command_list[Command.PUSH] = self.command_push
self.command_list[Command.POP] = self.command_pop
self.command_list[Command.ADD] = self.command_add
self.command_list[Command.SUB] = self.command_sub
self.command_list[Command.MUL] = self.command_mul
self.command_list[Command.DIV] = self.command_div
self.command_list[Command.MOD] = self.command_mod
self.command_list[Command.DUP] = self.command_dup
self.command_list[Command.SWAP] = self.command_swap
self.command_list[Command.JMP] = self.command_jmp
self.command_list[Command.JG] = self.command_jg
self.command_list[Command.JL] = self.command_jl
self.command_list[Command.JZ] = self.command_jz
self.command_list[Command.JNZ] = self.command_jnz
self.command_list[Command.CMP] = self.command_cmp
self.command_list[Command.MOV] = self.command_mov
self.command_list[Command.NOP] = self.command_nop
self.command_list[Command.HALT] = self.command_halt
self.command_list[Command.LABEL] = self.command_label
def load_program(self, program):
self.program = program
self.labels = {line[:-1]: i for i, line in enumerate(program) if line.endswith(':')}
def parse_command(self, command_string):
command, *args = command_string.split()
if command not in Command.__members__:
if ':' in command:
return Command['LABEL'], args
else:
raise ValueError(f"Unknown command: {command}")
return Command[command], args
def execute_command(self, command, args):
if command in self.command_list:
self.command_list[command](args)
else:
raise ValueError(f"Unknown command: {command}")
self.instruction_pointer += 1
def command_push(self, args):
value = int(args[0]) if 'X' not in args[0] else self.registers[args[0]]
self.stack.append(value)
def command_pop(self, args):
self.stack.pop()
def command_add(self, args):
self.stack.append(self.stack.pop() + self.stack.pop())
def command_sub(self, args):
b, a = self.stack.pop(), self.stack.pop()
self.stack.append(a - b)
def command_mul(self, args):
self.stack.append(self.stack.pop() * self.stack.pop())
def command_div(self, args):
b, a = self.stack.pop(), self.stack.pop()
self.stack.append(a // b)
def command_mod(self, args):
b, a = self.stack.pop(), self.stack.pop()
self.stack.append(a % b)
def command_dup(self, args):
self.stack.append(self.stack[-1])
def command_swap(self, args):
self.stack[-1], self.stack[-2] = self.stack[-2], self.stack[-1]
def command_jmp(self, args):
self.instruction_pointer = self.labels[args[0]]
def command_jg(self, args):
if self.stack.pop() == 1:
self.instruction_pointer = self.labels[args[0]]
def command_jl(self, args):
if self.stack.pop() == -1:
self.instruction_pointer = self.labels[args[0]]
def command_jz(self, args):
if self.stack.pop() == 0:
self.instruction_pointer = self.labels[args[0]]
def command_jnz(self, args):
if self.stack.pop() != 0:
self.instruction_pointer = self.labels[args[0]]
def command_cmp(self, args):
b, a = self.stack.pop(), self.stack.pop()
self.stack.append((a > b) - (a < b))
def command_mov(self, args):
self.registers[args[0]] = self.stack[-1]
def command_nop(self, args):
pass
def command_halt(self, args):
pass
def command_label(self, args):
pass
def run(self):
for _ in range(20000):
if self.instruction_pointer >= len(self.program):
break
command_string = self.program[self.instruction_pointer]
command, args = self.parse_command(command_string)
self.execute_command(command, args)
class StackMachine:
"""
PUSH <value>: Push a value onto the stack. e.g., PUSH 10
POP: Remove and return the top value from the stack.
ADD: Remove the top two values from the stack, calculate their sum, and push the result onto the stack.
SUB: Remove the top two values from the stack, calculate their difference, and push the result onto the stack.
MUL: Remove the top two values from the stack, calculate their product, and push the result onto the stack.
DIV: Remove the top two values from the stack, calculate their division, and push the result onto the stack.
MOD: Remove the top two values from the stack, calculate their remainder, and push the result onto the stack.
DUP: Copy the top value from the stack and push it onto the stack.
SWAP: Swap the top two values on the stack.
JMP <label>: Move program execution to the given label. e.g., JMP LOOP
JZ <label>: If the top value on the stack is 0, move program execution to the given label.
JNZ <label>: If the top value on the stack is not 0, move program execution to the given label.
CMP: Compare the top two values on the stack and push the result onto the stack.
(-1: First value is smaller, 0: Both values are equal, 1: First value is larger)
CALL <label>: Save the current position and move program execution to the given label.
RET: Return to the previously saved position and continue program execution.
NOP: Perform no operation. This command is ignored in the program.
HALT: Stop program execution.
"""
def __init__(self):
self.stack = []
self.program = []
self.labels = {}
self.register = {'AX': 0, 'BX': 0, 'CX': 0, 'DX': 0}
self.ip = 0
def load_program(self, program):
self.program = program
for i, line in enumerate(program):
if line.endswith(':'):
self.labels[line[:-1]] = i
def run(self):
count = 20000
while self.ip < len(self.program) and count > 0:
count -= 1
cmd = self.program[self.ip].split()
op = cmd[0].upper()
if op == 'PUSH':
if type(cmd[1]) == int:
self.stack.append(int(cmd[1]))
else:
self.stack.append(int(self.register[cmd[1]]))
elif op == 'POP':
self.stack.pop()
elif op == 'MOV':
if cmd[1] == 'AX':
self.ax = self.stack[-1]
elif cmd[1] == 'BX':
self.bx = self.stack[-1]
elif cmd[1] == 'CX':
self.cx = self.stack[-1]
elif cmd[1] == 'DX':
self.dx = self.stack[-1]
elif op == 'ADD':
self.stack.append(self.stack.pop() + self.stack.pop())
elif op == 'SUB':
b, a = self.stack.pop(), self.stack.pop()
self.stack.append(a - b)
elif op == 'MUL':
self.stack.append(self.stack.pop() * self.stack.pop())
elif op == 'DIV':
b, a = self.stack.pop(), self.stack.pop()
self.stack.append(a // b)
elif op == 'MOD':
b, a = self.stack.pop(), self.stack.pop()
self.stack.append(a % b)
elif op == 'DUP':
self.stack.append(self.stack[-1])
elif op == 'SWAP':
self.stack[-1], self.stack[-2] = self.stack[-2], self.stack[-1]
elif op == 'JMP':
self.ip = self.labels[cmd[1]]
continue
elif op == 'JG':
if self.stack.pop() == 1:
self.ip = self.labels[cmd[1]]
continue
elif op == 'JL':
if self.stack.pop() == -1:
self.ip = self.labels[cmd[1]]
continue
elif op == 'JZ':
if self.stack.pop() == 0:
self.ip = self.labels[cmd[1]]
continue
elif op == 'JNZ':
if self.stack.pop() != 0:
self.ip = self.labels[cmd[1]]
continue
elif op == 'CMP':
b, a = self.stack.pop(), self.stack.pop()
self.stack.append((a > b) - (a < b))
elif op == 'NOP':
pass
elif op == 'HALT':
break
self.ip += 1
# print(f"{self.stack} / {self.ax}")
7. 주석과 코드가 안 맞습니다. 주석을 코드 베이스로 업데이트 해달라고 요청합니다.
class StackMachine:
"""
PUSH <value>: Push a value onto the stack. e.g., PUSH 10
POP: Remove and return the top value from the stack.
ADD: Remove the top two values from the stack, calculate their sum, and push the result onto the stack.
SUB: Remove the top two values from the stack, calculate their difference, and push the result onto the stack.
MUL: Remove the top two values from the stack, calculate their product, and push the result onto the stack.
DIV: Remove the top two values from the stack, calculate their division, and push the result onto the stack.
MOD: Remove the top two values from the stack, calculate their remainder, and push the result onto the stack.
DUP: Copy the top value from the stack and push it onto the stack.
SWAP: Swap the top two values on the stack.
JMP <label>: Move program execution to the given label. e.g., JMP LOOP
JG <label>: If the top value on the stack is greater than 0, move program execution to the given label.
JL <label>: If the top value on the stack is less than 0, move program execution to the given label.
JZ <label>: If the top value on the stack is 0, move program execution to the given label.
JNZ <label>: If the top value on the stack is not 0, move program execution to the given label.
CMP: Compare the top two values on the stack and push the result onto the stack.
(-1: First value is smaller, 0: Both values are equal, 1: First value is larger)
MOV <register>: Move the top value from the stack to the specified register (AX, BX, CX, or DX).
NOP: Perform no operation. This command is ignored in the program.
HALT: Stop program execution.
"""
def __init__(self):
self.stack = []
self.program = []
self.labels = {}
self.register = {'AX': 0, 'BX': 0, 'CX': 0, 'DX': 0}
self.ip = 0
def load_program(self, program):
self.program = program
for i, line in enumerate(program):
if line.endswith(':'):
self.labels[line[:-1]] = i
def run(self):
count = 20000
while self.ip < len(self.program) and count > 0:
count -= 1
cmd = self.program[self.ip].split()
op = cmd[0].upper()
if op == 'PUSH':
if 'X' in cmd[1]:
self.stack.append(int(self.register[cmd[1]]))
else:
self.stack.append(int(cmd[1]))
elif op == 'POP':
self.stack.pop()
elif op == 'MOV':
self.register[cmd[1]] = self.stack[-1]
elif op == 'ADD':
self.stack.append(self.stack.pop() + self.stack.pop())
elif op == 'SUB':
b, a = self.stack.pop(), self.stack.pop()
self.stack.append(a - b)
elif op == 'MUL':
self.stack.append(self.stack.pop() * self.stack.pop())
elif op == 'DIV':
b, a = self.stack.pop(), self.stack.pop()
self.stack.append(a // b)
elif op == 'MOD':
b, a = self.stack.pop(), self.stack.pop()
self.stack.append(a % b)
elif op == 'DUP':
self.stack.append(self.stack[-1])
elif op == 'SWAP':
self.stack[-1], self.stack[-2] = self.stack[-2], self.stack[-1]
elif op == 'JMP':
self.ip = self.labels[cmd[1]]
continue
elif op == 'JG':
if self.stack.pop() == 1:
self.ip = self.labels[cmd[1]]
continue
elif op == 'JL':
if self.stack.pop() == -1:
self.ip = self.labels[cmd[1]]
continue
elif op == 'JZ':
if self.stack.pop() == 0:
self.ip = self.labels[cmd[1]]
continue
elif op == 'JNZ':
if self.stack.pop() != 0:
self.ip = self.labels[cmd[1]]
continue
elif op == 'CMP':
b, a = self.stack.pop(), self.stack.pop()
self.stack.append((a > b) - (a < b))
elif op == 'NOP':
pass
elif op == 'HALT':
break
self.ip += 1
# print(f"{self.stack} / {self.ax}")
ChatGPT의 도움을 받아서, Python으로 간단한 Stack 기반의 virtual machine을 만들어 보기로 했다.
1. 명령어 정의 하기
Q: stack 기반 simple vm 용 어셈블리 명령어 리스트를 만들어줘
A:
아래는 스택 기반의 간단한 가상 머신 (VM)을 위한 어셈블리 명령어 리스트입니다.
이러한 기본 명령어를 통해 작은 프로그램을 구현하고 실행할 수 있습니다.
PUSH <value>: 스택에 값을 푸시합니다. 예: PUSH 10
POP: 스택의 맨 위 값을 제거하고 반환합니다.
ADD: 스택의 맨 위 두 개의 값을 제거하고 합계를 계산한 후, 스택에 결과를 푸시합니다.
SUB: 스택의 맨 위 두 개의 값을 제거하고 차이를 계산한 후, 스택에 결과를 푸시합니다.
MUL: 스택의 맨 위 두 개의 값을 제거하고 곱을 계산한 후, 스택에 결과를 푸시합니다.
DIV: 스택의 맨 위 두 개의 값을 제거하고 나눗셈을 계산한 후, 스택에 결과를 푸시합니다.
(0으로 나누는 경우에 대한 오류 처리를 고려하세요.)
MOD: 스택의 맨 위 두 개의 값을 제거하고 나머지를 계산한 후, 스택에 결과를 푸시합니다.
DUP: 스택의 맨 위 값을 복사하여 스택에 푸시합니다.
SWAP: 스택의 맨 위 두 개의 값을 교환합니다.
JMP <label>: 주어진 레이블로 프로그램 실행을 이동합니다. 예: JMP LOOP
JZ <label>: 스택의 맨 위 값이 0이면 주어진 레이블로 프로그램 실행을 이동합니다.
JNZ <label>: 스택의 맨 위 값이 0이 아니면 주어진 레이블로 프로그램 실행을 이동합니다.
CMP: 스택의 맨 위 두 개의 값을 비교하고 결과를 스택에 푸시합니다.
(-1: 첫 번째 값이 작음, 0: 두 값이 같음, 1: 첫 번째 값이 큼)
CALL <label>: 현재 위치를 저장하고 주어진 레이블로 프로그램 실행을 이동합니다.
RET: 이전에 저장된 위치로 돌아가 프로그램 실행을 계속합니다.
NOP: 아무 작업도 수행하지 않습니다. 이 명령은 프로그램에서 무시됩니다.
HALT: 프로그램 실행을 중지합니다.
이 명령어들은 가장 기본적인 스택 기반 VM을 위한 것이며,
필요에 따라 추가 명령어를 구현하여 더 복잡한 기능을 수행할 수 있습니다.
2. 명령어를 실행 할 수 있는 VM 만들기
위 명령어로 1부터 10까지 곱하는 어셈블리 프로그램을 짜줘
1부터 10까지 곱하는 스택 기반 가상 머신의 어셈블리 프로그램을 작성해 보겠습니다.
이 프로그램은 10! (10 팩토리얼)을 계산하는 것과 동일합니다. 결과는 스택의 맨 위에 있게 됩니다.
vbnet
Copy code
; 팩토리얼 계산 시작
PUSH 1 ; 스택에 초기 결과 (1)을 푸시
PUSH 1 ; 스택에 초기 반복 횟수 (1)을 푸시
; 루프 시작
LOOP:
DUP ; 반복 횟수를 복사
PUSH 10 ; 10을 스택에 푸시
CMP ; 반복 횟수와 10을 비교
JG END_LOOP ; 반복 횟수가 10보다 크면 루프 종료
DUP ; 반복 횟수를 복사
MUL ; 현재 결과와 반복 횟수를 곱하기
PUSH 1 ; 1을 스택에 푸시
ADD ; 반복 횟수 증가
JMP LOOP ; 루프로 돌아가기
; 루프 종료
END_LOOP:
NOP ; 여기서 프로그램이 끝납니다. 필요한 경우 HALT 명령어를 사용할 수 있습니다.
이 프로그램을 실행하면 스택의 맨 위에 10! (3,628,800)이라는 결과가 남게 됩니다.
이렇게 버그가 포함된 코드를 짜주셨다. 이걸 실행 하면, 26이 나온다.
왜냐면 아래 코드가 잘못되었기 때문이다.
DUP ; 반복 횟수를 복사 MUL ; 현재 결과와 반복 횟수를 곱하기
야간의 삽질 끝에 고친 실제 동작하는 코드는 아래와 같다. (ChatGPT 의 코드를 디버깅 하고 있었다;;;)
; 팩토리얼 계산 시작
PUSH 1 ; 스택에 초기 결과 (1)을 푸시
PUSH 1 ; 스택에 초기 반복 횟수 (1)을 푸시
; 루프 시작
LOOP:
DUP ; 반복 횟수를 복사
PUSH 10 ; 10을 스택에 푸시
CMP ; 반복 횟수와 10을 비교
JG END_LOOP ; 반복 횟수가 10보다 크면 루프 종료
MOV AX ; 반복 횟수를 AX 레지스터에 복사
MUL ; 현재 결과와 반복 횟수를 곱하기
PUSH AX ; AX 레지스터의 값을 스택에 푸시
PUSH 1 ; 1을 스택에 푸시
ADD ; 반복 횟수 증가
JMP LOOP ; 루프로 돌아가기
; 루프 종료
END_LOOP:
NOP ; 여기서 프로그램이 끝납니다. 필요한 경우 HALT 명령어를 사용할 수 있습니다.
이 프로그램을 실행하면 스택의 맨 위에 10! (3,628,800)이라는 결과가 남게 됩니다.
3. 위 코드를 실행 할 수 있는 VM 코드 생성
Q: 위 스택머신을 이용해서 파이썬으로 gwbasic 인터프리터 만들어줘
A:
GW-BASIC 인터프리터를 파이썬으로 만드는 것은 복잡한 작업입니다.
그러나 여기 간단한 스택 기반 가상 머신 인터프리터를 제공합니다.
이 인터프리터는 위에서 제공한 어셈블리 명령어를 사용할 수 있습니다.
python
Copy code
class StackMachine:
def __init__(self):
self.stack = []
self.program = []
self.labels = {}
self.ip = 0
def load_program(self, program):
self.program = program
for i, line in enumerate(program):
if line.endswith(':'):
self.labels[line[:-1]] = i
def run(self):
while self.ip < len(self.program):
cmd = self.program[self.ip].split()
op = cmd[0].upper()
if op == 'PUSH':
self.stack.append(int(cmd[1]))
elif op == 'POP':
self.stack.pop()
elif op == 'ADD':
self.stack.append(self.stack.pop() + self.stack.pop())
elif op == 'SUB':
b, a = self.stack.pop(), self.stack.pop()
self.stack.append(a - b)
elif op == 'MUL':
self.stack.append(self.stack.pop() * self.stack.pop())
elif op == 'DIV':
b, a = self.stack.pop(), self.stack.pop()
self.stack.append(a // b)
elif op == 'MOD':
b, a = self.stack.pop(), self.stack.pop()
self.stack.append(a % b)
elif op == 'DUP':
self.stack.append(self.stack[-1])
elif op == 'SWAP':
self.stack[-1], self.stack[-2] = self.stack[-2], self.stack[-1]
elif op == 'JMP':
self.ip = self.labels[cmd[1]]
continue
elif op == 'JZ':
if self.stack.pop() == 0:
self.ip = self.labels[cmd[1]]
continue
elif op == 'JNZ':
if self.stack.pop() != 0:
self.ip = self.labels[cmd[1]]
continue
elif op == 'CMP':
b, a = self.stack.pop(), self.stack.pop()
self.stack.append((a > b) - (a < b))
elif op == 'NOP':
pass
elif op == 'HALT':
break
self.ip += 1
# 예제 프로그램
program = [
'PUSH 1',
'PUSH 1',
'LOOP:',
'DUP',
'PUSH 10',
'CMP',
'JG END_LOOP',
'DUP',
'MUL',
'PUSH 1',
'ADD',
'JMP LOOP',
'END_LOOP:',
'NOP',
]
# 스택 머신 인스턴스 생성
stack_machine = StackMachine()
# 프로그램 로드 및 실행
stack_machine.load_program(program)
stack_machine.run()
# 결과 출력
print("계산 결과:", stack_machine.stack[-1])
이 코드는 간단한 스택 기반 가상 머신 인터프리터를 구현합니다.
이것은 GW-BASIC 인터프리터가 아니지만, 이 인터프리터를 확장하여
GW-BASIC 인터프리터를 구현하는 데 사용할 수 있습니다.
GW-BASIC 인터프리터를 구현하려면 다음 단계를 수행하세요:
GW-BASIC 문법 정의: GW-BASIC의 문법을 정의하고 토큰화합니다.
키워드, 상수, 변수, 연산자, 함수 등을 인식하도록 토크나이저를 작성해야 합니다.
파서 작성: 토크나이저에서 생성된 토큰을 사용하여 문법 트리를 작성하는 파서를 구현합니다.
이 파서는 GW-BASIC 프로그램을 읽고 실행 가능한 중간 표현으로 변환합니다.
실행 엔진 확장: 현재 스택 기반 가상 머신을 확장하여 GW-BASIC의
기능에 맞게 사용자 정의 변수, 프로시저, 함수, 조건문, 반복문 등을 처리할 수 있도록 해야 합니다.
입출력 및 에러 처리: GW-BASIC 인터프리터는 사용자 입력을 처리하고 출력을 표시해야 합니다.
또한 실행 중 발생할 수 있는 에러를 적절하게 처리해야 합니다.
테스트: 다양한 GW-BASIC 프로그램을 사용하여 인터프리터를 테스트하고 버그를 수정합니다.
위 단계를 따르면 파이썬으로 GW-BASIC 인터프리터를 만드는 데 도움이 됩니다.
그러나 이 작업은 상당한 시간과 노력이 필요합니다.
이미 만들어진 BASIC 인터프리터가 여러 개 있으므로,
이러한 프로젝트 중 하나를 참조하거나 사용하는 것도 좋은 방법입니다.
이러한 프로젝트 중 하나는 pcbasic이며,
다음 링크에서 확인할 수 있습니다: https://github.com/robhagemans/pcbasic
일단 위와 같이 대충 돌아가는 것 처럼 보이는 코드를 작성해 주신다. (물론 안돌아 간다)
A: Python API 작명법은 일반적으로 PEP 8 스타일 가이드에 따라 작성됩니다. PEP 8은 파이썬의 공식 스타일 가이드로, 코드를 읽기 쉽고 이해하기 쉽게 만들어줍니다. API 작성에도 적용할 수 있는 일반적인 규칙은 다음과 같습니다.
함수와 메서드의 이름은 소문자와 밑줄을 사용하여 작성합니다.
변수와 함수 매개변수의 이름도 소문자와 밑줄을 사용하여 작성합니다.
클래스의 이름은 대문자로 시작하는 카멜 케이스(CamelCase)로 작성합니다.
모듈의 이름도 소문자와 밑줄을 사용하여 작성합니다.
예를 들어, 다음은 PEP 8에 따른 예시입니다.
# 함수와 메서드의 이름
def calculate_average(numbers_list):
...
class MyClass:
def __init__(self):
...
# 변수와 함수 매개변수의 이름
my_variable = 42
def print_message(message):
...
# 모듈의 이름
import my_module
API 작성에는 더욱 구체적인 규칙이 존재할 수 있습니다. 따라서 해당 API의 문서를 참조하여 작명법을 따르는 것이 좋습니다.
상수의 이름은 모두 대문자와 밑줄을 사용하여 작성합니다.
함수나 메서드의 이름은 가능한 명사 또는 동사로 작성합니다. 함수나 메서드의 이름에서 명사와 동사는 함께 사용하여 기능을 명확하게 설명해야 합니다.
클래스의 이름은 명사로 작성하며, 클래스 이름이 여러 단어로 이루어진 경우 각 단어의 첫 글자를 대문자로 작성합니다. 이 스타일을 카멜 케이스(CamelCase)라고 합니다.
모듈의 이름은 짧고 명확해야 하며, 가능한 단일 단어로 작성하는 것이 좋습니다. 모듈의 이름도 카멜 케이스를 사용할 수 있지만, 보통은 소문자와 밑줄을 사용하여 작성합니다.
예를 들어, 다음은 PEP 8에 따른 예시입니다.
# 상수의 이름
MAXIMUM_NUMBER = 100
# 함수나 메서드의 이름
def calculate_average(numbers_list):
...
class MyClass:
def __init__(self):
...
def perform_action(self):
...
# 클래스의 이름
class MyClassName:
...
# 모듈의 이름
import my_module
API 작성 시에는 일관성이 매우 중요합니다. 같은 기능을 수행하는 함수나 메서드의 이름이 다른 경우, 사용자가 API를 이해하는 데 어려움을 겪을 수 있습니다. 따라서 API 작성 시에는 팀의 스타일 가이드를 따르거나, 기존 코드와 일관성 있게 작성하는 것이 좋습니다.
함수나 메서드의 이름에는 가능한한 매개변수의 자료형을 포함하지 않습니다. 함수나 메서드의 매개변수는 일반적으로 자료형에 대한 힌트(hint)를 포함할 수 있지만, 함수나 메서드의 이름에는 자료형에 대한 정보가 포함되어 있지 않아야 합니다.
함수나 메서드의 이름은 가능한한 짧게 작성하지 않습니다. 함수나 메서드의 이름은 가능한한 짧게 작성하는 것이 좋습니다. 그러나, 이름이 너무 짧으면 기능을 명확하게 설명하지 못할 수 있습니다. 따라서 이름은 가능한한 기능을 명확하게 설명할 수 있도록 작성하는 것이 좋습니다.
예를 들어, 다음은 PEP 8에 따른 예시입니다.
# 함수나 메서드의 이름에 자료형을 포함하지 않기
def calculate_average(numbers):
...
def add_numbers(x: int, y: int) -> int:
...
# 함수나 메서드의 이름이 너무 짧지 않게 작성하기
def calculate_average_of_numbers_in_list(numbers_list):
...
def add_two_numbers_and_return_result(first_number, second_number):
...
API 작성 시에는 사용자가 쉽게 이해할 수 있도록 기능을 명확하게 설명하는 것이 중요합니다. 또한, 코드의 가독성을 높이기 위해 이름을 가능한한 명확하게 작성하는 것이 좋습니다.
import numpy as np
import matplotlib.pyplot as plt
# 파라미터 정의
r = 1 # 원의 반지름
a = 1 # 원주와 돌기의 간격
# 각도 범위 설정
t = np.linspace(0, 4*np.pi, 1000)
# 싸이클로이드의 수식
x = r * (t - np.sin(t))
y = r * (1 - np.cos(t)) - a
# 그래프 그리기
fig, ax = plt.subplots(figsize=(8, 8))
ax.plot(x, y, color='blue', linewidth=2)
ax.set_aspect('equal', adjustable='box')
ax.set_title('Cycloid')
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.grid(True)
plt.show()