diff --git a/build1.py b/build1.py
new file mode 100644
index 0000000..1529cce
--- /dev/null
+++ b/build1.py
@@ -0,0 +1,653 @@
+#!/usr/bin/env python3
+import os
+import sys
+import subprocess
+import platform
+from pathlib import Path
+
+# 定义颜色类
+class Colors:
+ RESET = '\033[0m'
+ RED = '\033[31m'
+ GREEN = '\033[32m'
+ YELLOW = '\033[33m'
+ BLUE = '\033[34m'
+ MAGENTA = '\033[35m'
+ CYAN = '\033[36m'
+ WHITE = '\033[37m'
+ BOLD = '\033[1m'
+ BOLD_GREEN = '\033[1;32m'
+ BOLD_YELLOW = '\033[1;33m'
+ BOLD_CYAN = '\033[1;36m'
+ BOLD_RED = '\033[1;31m'
+
+# 确保Windows终端支持ANSI颜色
+def enable_windows_ansi_support():
+ if platform.system() == 'Windows':
+ try:
+ import ctypes
+ kernel32 = ctypes.windll.kernel32
+ kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)
+ except:
+ pass
+
+# 打印带颜色的文本
+def print_color(text, color=Colors.RESET):
+ print(f"{color}{text}{Colors.RESET}")
+
+# 运行命令并返回结果
+def run_command(cmd, cwd=None, shell=True, check=False):
+ try:
+ result = subprocess.run(cmd, cwd=cwd, shell=shell, check=check,
+ capture_output=True, text=True)
+ return result
+ except subprocess.CalledProcessError as e:
+ return e
+
+# 构建Windows EXE
+def build_exe():
+ print_color("\n========================================", Colors.BOLD_CYAN)
+ print_color("Building Windows EXE", Colors.BOLD_YELLOW)
+ print_color("========================================", Colors.BOLD_CYAN)
+ print()
+
+ print_color("Checking PyInstaller...", Colors.CYAN)
+ result = run_command("pip show pyinstaller", check=False)
+ if result.returncode != 0:
+ print_color("PyInstaller not found, installing...", Colors.YELLOW)
+ result = run_command("pip install pyinstaller", check=True)
+ if result.returncode != 0:
+ print_color("Failed to install PyInstaller", Colors.BOLD_RED)
+ input("Press Enter to continue...")
+ return False
+
+ print_color("Building scl.py...", Colors.CYAN)
+ result = run_command(
+ "pyinstaller --onefile --name scl --distpath dist\\exe --workpath build\\exe --specpath build\\exe scl.py",
+ check=True
+ )
+ if result.returncode != 0:
+ print_color("Failed to build scl.py", Colors.BOLD_RED)
+ input("Press Enter to continue...")
+ return False
+
+ print()
+ print_color("EXE build completed!", Colors.BOLD_GREEN)
+ print_color("Output directory: dist\\exe", Colors.GREEN)
+ print()
+
+ print_color("Copying plugins directory...", Colors.CYAN)
+ os.makedirs("dist\\exe\\plugins", exist_ok=True)
+ result = run_command("xcopy /E /I /Y plugins dist\\exe\\plugins", check=False)
+ print()
+ input("Press Enter to continue...")
+ return True
+
+# 构建Windows EXE - 愚人节版本
+def build_exe_april_fools():
+ print_color("\n========================================", Colors.BOLD_CYAN)
+ print_color("Building Windows EXE - April Fools Edition", Colors.BOLD_YELLOW)
+ print_color("========================================", Colors.BOLD_CYAN)
+ print()
+
+ print_color("Checking PyInstaller...", Colors.CYAN)
+ result = run_command("pip show pyinstaller", check=False)
+ if result.returncode != 0:
+ print_color("PyInstaller not found, installing...", Colors.YELLOW)
+ result = run_command("pip install pyinstaller", check=True)
+ if result.returncode != 0:
+ print_color("Failed to install PyInstaller", Colors.BOLD_RED)
+ input("Press Enter to continue...")
+ return False
+
+ if not os.path.exists("sdp_yrj.py"):
+ print_color("Error: sdp_yrj.py not found", Colors.BOLD_RED)
+ input("Press Enter to continue...")
+ return False
+
+ print_color("Building sdp_yrj.py...", Colors.CYAN)
+ result = run_command(
+ "pyinstaller --onefile --name sdp_yrj --distpath dist\\exe --workpath build\\exe --specpath build\\exe sdp_yrj.py",
+ check=False
+ )
+ if result.returncode != 0:
+ print_color("Failed to build sdp_yrj.py", Colors.BOLD_RED)
+ else:
+ print_color("Successfully built sdp_yrj.exe", Colors.BOLD_GREEN)
+
+ print()
+ print_color("April Fools EXE build completed!", Colors.BOLD_GREEN)
+ print_color("Output directory: dist\\exe", Colors.GREEN)
+ print()
+
+ print_color("Copying plugins directory...", Colors.CYAN)
+ os.makedirs("dist\\exe\\plugins", exist_ok=True)
+ result = run_command("xcopy /E /I /Y plugins dist\\exe\\plugins", check=False)
+ print()
+ input("Press Enter to continue...")
+ return True
+
+# 构建Linux DEB - SCL Full Package (via WSL)
+def build_deb():
+ print_color("\n========================================", Colors.BOLD_CYAN)
+ print_color("Building Linux DEB via WSL", Colors.BOLD_YELLOW)
+ print_color("========================================", Colors.BOLD_CYAN)
+ print()
+
+ print_color("Checking WSL...", Colors.CYAN)
+ result = run_command("wsl echo 'WSL OK'", check=False)
+ if result.returncode != 0:
+ print_color("WSL is not installed or not available", Colors.BOLD_RED)
+ print_color("Please install WSL first:", Colors.YELLOW)
+ print_color(" wsl --install", Colors.GREEN)
+ input("Press Enter to continue...")
+ return False
+
+ print_color("WSL found. Preparing build environment...", Colors.GREEN)
+
+ # 获取当前目录的WSL路径
+ result = run_command("wsl wslpath '%cd%'", check=True)
+ wsl_path = result.stdout.strip()
+ print_color(f"WSL path: {wsl_path}", Colors.BLUE)
+
+ print_color("Creating dist directory...", Colors.CYAN)
+ os.makedirs("dist", exist_ok=True)
+
+ print_color("Copying build script to WSL...", Colors.CYAN)
+ result = run_command(f"wsl cp '{wsl_path}/build_deb_wsl.sh' /tmp/build_deb_wsl.sh", check=True)
+ result = run_command("wsl chmod +x /tmp/build_deb_wsl.sh", check=True)
+
+ print_color("Building DEB package in WSL...", Colors.CYAN)
+ result = run_command(f"wsl /tmp/build_deb_wsl.sh '{wsl_path}'", check=True)
+ if result.returncode != 0:
+ print()
+ print_color("Failed to build DEB package", Colors.BOLD_RED)
+ print_color("Make sure dpkg-deb is available in WSL", Colors.YELLOW)
+ print_color("Run: wsl sudo apt update ^&^& wsl sudo apt install dpkg-dev", Colors.GREEN)
+ input("Press Enter to continue...")
+ return False
+
+ print()
+ print_color("DEB build completed!", Colors.BOLD_GREEN)
+ print_color("Output file: dist\\scl-1.0.0.deb", Colors.GREEN)
+ print()
+ input("Press Enter to continue...")
+ return True
+
+# 构建Linux DEB - SDP Only (via WSL)
+def build_sdp_deb():
+ print_color("\n========================================", Colors.BOLD_CYAN)
+ print_color("Building Linux DEB - SDP Only (via WSL)", Colors.BOLD_YELLOW)
+ print_color("========================================", Colors.BOLD_CYAN)
+ print()
+
+ print_color("Checking WSL...", Colors.CYAN)
+ result = run_command("wsl echo 'WSL OK'", check=False)
+ if result.returncode != 0:
+ print_color("WSL is not installed or not available", Colors.BOLD_RED)
+ print_color("Please install WSL first:", Colors.YELLOW)
+ print_color(" wsl --install", Colors.GREEN)
+ input("Press Enter to continue...")
+ return False
+
+ print_color("WSL found. Preparing build environment...", Colors.GREEN)
+
+ # 获取当前目录的WSL路径
+ result = run_command("wsl wslpath '%cd%'", check=True)
+ wsl_path = result.stdout.strip()
+ print_color(f"WSL path: {wsl_path}", Colors.BLUE)
+
+ print_color("Creating dist directory...", Colors.CYAN)
+ os.makedirs("dist", exist_ok=True)
+
+ print_color("Copying build script to WSL...", Colors.CYAN)
+ result = run_command(f"wsl cp '{wsl_path}/build_sdp_deb.sh' /tmp/build_sdp_deb.sh", check=True)
+
+ print_color("Building SDP DEB package in WSL...", Colors.CYAN)
+ result = run_command(f"wsl /tmp/build_sdp_deb.sh '{wsl_path}'", check=True)
+ if result.returncode != 0:
+ print()
+ print_color("Failed to build SDP DEB package", Colors.BOLD_RED)
+ print_color("Make sure dpkg-deb is available in WSL", Colors.YELLOW)
+ print_color("Run: wsl sudo apt update ^&^& wsl sudo apt install dpkg-dev", Colors.GREEN)
+ input("Press Enter to continue...")
+ return False
+
+ print()
+ print_color("SDP DEB build completed!", Colors.BOLD_GREEN)
+ print_color("Output file: dist\\scl-sdp-1.0.0.deb", Colors.GREEN)
+ print()
+ input("Press Enter to continue...")
+ return True
+
+# 构建Linux DEB - SDP April Fools Edition (via WSL)
+def build_sdp_deb_april_fools():
+ print_color("\n========================================", Colors.BOLD_CYAN)
+ print_color("Building Linux DEB - SDP April Fools Edition (via WSL)", Colors.BOLD_YELLOW)
+ print_color("========================================", Colors.BOLD_CYAN)
+ print()
+
+ if not os.path.exists("sdp_yrj.py"):
+ print_color("Error: sdp_yrj.py not found", Colors.BOLD_RED)
+ input("Press Enter to continue...")
+ return False
+
+ print_color("Checking WSL...", Colors.CYAN)
+ result = run_command("wsl echo 'WSL OK'", check=False)
+ if result.returncode != 0:
+ print_color("WSL is not installed or not available", Colors.BOLD_RED)
+ print_color("Please install WSL first:", Colors.YELLOW)
+ print_color(" wsl --install", Colors.GREEN)
+ input("Press Enter to continue...")
+ return False
+
+ print_color("WSL found. Preparing build environment...", Colors.GREEN)
+
+ # 获取当前目录的WSL路径
+ result = run_command("wsl wslpath '%cd%'", check=True)
+ wsl_path = result.stdout.strip()
+ print_color(f"WSL path: {wsl_path}", Colors.BLUE)
+
+ print_color("Creating dist directory...", Colors.CYAN)
+ os.makedirs("dist", exist_ok=True)
+
+ # 创建愚人节版本的构建脚本
+ print_color("Creating April Fools build script...", Colors.CYAN)
+ sdp_yrj_deb_script = '''#!/bin/bash
+set -e
+
+WSL_PATH="$1"
+VERSION="1.0.0"
+
+cd "$WSL_PATH"
+
+# 创建临时目录
+mkdir -p deb_temp/usr/local/bin
+mkdir -p deb_temp/DEBIAN
+
+# 复制sdp_yrj.py
+echo "Copying sdp_yrj.py..."
+cp sdp_yrj.py deb_temp/usr/local/bin/sdp_yrj
+chmod +x deb_temp/usr/local/bin/sdp_yrj
+
+# 复制插件目录
+echo "Copying plugins directory..."
+mkdir -p deb_temp/usr/local/lib/scl/plugins
+cp -r plugins/* deb_temp/usr/local/lib/scl/plugins/
+
+# 创建DEBIAN控制文件
+echo "Creating DEBIAN control file..."
+cat > deb_temp/DEBIAN/control << 'EOF'
+Package: scl-sdp-yrj
+Version: 1.0.0
+Section: utils
+Priority: optional
+Architecture: all
+Depends: python3
+Maintainer: SCL Team
+Description: SCL Package Manager - April Fools Edition
+EOF
+
+# 构建DEB包
+echo "Building DEB package..."
+dpkg-deb --build deb_temp "$WSL_PATH/dist/scl-sdp-yrj-1.0.0.deb"
+
+# 清理
+echo "Cleaning up..."
+rm -rf deb_temp
+
+echo "SDP April Fools DEB build completed!"
+echo "Output: $WSL_PATH/dist/scl-sdp-yrj-1.0.0.deb"
+'''
+
+ # 写入构建脚本
+ with open("build_sdp_yrj_deb.sh", "w") as f:
+ f.write(sdp_yrj_deb_script)
+
+ # 为脚本添加执行权限
+ os.chmod("build_sdp_yrj_deb.sh", 0o755)
+
+ print_color("Copying build script to WSL...", Colors.CYAN)
+ result = run_command(f"wsl cp '{wsl_path}/build_sdp_yrj_deb.sh' /tmp/build_sdp_yrj_deb.sh", check=True)
+ result = run_command("wsl chmod +x /tmp/build_sdp_yrj_deb.sh", check=True)
+
+ print_color("Building SDP April Fools DEB package in WSL...", Colors.CYAN)
+ result = run_command(f"wsl /tmp/build_sdp_yrj_deb.sh '{wsl_path}'", check=True)
+ if result.returncode != 0:
+ print()
+ print_color("Failed to build SDP April Fools DEB package", Colors.BOLD_RED)
+ print_color("Make sure dpkg-deb is available in WSL", Colors.YELLOW)
+ print_color("Run: wsl sudo apt update ^&^& wsl sudo apt install dpkg-dev", Colors.GREEN)
+ input("Press Enter to continue...")
+ return False
+
+ print()
+ print_color("SDP April Fools DEB build completed!", Colors.BOLD_GREEN)
+ print_color("Output file: dist\\scl-sdp-yrj-1.0.0.deb", Colors.GREEN)
+ print()
+ input("Press Enter to continue...")
+ return True
+
+# 构建Linux DEB - Editor Only (via WSL)
+def build_editor_deb():
+ print_color("\n========================================", Colors.BOLD_CYAN)
+ print_color("Building Linux DEB - Editor Only (via WSL)", Colors.BOLD_YELLOW)
+ print_color("========================================", Colors.BOLD_CYAN)
+ print()
+
+ print_color("Checking WSL...", Colors.CYAN)
+ result = run_command("wsl echo 'WSL OK'", check=False)
+ if result.returncode != 0:
+ print_color("WSL is not installed or not available", Colors.BOLD_RED)
+ print_color("Please install WSL first:", Colors.YELLOW)
+ print_color(" wsl --install", Colors.GREEN)
+ input("Press Enter to continue...")
+ return False
+
+ print_color("WSL found. Preparing build environment...", Colors.GREEN)
+
+ # 获取当前目录的WSL路径
+ result = run_command("wsl wslpath '%cd%'", check=True)
+ wsl_path = result.stdout.strip()
+ print_color(f"WSL path: {wsl_path}", Colors.BLUE)
+
+ print_color("Creating dist directory...", Colors.CYAN)
+ os.makedirs("dist", exist_ok=True)
+
+ print_color("Copying build script to WSL...", Colors.CYAN)
+ result = run_command(f"wsl cp '{wsl_path}/build_editor_deb.sh' /tmp/build_editor_deb.sh", check=True)
+
+ print_color("Building Editor DEB package in WSL...", Colors.CYAN)
+ result = run_command(f"wsl /tmp/build_editor_deb.sh '{wsl_path}'", check=True)
+ if result.returncode != 0:
+ print()
+ print_color("Failed to build Editor DEB package", Colors.BOLD_RED)
+ print_color("Make sure dpkg-deb is available in WSL", Colors.YELLOW)
+ print_color("Run: wsl sudo apt update ^&^& wsl sudo apt install dpkg-dev", Colors.GREEN)
+ input("Press Enter to continue...")
+ return False
+
+ print()
+ print_color("Editor DEB build completed!", Colors.BOLD_GREEN)
+ print_color("Output file: dist\\scl-editor-1.0.0.deb", Colors.GREEN)
+ print()
+ input("Press Enter to continue...")
+ return True
+
+# 生成macOS Build Script (Mach-O)
+def generate_macos_script():
+ print_color("\n========================================", Colors.BOLD_CYAN)
+ print_color("Generating macOS Build Script", Colors.BOLD_YELLOW)
+ print_color("========================================", Colors.BOLD_CYAN)
+ print()
+
+ print_color("Creating macOS build script...", Colors.CYAN)
+ script_content = '''#!/bin/bash
+set -e
+
+echo "Building SCL for macOS (Mach-O)..."
+
+VERSION="1.0.0"
+BUILD_DIR="build/macos"
+DIST_DIR="dist/macos"
+
+echo "Checking PyInstaller..."
+if ! command -v pyinstaller &> /dev/null; then
+ echo "PyInstaller found"
+else
+ echo "Installing PyInstaller..."
+ pip3 install pyinstaller
+fi
+
+echo "Creating build directories..."
+mkdir -p "$BUILD_DIR"
+mkdir -p "$DIST_DIR"
+
+echo "Building scl..."
+pyinstaller --onefile --name scl --distpath "$DIST_DIR" --workpath "$BUILD_DIR" --specpath "$BUILD_DIR" scl.py
+
+echo "Building sdp..."
+pyinstaller --onefile --name sdp --distpath "$DIST_DIR" --workpath "$BUILD_DIR" --specpath "$BUILD_DIR" sdp.py
+
+echo "Building scl_editor_linux..."
+pyinstaller --onefile --name scl_editor --distpath "$DIST_DIR" --workpath "$BUILD_DIR" --specpath "$BUILD_DIR" scl_editor_linux.py
+
+echo "Creating DMG package..."
+mkdir -p "$DIST_DIR/SCL.app/Contents/MacOS"
+mkdir -p "$DIST_DIR/SCL.app/Contents/Resources"
+
+echo "Copying executables..."
+cp "$DIST_DIR/scl" "$DIST_DIR/SCL.app/Contents/MacOS/"
+cp "$DIST_DIR/sdp" "$DIST_DIR/SCL.app/Contents/MacOS/"
+cp "$DIST_DIR/scl_editor" "$DIST_DIR/SCL.app/Contents/MacOS/"
+
+echo "Creating Info.plist..."
+cat < "$DIST_DIR/SCL.app/Contents/Info.plist" << EOF
+
+
+
+
+ CFBundleExecutable
+ scl
+ CFBundleIdentifier
+ com.scl.lang
+ CFBundleName
+ SCL
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ $VERSION
+ CFBundleVersion
+ $VERSION
+
+
+EOF
+
+echo "Building DMG..."
+if command -v hdiutil &> /dev/null; then
+ hdiutil create -volname "SCL" -srcfolder "$DIST_DIR/SCL.app" -ov -format UDZO "$DIST_DIR/SCL-$VERSION.dmg"
+ echo "DMG created: $DIST_DIR/SCL-$VERSION.dmg"
+else
+ echo "hdiutil not found, skipping DMG creation"
+ echo "App bundle created: $DIST_DIR/SCL.app"
+fi
+
+echo "Build completed!"
+echo "Output: $DIST_DIR/"
+'''
+
+ with open("build_macos.sh", "w") as f:
+ f.write(script_content)
+
+ # 为脚本添加执行权限
+ os.chmod("build_macos.sh", 0o755)
+
+ print()
+ print_color("macOS build script created: build_macos.sh", Colors.BOLD_GREEN)
+ print()
+ print_color("Instructions:", Colors.BOLD_YELLOW)
+ print_color("1. Copy build_macos.sh to a macOS system", Colors.GREEN)
+ print_color("2. Make it executable: chmod +x build_macos.sh", Colors.GREEN)
+ print_color("3. Run it: ./build_macos.sh", Colors.GREEN)
+ print()
+ input("Press Enter to continue...")
+ return True
+
+# 生成SDP macOS Build Script
+def generate_sdp_macos_script():
+ print_color("\n========================================", Colors.BOLD_CYAN)
+ print_color("Generating SDP macOS Build Script", Colors.BOLD_YELLOW)
+ print_color("========================================", Colors.BOLD_CYAN)
+ print()
+
+ print_color("Creating SDP macOS build script...", Colors.CYAN)
+ script_content = '''#!/bin/bash
+set -e
+
+echo "Building SDP for macOS (Mach-O)..."
+
+VERSION="1.0.0"
+BUILD_DIR="build/sdp-macos"
+DIST_DIR="dist/sdp-macos"
+
+echo "Checking PyInstaller..."
+if ! command -v pyinstaller &> /dev/null; then
+ echo "Installing PyInstaller..."
+ pip3 install pyinstaller
+else
+ echo "PyInstaller found"
+fi
+
+echo "Creating build directories..."
+mkdir -p "$BUILD_DIR"
+mkdir -p "$DIST_DIR"
+
+echo "Building SDP..."
+pyinstaller --onefile --name sdp --distpath "$DIST_DIR" --workpath "$BUILD_DIR" --specpath "$BUILD_DIR" sdp.py
+
+echo "Creating SDP App Bundle..."
+APP_DIR="$DIST_DIR/SDP.app"
+mkdir -p "$APP_DIR/Contents/MacOS"
+mkdir -p "$APP_DIR/Contents/Resources"
+
+echo "Copying executable..."
+cp "$DIST_DIR/sdp" "$APP_DIR/Contents/MacOS/"
+
+echo "Creating Info.plist..."
+cat > "$APP_DIR/Contents/Info.plist" << 'EOF'
+
+
+
+
+ CFBundleExecutable
+ sdp
+ CFBundleIdentifier
+ com.scl.sdp
+ CFBundleName
+ SDP
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ $VERSION
+ CFBundleVersion
+ $VERSION
+
+
+EOF
+
+echo "Building SDP DMG..."
+if command -v hdiutil &> /dev/null; then
+ hdiutil create -volname "SDP" -srcfolder "$APP_DIR" -ov -format UDZO "$DIST_DIR/SDP-$VERSION.dmg"
+ echo "DMG created: $DIST_DIR/SDP-$VERSION.dmg"
+else
+ echo "hdiutil not found, skipping DMG creation"
+ echo "App bundle created: $APP_DIR"
+fi
+
+echo "SDP build completed!"
+echo "Output: $DIST_DIR/"
+'''
+
+ with open("build_sdp_macos.sh", "w") as f:
+ f.write(script_content)
+
+ # 为脚本添加执行权限
+ os.chmod("build_sdp_macos.sh", 0o755)
+
+ print()
+ print_color("SDP macOS build script created: build_sdp_macos.sh", Colors.BOLD_GREEN)
+ print()
+ print_color("Instructions:", Colors.BOLD_YELLOW)
+ print_color("1. Copy build_sdp_macos.sh to a macOS system", Colors.GREEN)
+ print_color("2. Make it executable: chmod +x build_sdp_macos.sh", Colors.GREEN)
+ print_color("3. Run it: ./build_sdp_macos.sh", Colors.GREEN)
+ print()
+ input("Press Enter to continue...")
+ return True
+
+# 构建所有 (EXE + DEB)
+def build_all():
+ print_color("\n========================================", Colors.BOLD_CYAN)
+ print_color("Building All Packages (EXE + DEB)", Colors.BOLD_YELLOW)
+ print_color("========================================", Colors.BOLD_CYAN)
+ print()
+
+ print_color("Starting Windows EXE build...", Colors.CYAN)
+ print()
+ if not build_exe():
+ return False
+
+ print_color("Starting Linux DEB build...", Colors.CYAN)
+ print()
+ if not build_deb():
+ return False
+
+ print()
+ print_color("All builds completed!", Colors.BOLD_GREEN)
+ print()
+ input("Press Enter to continue...")
+ return True
+
+# 显示菜单
+def show_menu():
+ while True:
+ print_color("========================================", Colors.BOLD_CYAN)
+ print_color("SCL Build Tool", Colors.BOLD_YELLOW)
+ print_color("========================================", Colors.BOLD_CYAN)
+ print()
+
+ print_color("Please select build type:", Colors.BOLD_YELLOW)
+ print_color("[1] Build Windows EXE", Colors.GREEN)
+ print_color("[2] Build Linux DEB - SCL Full Package (via WSL)", Colors.GREEN)
+ print_color("[3] Build Linux DEB - SDP Only (via WSL)", Colors.GREEN)
+ print_color("[4] Build Linux DEB - Editor Only (via WSL)", Colors.GREEN)
+ print_color("[5] Generate macOS Build Script (Mach-O)", Colors.GREEN)
+ print_color("[6] Generate SDP macOS Build Script", Colors.GREEN)
+ print_color("[7] Build All (EXE + DEB)", Colors.GREEN)
+ print_color("[8] Build Windows EXE - April Fools Edition", Colors.GREEN)
+ print_color("[9] Build Linux DEB - SDP April Fools Edition (via WSL)", Colors.GREEN)
+ print_color("[10] Exit", Colors.GREEN)
+ print()
+
+ try:
+ choice = input(f"{Colors.BOLD_CYAN}Enter option (1-10):{Colors.RESET} ")
+ print()
+
+ if choice == "1":
+ build_exe()
+ elif choice == "2":
+ build_deb()
+ elif choice == "3":
+ build_sdp_deb()
+ elif choice == "4":
+ build_editor_deb()
+ elif choice == "5":
+ generate_macos_script()
+ elif choice == "6":
+ generate_sdp_macos_script()
+ elif choice == "7":
+ build_all()
+ elif choice == "8":
+ build_exe_april_fools()
+ elif choice == "9":
+ build_sdp_deb_april_fools()
+ elif choice == "10":
+ break
+ else:
+ print_color("Invalid option, please try again", Colors.BOLD_RED)
+ print()
+ except KeyboardInterrupt:
+ print()
+ break
+ except Exception as e:
+ print_color(f"An error occurred: {e}", Colors.BOLD_RED)
+ print()
+
+ print_color("\nThank you for using SCL Build Tool!", Colors.BOLD_YELLOW)
+ print()
+ input("Press Enter to exit...")
+
+# 主函数
+if __name__ == "__main__":
+ enable_windows_ansi_support()
+ show_menu()
\ No newline at end of file
diff --git a/pack.py b/pack.py
new file mode 100644
index 0000000..7bc0289
--- /dev/null
+++ b/pack.py
@@ -0,0 +1,424 @@
+#!/usr/bin/env python3
+"""
+SCL to Python Compiler
+Converts SCL code to Python code with interpreter and plugins embedded
+"""
+
+import sys
+import os
+import re
+import ast
+
+
+class PluginAnalyzer:
+ """Analyze plugin code to understand its syntax and implementation"""
+
+ def __init__(self):
+ self.plugins = {} # Cache for analyzed plugins
+ self.plugin_file_cache = {} # Cache for plugin file contents
+
+ def analyze_plugin(self, plugin_name):
+ """Analyze a plugin's code"""
+ if plugin_name in self.plugins:
+ return self.plugins[plugin_name]
+
+ # Get the plugins directory path relative to the script
+ script_dir = os.path.dirname(os.path.abspath(__file__))
+ plugin_file = os.path.join(script_dir, 'plugins', f'{plugin_name}.py')
+
+ if not os.path.exists(plugin_file):
+ print(f"Error: Plugin file '{plugin_file}' not found")
+ return None
+
+ # Check if file content is cached
+ mtime = os.path.getmtime(plugin_file)
+ cache_key = (plugin_file, mtime)
+ if cache_key in self.plugin_file_cache:
+ code = self.plugin_file_cache[cache_key]
+ else:
+ try:
+ with open(plugin_file, 'r', encoding='utf-8') as f:
+ code = f.read()
+ # Cache the file content
+ self.plugin_file_cache[cache_key] = code
+ except Exception as e:
+ print(f"Error reading plugin file {plugin_file}: {e}")
+ return None
+
+ # Parse the code
+ try:
+ tree = ast.parse(code)
+ except SyntaxError as e:
+ print(f"Error parsing plugin {plugin_name}: Syntax error at line {e.lineno}, column {e.offset}: {e.msg}")
+ return None
+
+ # Extract information
+ try:
+ plugin_info = self._extract_plugin_info(tree, plugin_name)
+ plugin_info['code'] = code
+ self.plugins[plugin_name] = plugin_info
+ return plugin_info
+ except Exception as e:
+ print(f"Error extracting plugin information from {plugin_name}: {e}")
+ import traceback
+ traceback.print_exc()
+ return None
+
+ def _extract_plugin_info(self, tree, plugin_name):
+ """Extract plugin information from AST"""
+ info = {
+ 'name': plugin_name,
+ 'imports': [],
+ 'dependencies': [],
+ 'commands': {},
+ 'class_name': f'{plugin_name.capitalize()}Plugin'
+ }
+
+ # Get the plugins directory path relative to the script
+ script_dir = os.path.dirname(os.path.abspath(__file__))
+ plugins_dir = os.path.join(script_dir, 'plugins')
+
+ # Extract imports and dependencies
+ for node in ast.walk(tree):
+ if isinstance(node, ast.Import):
+ for alias in node.names:
+ info['imports'].append(alias.name)
+ # Check if this is a plugin dependency
+ if os.path.exists(plugins_dir) and alias.name in os.listdir(plugins_dir) and os.path.isfile(os.path.join(plugins_dir, f'{alias.name}.py')):
+ info['dependencies'].append(alias.name)
+ elif isinstance(node, ast.ImportFrom):
+ if node.module:
+ info['imports'].append(node.module)
+ # Check if this is a plugin dependency
+ if os.path.exists(plugins_dir) and node.module in os.listdir(plugins_dir) and os.path.isfile(os.path.join(plugins_dir, f'{node.module}.py')):
+ info['dependencies'].append(node.module)
+
+ # Extract command patterns from parse_statement
+ elif isinstance(node, ast.FunctionDef) and node.name == 'parse_statement':
+ self._extract_commands_from_parse(node, info)
+
+ return info
+
+ def _extract_commands_from_parse(self, node, info):
+ """Extract command patterns from parse_statement function"""
+ for child in ast.walk(node):
+ if isinstance(child, ast.Compare) and isinstance(child.left, ast.Subscript):
+ # Check for tokens[pos][1] == 'command'
+ if self._is_tokens_access(child.left):
+ if isinstance(child.comparators[0], ast.Constant):
+ command = child.comparators[0].value
+ if isinstance(command, str):
+ info['commands'][command] = {'type': 'direct'}
+
+ elif isinstance(child, ast.Compare) and isinstance(child.left, ast.Name):
+ # Check for command_suffix == 'c'
+ if child.left.id == 'command_suffix':
+ if isinstance(child.comparators[0], ast.Constant):
+ suffix = child.comparators[0].value
+ if isinstance(suffix, str):
+ info['commands'][f'sui-{suffix}'] = {'type': 'sui'}
+
+ def _is_tokens_access(self, node):
+ """Check if node is tokens[pos][1]"""
+ if (isinstance(node, ast.Subscript) and
+ isinstance(node.value, ast.Name) and
+ node.value.id == 'tokens'):
+ if isinstance(node.slice, ast.Constant) or isinstance(node.slice, ast.Name):
+ return True
+ return False
+
+
+class SCLCompiler:
+ def __init__(self):
+ self.variables = {}
+ self.imports = set()
+ self.indent_level = 0
+ self.plugin_analyzer = PluginAnalyzer()
+ self.loaded_plugins = set()
+
+ def _detect_plugin_conflicts(self, resolved_plugins):
+ """Detect command conflicts between plugins"""
+ command_map = {}
+ conflicts = []
+
+ for plugin_name in resolved_plugins:
+ plugin_info = self.plugin_analyzer.analyze_plugin(plugin_name)
+ if not plugin_info:
+ continue
+
+ for command in plugin_info.get('commands', {}):
+ if command in command_map:
+ conflicts.append((command, command_map[command], plugin_name))
+ else:
+ command_map[command] = plugin_name
+
+ return conflicts
+
+ def compile(self, scl_code, filename="input.scl", minify=False):
+ """Compile SCL code to Python code with embedded interpreter and plugins"""
+ lines = scl_code.split('\n')
+
+ # First pass: collect plugins
+ plugins_to_load = []
+ for line in lines:
+ if line.startswith('simp{') and line.endswith('}'):
+ import_content = line[5:-1].strip()
+ if not import_content.startswith('scl :'):
+ plugins_to_load.append(import_content)
+
+ # Resolve plugin dependencies
+ resolved_plugins = self._resolve_plugin_dependencies(plugins_to_load)
+
+ # Detect plugin conflicts
+ conflicts = self._detect_plugin_conflicts(resolved_plugins)
+ if conflicts:
+ print("Warning: Command conflicts detected between plugins:")
+ for command, plugin1, plugin2 in conflicts:
+ print(f" Command '{command}' is defined in both {plugin1} and {plugin2}")
+ print(" The last loaded plugin will take precedence.")
+
+ # Load SCL interpreter code
+ scl_interpreter_code = self._load_scl_interpreter()
+
+ # Load plugin codes
+ plugin_codes = {}
+ for plugin_name in resolved_plugins:
+ plugin_info = self.plugin_analyzer.analyze_plugin(plugin_name)
+ if plugin_info:
+ plugin_codes[plugin_name] = plugin_info['code']
+
+ # Generate embedded Python code
+ python_code = self._generate_embedded_code(scl_code, scl_interpreter_code, plugin_codes, filename, minify)
+ return python_code
+
+ def _resolve_plugin_dependencies(self, plugins_to_load):
+ """Resolve plugin dependencies and return an ordered list"""
+ resolved = set()
+ unresolved = set(plugins_to_load)
+ order = []
+
+ print(f"Resolving dependencies for plugins: {plugins_to_load}")
+
+ while unresolved:
+ # Find plugins with all dependencies resolved
+ progress = False
+ for plugin_name in list(unresolved):
+ plugin_info = self.plugin_analyzer.analyze_plugin(plugin_name)
+ if not plugin_info:
+ print(f"Warning: Skipping plugin {plugin_name} due to analysis errors")
+ unresolved.remove(plugin_name)
+ continue
+
+ # Check if all dependencies are resolved
+ dependencies = plugin_info.get('dependencies', [])
+ print(f"Plugin {plugin_name} has dependencies: {dependencies}")
+
+ if all(dep in resolved for dep in dependencies):
+ # Add to resolved and order
+ resolved.add(plugin_name)
+ order.append(plugin_name)
+ unresolved.remove(plugin_name)
+ progress = True
+ print(f"Resolved plugin: {plugin_name}")
+
+ # Add dependencies to unresolved if not already processed
+ for dep in dependencies:
+ if dep not in resolved and dep not in unresolved:
+ print(f"Adding dependency {dep} for plugin {plugin_name}")
+ unresolved.add(dep)
+
+ if not progress:
+ # No progress made, break to avoid infinite loop
+ print(f"Warning: Unable to resolve all dependencies. Remaining: {unresolved}")
+ break
+
+ print(f"Resolved plugin order: {order}")
+ return order
+
+ def _minify_code(self, code):
+ """Minify Python code by removing comments and whitespace"""
+ lines = code.split('\n')
+ minified_lines = []
+ in_multiline_comment = False
+
+ for line in lines:
+ # Skip empty lines
+ if not line.strip():
+ continue
+
+ # Handle multiline comments
+ if '"""' in line or "''" in line:
+ in_multiline_comment = not in_multiline_comment
+ continue
+
+ if in_multiline_comment:
+ continue
+
+ # Remove single-line comments
+ if '#' in line:
+ line = line.split('#')[0].rstrip()
+ if not line:
+ continue
+
+ # Remove leading and trailing whitespace
+ line = line.strip()
+ if line:
+ minified_lines.append(line)
+
+ return '\n'.join(minified_lines)
+
+ def _load_scl_interpreter(self):
+ """Load SCL interpreter code"""
+ try:
+ # Get the script directory
+ script_dir = os.path.dirname(os.path.abspath(__file__))
+ scl_path = os.path.join(script_dir, 'scl.py')
+
+ with open(scl_path, 'r', encoding='utf-8') as f:
+ code = f.read()
+
+ # Remove the main function execution at the end
+ lines = code.split('\n')
+ filtered_lines = []
+ in_main_block = False
+
+ for line in lines:
+ if line.strip() == 'if __name__ == "__main__":':
+ in_main_block = True
+ continue
+ if in_main_block:
+ if line.strip() and not line.startswith(' '):
+ in_main_block = False
+ else:
+ continue
+ filtered_lines.append(line)
+
+ return '\n'.join(filtered_lines)
+ except Exception as e:
+ print(f"Error loading SCL interpreter: {e}")
+ return ""
+
+ def _generate_header(self, filename):
+ """Generate the header for the embedded code"""
+ return [
+ '#!/usr/bin/env python3',
+ f'# Generated from {filename} by SCL Compiler',
+ '# Embedded SCL interpreter and plugins',
+ ''
+ ]
+
+ def _generate_interpreter_code(self, scl_interpreter_code, minify):
+ """Generate the SCL interpreter code"""
+ code = ['\n# ==== SCL INTERPRETER ====\n']
+ if minify:
+ code.append(self._minify_code(scl_interpreter_code))
+ else:
+ code.append(scl_interpreter_code)
+ code.append('')
+ return code
+
+ def _generate_plugin_codes(self, plugin_codes, minify):
+ """Generate the plugin codes"""
+ code = []
+ for plugin_name, plugin_code in plugin_codes.items():
+ code.append(f'\n# ==== PLUGIN: {plugin_name} ====\n')
+ if minify:
+ code.append(self._minify_code(plugin_code))
+ else:
+ code.append(plugin_code)
+ code.append('')
+ return code
+
+ def _generate_main_function(self, plugin_codes, scl_code):
+ """Generate the main function code"""
+ code = ['\n# ==== MAIN EXECUTION ====\n\ndef main():\n """Run the embedded SCL code"""\n print("Running embedded SCL code...")\n print("=" * 50)\n \n # Create SCL interpreter\n interpreter = SCLInterpreter()\n \n # Load plugins\n']
+
+ # Add plugin loading code
+ for plugin_name in plugin_codes:
+ code.append(f" interpreter.load_plugin('{plugin_name}')\n")
+
+ # Add SCL code execution
+ code.append('\n # SCL code to execute\n scl_code = """')
+ # 安全地嵌入 SCL 代码,处理三引号
+ for line in scl_code.split('\n'):
+ code.append(line.replace('"""', '\\"""'))
+ code.append('\n')
+ code.append('"""\n')
+
+ # Add execution code
+ code.append('\n # Execute SCL code\n try:\n success = interpreter.execute(scl_code, "embedded")\n if not success:\n print("Error: Failed to execute SCL code")\n except Exception as e:\n print(f"Error: {e}")\n \n print("=" * 50)\n print("Execution completed.")\n\n\nif __name__ == "__main__":\n main()\n')
+
+ return code
+
+ def _generate_embedded_code(self, scl_code, scl_interpreter_code, plugin_codes, filename, minify=False):
+ """Generate embedded Python code"""
+ # Use a single string buffer for more efficient concatenation
+ code_buffer = []
+
+ # Add header
+ code_buffer.extend(self._generate_header(filename))
+
+ # Add SCL interpreter code
+ code_buffer.extend(self._generate_interpreter_code(scl_interpreter_code, minify))
+
+ # Add plugin codes
+ code_buffer.extend(self._generate_plugin_codes(plugin_codes, minify))
+
+ # Add main function
+ code_buffer.extend(self._generate_main_function(plugin_codes, scl_code))
+
+ return ''.join(code_buffer)
+
+
+def main():
+ """Main function"""
+ import argparse
+
+ # Parse command line arguments
+ parser = argparse.ArgumentParser(description='SCL to Python Compiler')
+ parser.add_argument('input_file', help='Input SCL file')
+ parser.add_argument('output_file', nargs='?', help='Output Python file (optional, default: stdout)')
+ parser.add_argument('-m', '--minify', action='store_true', help='Minify the generated code')
+
+ args = parser.parse_args()
+
+ input_file = args.input_file
+ output_file = args.output_file
+ minify = args.minify
+
+ # Check if input file exists
+ if not os.path.exists(input_file):
+ print(f"Error: Input file '{input_file}' not found")
+ sys.exit(1)
+
+ # Read SCL code
+ try:
+ with open(input_file, 'r', encoding='utf-8') as f:
+ scl_code = f.read()
+ except Exception as e:
+ print(f"Error reading file: {e}")
+ sys.exit(1)
+
+ # Compile to Python
+ compiler = SCLCompiler()
+ python_code = compiler.compile(scl_code, input_file, minify)
+
+ # Output
+ if output_file:
+ try:
+ with open(output_file, 'w', encoding='utf-8') as f:
+ f.write(python_code)
+ print(f"Successfully compiled {input_file} to {output_file}")
+ if minify:
+ print("The file has been minified to reduce size.")
+ else:
+ print("The file includes embedded SCL interpreter and plugins.")
+ except Exception as e:
+ print(f"Error writing file: {e}")
+ sys.exit(1)
+ else:
+ print(python_code)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/scl.py b/scl.py
new file mode 100644
index 0000000..e2d951c
--- /dev/null
+++ b/scl.py
@@ -0,0 +1,1366 @@
+#!/usr/bin/env python3
+"""
+SunsetCodeLang (SCL) Interpreter
+Main interpreter for the SCL language
+"""
+
+import sys
+import os
+import re
+import importlib.util
+import tkinter as tk
+
+class SCLInterpreter:
+ def __init__(self):
+ self.variables = {}
+ self.functions = {}
+ self.plugins = {}
+ self.loaded_plugins = set()
+ self.debug_mode = False # 默认为false,不开启debug模式
+
+ def _find_plugin_class(self, plugin_module):
+ """Find the plugin class in a module"""
+ for name in dir(plugin_module):
+ if name.endswith('Plugin') and name[0].isupper():
+ return name
+ return None
+
+ def _register_plugin_instance(self, plugin_name, plugin_instance, is_web=False, url=None):
+ """Register a plugin instance"""
+ self.plugins[plugin_name] = plugin_instance
+ if hasattr(plugin_instance, 'register_syntax'):
+ plugin_instance.register_syntax()
+ if self.debug_mode:
+ if is_web:
+ print(f"Debug: Loaded web plugin: {plugin_name} from {url}, Total plugins: {list(self.plugins.keys())}")
+ else:
+ print(f"Debug: Loaded plugin: {plugin_name}, Total plugins: {list(self.plugins.keys())}")
+
+ def load_plugin(self, plugin_path):
+ """Load a plugin from the given path or URL"""
+ try:
+ if plugin_path.startswith('web : '):
+ return self._load_web_plugin(plugin_path)
+ else:
+ return self._load_local_plugin(plugin_path)
+ except Exception as e:
+ print(f"Error loading plugin {plugin_path}: {e}")
+ import traceback
+ traceback.print_exc()
+ return False
+
+ def _load_web_plugin(self, plugin_path):
+ """Load a plugin from a web URL"""
+ import urllib.request
+ import tempfile
+ import hashlib
+
+ url = plugin_path[6:].strip()
+ plugin_name = 'web_' + hashlib.md5(url.encode()).hexdigest()[:8]
+
+ if plugin_name in self.plugins:
+ return True
+
+ print(f"Downloading plugin from: {url}")
+ try:
+ with urllib.request.urlopen(url) as response:
+ if hasattr(response, 'status') and response.status is not None and response.status != 200:
+ print(f"Error: Failed to download plugin from {url}, status code: {response.status}")
+ return False
+ plugin_code = response.read().decode('utf-8')
+ except Exception as e:
+ print(f"Error: Failed to download plugin from {url}: {e}")
+ return False
+
+ temp_plugin_file = None
+ try:
+ with tempfile.NamedTemporaryFile(suffix='.py', delete=False) as temp_file:
+ temp_file.write(plugin_code.encode('utf-8'))
+ temp_plugin_file = temp_file.name
+
+ spec = importlib.util.spec_from_file_location(plugin_name, temp_plugin_file)
+ plugin_module = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(plugin_module)
+
+ plugin_class_name = self._find_plugin_class(plugin_module)
+ if not plugin_class_name:
+ print(f"Error: Plugin from {url} does not have a proper plugin class")
+ return False
+
+ plugin_class = getattr(plugin_module, plugin_class_name)
+ plugin_instance = plugin_class(self)
+ self._register_plugin_instance(plugin_name, plugin_instance, True, url)
+ return True
+ finally:
+ if temp_plugin_file and os.path.exists(temp_plugin_file):
+ os.unlink(temp_plugin_file)
+
+ def _load_local_plugin(self, plugin_path):
+ """Load a plugin from the local filesystem"""
+ module_name = plugin_path.replace('>', '.')
+ full_module_name = f'plugins.{module_name}'
+
+ if full_module_name in self.loaded_plugins:
+ return True
+
+ plugin_file = os.path.join('plugins', plugin_path.replace('>', os.sep) + '.py')
+
+ if not os.path.exists(plugin_file):
+ print(f"Error: Plugin {plugin_path} not found at {plugin_file}")
+ return False
+
+ spec = importlib.util.spec_from_file_location(full_module_name, plugin_file)
+ plugin_module = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(plugin_module)
+
+ plugin_class = getattr(plugin_module, f'{plugin_path.split(">")[-1].capitalize()}Plugin', None)
+ if not plugin_class:
+ print(f"Error: Plugin {plugin_path} does not have a proper plugin class")
+ return False
+
+ plugin_instance = plugin_class(self)
+ self.plugins[plugin_path] = plugin_instance
+ self.loaded_plugins.add(full_module_name)
+ self._register_plugin_instance(plugin_path, plugin_instance)
+ return True
+
+ def import_scl_file(self, scl_path, line_num):
+ """Import and execute another SCL file
+
+ Args:
+ scl_path: Path to SCL file (local or URL)
+ line_num: Current line number for error reporting
+ """
+ try:
+ # Check if path is a URL
+ if scl_path.startswith('http://') or scl_path.startswith('https://'):
+ # Download from URL
+ import urllib.request
+ import tempfile
+
+ print(f"Downloading SCL file from: {scl_path}")
+
+ try:
+ with urllib.request.urlopen(scl_path) as response:
+ code = response.read().decode('utf-8')
+ except Exception as e:
+ print(f"Error at line {line_num}: Failed to download SCL file from {scl_path}")
+ print(f"Error: {e}")
+ return False
+ else:
+ # Load from local file
+ if not os.path.exists(scl_path):
+ print(f"Error at line {line_num}: SCL file not found: {scl_path}")
+ return False
+
+ with open(scl_path, 'r', encoding='utf-8') as f:
+ code = f.read()
+
+ # Execute the imported SCL code
+ print(f"Executing imported SCL file: {scl_path}")
+ success = self.execute(code, scl_path)
+
+ if not success:
+ print(f"Error at line {line_num}: Failed to execute imported SCL file: {scl_path}")
+ return False
+
+ return True
+ except Exception as e:
+ print(f"Error at line {line_num}: Failed to import SCL file {scl_path}")
+ print(f"Error: {e}")
+ import traceback
+ traceback.print_exc()
+ return False
+
+ def tokenize(self, code):
+ """Tokenize the SCL code (optimized)"""
+ tokens = []
+ code = code.strip()
+ i = 0
+ n = len(code)
+
+ # 预定义常量
+ whitespace_chars = {' ', '\t', '\n', '\r'}
+ operator_chars = {'+', '-', '*', '/', '=', '<', '>', '!', '&', '|'}
+ paren_chars = {'(', ')', '[', ']', '{', '}'}
+
+ while i < n:
+ # 快速跳过空白字符
+ while i < n and code[i] in whitespace_chars:
+ i += 1
+
+ if i >= n:
+ break
+
+ char = code[i]
+
+ if char == '"':
+ # 字符串处理
+ j = i + 1
+ string_content = []
+ while j < n and code[j] != '"':
+ if code[j] == '\\' and j + 1 < n:
+ j += 1
+ if code[j] == 'n':
+ string_content.append('\n')
+ elif code[j] == '\\':
+ string_content.append('\\')
+ else:
+ string_content.append(code[j])
+ else:
+ string_content.append(code[j])
+ j += 1
+ tokens.append(('STRING', ''.join(string_content)))
+ i = j + 1 if j < n else n
+
+ elif char.isdigit():
+ # 数字处理
+ j = i
+ while j < n and (code[j].isdigit() or code[j] == '.'):
+ j += 1
+ tokens.append(('NUMBER', code[i:j]))
+ i = j
+
+ elif char.isalpha() or char == '_':
+ # 标识符处理
+ j = i
+ while j < n and (code[j].isalnum() or code[j] == '_'):
+ j += 1
+ tokens.append(('IDENTIFIER', code[i:j]))
+ i = j
+
+ elif char == '|':
+ tokens.append(('SEPARATOR', char))
+ i += 1
+
+ elif char == ':':
+ tokens.append(('ASSIGN', char))
+ i += 1
+
+ elif char in operator_chars:
+ # 运算符处理
+ j = i
+ while j < n and code[j] in operator_chars:
+ j += 1
+ tokens.append(('OPERATOR', code[i:j]))
+ i = j
+
+ elif char in paren_chars:
+ tokens.append(('PAREN', char))
+ i += 1
+
+ elif char == '#':
+ # 注释处理
+ j = i
+ while j < n and code[j] != '\n':
+ j += 1
+ tokens.append(('COMMENT', code[i:j]))
+ i = j
+
+ elif char == '/' and i + 1 < n and code[i + 1] == '/':
+ # 注释处理
+ j = i
+ while j < n and code[j] != '\n':
+ j += 1
+ tokens.append(('COMMENT', code[i:j]))
+ i = j
+
+ else:
+ tokens.append(('UNKNOWN', char))
+ i += 1
+
+ return tokens
+
+ def parse_expression(self, tokens, pos):
+ """Parse an expression from the tokens"""
+ # 简单表达式解析器,支持二元运算
+ if pos >= len(tokens):
+ return None, pos
+
+ # 解析左操作数
+ left, pos = self.parse_primary(tokens, pos)
+ if left is None:
+ return None, pos
+
+ # 解析运算符和右操作数
+ while pos < len(tokens) and tokens[pos][0] == 'OPERATOR' and tokens[pos][1] in '+-*/':
+ op = tokens[pos]
+ pos += 1
+ right, pos = self.parse_primary(tokens, pos)
+ if right is None:
+ return None, pos
+ # 构建二元表达式
+ left = [left, op, right]
+
+ return left, pos
+
+ def parse_primary(self, tokens, pos):
+ """Parse a primary expression"""
+ if pos >= len(tokens):
+ return None, pos
+
+ token = tokens[pos]
+ if token[0] == 'STRING' or token[0] == 'NUMBER':
+ return token, pos + 1
+ elif token[0] == 'IDENTIFIER':
+ # Check if this is a function call: identifier(arguments)
+ if pos + 1 < len(tokens) and tokens[pos + 1][0] == 'PAREN' and tokens[pos + 1][1] == '(':
+ # Parse function name
+ func_name = token[1]
+ pos += 2 # Skip identifier and '('
+
+ # Parse arguments
+ args = []
+ while pos < len(tokens) and not (tokens[pos][0] == 'PAREN' and tokens[pos][1] == ')'):
+ arg, pos = self.parse_expression(tokens, pos)
+ if arg:
+ args.append(arg)
+ # Skip commas
+ if pos < len(tokens) and (tokens[pos][0] == 'SEPARATOR' and tokens[pos][1] == ',' or tokens[pos][0] == 'UNKNOWN' and tokens[pos][1] == ','):
+ pos += 1
+
+ if pos < len(tokens) and tokens[pos][0] == 'PAREN' and tokens[pos][1] == ')':
+ pos += 1
+ # Return a function call expression
+ return ('FUNCTION_CALL', func_name, args), pos
+ # Otherwise, return the identifier
+ return token, pos + 1
+ elif token[0] == 'PAREN' and token[1] == '(':
+ expr, pos = self.parse_expression(tokens, pos + 1)
+ if pos < len(tokens) and tokens[pos][0] == 'PAREN' and tokens[pos][1] == ')':
+ return expr, pos + 1
+ return None, pos
+ return None, pos
+
+ def _skip_whitespace(self, tokens, pos):
+ """Skip whitespace tokens"""
+ while pos < len(tokens) and (tokens[pos][0] == 'UNKNOWN' and tokens[pos][1].isspace()):
+ pos += 1
+ return pos
+
+ def parse_statement(self, tokens, pos):
+ """Parse a statement from the tokens"""
+ # Check for variable assignment with plugin pipeline: set var | var : plugin : command
+ if pos < len(tokens) and tokens[pos][0] == 'IDENTIFIER' and tokens[pos][1] == 'set':
+ var_pos = pos + 1
+ if var_pos < len(tokens) and tokens[var_pos][0] == 'IDENTIFIER':
+ var_name = tokens[var_pos][1]
+ pipe_pos = var_pos + 1
+ if pipe_pos < len(tokens) and tokens[pipe_pos][0] == 'SEPARATOR' and tokens[pipe_pos][1] == '|':
+ after_pipe_pos = pipe_pos + 1
+ if after_pipe_pos < len(tokens) and tokens[after_pipe_pos][0] == 'IDENTIFIER' and tokens[after_pipe_pos][1] == var_name:
+ assign_pos = after_pipe_pos + 1
+ if assign_pos < len(tokens) and tokens[assign_pos][0] == 'ASSIGN' and tokens[assign_pos][1] == ':':
+ plugin_pos = assign_pos + 1
+ if plugin_pos < len(tokens) and tokens[plugin_pos][0] == 'IDENTIFIER':
+ plugin_name = tokens[plugin_pos][1]
+ plugin_assign_pos = plugin_pos + 1
+ if plugin_assign_pos < len(tokens) and tokens[plugin_assign_pos][0] == 'ASSIGN' and tokens[plugin_assign_pos][1] == ':':
+ command_pos = plugin_assign_pos + 1
+ if command_pos < len(tokens) and tokens[command_pos][0] == 'IDENTIFIER':
+ command_name = tokens[command_pos][1]
+ args_pos = command_pos + 1
+
+ # Parse command arguments in angle brackets
+ args_tokens = []
+ if args_pos < len(tokens) and tokens[args_pos][0] == 'OPERATOR' and tokens[args_pos][1] == '<':
+ current_arg_pos = args_pos + 1
+ while current_arg_pos < len(tokens) and not (tokens[current_arg_pos][0] == 'OPERATOR' and tokens[current_arg_pos][1] == '>'):
+ args_tokens.append(tokens[current_arg_pos])
+ current_arg_pos += 1
+ if current_arg_pos < len(tokens) and tokens[current_arg_pos][0] == 'OPERATOR' and tokens[current_arg_pos][1] == '>':
+ current_arg_pos += 1
+ return ('PLUGIN_ASSIGN', var_name, plugin_name, command_name, args_tokens), current_arg_pos
+
+ if pos < len(tokens):
+ token = tokens[pos]
+ if token[0] == 'IDENTIFIER' and token[1] == 'sdebug':
+ if pos + 1 < len(tokens) and tokens[pos + 1][0] == 'ASSIGN' and tokens[pos + 1][1] == ':':
+ if pos + 2 < len(tokens) and tokens[pos + 2][0] == 'IDENTIFIER' and tokens[pos + 2][1] in ['true', 'false']:
+ debug_value = tokens[pos + 2][1] == 'true'
+ return ('DEBUG', debug_value), pos + 3
+
+
+
+ # 处理end语句
+ if pos < len(tokens) and tokens[pos][0] == 'IDENTIFIER' and tokens[pos][1] == 'end':
+ return ('END',), pos + 1
+
+ # 直接处理sif、seif、sel和swhi语句
+ if pos < len(tokens) and tokens[pos][0] == 'IDENTIFIER' and tokens[pos][1] == 'sif':
+ current_pos = pos + 1 # Skip 'sif'
+
+ # Find separator '|' for condition
+ cond_end = current_pos
+ while cond_end < len(tokens) and not (tokens[cond_end][0] == 'SEPARATOR' and tokens[cond_end][1] == '|'):
+ cond_end += 1
+
+ if cond_end >= len(tokens):
+ return None, pos
+
+ # Extract condition
+ condition = tokens[current_pos:cond_end]
+ current_pos = cond_end + 1 # Skip '|'
+
+ # Parse if body
+ if_body = []
+ body_end = current_pos
+ while body_end < len(tokens):
+ token = tokens[body_end]
+ if token[0] == 'IDENTIFIER' and token[1] in ['seif', 'sel', 'end']:
+ break
+ stmt, new_pos = self.parse_statement(tokens, body_end)
+ if stmt:
+ if_body.append(stmt)
+ body_end = new_pos
+ else:
+ body_end += 1
+
+ current_pos = body_end
+
+ # Parse elif clauses
+ elif_clauses = []
+ while current_pos < len(tokens) and tokens[current_pos][0] == 'IDENTIFIER' and tokens[current_pos][1] == 'seif':
+ current_pos += 1 # Skip 'seif'
+
+ # Find separator '|' for elif condition
+ elif_cond_end = current_pos
+ while elif_cond_end < len(tokens) and not (tokens[elif_cond_end][0] == 'SEPARATOR' and tokens[elif_cond_end][1] == '|'):
+ elif_cond_end += 1
+
+ if elif_cond_end >= len(tokens):
+ return None, pos
+
+ # Extract elif condition
+ elif_condition = tokens[current_pos:elif_cond_end]
+ current_pos = elif_cond_end + 1 # Skip '|'
+
+ # Parse elif body
+ elif_body = []
+ elif_body_end = current_pos
+ while elif_body_end < len(tokens):
+ token = tokens[elif_body_end]
+ if token[0] == 'IDENTIFIER' and token[1] in ['seif', 'sel', 'end']:
+ break
+ stmt, new_pos = self.parse_statement(tokens, elif_body_end)
+ if stmt:
+ elif_body.append(stmt)
+ elif_body_end = new_pos
+ else:
+ elif_body_end += 1
+
+ elif_clauses.append((elif_condition, elif_body))
+ current_pos = elif_body_end
+
+ # Parse else clause
+ else_body = []
+ if current_pos < len(tokens) and tokens[current_pos][0] == 'IDENTIFIER' and tokens[current_pos][1] == 'sel':
+ current_pos += 1 # Skip 'sel'
+
+ # Check for separator '|'
+ if current_pos >= len(tokens) or not (tokens[current_pos][0] == 'SEPARATOR' and tokens[current_pos][1] == '|'):
+ return None, pos
+
+ current_pos += 1 # Skip '|'
+
+ # Parse else body
+ else_body_end = current_pos
+ while else_body_end < len(tokens):
+ token = tokens[else_body_end]
+ if token[0] == 'IDENTIFIER' and token[1] == 'end':
+ break
+ stmt, new_pos = self.parse_statement(tokens, else_body_end)
+ if stmt:
+ else_body.append(stmt)
+ else_body_end = new_pos
+ else:
+ else_body_end += 1
+
+ current_pos = else_body_end
+
+ # Check for end statement
+ if current_pos >= len(tokens) or not (tokens[current_pos][0] == 'IDENTIFIER' and tokens[current_pos][1] == 'end'):
+ return None, pos
+
+ current_pos += 1 # Skip 'end'
+
+ return ('IF_ELSE', condition, if_body, elif_clauses, else_body), current_pos
+
+ # Handle while statement (swhi)
+ elif pos < len(tokens) and tokens[pos][0] == 'IDENTIFIER' and tokens[pos][1] == 'swhi':
+ current_pos = pos + 1 # Skip 'swhi'
+
+ # Find separator '|' for condition
+ cond_end = current_pos
+ while cond_end < len(tokens) and not (tokens[cond_end][0] == 'SEPARATOR' and tokens[cond_end][1] == '|'):
+ cond_end += 1
+
+ if cond_end >= len(tokens):
+ return None, pos
+
+ # Extract condition
+ condition = tokens[current_pos:cond_end]
+ current_pos = cond_end + 1 # Skip '|'
+
+ # Parse while body
+ body = []
+ body_end = current_pos
+ while body_end < len(tokens):
+ token = tokens[body_end]
+ if token[0] == 'IDENTIFIER' and token[1] == 'end':
+ break
+ stmt, new_pos = self.parse_statement(tokens, body_end)
+ if stmt:
+ body.append(stmt)
+ body_end = new_pos
+ else:
+ body_end += 1
+
+ current_pos = body_end
+
+ # Check for end statement
+ if current_pos >= len(tokens) or not (tokens[current_pos][0] == 'IDENTIFIER' and tokens[current_pos][1] == 'end'):
+ return None, pos
+
+ current_pos += 1 # Skip 'end'
+
+ return ('WHILE', condition, body), current_pos
+
+ if pos < len(tokens):
+ token = tokens[pos]
+ if token[0] == 'IDENTIFIER':
+ local_pos = pos + 1
+ local_pos = self._skip_whitespace(tokens, local_pos)
+ if local_pos < len(tokens) and tokens[local_pos][0] == 'OPERATOR' and tokens[local_pos][1] == '=':
+ local_pos += 1
+ local_pos = self._skip_whitespace(tokens, local_pos)
+ expr, new_pos = self.parse_expression(tokens, local_pos)
+ if expr:
+ return ('ASSIGNMENT', token[1], expr), new_pos
+
+ # 然后调用siew插件,确保它能处理sif和swhile语句
+ if 'siew' in self.plugins:
+ stmt, new_pos = self.plugins['siew'].parse_statement(tokens, pos)
+ if stmt:
+ return stmt, new_pos
+
+ # 最后调用其他插件
+ if self.debug_mode:
+ print(f"Debug: Available plugins: {list(self.plugins.keys())}")
+ for plugin_name, plugin in self.plugins.items():
+ if plugin_name != 'siew':
+ if self.debug_mode:
+ print(f"Debug: Checking plugin {plugin_name}")
+ if hasattr(plugin, 'parse_statement'):
+ if self.debug_mode:
+ print(f"Debug: Calling {plugin_name}.parse_statement at pos {pos}")
+ stmt, new_pos = plugin.parse_statement(tokens, pos)
+ if stmt:
+ if self.debug_mode:
+ print(f"Debug: {plugin_name} returned statement: {stmt}")
+ return stmt, new_pos
+ else:
+ if self.debug_mode:
+ print(f"Debug: {plugin_name} returned None")
+ else:
+ if self.debug_mode:
+ print(f"Debug: {plugin_name} has no parse_statement method")
+ return None, pos
+ def evaluate_condition(self, condition_tokens):
+ """Evaluate a condition from tokens"""
+ if len(condition_tokens) >= 3:
+ left = condition_tokens[0]
+ op = condition_tokens[1]
+ right = condition_tokens[2]
+
+ left_value = self.evaluate_expression(left)
+ right_value = self.evaluate_expression(right)
+
+ if op[1] == '>':
+ return left_value > right_value
+ elif op[1] == '<':
+ return left_value < right_value
+ elif op[1] == '==':
+ return left_value == right_value
+ elif op[1] == '!=':
+ return left_value != right_value
+ elif op[1] == '>=':
+ return left_value >= right_value
+ elif op[1] == '<=':
+ return left_value <= right_value
+ elif op[1] == '&':
+ return left_value and right_value
+ elif op[1] == '|':
+ return left_value or right_value
+ return False
+
+ def evaluate_expression(self, expr):
+ """Evaluate an expression (optimized)"""
+ if isinstance(expr, list):
+ if len(expr) == 3 and expr[1][0] == 'OPERATOR':
+ left = self.evaluate_expression(expr[0])
+ op = expr[1][1]
+ right = self.evaluate_expression(expr[2])
+
+ # 使用字典映射代替多个 if-elif 分支
+ op_map = {
+ '+': lambda l, r: str(l) + str(r) if isinstance(l, str) or isinstance(r, str) else l + r,
+ '-': lambda l, r: l - r,
+ '*': lambda l, r: l * r,
+ '/': lambda l, r: l / r if r != 0 else 0
+ }
+ return op_map.get(op, lambda l, r: 0)(left, right)
+
+ if expr[0] == 'STRING':
+ return expr[1]
+ elif expr[0] == 'NUMBER':
+ # 缓存数字转换结果
+ try:
+ num_str = expr[1]
+ if '.' in num_str:
+ return float(num_str)
+ else:
+ return int(num_str)
+ except (ValueError, TypeError):
+ return 0
+ elif expr[0] == 'IDENTIFIER':
+ # 快速变量查找
+ return self.variables.get(expr[1], 0)
+ elif expr[0] == 'FUNCTION_CALL':
+ # 处理函数调用
+ func_name = expr[1]
+ args = expr[2]
+
+ # 评估参数
+ evaluated_args = [self.evaluate_expression(arg) for arg in args]
+
+ # 检查是否有插件处理函数调用
+ for plugin in self.plugins.values():
+ result = plugin.execute_statement(('FUNCTION_CALL', func_name, evaluated_args))
+ # 检查结果是否不是布尔值 - 这意味着函数返回了一个值
+ if not isinstance(result, bool):
+ return result
+
+ # 如果没有插件处理,返回 0
+ return 0
+ return 0
+
+ def execute_statement(self, stmt):
+ """Execute a statement"""
+ # 检查是否是debug语句
+ if stmt[0] == 'DEBUG':
+ # 设置debug模式
+ self.debug_mode = stmt[1]
+ # 打印debug信息
+ print(f"Debug mode: {self.debug_mode}")
+ return True
+
+ # 直接执行PRINT语句,不依赖basic插件
+ if stmt[0] == 'PRINT':
+ value = self.evaluate_expression(stmt[1])
+ print(value)
+ return True
+
+
+
+ # 处理END语句
+ if stmt[0] == 'END':
+ return True
+
+ # 处理IF_ELSE语句
+ if stmt[0] == 'IF_ELSE':
+ if_condition = stmt[1]
+ if_body = stmt[2]
+ elif_clauses = stmt[3]
+ else_body = stmt[4]
+
+ if self.evaluate_condition(if_condition):
+ for body_stmt in if_body:
+ self.execute_statement(body_stmt)
+ return True
+
+ for elif_condition, elif_body in elif_clauses:
+ if self.evaluate_condition(elif_condition):
+ for body_stmt in elif_body:
+ self.execute_statement(body_stmt)
+ return True
+
+ if else_body:
+ for body_stmt in else_body:
+ self.execute_statement(body_stmt)
+
+ return True
+
+ # 处理WHILE语句
+ elif stmt[0] == 'WHILE':
+ condition_tokens = stmt[1]
+ body = stmt[2]
+
+ while self.evaluate_condition(condition_tokens):
+ for body_stmt in body:
+ self.execute_statement(body_stmt)
+
+ return True
+
+ # 执行赋值语句
+ if stmt[0] == 'ASSIGNMENT':
+ var_name = stmt[1]
+ value = self.evaluate_expression(stmt[2])
+ self.variables[var_name] = value
+ if self.debug_mode:
+ print(f"Debug: Assigned {value} to variable {var_name}")
+ return True
+
+ # 处理插件赋值语句: set var | var : plugin : command
+ if stmt[0] == 'PLUGIN_ASSIGN':
+ var_name = stmt[1]
+ plugin_name = stmt[2]
+ command_name = stmt[3]
+ args_tokens = stmt[4]
+
+ # Check if plugin exists
+ if plugin_name not in self.plugins:
+ print(f"Error: Plugin '{plugin_name}' not found")
+ return False
+
+ plugin = self.plugins[plugin_name]
+
+ # Create a statement that the plugin can understand
+ plugin_stmt = None
+
+ # For math plugin
+ if plugin_name == 'math':
+ plugin_stmt = ('MATH', command_name, args_tokens)
+ # For string plugin
+ elif plugin_name == 'string':
+ plugin_stmt = ('STRING_OP', command_name, args_tokens)
+ # For crypto plugin
+ elif plugin_name == 'crypto':
+ plugin_stmt = ('CRYPTO_OP', command_name, args_tokens)
+ # For unit plugin
+ elif plugin_name == 'unit':
+ plugin_stmt = ('UNIT_CONVERT', command_name, args_tokens)
+ # For request plugin
+ elif plugin_name == 'request':
+ plugin_stmt = ('REQUEST', command_name.upper(), args_tokens)
+ # For json plugin
+ elif plugin_name == 'json':
+ plugin_stmt = ('JSON_OP', command_name, args_tokens)
+ # For color plugin
+ elif plugin_name == 'color':
+ # Color plugin doesn't return values, it just prints
+ return plugin.execute_statement(('COLOR_OUTPUT', args_tokens[0], args_tokens[2])) if len(args_tokens) >= 3 else False
+
+ if plugin_stmt:
+ # Create a temporary variable to capture the result
+ temp_result = None
+
+ # Override print for this execution to capture the result
+ import builtins
+ original_print = builtins.print
+
+ def capture_print(*args, **kwargs):
+ nonlocal temp_result
+ temp_result = ' '.join(map(str, args))
+ original_print(*args, **kwargs)
+
+ builtins.print = capture_print
+
+ # Execute the plugin statement
+ try:
+ success = plugin.execute_statement(plugin_stmt)
+ finally:
+ # Restore original print
+ builtins.print = original_print
+
+ if success and temp_result is not None:
+ # Try to convert to appropriate type
+ try:
+ if '.' in temp_result:
+ result_value = float(temp_result)
+ else:
+ result_value = int(temp_result)
+ except ValueError:
+ # Keep as string
+ result_value = temp_result
+
+ # Assign the result to the variable
+ self.variables[var_name] = result_value
+ if self.debug_mode:
+ print(f"Debug: Assigned plugin result '{result_value}' to variable '{var_name}'")
+ return True
+
+ print(f"Error: Failed to execute plugin assignment for '{var_name}'")
+ return False
+
+ # 优先调用siew插件,确保它能处理IF、IF_ELSE和WHILE语句
+ if 'siew' in self.plugins:
+ if self.plugins['siew'].execute_statement(stmt):
+ return True
+
+ # 然后调用其他插件
+ for plugin_name, plugin in self.plugins.items():
+ if plugin_name != 'siew' and hasattr(plugin, 'execute_statement'):
+ if plugin.execute_statement(stmt):
+ return True
+ return False
+
+ def execute(self, code, file_path=None):
+ """Execute the given SCL code (optimized)"""
+ def smart_split(code):
+ """Optimized line splitting that handles strings correctly"""
+ lines = []
+ current_line = []
+ in_string = False
+
+ # 预分配空间,减少列表扩展开销
+ code_len = len(code)
+ current_line = [''] * code_len
+ line_pos = 0
+
+ for char in code:
+ if char == '"':
+ in_string = not in_string
+ current_line[line_pos] = char
+ line_pos += 1
+ elif char == '\n' and not in_string:
+ lines.append(''.join(current_line[:line_pos]))
+ current_line = [''] * code_len
+ line_pos = 0
+ else:
+ current_line[line_pos] = char
+ line_pos += 1
+
+ if line_pos > 0:
+ lines.append(''.join(current_line[:line_pos]))
+
+ return lines
+
+ lines = smart_split(code)
+
+ # 用于处理多行语句
+ multi_line_buffer = []
+ in_multi_line = False
+ nested_level = 0 # 用于跟踪嵌套级别
+
+ # 预定义常量
+ multi_line_starts = {
+ 'sif ', 'swhile :', 'swhile |', 'swhi ', 'srg', 'sdef <', 'sclass <'
+ }
+
+ for line_num, line in enumerate(lines, 1):
+ # 快速跳过空行和注释
+ stripped_line = line.strip()
+ if not stripped_line or stripped_line.startswith('#') or stripped_line.startswith('//'):
+ continue
+
+ # 处理插件导入和SCL文件导入
+ if line.startswith('simp{') and line.endswith('}'):
+ import_content = line[5:-1].strip()
+
+ # 检查是否是SCL文件导入: simp{scl : 路径}
+ if import_content.startswith('scl :'):
+ scl_path = import_content[5:].strip()
+ if not self.import_scl_file(scl_path, line_num):
+ return False
+ else:
+ # 插件导入
+ if not self.load_plugin(import_content):
+ print(f"Error at line {line_num}: Failed to load plugin {import_content}")
+ print(f"Code: {line}")
+ return False
+ continue
+
+ try:
+ # 检查是否是多行语句的开始
+ is_multi_line_start = False
+ for prefix in multi_line_starts:
+ if line.startswith(prefix):
+ is_multi_line_start = True
+ break
+
+ # 特殊处理 sde 语句
+ if not is_multi_line_start and line.startswith('sde ') and line.endswith(' :'):
+ is_multi_line_start = True
+
+ if is_multi_line_start:
+ # 打印调试信息
+ if self.debug_mode:
+ print(f"Debug: Found multi-line statement start: {line}")
+ multi_line_buffer.append(line)
+ if not in_multi_line:
+ in_multi_line = True
+ nested_level = 1 # 开始一个新的多行语句,嵌套级别为1
+ # 打印调试信息
+ if self.debug_mode:
+ print(f"Debug: Started new multi-line statement, nested_level = {nested_level}")
+ else:
+ nested_level += 1 # 遇到新的嵌套语句,嵌套级别加1
+ # 打印调试信息
+ if self.debug_mode:
+ print(f"Debug: Found nested statement, nested_level = {nested_level}")
+ # 检查是否是多行语句的结束
+ elif line == 'end' and in_multi_line:
+ # 打印调试信息
+ if self.debug_mode:
+ print(f"Debug: Found multi-line statement end: {line}")
+ multi_line_buffer.append(line)
+ nested_level -= 1 # 遇到end,嵌套级别减1
+ # 打印调试信息
+ if self.debug_mode:
+ print(f"Debug: Decreased nested_level to {nested_level}")
+ if nested_level == 0:
+ # 嵌套级别为0,说明是外部语句的结束
+ in_multi_line = False
+ # 处理完整的多行语句
+ multi_line_code = '\n'.join(multi_line_buffer)
+ multi_line_buffer = []
+ # 打印调试信息
+ if self.debug_mode:
+ print(f"Debug: Processing complete multi-line statement:\n{multi_line_code}")
+ # 这里我们需要特殊处理多行语句
+ # 简单起见,我们将多行语句作为一个整体进行tokenize和parse
+ # 注意:这种方法可能不是最优的,但是可以解决当前的问题
+ tokens = self.tokenize(multi_line_code)
+ # 打印调试信息
+ if self.debug_mode:
+ print(f"Debug: Tokens: {tokens}")
+ if tokens:
+ stmt, _ = self.parse_statement(tokens, 0)
+ # 打印调试信息
+ if self.debug_mode:
+ print(f"Debug: Parsed statement: {stmt}")
+ if stmt:
+ if not self.execute_statement(stmt):
+ print(f"Error at line {line_num}: Failed to execute statement")
+ print(f"Code: {multi_line_code}")
+ return False
+ else:
+ print(f"Error at line {line_num}: Invalid syntax")
+ print(f"Code: {multi_line_code}")
+ return False
+ # 处理多行语句的中间部分
+ elif in_multi_line:
+ multi_line_buffer.append(line)
+ # 检查是否是新的嵌套语句的开始
+ if line.startswith('sif :') or line.startswith('swhile :'):
+ nested_level += 1 # 遇到新的嵌套语句,嵌套级别加1
+ # 处理单行语句
+ else:
+ # 使用原始的line,不移除空白字符
+ tokens = self.tokenize(line)
+ if tokens:
+ stmt, _ = self.parse_statement(tokens, 0)
+ if stmt:
+ if not self.execute_statement(stmt):
+ print(f"Error at line {line_num}: Failed to execute statement")
+ print(f"Code: {line}")
+ return False
+ else:
+ print(f"Error at line {line_num}: Invalid syntax")
+ print(f"Code: {line}")
+ return False
+ except Exception as e:
+ print(f"Error at line {line_num}: {e}")
+ print(f"Code: {line}")
+ import traceback
+ traceback.print_exc()
+ return False
+
+ # 检查是否有未完成的多行语句
+ if in_multi_line:
+ print(f"Error at line {line_num}: Unclosed multi-line statement")
+ print(f"Code: {''.join(multi_line_buffer)}")
+ return False
+
+ return True
+
+def nano_editor(file_path=None):
+ """仿nano的终端文本编辑器"""
+ import shutil
+
+ # 获取终端大小
+ def get_terminal_size():
+ return shutil.get_terminal_size()
+
+ # 清屏
+ def clear_screen():
+ os.system('cls' if os.name == 'nt' else 'clear')
+
+ # 读取文件内容
+ lines = []
+ if file_path and os.path.exists(file_path):
+ try:
+ with open(file_path, 'r', encoding='utf-8') as f:
+ lines = f.read().split('\n')
+ except:
+ lines = ['']
+ else:
+ lines = ['']
+
+ # 确保至少有一行
+ if not lines:
+ lines = ['']
+
+ # 编辑器状态
+ cursor_x = 0
+ cursor_y = 0
+ scroll_y = 0
+ modified = False
+ filename = file_path if file_path else "New File"
+
+ # 隐藏光标(Windows)
+ if os.name == 'nt':
+ import ctypes
+ kernel32 = ctypes.windll.kernel32
+ handle = kernel32.GetStdHandle(-11)
+ # 禁用行输入和回显
+ mode = ctypes.c_uint32()
+ kernel32.GetConsoleMode(handle, ctypes.byref(mode))
+ kernel32.SetConsoleMode(handle, mode.value & ~0x0004 & ~0x0002)
+
+ def draw_editor():
+ """绘制编辑器界面"""
+ clear_screen()
+ term_width, term_height = get_terminal_size()
+
+ # 标题栏
+ title = f" SCL Nano Editor - {filename}"
+ if modified:
+ title += " [Modified]"
+ title = title[:term_width].ljust(term_width)
+ print(f"\033[7m{title}\033[0m")
+
+ # 编辑区域
+ edit_height = term_height - 3
+ for i in range(edit_height):
+ line_idx = scroll_y + i
+ if line_idx < len(lines):
+ line = lines[line_idx]
+ # 显示行号
+ line_num = str(line_idx + 1).rjust(4)
+ # 截断行内容以适应屏幕
+ content = line[:term_width - 6]
+ content = content.ljust(term_width - 6)
+
+ # 高亮当前行
+ if line_idx == cursor_y:
+ print(f"\033[7m{line_num}\033[0m {content}")
+ else:
+ print(f"{line_num} {content}")
+ else:
+ print("~".ljust(term_width))
+
+ # 状态栏
+ status = f" Line {cursor_y + 1}/{len(lines)}, Col {cursor_x + 1} | Ctrl+S=Save, Ctrl+X=Exit, Ctrl+O=Open, Ctrl+R=Run"
+ status = status[:term_width].ljust(term_width)
+ print(f"\033[7m{status}\033[0m")
+
+ # 移动光标到正确位置
+ cursor_screen_y = cursor_y - scroll_y + 1
+ # 光标位置 = 行号宽度(5) + cursor_x + 1
+ # 行号占4位 + 1个空格 = 5,内容从第6列开始
+ # cursor_x=0 时,光标应在第6列(内容的第1个字符)
+ cursor_screen_x = 5 + cursor_x + 1
+ # 限制在屏幕范围内
+ cursor_screen_x = min(cursor_screen_x, term_width)
+ print(f"\033[{cursor_screen_y + 1};{cursor_screen_x}H", end='', flush=True)
+
+ def save_file():
+ """保存文件"""
+ nonlocal modified, filename
+ if not filename or filename == "New File":
+ # 提示输入文件名
+ term_width, term_height = get_terminal_size()
+ prompt = "Filename to write: "
+ print(f"\033[{term_height};1H\033[0K{prompt}", end='', flush=True)
+
+ # 简单输入文件名
+ new_filename = input()
+ if new_filename:
+ filename = new_filename
+ else:
+ return False
+
+ try:
+ with open(filename, 'w', encoding='utf-8') as f:
+ f.write('\n'.join(lines))
+ modified = False
+ return True
+ except Exception as e:
+ term_width, term_height = get_terminal_size()
+ print(f"\033[{term_height};1H\033[0KError saving: {e}", end='', flush=True)
+ input(" Press Enter to continue...")
+ return False
+
+ def open_file():
+ """打开文件"""
+ nonlocal lines, cursor_x, cursor_y, scroll_y, modified, filename
+ term_width, term_height = get_terminal_size()
+ prompt = "Filename to open: "
+ print(f"\033[{term_height};1H\033[0K{prompt}", end='', flush=True)
+
+ new_filename = input()
+ if new_filename and os.path.exists(new_filename):
+ try:
+ with open(new_filename, 'r', encoding='utf-8') as f:
+ lines = f.read().split('\n')
+ if not lines:
+ lines = ['']
+ filename = new_filename
+ cursor_x = 0
+ cursor_y = 0
+ scroll_y = 0
+ modified = False
+ return True
+ except Exception as e:
+ print(f"\033[{term_height};1H\033[0KError opening: {e}", end='', flush=True)
+ input(" Press Enter to continue...")
+ return False
+
+ # 主循环
+ try:
+ while True:
+ draw_editor()
+
+ # 读取按键
+ if os.name == 'nt':
+ import msvcrt
+ key = msvcrt.getch()
+
+ # 处理特殊键
+ if key == b'\x00' or key == b'\xe0':
+ key = msvcrt.getch()
+ if key == b'H': # 上箭头
+ if cursor_y > 0:
+ cursor_y -= 1
+ cursor_x = min(cursor_x, len(lines[cursor_y]) if lines[cursor_y] else 0)
+ elif key == b'P': # 下箭头
+ if cursor_y < len(lines) - 1:
+ cursor_y += 1
+ cursor_x = min(cursor_x, len(lines[cursor_y]) if lines[cursor_y] else 0)
+ elif key == b'K': # 左箭头
+ if cursor_x > 0:
+ cursor_x -= 1
+ elif key == b'M': # 右箭头
+ if cursor_x < len(lines[cursor_y]):
+ cursor_x += 1
+ elif key == b'\x13': # Ctrl+S
+ save_file()
+ elif key == b'\x18': # Ctrl+X
+ if modified:
+ term_width, term_height = get_terminal_size()
+ print(f"\033[{term_height};1H\033[0KSave modified buffer? (Y/N/C)", end='', flush=True)
+ response = input().upper()
+ if response == 'Y':
+ if save_file():
+ break
+ elif response == 'N':
+ break
+ else:
+ break
+ elif key == b'\x0f': # Ctrl+O
+ open_file()
+ elif key == b'\x12': # Ctrl+R - 运行文件
+ if filename and filename != "New File":
+ # 先保存文件
+ if modified:
+ save_file()
+ # 退出编辑器并运行文件
+ clear_screen()
+ print(f"Running {filename}...")
+ print("=" * 50)
+ # 创建解释器并执行
+ interpreter = SCLInterpreter()
+ try:
+ with open(filename, 'r', encoding='utf-8') as f:
+ code = f.read()
+ interpreter.execute(code, filename)
+ except Exception as e:
+ print(f"Error: {e}")
+ print("=" * 50)
+ print("Press any key to return to editor...")
+ msvcrt.getch()
+ else:
+ # 提示先保存文件
+ term_width, term_height = get_terminal_size()
+ print(f"\033[{term_height};1H\033[0KPlease save file first (Ctrl+S)", end='', flush=True)
+ msvcrt.getch()
+ elif key == b'\r': # Enter
+ # 在当前位置分割行
+ current_line = lines[cursor_y]
+ lines.insert(cursor_y + 1, current_line[cursor_x:])
+ lines[cursor_y] = current_line[:cursor_x]
+ cursor_y += 1
+ cursor_x = 0
+ modified = True
+ elif key == b'\x08': # Backspace
+ if cursor_x > 0:
+ lines[cursor_y] = lines[cursor_y][:cursor_x-1] + lines[cursor_y][cursor_x:]
+ cursor_x -= 1
+ modified = True
+ elif cursor_y > 0:
+ # 合并到上一行
+ cursor_x = len(lines[cursor_y - 1])
+ lines[cursor_y - 1] += lines[cursor_y]
+ del lines[cursor_y]
+ cursor_y -= 1
+ modified = True
+ elif key == b'\x7f': # Delete
+ if cursor_x < len(lines[cursor_y]):
+ lines[cursor_y] = lines[cursor_y][:cursor_x] + lines[cursor_y][cursor_x+1:]
+ modified = True
+ elif cursor_y < len(lines) - 1:
+ # 合并下一行
+ lines[cursor_y] += lines[cursor_y + 1]
+ del lines[cursor_y + 1]
+ modified = True
+ elif key == b'"': # 双引号
+ lines[cursor_y] = lines[cursor_y][:cursor_x] + '"' + lines[cursor_y][cursor_x:]
+ cursor_x += 1
+ modified = True
+ elif key == b"'": # 单引号
+ lines[cursor_y] = lines[cursor_y][:cursor_x] + "'" + lines[cursor_y][cursor_x:]
+ cursor_x += 1
+ modified = True
+ elif key >= b' ' and key <= b'~': # 其他可打印字符
+ try:
+ char = key.decode('utf-8')
+ except:
+ char = key.decode('ascii', errors='replace')
+ lines[cursor_y] = lines[cursor_y][:cursor_x] + char + lines[cursor_y][cursor_x:]
+ cursor_x += 1
+ modified = True
+ else:
+ # Linux/Mac 使用简单输入
+ import tty
+ import termios
+ import select
+
+ fd = sys.stdin.fileno()
+ old_settings = termios.tcgetattr(fd)
+ try:
+ tty.setraw(fd)
+ key = sys.stdin.read(1)
+
+ if key == '\x13': # Ctrl+S
+ save_file()
+ elif key == '\x18': # Ctrl+X
+ if modified:
+ print("\nSave modified buffer? (Y/N/C)")
+ response = input().upper()
+ if response == 'Y':
+ if save_file():
+ break
+ elif response == 'N':
+ break
+ else:
+ break
+ elif key == '\x0f': # Ctrl+O
+ open_file()
+ elif key == '\x12': # Ctrl+R - 运行文件
+ if filename and filename != "New File":
+ # 先保存文件
+ if modified:
+ save_file()
+ # 退出编辑器并运行文件
+ clear_screen()
+ print(f"Running {filename}...")
+ print("=" * 50)
+ # 创建解释器并执行
+ interpreter = SCLInterpreter()
+ try:
+ with open(filename, 'r', encoding='utf-8') as f:
+ code = f.read()
+ interpreter.execute(code, filename)
+ except Exception as e:
+ print(f"Error: {e}")
+ print("=" * 50)
+ print("Press any key to return to editor...")
+ sys.stdin.read(1)
+ else:
+ # 提示先保存文件
+ term_width, term_height = get_terminal_size()
+ print(f"\033[{term_height};1H\033[0KPlease save file first (Ctrl+S)")
+ sys.stdin.read(1)
+ elif key == '\r': # Enter
+ current_line = lines[cursor_y]
+ lines.insert(cursor_y + 1, current_line[cursor_x:])
+ lines[cursor_y] = current_line[:cursor_x]
+ cursor_y += 1
+ cursor_x = 0
+ modified = True
+ elif key == '\x7f': # Backspace
+ if cursor_x > 0:
+ lines[cursor_y] = lines[cursor_y][:cursor_x-1] + lines[cursor_y][cursor_x:]
+ cursor_x -= 1
+ modified = True
+ elif key == '"' or key == "'": # 引号
+ lines[cursor_y] = lines[cursor_y][:cursor_x] + key + lines[cursor_y][cursor_x:]
+ cursor_x += 1
+ modified = True
+ elif ord(key) >= 32 and ord(key) <= 126: # 其他可打印字符
+ lines[cursor_y] = lines[cursor_y][:cursor_x] + key + lines[cursor_y][cursor_x:]
+ cursor_x += 1
+ modified = True
+ finally:
+ termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
+
+ # 调整滚动位置
+ term_width, term_height = get_terminal_size()
+ edit_height = term_height - 3
+ if cursor_y < scroll_y:
+ scroll_y = cursor_y
+ elif cursor_y >= scroll_y + edit_height:
+ scroll_y = cursor_y - edit_height + 1
+
+ finally:
+ # 恢复光标(Windows)
+ if os.name == 'nt':
+ import ctypes
+ kernel32 = ctypes.windll.kernel32
+ handle = kernel32.GetStdHandle(-11)
+ mode = ctypes.c_uint32()
+ kernel32.GetConsoleMode(handle, ctypes.byref(mode))
+ kernel32.SetConsoleMode(handle, mode.value | 0x0004 | 0x0002)
+
+ clear_screen()
+ print(f"Exited nano editor. File: {filename}")
+
+def main():
+ """Main function"""
+ if len(sys.argv) < 2:
+ print("Usage: python scl.py ")
+ print(" python scl.py -nano [file.scl] # Open nano editor")
+ sys.exit(1)
+
+ # 检查是否是nano编辑器模式
+ if sys.argv[1] == '-nano':
+ file_path = sys.argv[2] if len(sys.argv) > 2 else None
+ nano_editor(file_path)
+ sys.exit(0)
+
+ file_path = sys.argv[1]
+
+ if not os.path.exists(file_path):
+ print(f"Error: File {file_path} not found")
+ sys.exit(1)
+
+ interpreter = SCLInterpreter()
+
+ try:
+ with open(file_path, 'r', encoding='utf-8') as f:
+ code = f.read()
+
+ success = interpreter.execute(code, file_path)
+ if not success:
+ sys.exit(1)
+ except Exception as e:
+ print(f"Error executing file: {e}")
+ import traceback
+ traceback.print_exc()
+ sys.exit(1)
+
+if __name__ == "__main__":
+ main()
+
diff --git a/sdp.py b/sdp.py
new file mode 100644
index 0000000..215202a
--- /dev/null
+++ b/sdp.py
@@ -0,0 +1,1059 @@
+#!/usr/bin/env python3
+"""
+SCL Package Manager (SDP)
+Package manager for SCL plugins - similar to apt/yum/composer
+"""
+
+import os
+import sys
+import json
+import urllib.request
+import urllib.parse
+
+class SCLEPackageManager:
+ # Language translations
+ TRANSLATIONS = {
+ "english": {
+ "installing": "Installing",
+ "updating": "Updating",
+ "reinstalling": "Reinstalling",
+ "removing": "Removing",
+ "downloading": "Downloading",
+ "downloaded": "Downloaded",
+ "installed": "installed successfully",
+ "updated": "updated successfully",
+ "reinstalled": "reinstalled successfully",
+ "removed": "removed successfully",
+ "already_installed": "is already installed",
+ "not_installed": "is not installed",
+ "use_install": "Use 'sdp install' to install",
+ "use_update": "Use 'sdp update' to update",
+ "no_plugins": "No plugins installed",
+ "local_plugins": "Local plugins:",
+ "community_plugins": "Community plugins:",
+ "upgradable_plugins": "Plugins available for upgrade:",
+ "testing_network": "Testing network connectivity...",
+ "network_ok": "Network connection OK",
+ "network_fail": "Network connection failed",
+ "config_saved": "Configuration saved",
+ "config_created": "Created",
+ "language_set": "Language set to",
+ "available_languages": "Available languages:",
+ "unknown_command": "Unknown command",
+ "usage": "Usage",
+ "options": "Options",
+ "commands": "Commands"
+ },
+ "chinese_simplified": {
+ "installing": "正在安装",
+ "updating": "正在更新",
+ "reinstalling": "正在重新安装",
+ "removing": "正在删除",
+ "downloading": "正在下载",
+ "downloaded": "已下载",
+ "installed": "安装成功",
+ "updated": "更新成功",
+ "reinstalled": "重新安装成功",
+ "removed": "删除成功",
+ "already_installed": "已安装",
+ "not_installed": "未安装",
+ "use_install": "使用 'sdp install' 安装",
+ "use_update": "使用 'sdp update' 更新",
+ "no_plugins": "没有已安装的插件",
+ "local_plugins": "本地插件:",
+ "community_plugins": "社区插件:",
+ "upgradable_plugins": "可升级的插件:",
+ "testing_network": "测试网络连接...",
+ "network_ok": "网络连接正常",
+ "network_fail": "网络连接失败",
+ "config_saved": "配置已保存",
+ "config_created": "已创建",
+ "language_set": "语言已设置为",
+ "available_languages": "可用语言:",
+ "unknown_command": "未知命令",
+ "usage": "用法",
+ "options": "选项",
+ "commands": "命令"
+ },
+ "chinese_hongkong": {
+ "installing": "正在安裝",
+ "updating": "正在更新",
+ "reinstalling": "正在重新安裝",
+ "removing": "正在移除",
+ "downloading": "正在下載",
+ "downloaded": "已下載",
+ "installed": "安裝成功",
+ "updated": "更新成功",
+ "reinstalled": "重新安裝成功",
+ "removed": "移除成功",
+ "already_installed": "已安裝",
+ "not_installed": "未安裝",
+ "use_install": "使用 'sdp install' 安裝",
+ "use_update": "使用 'sdp update' 更新",
+ "no_plugins": "沒有已安裝的插件",
+ "local_plugins": "本地插件:",
+ "community_plugins": "社區插件:",
+ "upgradable_plugins": "可升級的插件:",
+ "testing_network": "測試網絡連接...",
+ "network_ok": "網絡連接正常",
+ "network_fail": "網絡連接失敗",
+ "config_saved": "配置已保存",
+ "config_created": "已創建",
+ "language_set": "語言已設置為",
+ "available_languages": "可用語言:",
+ "unknown_command": "未知指令",
+ "usage": "用法",
+ "options": "選項",
+ "commands": "指令"
+ },
+ "classical_chinese": {
+ "installing": "正在安裝",
+ "updating": "正在更新",
+ "reinstalling": "正在重裝",
+ "removing": "正在移除",
+ "downloading": "正在下載",
+ "downloaded": "已下載",
+ "installed": "安裝畢",
+ "updated": "更新畢",
+ "reinstalled": "重裝畢",
+ "removed": "移除畢",
+ "already_installed": "已安裝矣",
+ "not_installed": "未安裝",
+ "use_install": "以 'sdp install' 安裝之",
+ "use_update": "以 'sdp update' 更新之",
+ "no_plugins": "無已安裝之插件",
+ "local_plugins": "本地插件:",
+ "community_plugins": "社群插件:",
+ "upgradable_plugins": "可升級之插件:",
+ "testing_network": "測試網絡...",
+ "network_ok": "網絡通暢",
+ "network_fail": "網絡不通",
+ "config_saved": "配置已存",
+ "config_created": "已創建",
+ "language_set": "語言已設為",
+ "available_languages": "可用語言:",
+ "unknown_command": "未知之令",
+ "usage": "用法",
+ "options": "選項",
+ "commands": "指令"
+ },
+ "latin": {
+ "installing": "Installans",
+ "updating": "Actualizans",
+ "reinstalling": "Reinstallans",
+ "removing": "Removens",
+ "downloading": "Downloadens",
+ "downloaded": "Downloadatum",
+ "installed": "installatum est",
+ "updated": "actualizatum est",
+ "reinstalled": "reinstallatum est",
+ "removed": "removitum est",
+ "already_installed": "iam installatum est",
+ "not_installed": "non installatum est",
+ "use_install": "Utere 'sdp install' ad installandum",
+ "use_update": "Utere 'sdp update' ad actualizandum",
+ "no_plugins": "Nulla plugins installata",
+ "local_plugins": "Plugins locales:",
+ "community_plugins": "Plugins communitatis:",
+ "upgradable_plugins": "Plugins actualizabiles:",
+ "testing_network": "Testans conexionem rete...",
+ "network_ok": "Conexio retis bona est",
+ "network_fail": "Conexio retis fallita est",
+ "config_saved": "Configuratio servata est",
+ "config_created": "Creatum est",
+ "language_set": "Lingua posita est ad",
+ "available_languages": "Linguae disponibiles:",
+ "unknown_command": "Commandum ignotum",
+ "usage": "Usus",
+ "options": "Optiones",
+ "commands": "Commanda"
+ },
+ "portuguese": {
+ "installing": "Instalando",
+ "updating": "Atualizando",
+ "reinstalling": "Reinstalando",
+ "removing": "Removendo",
+ "downloading": "Baixando",
+ "downloaded": "Baixado",
+ "installed": "instalado com sucesso",
+ "updated": "atualizado com sucesso",
+ "reinstalled": "reinstalado com sucesso",
+ "removed": "removido com sucesso",
+ "already_installed": "já está instalado",
+ "not_installed": "não está instalado",
+ "use_install": "Use 'sdp install' para instalar",
+ "use_update": "Use 'sdp update' para atualizar",
+ "no_plugins": "Nenhum plugin instalado",
+ "local_plugins": "Plugins locais:",
+ "community_plugins": "Plugins da comunidade:",
+ "upgradable_plugins": "Plugins disponíveis para atualização:",
+ "testing_network": "Testando conectividade de rede...",
+ "network_ok": "Conexão de rede OK",
+ "network_fail": "Falha na conexão de rede",
+ "config_saved": "Configuração salva",
+ "config_created": "Criado",
+ "language_set": "Idioma definido para",
+ "available_languages": "Idiomas disponíveis:",
+ "unknown_command": "Comando desconhecido",
+ "usage": "Uso",
+ "options": "Opções",
+ "commands": "Comandos"
+ },
+ "korean": {
+ "installing": "설치 중",
+ "updating": "업데이트 중",
+ "reinstalling": "재설치 중",
+ "removing": "제거 중",
+ "downloading": "다운로드 중",
+ "downloaded": "다운로드 완료",
+ "installed": "설치 성공",
+ "updated": "업데이트 성공",
+ "reinstalled": "재설치 성공",
+ "removed": "제거 성공",
+ "already_installed": "이미 설치됨",
+ "not_installed": "설치되지 않음",
+ "use_install": "'sdp install'로 설치하세요",
+ "use_update": "'sdp update'로 업데이트하세요",
+ "no_plugins": "설치된 플러그인 없음",
+ "local_plugins": "로컬 플러그인:",
+ "community_plugins": "커뮤니티 플러그인:",
+ "upgradable_plugins": "업그레이드 가능한 플러그인:",
+ "testing_network": "네트워크 연결 테스트 중...",
+ "network_ok": "네트워크 연결 정상",
+ "network_fail": "네트워크 연결 실패",
+ "config_saved": "설정 저장됨",
+ "config_created": "생성됨",
+ "language_set": "언어가 설정됨",
+ "available_languages": "사용 가능한 언어:",
+ "unknown_command": "알 수 없는 명령",
+ "usage": "사용법",
+ "options": "옵션",
+ "commands": "명령"
+ }
+ }
+
+ LANGUAGE_NAMES = {
+ "english": "English",
+ "chinese_simplified": "简体中文",
+ "chinese_hongkong": "中文(香港)",
+ "classical_chinese": "文言文",
+ "latin": "Latin",
+ "portuguese": "Português",
+ "korean": "한국어"
+ }
+
+ # Acknowledgments list
+ ACKNOWLEDGMENTS = [
+ "JGZ_YES - Create SunsetCodeLang(scl) & SunsetCodeLang-DownloadPlugins(sdp)",
+ "XiaoXiaoBai - Termux Adapt"
+ ]
+
+ def __init__(self):
+ self.base_url = "https://scl.ecuil.com/forum/api/download.php"
+ self.api_url = "https://scl.ecuil.com/forum/api/packages.php"
+ self.run_plugins_url = "https://scl.ecuil.com/run/plugins/"
+ self.plugins_dir = "./plugins"
+ self.config_file = "composer.json"
+ self.settings_file = "sdp_settings.json"
+ self.version = "1.0"
+ self.language = self.load_language()
+ self.repositories = self.load_repositories()
+
+ def t(self, key):
+ """Get translation for a key"""
+ return self.TRANSLATIONS.get(self.language, self.TRANSLATIONS["english"]).get(key, key)
+
+ def load_language(self):
+ """Load language from settings file"""
+ if os.path.exists(self.settings_file):
+ try:
+ with open(self.settings_file, 'r', encoding='utf-8') as f:
+ settings = json.load(f)
+ lang = settings.get('language', 'english')
+ if lang in self.TRANSLATIONS:
+ return lang
+ except Exception:
+ pass
+ return "english"
+
+ def save_language(self, language):
+ """Save language to settings file"""
+ if language not in self.TRANSLATIONS:
+ return False
+
+ settings = {}
+ if os.path.exists(self.settings_file):
+ try:
+ with open(self.settings_file, 'r', encoding='utf-8') as f:
+ settings = json.load(f)
+ except Exception:
+ pass
+
+ settings['language'] = language
+
+ try:
+ with open(self.settings_file, 'w', encoding='utf-8') as f:
+ json.dump(settings, f, indent=2)
+ return True
+ except Exception as e:
+ self.error(f"Failed to save language setting: {e}")
+ return False
+
+ def set_language(self, language):
+ """Set the interface language"""
+ lang_map = {
+ "english": "english",
+ "chinese": "chinese_simplified",
+ "chinese_simplified": "chinese_simplified",
+ "简体中文": "chinese_simplified",
+ "chinese_hongkong": "chinese_hongkong",
+ "中文(香港)": "chinese_hongkong",
+ "classical_chinese": "classical_chinese",
+ "文言文": "classical_chinese",
+ "latin": "latin",
+ "portuguese": "portuguese",
+ "korean": "korean"
+ }
+
+ normalized_lang = lang_map.get(language.lower(), language.lower())
+
+ if normalized_lang not in self.TRANSLATIONS:
+ self.error(f"Unknown language: {language}")
+ self.show_available_languages()
+ return False
+
+ if self.save_language(normalized_lang):
+ self.language = normalized_lang
+ self.success(f"{self.t('language_set')} {self.LANGUAGE_NAMES[normalized_lang]}")
+ return True
+ return False
+
+ def show_available_languages(self):
+ """Show available languages"""
+ self.log(self.t("available_languages"))
+ for key, name in self.LANGUAGE_NAMES.items():
+ marker = " *" if key == self.language else ""
+ self.log(f" {key}: {name}{marker}")
+
+ def load_repositories(self):
+ """Load repositories from settings file"""
+ default_repos = [
+ {
+ "name": "official",
+ "base_url": "https://scl.ecuil.com/forum/api/download.php",
+ "api_url": "https://scl.ecuil.com/forum/api/packages.php",
+ "run_plugins_url": "https://scl.ecuil.com/run/plugins/"
+ },
+ {
+ "name": "official_hk",
+ "base_url": "https://hk-api.ecuil.com/scl/repo/api/download.php",
+ "api_url": "https://hk-api.ecuil.com/scl/repo/api/packages.php",
+ "run_plugins_url": "https://hk-api.ecuil.com/scl/repo/"
+ }
+ ]
+
+ if os.path.exists(self.settings_file):
+ try:
+ with open(self.settings_file, 'r', encoding='utf-8') as f:
+ settings = json.load(f)
+ repos = settings.get('repositories', [])
+ if repos:
+ return repos
+ except Exception:
+ pass
+ return default_repos
+
+ def save_repositories(self):
+ """Save repositories to settings file"""
+ settings = {}
+ if os.path.exists(self.settings_file):
+ try:
+ with open(self.settings_file, 'r', encoding='utf-8') as f:
+ settings = json.load(f)
+ except Exception:
+ pass
+
+ settings['repositories'] = self.repositories
+
+ try:
+ with open(self.settings_file, 'w', encoding='utf-8') as f:
+ json.dump(settings, f, indent=2)
+ return True
+ except Exception as e:
+ self.error(f"Failed to save repositories: {e}")
+ return False
+
+ def add_repository(self, repo_url):
+ """Add a new repository"""
+ # Normalize URL
+ repo_url = repo_url.rstrip('/')
+
+ # Check if already exists
+ for repo in self.repositories:
+ if repo.get('run_plugins_url', '').rstrip('/') == repo_url or \
+ repo.get('base_url', '').rstrip('/') == repo_url:
+ self.warning(f"Repository already exists: {repo_url}")
+ return False
+
+ # Try to detect repository structure
+ # Support formats:
+ # 1. https://example.com/run/plugins/ -> direct plugin URL
+ # 2. https://example.com/ -> try to detect API endpoints
+
+ new_repo = {
+ "name": f"repo_{len(self.repositories)}",
+ "base_url": f"{repo_url}/api/download.php" if not repo_url.endswith('.php') else repo_url,
+ "api_url": f"{repo_url}/api/packages.php" if not repo_url.endswith('.php') else repo_url.replace('download.php', 'packages.php'),
+ "run_plugins_url": repo_url if '/run/plugins' in repo_url else f"{repo_url}/run/plugins/"
+ }
+
+ # Test repository
+ try:
+ test_url = f"{new_repo['run_plugins_url']}test.py"
+ req = urllib.request.Request(test_url, method='HEAD')
+ req.add_header('User-Agent', 'SCL-PackageManager/1.0')
+ with urllib.request.urlopen(req, timeout=5) as response:
+ pass
+ except:
+ pass # Ignore test failure, repository might still work
+
+ self.repositories.append(new_repo)
+
+ if self.save_repositories():
+ self.success(f"Repository added: {repo_url}")
+ return True
+ return False
+
+ def remove_repository(self, repo_name_or_url):
+ """Remove a repository"""
+ for i, repo in enumerate(self.repositories):
+ if repo.get('name') == repo_name_or_url or \
+ repo.get('run_plugins_url', '').rstrip('/') == repo_name_or_url.rstrip('/') or \
+ repo.get('base_url', '').rstrip('/') == repo_name_or_url.rstrip('/'):
+ if i == 0 and len(self.repositories) == 1:
+ self.error("Cannot remove the last repository")
+ return False
+ removed = self.repositories.pop(i)
+ if self.save_repositories():
+ self.success(f"Repository removed: {removed.get('name', repo_name_or_url)}")
+ return True
+ return False
+
+ self.error(f"Repository not found: {repo_name_or_url}")
+ return False
+
+ def list_repositories(self):
+ """List all repositories"""
+ self.log("Configured repositories:")
+ self.log("-" * 60)
+ for i, repo in enumerate(self.repositories, 1):
+ name = repo.get('name', f'repo_{i}')
+ url = repo.get('run_plugins_url', repo.get('base_url', 'Unknown'))
+ marker = " (default)" if i == 1 else ""
+ self.log(f"{i}. {name}{marker}")
+ self.log(f" URL: {url}")
+
+ def log(self, message):
+ print(message)
+
+ def error(self, message):
+ print(f"Error: {message}", file=sys.stderr)
+
+ def success(self, message):
+ print(f"✓ {message}")
+
+ def warning(self, message):
+ print(f"⚠ {message}")
+
+ def ensure_plugins_dir(self):
+ os.makedirs(self.plugins_dir, exist_ok=True)
+
+ def get_installed_plugins(self):
+ if not os.path.exists(self.plugins_dir):
+ return []
+
+ plugins = []
+ for file in os.listdir(self.plugins_dir):
+ if file.endswith('.py') and file != '__init__.py':
+ plugin_name = file[:-3]
+ plugins.append(plugin_name.lower())
+ return plugins
+
+ def get_plugin_info(self, plugin_name):
+ try:
+ req = urllib.request.Request(f"{self.api_url}?action=info&name={plugin_name.lower()}")
+ req.add_header('User-Agent', 'SCL-PackageManager/1.0')
+
+ with urllib.request.urlopen(req, timeout=10) as response:
+ if response.status == 200:
+ data = json.loads(response.read().decode('utf-8'))
+ if data.get('success'):
+ return data.get('package', None)
+ except Exception as e:
+ self.error(f"Failed to get plugin info: {e}")
+ return None
+
+ def download_plugin(self, plugin_name):
+ self.log(f"{self.t('downloading')} {plugin_name}...")
+
+ # Try each repository in order
+ for i, repo in enumerate(self.repositories):
+ repo_name = repo.get('name', f'repo_{i}')
+ run_plugins_url = repo.get('run_plugins_url', '')
+ base_url = repo.get('base_url', '')
+
+ try:
+ # Try run_plugins_url first
+ if run_plugins_url:
+ run_url = f"{run_plugins_url}{plugin_name.lower()}.py"
+ req = urllib.request.Request(run_url)
+ req.add_header('User-Agent', 'SCL-PackageManager/1.0')
+
+ try:
+ with urllib.request.urlopen(req, timeout=15) as response:
+ if response.status == 200:
+ content = response.read()
+ self.success(f"{self.t('downloaded')} {len(content)} bytes from {repo_name}")
+ return content
+ except urllib.error.HTTPError as e:
+ if e.code != 404:
+ raise
+
+ # Try base_url as fallback
+ if base_url:
+ url = f"{base_url}?name={plugin_name.lower()}"
+ req = urllib.request.Request(url)
+ req.add_header('User-Agent', 'SCL-PackageManager/1.0')
+
+ with urllib.request.urlopen(req, timeout=30) as response:
+ content = response.read()
+ content_type = response.headers.get('Content-Type', '')
+
+ if 'application/json' in content_type:
+ data = json.loads(content.decode('utf-8'))
+ if data.get('error'):
+ continue # Try next repository
+ return None
+
+ self.success(f"{self.t('downloaded')} {len(content)} bytes from {repo_name}")
+ return content
+
+ except urllib.error.HTTPError as e:
+ if e.code == 404:
+ continue # Try next repository
+ self.warning(f"{repo_name}: HTTP {e.code}")
+ except Exception as e:
+ self.warning(f"{repo_name}: {e}")
+ continue
+
+ self.error(f"Plugin '{plugin_name}' not found in any repository")
+ return None
+
+ def install_plugin(self, plugin_name):
+ self.ensure_plugins_dir()
+
+ installed = self.get_installed_plugins()
+ if plugin_name.lower() in installed:
+ self.warning(f"Plugin '{plugin_name}' {self.t('already_installed')}")
+ self.log(self.t('use_update'))
+ return False
+
+ content = self.download_plugin(plugin_name)
+ if not content:
+ return False
+
+ dest_path = os.path.join(self.plugins_dir, f"{plugin_name.lower()}.py")
+ with open(dest_path, 'wb') as f:
+ f.write(content)
+
+ self.success(f"Plugin '{plugin_name}' {self.t('installed')}")
+ self.log(f"Usage: simp{{{plugin_name}}}")
+ return True
+
+ def install_plugins(self, plugin_names):
+ """Install multiple plugins at once"""
+ self.ensure_plugins_dir()
+
+ installed_count = 0
+ failed_count = 0
+
+ for plugin_name in plugin_names:
+ self.log(f"\n=== Installing {plugin_name} ===")
+ if self.install_plugin(plugin_name):
+ installed_count += 1
+ else:
+ failed_count += 1
+
+ self.log(f"\n=== Summary ===")
+ self.success(f"Successfully installed: {installed_count}")
+ if failed_count > 0:
+ self.error(f"Failed to install: {failed_count}")
+
+ return installed_count > 0
+
+ def update_plugin(self, plugin_name):
+ self.ensure_plugins_dir()
+
+ installed = self.get_installed_plugins()
+ if plugin_name.lower() not in installed:
+ self.error(f"Plugin '{plugin_name}' {self.t('not_installed')}")
+ self.log(self.t('use_install'))
+ return False
+
+ content = self.download_plugin(plugin_name)
+ if not content:
+ return False
+
+ dest_path = os.path.join(self.plugins_dir, f"{plugin_name.lower()}.py")
+ with open(dest_path, 'wb') as f:
+ f.write(content)
+
+ self.success(f"Plugin '{plugin_name}' {self.t('updated')}")
+ return True
+
+ def update_plugins(self, plugin_names=None):
+ """Update multiple plugins at once. If no plugins specified, update all installed plugins"""
+ self.ensure_plugins_dir()
+
+ if not plugin_names:
+ plugin_names = self.get_installed_plugins()
+ if not plugin_names:
+ self.log(self.t('no_plugins'))
+ return False
+
+ updated_count = 0
+ failed_count = 0
+
+ for plugin_name in plugin_names:
+ self.log(f"\n=== Updating {plugin_name} ===")
+ if self.update_plugin(plugin_name):
+ updated_count += 1
+ else:
+ failed_count += 1
+
+ self.log(f"\n=== Summary ===")
+ self.success(f"Successfully updated: {updated_count}")
+ if failed_count > 0:
+ self.error(f"Failed to update: {failed_count}")
+
+ return updated_count > 0
+
+ def reinstall_plugin(self, plugin_name):
+ self.ensure_plugins_dir()
+
+ installed = self.get_installed_plugins()
+ if plugin_name.lower() not in installed:
+ self.error(f"Plugin '{plugin_name}' {self.t('not_installed')}")
+ self.log(self.t('use_install'))
+ return False
+
+ self.log(f"{self.t('reinstalling')} {plugin_name}...")
+
+ content = self.download_plugin(plugin_name)
+ if not content:
+ return False
+
+ dest_path = os.path.join(self.plugins_dir, f"{plugin_name.lower()}.py")
+ with open(dest_path, 'wb') as f:
+ f.write(content)
+
+ self.success(f"Plugin '{plugin_name}' {self.t('reinstalled')}")
+ return True
+
+ def remove_plugin(self, plugin_name):
+ installed = self.get_installed_plugins()
+ if plugin_name.lower() not in installed:
+ self.error(f"Plugin '{plugin_name}' {self.t('not_installed')}")
+ return False
+
+ dest_path = os.path.join(self.plugins_dir, f"{plugin_name.lower()}.py")
+ os.remove(dest_path)
+
+ self.success(f"Plugin '{plugin_name}' {self.t('removed')}")
+ return True
+
+ def autoremove_plugins(self):
+ installed = self.get_installed_plugins()
+ if not installed:
+ self.log(self.t('no_plugins'))
+ return True
+
+ self.log("Checking for unused plugins...")
+ self.warning("This feature requires dependency tracking")
+ self.log("Currently removing all installed plugins")
+
+ for plugin_name in installed:
+ self.remove_plugin(plugin_name)
+
+ self.success("All plugins removed")
+ return True
+
+ def test_network(self):
+ self.log(self.t('testing_network'))
+
+ test_urls = [
+ "https://scl.ecuil.com",
+ "https://ssl.ecuil.com",
+ "https://tg.ecuil.com"
+ ]
+
+ for url in test_urls:
+ try:
+ req = urllib.request.Request(url, method='HEAD')
+ req.add_header('User-Agent', 'SCL-PackageManager/1.0')
+
+ with urllib.request.urlopen(req, timeout=5) as response:
+ if response.status == 200:
+ self.success(f"{url} - OK")
+ else:
+ self.warning(f"{url} - HTTP {response.status}")
+ except Exception as e:
+ self.error(f"{url} - Failed: {e}")
+
+ return True
+
+ def list_plugins(self, mode='community'):
+ if mode == 'local':
+ self.list_local_plugins()
+ elif mode == 'upgraded':
+ self.list_upgraded_plugins()
+ else:
+ self.list_community_plugins()
+
+ def list_local_plugins(self):
+ self.log(self.t('local_plugins'))
+ self.log("-" * 60)
+
+ installed = self.get_installed_plugins()
+ if not installed:
+ self.log(self.t('no_plugins'))
+ return
+
+ for i, plugin in enumerate(installed, 1):
+ plugin_path = os.path.join(self.plugins_dir, f"{plugin}.py")
+ size = os.path.getsize(plugin_path) if os.path.exists(plugin_path) else 0
+ self.log(f"{i}. {plugin} ({size} bytes)")
+
+ def search_plugins(self, query):
+ """Search for plugins by name or description"""
+ self.log(f"Searching for plugins matching '{query}'...")
+ self.log("-" * 60)
+
+ try:
+ req = urllib.request.Request(f"{self.api_url}?action=search&query={urllib.parse.quote(query)}")
+ req.add_header('User-Agent', 'SCL-PackageManager/1.0')
+
+ with urllib.request.urlopen(req, timeout=30) as response:
+ if response.status != 200:
+ self.error(f"Failed to search: HTTP {response.status}")
+ return
+
+ data = json.loads(response.read().decode('utf-8'))
+
+ if data.get('success'):
+ packages = data.get('packages', [])
+ installed = self.get_installed_plugins()
+
+ if not packages:
+ self.log("No plugins found matching your query")
+ return
+
+ for i, pkg in enumerate(packages, 1):
+ rating = pkg['rating_count'] > 0 and round(pkg['rating'] / pkg['rating_count'], 1) or 'N/A'
+ status = "✓ Installed" if pkg['name'].lower() in installed else ""
+
+ self.log(f"{i}. {pkg['name']}")
+ self.log(f" Description: {pkg['description']}")
+ self.log(f" Category: {pkg['category']} | Version: {pkg['version']} | Author: {pkg['author_name']}")
+ self.log(f" Downloads: {pkg['downloads']} | Rating: {rating} {status}")
+ self.log("")
+ else:
+ self.error(f"Failed to search: {data.get('error', 'Unknown error')}")
+
+ except Exception as e:
+ self.error(f"Failed to search: {e}")
+
+ def list_community_plugins(self):
+ self.log(self.t('community_plugins'))
+ self.log("-" * 60)
+
+ try:
+ req = urllib.request.Request(f"{self.api_url}?action=list")
+ req.add_header('User-Agent', 'SCL-PackageManager/1.0')
+
+ with urllib.request.urlopen(req, timeout=30) as response:
+ if response.status != 200:
+ self.error(f"Failed to fetch list: HTTP {response.status}")
+ return
+
+ data = json.loads(response.read().decode('utf-8'))
+
+ if data.get('success'):
+ packages = data.get('packages', [])
+ installed = self.get_installed_plugins()
+
+ for i, pkg in enumerate(packages, 1):
+ rating = pkg['rating_count'] > 0 and round(pkg['rating'] / pkg['rating_count'], 1) or 'N/A'
+ status = "✓ Installed" if pkg['name'].lower() in installed else ""
+
+ self.log(f"{i}. {pkg['name']}")
+ self.log(f" Description: {pkg['description']}")
+ self.log(f" Category: {pkg['category']} | Version: {pkg['version']} | Author: {pkg['author_name']}")
+ self.log(f" Downloads: {pkg['downloads']} | Rating: {rating} {status}")
+ self.log("")
+ else:
+ self.error(f"Failed to fetch list: {data.get('error', 'Unknown error')}")
+
+ except Exception as e:
+ self.error(f"Failed to fetch list: {e}")
+
+ def list_upgraded_plugins(self):
+ self.log(self.t('upgradable_plugins'))
+ self.log("-" * 60)
+
+ installed = self.get_installed_plugins()
+ if not installed:
+ self.log(self.t('no_plugins'))
+ return
+
+ for plugin_name in installed:
+ info = self.get_plugin_info(plugin_name)
+ if info:
+ self.log(f"{plugin_name} - Latest: {info.get('version', 'Unknown')}")
+ else:
+ self.log(f"{plugin_name} - Version info unavailable")
+
+ def load_composer_config(self):
+ if not os.path.exists(self.config_file):
+ return None
+
+ try:
+ with open(self.config_file, 'r', encoding='utf-8') as f:
+ return json.load(f)
+ except Exception as e:
+ self.error(f"Failed to load {self.config_file}: {e}")
+ return None
+
+ def save_composer_config(self, config):
+ try:
+ with open(self.config_file, 'w', encoding='utf-8') as f:
+ json.dump(config, f, indent=2)
+ self.success(f"Configuration saved to {self.config_file}")
+ except Exception as e:
+ self.error(f"Failed to save {self.config_file}: {e}")
+
+ def init_composer(self):
+ if os.path.exists(self.config_file):
+ self.warning(f"{self.config_file} already exists")
+ return
+
+ config = {
+ "name": "scl-project",
+ "description": "SCL Project",
+ "require": {},
+ "version": "1.0.0"
+ }
+
+ self.save_composer_config(config)
+ self.log(f"Created {self.config_file}")
+
+ def show_acknowledgments(self):
+ """Show acknowledgments list"""
+ self.log("=" * 60)
+ self.log("SCL Package Manager - Acknowledgments")
+ self.log("=" * 60)
+ self.log("Special thanks to everyone who has contributed to SCL:")
+ print()
+ for i, person in enumerate(self.ACKNOWLEDGMENTS, 1):
+ self.log(f"{i}. {person}")
+ print()
+ self.log("Without your contributions, SCL wouldn't be what it is today!")
+ self.log("=" * 60)
+
+ def show_help(self):
+ self.log("=" * 60)
+ self.log("SCL Package Manager (SDP) v" + self.version)
+ self.log("=" * 60)
+ print()
+ self.log("Usage: sdp [command] [options]")
+ print()
+ self.log("Commands:")
+ self.log(" install Install a plugin")
+ self.log(" install-multi ... Install multiple plugins")
+ self.log(" update Update a plugin")
+ self.log(" update-all Update all installed plugins")
+ self.log(" update-multi ... Update multiple plugins")
+ self.log(" reinstall Reinstall a plugin")
+ self.log(" remove Remove a plugin")
+ self.log(" autoremove Auto-remove unused plugins")
+ print()
+ self.log(" search Search for plugins")
+ self.log(" test Test network connectivity")
+ self.log(" list [option] List plugins")
+ self.log(" -lw List community plugins (default)")
+ self.log(" -local List local plugins")
+ self.log(" -upgraded List upgradable plugins")
+ self.log(" -u, --upgrade List plugins available for upgrade")
+ self.log(" -a, --all List all plugins in current repository")
+ print()
+ self.log(" init Initialize composer.json")
+ self.log(" add Add a plugin repository")
+ self.log(" remove-repo Remove a repository")
+ self.log(" repos List all repositories")
+ self.log(" setting -lang Set language (english/chinese_simplified/chinese_hongkong/classical_chinese/latin/portuguese/korean)")
+ self.log(" ae Show acknowledgments")
+ self.log(" help Show this help")
+ print()
+ self.log("Examples:")
+ self.log(" sdp install basic")
+ self.log(" sdp install-multi basic color fileio")
+ self.log(" sdp update basic")
+ self.log(" sdp update-all")
+ self.log(" sdp search text")
+ self.log(" sdp list -local")
+ self.log(" sdp test")
+ self.log(" sdp add https://scl.ecuil.com/run/plugins/")
+ self.log(" sdp repos")
+ self.log(" sdp ae")
+ print()
+ self.log("Configuration:")
+ self.log(" composer.json - Local package configuration")
+ self.log(" sdp_settings.json - SDP settings")
+ self.log(" Use 'sdp init' to create composer.json")
+ self.log("=" * 60)
+
+def main():
+ if len(sys.argv) < 2:
+ manager = SCLEPackageManager()
+ manager.show_help()
+ return
+
+ # Check for short options first
+ if sys.argv[1] in ['-u', '--upgrade']:
+ manager = SCLEPackageManager()
+ manager.list_plugins('upgraded')
+ return
+ elif sys.argv[1] in ['-a', '--all']:
+ manager = SCLEPackageManager()
+ manager.list_plugins('community')
+ return
+
+ command = sys.argv[1].lower()
+ manager = SCLEPackageManager()
+
+ if command == 'help' or command == '-h' or command == '--help':
+ manager.show_help()
+
+ elif command == 'ae':
+ manager.show_acknowledgments()
+
+ elif command == 'install':
+ if len(sys.argv) < 3:
+ manager.error("Usage: sdp install ")
+ return
+ manager.install_plugin(sys.argv[2])
+
+ elif command == 'install-multi':
+ if len(sys.argv) < 3:
+ manager.error("Usage: sdp install-multi ...")
+ return
+ manager.install_plugins(sys.argv[2:])
+
+ elif command == 'update':
+ if len(sys.argv) < 3:
+ manager.error("Usage: sdp update ")
+ return
+ manager.update_plugin(sys.argv[2])
+
+ elif command == 'update-all':
+ manager.update_plugins()
+
+ elif command == 'update-multi':
+ if len(sys.argv) < 3:
+ manager.error("Usage: sdp update-multi ...")
+ return
+ manager.update_plugins(sys.argv[2:])
+
+ elif command == 'reinstall':
+ if len(sys.argv) < 3:
+ manager.error("Usage: sdp reinstall ")
+ return
+ manager.reinstall_plugin(sys.argv[2])
+
+ elif command == 'remove':
+ if len(sys.argv) < 3:
+ manager.error("Usage: sdp remove ")
+ return
+ manager.remove_plugin(sys.argv[2])
+
+ elif command == 'autoremove':
+ manager.autoremove_plugins()
+
+ elif command == 'search':
+ if len(sys.argv) < 3:
+ manager.error("Usage: sdp search ")
+ return
+ manager.search_plugins(sys.argv[2])
+
+ elif command == 'test':
+ manager.test_network()
+
+ elif command == 'list':
+ if len(sys.argv) >= 3:
+ option = sys.argv[2].lower()
+ if option == '-lw':
+ manager.list_plugins('community')
+ elif option == '-local':
+ manager.list_plugins('local')
+ elif option == '-upgraded':
+ manager.list_plugins('upgraded')
+ else:
+ manager.error(f"Unknown option: {option}")
+ manager.log("Use -lw, -local, or -upgraded")
+ else:
+ manager.list_plugins('community')
+
+ elif command == 'init':
+ manager.init_composer()
+
+ elif command == 'add':
+ if len(sys.argv) < 3:
+ manager.error("Usage: sdp add ")
+ manager.log("Example: sdp add https://scl.ecuil.com/run/plugins/")
+ return
+ manager.add_repository(sys.argv[2])
+
+ elif command == 'remove-repo':
+ if len(sys.argv) < 3:
+ manager.error("Usage: sdp remove-repo ")
+ manager.list_repositories()
+ return
+ manager.remove_repository(sys.argv[2])
+
+ elif command == 'repos':
+ manager.list_repositories()
+
+ elif command == 'setting':
+ if len(sys.argv) < 3:
+ manager.error("Usage: sdp setting -lang ")
+ manager.log("Available languages:")
+ manager.show_available_languages()
+ return
+
+ subcommand = sys.argv[2].lower()
+ if subcommand == '-lang' or subcommand == '--lang' or subcommand == 'lang':
+ if len(sys.argv) < 4:
+ manager.error("Usage: sdp setting -lang ")
+ manager.show_available_languages()
+ return
+ manager.set_language(sys.argv[3])
+ else:
+ manager.error(f"Unknown setting: {subcommand}")
+ manager.log("Use 'sdp setting -lang '")
+
+ else:
+ manager.error(f"Unknown command: {command}")
+ manager.log("Use 'sdp help' for usage information")
+
+if __name__ == "__main__":
+ main()