Refactor project structure: update .gitignore, and add base and utils modules for improved functionality

This commit is contained in:
Chipperfluff 2026-04-03 13:50:32 +02:00
parent df67db7c00
commit 09b585d5f6
5 changed files with 158 additions and 5 deletions

4
.gitignore vendored
View File

@ -1,4 +1,6 @@
venv/ venv/
__pycache__/ __pycache__/
build/ build/
nut.egg-info/ nut.egg-info/
*.zip
*.nut

View File

@ -1,3 +0,0 @@
"""nut package."""
__version__ = "0.1.0"

View File

@ -1,5 +1,5 @@
import sys import sys
from .utils import zip_dir
def main() -> int: def main() -> int:
for arg in sys.argv[1:]: for arg in sys.argv[1:]:

58
nut/builtins/base.py Normal file
View File

@ -0,0 +1,58 @@
import sys
class _Exit:
def __init__(self, code):
self.code = code
class _Jump:
def __init__(self, target):
self.target = target
class _Jmp(_Jump):
pass
class _Call(_Jump):
RETURN = object()
class _VM:
def __init__(self, entry, ctx=None):
self.current = entry
self.stack = []
self.ctx = ctx if ctx is not None else {}
def start(self):
while self.current is not None:
try:
self.loop()
except Exception as e:
print(f"Error: {e}")
sys.exit(1)
def loop(self):
action = self.current(self.ctx)
if isinstance(action, _Jmp):
self.current = action.target
return
if isinstance(action, _Call):
self.stack.append(self.current)
self.current = action.target
return
if action is _Call.RETURN:
if not self.stack:
self.current = None
return
self.current = self.stack.pop()
return
if isinstance(action, _Exit):
sys.exit(action.code)
print(f"Unknown action: {action}")
sys.exit(1)

96
nut/utils.py Normal file
View File

@ -0,0 +1,96 @@
import os
import zipfile
import json
import fnmatch
def _should_ignore(rel_path, patterns):
"""
Check if a file should be ignored based on patterns.
"""
rel_path = rel_path.replace("\\", "/")
for pattern in patterns:
pattern = pattern.strip()
if not pattern:
continue
if pattern.endswith("/"):
if rel_path.startswith(pattern):
return True
if fnmatch.fnmatch(rel_path, pattern):
return True
return False
def zip_dir(source_dir, output_zip_path, metadata=None, ignore_patterns=None):
"""
Compress a directory into a zip file with ignore support.
:param source_dir: path to folder
:param output_zip_path: where to save zip
:param metadata: dict -> stored as _meta.json inside zip
:param ignore_patterns: list like gitignore ["*.log", "__pycache__/", "build/*"]
"""
ignore_patterns = ignore_patterns or []
with zipfile.ZipFile(output_zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
for root, dirs, files in os.walk(source_dir):
rel_root = os.path.relpath(root, source_dir).replace("\\", "/")
# filter dirs in-place (important for performance)
dirs[:] = [
d for d in dirs
if not _should_ignore(
os.path.join(rel_root, d).replace("\\", "/"),
ignore_patterns
)
]
for file in files:
full_path = os.path.join(root, file)
rel_path = os.path.relpath(full_path, source_dir).replace("\\", "/")
if _should_ignore(rel_path, ignore_patterns):
continue
zipf.write(full_path, rel_path)
if metadata:
zipf.writestr("_meta.json", json.dumps(metadata, indent=2))
def unzip_to_dir(zip_path, output_dir):
"""
Extract zip file to a directory.
"""
with zipfile.ZipFile(zip_path, 'r') as zipf:
zipf.extractall(output_dir)
def unzip_in_memory(zip_path):
"""
Read zip contents into memory only.
:return: dict {filename: bytes}, metadata separately if exists
"""
data = {}
metadata = None
with zipfile.ZipFile(zip_path, 'r') as zipf:
for name in zipf.namelist():
content = zipf.read(name)
if name == "_meta.json":
metadata = json.loads(content.decode())
else:
data[name] = content
return {
"files": data,
"metadata": metadata
}