Coding/Python 삽질기2024. 5. 11. 00:34

1. Tab : 자동 완성

2. Shift + Tab : 함수의 설명을 보여준다 (Docstring)

3. ESC (명령어 모드) 에서 Shfit + L : 줄번호를 보여준다

 

4. ESC (명령어 모드) 에서 D, D : 해당 셀을 삭제 한다

5. ESC (명령어 모드) 에서 A : 해당 셀 아래에 새로운 행을 추가한다

6. Ctrl + Enter : 해당 셀을 실행한다

7. Alt + Enter :  해당 셀을 실행하고 아래에 새로운 셀을 추가한다

8. Shift + Enter : 해당 셀을 실행하고 아래 셀로 이동한다. 만약 아래 셀이 없으면 새로운 셀을 추가한다.

'Coding > Python 삽질기' 카테고리의 다른 글

[SQL] BEGIN TRANSACTION  (0) 2024.03.07
Cardioid 그리기  (0) 2024.02.17
[Python] PNG to ICO 변환 하기  (0) 2024.01.04
Posted by chobocho
Coding/Python 삽질기2024. 3. 7. 00:45

Python으로 작성한 SQL문에서 대량의 insert를 수행시 느려지는 문제가 있어서, 

DB전문가에게 문의를 하니, 간단히 답을 알려 주셨다.

해결책은 "BEGIN TRANSACTION"을 사용하면 되는 것이었다.

이 한줄을 넣고 나니, 10배 이상 속도가 빨라졌다;;

[기존코드]

    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

 

'Coding > Python 삽질기' 카테고리의 다른 글

[Python] Jupyter notebook 팁  (0) 2024.05.11
Cardioid 그리기  (0) 2024.02.17
[Python] PNG to ICO 변환 하기  (0) 2024.01.04
Posted by chobocho
Coding/Python 삽질기2024. 2. 17. 19:16
import math
import pygame


def init_game():
    pygame.init()
    pygame.key.set_repeat(0)
    canvas = pygame.display.set_mode((800, 600))
    fps_clock = pygame.time.Clock()
    return canvas, fps_clock


def main():
    canvas, fps_clock = init_game()

    title = 'Cardioid'
    pygame.display.set_caption(title)
    max_dots = 300

    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                return

        draw_cardioid(canvas, max_dots)
        pygame.display.update()
        fps_clock.tick(30)


def draw_cardioid(canvas, max_dots):

    canvas.fill((0, 0, 0))
    start_x = canvas.get_width() // 2
    start_y = canvas.get_height() // 2
    radius = 250

    for i in range(max_dots):
        radian = (math.pi / 180) * (360 / max_dots) * i
        sx = start_x + int(math.cos(radian) * radius)
        sy = start_y + int(math.sin(radian) * radius)

        ex = start_x + int(math.cos(radian * 2) * radius)
        ey = start_y + int(math.sin(radian * 2) * radius)

        pygame.draw.line(canvas, (0, 255, 0), (sx, sy), (ex, ey))


if __name__ == '__main__':
    main()

100개의 점을 사용한 경우
200개의 점을 사용한 경우
300개의 점을 사용한 경우

'Coding > Python 삽질기' 카테고리의 다른 글

[SQL] BEGIN TRANSACTION  (0) 2024.03.07
[Python] PNG to ICO 변환 하기  (0) 2024.01.04
[Python] 프랙탈 나무 만들기  (0) 2023.09.15
Posted by chobocho
Coding/Python 삽질기2024. 1. 4. 00:01
import sys

from PIL import Image


def convert_png_to_ico(png_path, ico_path, size=128):
    icon_size = (size, size)
    img = Image.open(png_path)
    img = img.resize(size=icon_size)
    img.save(ico_path)


def print_icon_file_info(ico_path):
    ico_image = Image.open(ico_path)
    print(f"Image type: {ico_image.format}\nSize: {ico_image.size}")


def main(source_file, target_file, icon_size=128):
    allowed_icon_size = (16, 32, 64, 128, 256)
    if icon_size not in allowed_icon_size:
        print("Error: Invalid icon size.\nAllowed size = 16, 32, 64, 128, 56\n")
        return

    convert_png_to_ico(source_file, target_file, size=icon_size)
    print_icon_file_info(target_file)


if __name__ == '__main__':
    if len(sys.argv) == 3:
        main(sys.argv[1], f"{sys.argv[1]}.ico", int(sys.argv[2]))
    elif len(sys.argv) == 2:
        main(sys.argv[1], f"{sys.argv[1]}.ico")
    else:
        print("Usage: ImageFileName [size]\n")

'Coding > Python 삽질기' 카테고리의 다른 글

Cardioid 그리기  (0) 2024.02.17
[Python] 프랙탈 나무 만들기  (0) 2023.09.15
[Python] Python 소스 코드 빌드 해보기  (1) 2023.09.06
Posted by chobocho
Coding/Python 삽질기2023. 9. 15. 00:48

오래 전, 라떼 시절 만들었던 파이썬 코드를  Chat GPT를 이용하여 개선해 보았다.

Python 2.X 시절 만든 코드를 3.X로 바꾸면서 저장 기능 까지 추가 하였다.

[원본 코드]

https://chobocho.tistory.com/2460823

 

Fractal Tree

#-*- coding: cp949 -*- from Tkinter import * import math import random RADIAN = math.pi / 180 aColor = [ "brown", "red", "blue", "green", "orange", "black" ] def DrawTree( startX, startY, angle, length, depth ): if depth == 0: return rand=random.random; en

chobocho.tistory.com

 

[개선 코드]

 

from tkinter import *
import math
import random
from PIL import Image, ImageDraw

RADIAN = math.pi / 180


def random_color():
    r = random.randint(0, 255)
    g = random.randint(0, 255)
    b = random.randint(0, 255)
    return r, g, b


def draw_tree(start_x, start_y, angle, length, depth, image_draw=None):
    if depth == 0:
        return

    scale = int(depth/12) + 1
    end_x = start_x + length * scale * math.cos(angle * RADIAN)
    end_y = start_y - length * scale * math.sin(angle * RADIAN)

    image_draw.line([start_x, start_y, end_x, end_y], width=depth, fill=random_color())

    draw_tree(end_x, end_y, angle + random.randint(10, 30), length * random.uniform(0.8, 0.9), depth - 1, image_draw)
    draw_tree(end_x, end_y, angle - random.randint(10, 30), length * random.uniform(0.8, 0.9), depth - 1, image_draw)


def draw_new_tree():
    myCanvas.delete("all")  # Clear the canvas

    image = Image.new("RGB", (800, 600), "white")
    image_draw = ImageDraw.Draw(image)
    draw_tree(400, 550, 90, 70, 12, image_draw)
    image.save("fractal_tree.png", "PNG")  # Save the image as PNG

    photo = PhotoImage(file="fractal_tree.png")
    myCanvas.create_image(0, 0, image=photo, anchor=NW)
    myCanvas.photo = photo


if __name__ == "__main__":
    root = Tk()
    root.title("프랙탈 나무")
    root.geometry("800x600")

    myCanvas = Canvas(width=800, height=600)

    draw_button = Button(text="새로운 나무 그리기", command=draw_new_tree)
    draw_button.pack()

    myCanvas.pack()
    root.mainloop()

 

'Coding > Python 삽질기' 카테고리의 다른 글

[Python] PNG to ICO 변환 하기  (0) 2024.01.04
[Python] Python 소스 코드 빌드 해보기  (1) 2023.09.06
HTTP Protocol  (0) 2023.08.18
Posted by chobocho
Coding/Python 삽질기2023. 9. 6. 23:44

파이썬 소스 코드를 빌드해서 python.exe  를 만들어 보자

1. 빌드 환경 구성

  • Visusal Studio를 설치한다
  • Python 3.10버전을 설치한다

 

2. 파이썬 소스를 다운 받는다 (우리는 3.11을 사용한다)

https://www.python.org/ftp/python/3.11.5/Python-3.11.5.tgz

 

3. 적당한 폴더에 압축을 푼다

4. 설치경로\PCbuild\build.bat 를 실행한다

5. 잠시 뒤 

설치경로\PCbuild\amd64\python.exe 가 생성 됨을 볼수있다.

'Coding > Python 삽질기' 카테고리의 다른 글

[Python] 프랙탈 나무 만들기  (0) 2023.09.15
HTTP Protocol  (0) 2023.08.18
[AI] Stable Diffusion 설치 하기  (0) 2023.06.09
Posted by chobocho
Coding/Python 삽질기2023. 8. 18. 00:22

■  HTTP 1.0 RFC
https://datatracker.ietf.org/doc/html/rfc1945

■  HTTP 1.1 RFC
https://datatracker.ietf.org/doc/html/rfc2616

■ 메시지 포멧
□ Request
Request-Line = Method SP Request-URI SP HTTP-Version CRLF

□ 예시1
GET /user/chobocho27 HTTP/1.1

□ 예시2
GET / HTTP/1.1
Host: 127.0.0.1:5000
Connection: keep-alive
Cache-Control: max-age=0
Sec-Ch-Ua: "Not/A)Brand";v="99", "Google Chrome";v="115", "Chromium";v="115"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7

□ Response
HTTP/1.1 200 OK

 

'Coding > Python 삽질기' 카테고리의 다른 글

[Python] Python 소스 코드 빌드 해보기  (1) 2023.09.06
[AI] Stable Diffusion 설치 하기  (0) 2023.06.09
[Python] PyTorch 설치  (0) 2023.04.05
Posted by chobocho
Coding/Python 삽질기2023. 6. 9. 23:28

1. Python 3.10.6 설치 

https://www.python.org/downloads/release/python-3106/

 

Python Release Python 3.10.6

The official home of the Python Programming Language

www.python.org

2.  Git 설치

https://git-scm.com/downloads

 

Git - Downloads

Downloads macOS Windows Linux/Unix Older releases are available and the Git source repository is on GitHub. GUI Clients Git comes with built-in GUI tools (git-gui, gitk), but there are several third-party tools for users looking for a platform-specific exp

git-scm.com

3. Stable diffusion webui 설치

https://github.com/AUTOMATIC1111/stable-diffusion-webui.git

 

GitHub - AUTOMATIC1111/stable-diffusion-webui: Stable Diffusion web UI

Stable Diffusion web UI. Contribute to AUTOMATIC1111/stable-diffusion-webui development by creating an account on GitHub.

github.com

git clone https://github.com/AUTOMATIC1111/stable-diffusion-webui

4. 모델 다운로드

https://civitai.com/

 

Civitai | Stable Diffusion models, embeddings, LoRAs and more

Civitai is a platform for Stable Diffusion AI Art models. Browse a collection of thousands of models from a growing number of creators. Join an engaged community in reviewing models and sharing images with prompts to get you started.

civitai.com

https://huggingface.co/WarriorMama777/OrangeMixs

 

WarriorMama777/OrangeMixs · Hugging Face

AOM3 Counterfeit2.5 Add SUM @ 1.0 0,0.6,0.6,0.6,0.6,0.6,0,0,0,0,0.6,0.1,0.6,0.6,0.6,0.6,0.6,0.5,0.1,0.1,0.6,0.6,0.2,0.6,0.6,0.6 AOM3A3

huggingface.co

 

모델을 다운 받아서, \stable-diffusion-webui\models\Stable-diffusion 에 넣어 준다.

확장자가 .safetensors 인 것을 받는 걸 추천 한다.

5. 실행

webui-user.bat 를 수행한다. (첫 실행시 이것 저것 설치한다고 시간이 걸린다.)

6. http://127.0.0.1:7860/ 로 웹 브라우저로 접속 하면 된다.

'Coding > Python 삽질기' 카테고리의 다른 글

HTTP Protocol  (0) 2023.08.18
[Python] PyTorch 설치  (0) 2023.04.05
[ChatGPT에게 묻다] python으로 Simple Stack VM 만들기 (3)  (0) 2023.03.17
Posted by chobocho
Coding/Python 삽질기2023. 4. 5. 01:25

1. 아래 사이트에서  설치해야 하는 PyTorch 버전 체크

https://pytorch.org/get-started/locally/

 

PyTorch

An open source machine learning framework that accelerates the path from research prototyping to production deployment.

pytorch.org

2. Command를 복사하여 실행한다.

pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu117

3.  Python을 실행하여 확인 한다.

 

Posted by chobocho
Coding/Python 삽질기2023. 3. 17. 01:20

2부에 이어서, 3부를 시작 합니다.

 

8. 이제 생성된 코드를 리팩토링 해달라고 부탁 합니다.

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)

위와 같이 아름답게 코드를 고쳐 주십니다.

그래서 나온 최종 버전은 아래에 있습니다.

https://github.com/chobocho/simple_vm

 

GitHub - chobocho/simple_vm: stack based simple stack machine

stack based simple stack machine. Contribute to chobocho/simple_vm development by creating an account on GitHub.

github.com

 

결론: 

1) 웬만하면 구글링 해서 open source 를 쓰자.

2) ChatGPT-4도 완전한 코드를 짜주지는 않는다 (디버깅은 필수)

Posted by chobocho