From dd1a3468dd1d444d1feaacedbd7d54fefc8f3278 Mon Sep 17 00:00:00 2001 From: "JGZSunShineMod1.0.3" Date: Sat, 25 Apr 2026 00:02:03 +0800 Subject: [PATCH] Remove Termux build workflow --- .github/workflows/termux.yml | 61 - build_deb.sh | 102 ++ termux/.github/workflows/build-termux.yml | 57 - termux/README.md | 527 -------- termux/oraset.py | 1424 --------------------- test_file.txt | 2 + 6 files changed, 104 insertions(+), 2069 deletions(-) delete mode 100644 .github/workflows/termux.yml create mode 100644 build_deb.sh delete mode 100644 termux/.github/workflows/build-termux.yml delete mode 100644 termux/README.md delete mode 100644 termux/oraset.py create mode 100644 test_file.txt diff --git a/.github/workflows/termux.yml b/.github/workflows/termux.yml deleted file mode 100644 index bef280f..0000000 --- a/.github/workflows/termux.yml +++ /dev/null @@ -1,61 +0,0 @@ -name: Build Oraset for Termux - -on: - push: - branches: [ main ] - pull_request: - branches: [ main ] - release: - types: [ created ] - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: 3.10 - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install pyinstaller - - name: Build binary - run: | - pyinstaller --onefile oraset.py - - name: Create DEB package - run: | - mkdir -p oraset_1.0.0_all/DEBIAN - echo "Package: oraset -Version: 1.0.0 -Section: utils -Priority: optional -Architecture: all -Depends: python3 -Maintainer: Oraset Team -Description: Oraset programming language interpreter for Termux" > oraset_1.0.0_all/DEBIAN/control - mkdir -p oraset_1.0.0_all/usr/bin - cp dist/oraset oraset_1.0.0_all/usr/bin/oraset - chmod +x oraset_1.0.0_all/usr/bin/oraset - dpkg-deb --build oraset_1.0.0_all - - name: Create zip package - run: | - mkdir -p build-termux - cp dist/oraset build-termux/ - cp oraset_1.0.0_all.deb build-termux/ - zip -r build-termux.zip build-termux/ - - name: Upload artifacts - uses: actions/upload-artifact@v4 - with: - name: oraset-termux - path: | - build-termux.zip - - name: Upload to Release - if: github.event_name == 'release' - uses: softprops/action-gh-release@v2 - with: - files: | - build-termux.zip - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/build_deb.sh b/build_deb.sh new file mode 100644 index 0000000..ddafb85 --- /dev/null +++ b/build_deb.sh @@ -0,0 +1,102 @@ +#!/bin/bash + +# Oraset Debian Package Builder +# This script packages Oraset into a .deb file for Debian/Ubuntu + +set -e + +PACKAGE_NAME="oraset" +VERSION="1.0.0" +ARCH="all" +MAINTAINER="Oraset Developer" +DESCRIPTION="A simple cross-platform programming language" +LICENSE="GPL-3.0" + +# Get the directory where the script is located +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "${SCRIPT_DIR}" + +# Build directory +BUILD_DIR="orasetdeb" +PACKAGE_DIR="${BUILD_DIR}/${PACKAGE_NAME}_${VERSION}_${ARCH}" + +echo "=== Oraset Debian Package Builder ===" +echo "" + +# Clean previous build +echo "[1/6] Cleaning previous build..." +rm -rf ${BUILD_DIR} +mkdir -p ${PACKAGE_DIR} + +# Install PyInstaller if not present +echo "[2/6] Checking PyInstaller..." +if ! command -v pyinstaller &> /dev/null; then + echo "Installing PyInstaller..." + pip install --break-system-packages pyinstaller +fi + +# Create binary with PyInstaller +echo "[3/6] Building binary with PyInstaller..." +pyinstaller --onefile --name oraset oraset.py + +# Copy binary to package directory +echo "[4/6] Copying files..." +mkdir -p ${PACKAGE_DIR}/usr/bin +cp dist/oraset ${PACKAGE_DIR}/usr/bin/ + +# Create DEBIAN directory and control file +echo "[5/6] Creating package metadata..." +mkdir -p ${PACKAGE_DIR}/DEBIAN + +cat > ${PACKAGE_DIR}/DEBIAN/control << EOF +Package: ${PACKAGE_NAME} +Version: ${VERSION} +Section: interpreters +Priority: optional +Architecture: ${ARCH} +Depends: python3 +Maintainer: ${MAINTAINER} +Description: ${DESCRIPTION} + A simple cross-platform programming language written in Python. + Supports Windows and Linux, with features like variables, + functions, classes, conditional statements, loops, and more. +EOF + +# Create copyright file +mkdir -p ${PACKAGE_DIR}/usr/share/doc/${PACKAGE_NAME} +cat > ${PACKAGE_DIR}/usr/share/doc/${PACKAGE_NAME}/copyright << EOF +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: ${PACKAGE_NAME} +Upstream-Contact: ${MAINTAINER} +Source: https://github.com/oraset/oraset + +Files: * +Copyright: ${LICENSE} +License: GPL-3.0 + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License. + . + On a Debian system, you can find the complete text of the GPL-3.0 + license in /usr/share/common-licenses/GPL-3. +EOF + +# Build the .deb package +echo "[6/6] Building .deb package..." +dpkg-deb --build ${PACKAGE_DIR} + +# Move package to current directory +mv ${PACKAGE_DIR}.deb ${BUILD_DIR}/ + +# Clean up PyInstaller build files +rm -rf build dist + +echo "" +echo "=== Build Complete ===" +echo "Package created: ${BUILD_DIR}/${PACKAGE_NAME}_${VERSION}_${ARCH}.deb" +echo "" +echo "To install the package, run:" +echo " sudo dpkg -i ${BUILD_DIR}/${PACKAGE_NAME}_${VERSION}_${ARCH}.deb" +echo "" +echo "To uninstall the package, run:" +echo " sudo dpkg -r ${PACKAGE_NAME}" diff --git a/termux/.github/workflows/build-termux.yml b/termux/.github/workflows/build-termux.yml deleted file mode 100644 index dcb17fd..0000000 --- a/termux/.github/workflows/build-termux.yml +++ /dev/null @@ -1,57 +0,0 @@ -name: Build Oraset for Termux - -on: - push: - branches: [ main ] - pull_request: - branches: [ main ] - release: - types: [ created ] - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: 3.10 - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install pyinstaller - - name: Build binary - run: | - pyinstaller --onefile oraset.py - - name: Create DEB package - run: | - mkdir -p oraset_1.0.0_all/DEBIAN - echo "Package: oraset -Version: 1.0.0 -Section: utils -Priority: optional -Architecture: all -Depends: python3 -Maintainer: Oraset Team -Description: Oraset programming language interpreter for Termux" > oraset_1.0.0_all/DEBIAN/control - mkdir -p oraset_1.0.0_all/usr/bin - cp dist/oraset oraset_1.0.0_all/usr/bin/oraset - chmod +x oraset_1.0.0_all/usr/bin/oraset - dpkg-deb --build oraset_1.0.0_all - - name: Upload artifacts - uses: actions/upload-artifact@v4 - with: - name: oraset-termux - path: | - dist/oraset - oraset_1.0.0_all.deb - - name: Upload to Release - if: github.event_name == 'release' - uses: softprops/action-gh-release@v2 - with: - files: | - dist/oraset - oraset_1.0.0_all.deb - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/termux/README.md b/termux/README.md deleted file mode 100644 index fb98c2c..0000000 --- a/termux/README.md +++ /dev/null @@ -1,527 +0,0 @@ -# Oraset 编程语言 - -Oraset是一种简单的跨平台编程语言,使用Python实现,支持Windows和Linux系统。 - -## 特性 - -- 跨平台运行(Windows和Linux) -- 简洁易懂的语法 -- 支持变量定义和赋值 -- 支持表达式计算(算术、比较、逻辑) -- 支持条件语句(if、else if、else) -- 支持循环语句(while) -- 支持函数定义和调用 -- 支持类定义 -- 支持输出功能 -- 支持注释 - -## 快速开始 - -### 运行环境 - -- Python 3.6+ - -### 运行方式 - -```bash -# 运行.oas或.oraset文件 -python oraset.py - -# 将.oas或.oraset文件打包成.osp文件 -python oraset.py pack - -# 运行.osp文件 -python oraset.py -osp -``` - -### 示例 - -创建一个名为`hello.oas`的文件,内容如下: - -```oraset -!! 输出Hello, Oraset! -show(`Hello, Oraset!`); - -!! 变量定义和计算 -x = 10; -y = 20; -sum = x + y; -show(`10 + 20 = `, sum); - -!! 条件语句 -if (sum > 25) { - show(`Sum is greater than 25`); -} else { - show(`Sum is less than or equal to 25`); -} - -!! 循环语句 -i = 0; -while (i < 5) { - show(`i = `, i); - i = i + 1; -} - -!! 函数定义 -def add(a, b) { - return a + b; -} - -!! 函数调用 -result = add(5, 6); -show(`5 + 6 = `, result); - -!! 类定义 -class Calculator { - def multiply(a, b) { - return a * b; - } -} -``` - -运行: - -```bash -python oraset.py hello.oas -``` - -## 语法指南 - -### 注释 - -使用`!!`开始单行注释: - -```oraset -!! 这是一个注释 -``` - -使用`!*`和`*!`包围多行注释: - -```oraset -!* 这是一个 -多行注释 *! -``` - -### 内置库函数 - -Oraset提供了以下内置库函数: - -#### 数学函数 - -| 函数名 | 描述 | 示例 | -|-------|------|------| -| `abs(x)` | 返回x的绝对值 | `abs(-10)` → `10` | -| `max(x, y)` | 返回x和y中的最大值 | `max(10, 20)` → `20` | -| `min(x, y)` | 返回x和y中的最小值 | `min(10, 20)` → `10` | -| `pow(x, y)` | 返回x的y次方 | `pow(2, 3)` → `8` | -| `sqrt(x)` | 返回x的平方根(取整数部分) | `sqrt(16)` → `4` | - -#### 字符串函数 - -| 函数名 | 描述 | 示例 | -|-------|------|------| -| `len(s)` | 返回字符串s的长度 | `len("Hello")` → `5` | -| `upper(s)` | 将字符串s转换为大写 | `upper("Hello")` → `HELLO` | -| `lower(s)` | 将字符串s转换为小写 | `lower("HELLO")` → `hello` | -| `substring(s, start, end)` | 返回字符串s从start到end的子串 | `substring("Hello", 0, 3)` → `Hel` | -| `startsWith(s, prefix)` | 检查字符串s是否以prefix开头 | `startsWith("Hello", "He")` → `1` | -| `endsWith(s, suffix)` | 检查字符串s是否以suffix结尾 | `endsWith("Hello", "lo")` → `1` | -| `replace(s, old, new)` | 替换字符串s中的old为new | `replace("Hello", "He", "Hi")` → `Hiello` | -| `split(s, sep)` | 以sep为分隔符分割字符串s | `split("a,b,c", ",")` → `["a", "b", "c"]` | -| `trim(s)` | 去除字符串s首尾的空白 | `trim(" Hello ")` → `Hello` | - -#### 数组函数 - -| 函数名 | 描述 | 示例 | -|-------|------|------| -| `array(...items)` | 创建一个包含指定元素的数组 | `array("a", "b", "c")` → `["a", "b", "c"]` | -| `push(arr, item)` | 向数组arr添加一个元素 | `push(["a", "b"], "c")` → `["a", "b", "c"]` | -| `pop(arr)` | 从数组arr中移除最后一个元素 | `pop(["a", "b", "c"])` → `["a", "b"]` | -| `indexOf(arr, item)` | 返回元素item在数组arr中的索引,若不存在则返回-1 | `indexOf(["a", "b", "c"], "b")` → `1` | - -#### 类型检查函数 - -| 函数名 | 描述 | 示例 | -|-------|------|------| -| `is_number(x)` | 检查x是否为数字 | `is_number("123")` → `1` | -| `is_string(x)` | 检查x是否为字符串 | `is_string("Hello")` → `1` | - -#### 其他函数 - -| 函数名 | 描述 | 示例 | -|-------|------|------| -| `input(prompt)` | 从用户输入获取值 | `input("请输入: ")` | -| `int(s)` | 将字符串s转换为整数 | `int("123")` → `123` | -| `str(x)` | 将x转换为字符串 | `str(123)` → `"123"` | - -#### 文件操作函数 - -| 函数名 | 描述 | 示例 | -|-------|------|------| -| `file_read(filename)` | 读取文件内容 | `file_read("test.txt")` | -| `file_write(filename, content)` | 写入内容到文件 | `file_write("test.txt", "Hello")` | -| `file_exists(filename)` | 检查文件是否存在 | `file_exists("test.txt")` → `1` (存在) 或 `0` (不存在) | - -#### 时间函数 - -| 函数名 | 描述 | 示例 | -|-------|------|------| -| `time_now()` | 获取当前时间戳 | `time_now()` | -| `time_sleep(seconds)` | 暂停指定秒数 | `time_sleep(2)` | - -#### 随机数函数 - -| 函数名 | 描述 | 示例 | -|-------|------|------| -| `random_int(min, max)` | 生成指定范围内的随机整数 | `random_int(1, 100)` | -| `random_float()` | 生成0-1之间的随机浮点数 | `random_float()` | - -#### 系统函数 - -| 函数名 | 描述 | 示例 | -|-------|------|------| -| `system(command)` | 执行系统命令 | `system("dir")` | -| `get_env(var_name)` | 获取环境变量 | `get_env("PATH")` | - - - -### 数学模块 - -**注意:使用数学函数需要先导入math模块** - -```oraset -import math -``` - -| 函数名 | 描述 | 示例 | -|-------|------|------| -| `abs(x)` | 返回x的绝对值 | `abs(-10)` → `10.0` | -| `max(x, y)` | 返回x和y中的最大值 | `max(10, 20)` → `20.0` | -| `min(x, y)` | 返回x和y中的最小值 | `min(10, 20)` → `10.0` | -| `pow(x, y)` | 返回x的y次方 | `pow(2, 3)` → `8.0` | -| `sqrt(x)` | 返回x的平方根 | `sqrt(16)` → `4.0` | -| `sin(x)` | 返回x(度)的正弦值 | `sin(90)` → `1.0` | -| `cos(x)` | 返回x(度)的余弦值 | `cos(0)` → `1.0` | -| `tan(x)` | 返回x(度)的正切值 | `tan(45)` → `1.0` | -| `asin(x)` | 返回x的反正弦值(度) | `asin(1)` → `90.0` | -| `acos(x)` | 返回x的反余弦值(度) | `acos(1)` → `0.0` | -| `atan(x)` | 返回x的反正切值(度) | `atan(1)` → `45.0` | -| `pi()` | 返回圆周率π的值 | `pi()` → `3.141592653589793` | -| `e()` | 返回自然常数e的值 | `e()` → `2.718281828459045` | - -#### 网络请求函数 - -**注意:使用网络请求函数需要先导入http模块** - -```oraset -import http -``` - -| 函数名 | 描述 | 示例 | -|-------|------|------| -| `http_get(url)` | 发送HTTP GET请求并返回响应内容 | `http_get("http://example.com")` | -| `http_post(url, data)` | 发送HTTP POST请求并返回响应内容 | `http_post("http://httpbin.org/post", "{\"name\": \"Oraset\", \"version\": \"1.0\"}")` | - -### 插件系统 - -Oraset支持用户自定义插件,您可以将自己的扩展功能放在`includes`文件夹中。 - -#### 创建插件 - -1. 在`includes`文件夹中创建一个以`.oraset`为后缀的文件 -2. 在文件中定义函数、类等 -3. 使用`import 插件名`指令加载插件 - -#### 示例 - -创建`includes/math_utils.oraset`文件: - -```oraset -!! 数学工具插件 - -def add(a, b) { - return a + b; -} - -def subtract(a, b) { - return a - b; -} -``` - -在您的代码中使用: - -```oraset -import math_utils - -result = add(10, 20); -show(`10 + 20 = `, result); -``` - -### 变量 - -变量定义和赋值: - -```oraset -x = 10; // 整数 -message = `Hello`; // 字符串 -``` - -### 数据类型 - -- 整数:`10`, `20`, `30` -- 字符串:`` `Hello` ``, `` `World` `` - -字符串支持以下转义符: - -| 转义符 | 描述 | -|-------|------| -| `\n` | 换行符 | -| `\t` | 制表符 | -| `\r` | 回车符 | -| `\\` | 反斜杠 | -| `` \` `` | 反引号 | - -示例: - -```oraset -// 输出多行文本 -show(`Line 1\nLine 2`); - -// 输出带制表符的文本 -show(`Column 1\tColumn 2`); - -// 输出包含反斜杠的文本 -show(`Path: C:\\Users\\Name`); -``` - -### 运算符 - -#### 算术运算符 - -- `+`:加法 -- `-`:减法 -- `*`:乘法 -- `/`:除法(整数除法) - -#### 比较运算符 - -- `==`:等于 -- `!=`:不等于 -- `<`:小于 -- `<=`:小于等于 -- `>`:大于 -- `>=`:大于等于 - -#### 逻辑运算符 - -- `&&`:逻辑与 -- `||`:逻辑或 - -#### 赋值运算符 - -- `=`:赋值 -- `+=`:加赋值 -- `-=`:减赋值 -- `*=`:乘赋值 -- `/=`:除赋值 - -### 条件语句 - -```oraset -if (condition) { - // 代码块 -} else if (condition) { - // 代码块 -} else { - // 代码块 -} -``` - -### 循环语句 - -```oraset -while (condition) { - // 代码块 -} -``` - -### 函数定义 - -```oraset -def function_name(parameter1, parameter2, ...) { - // 代码块 - return value; -} -``` - -### 函数调用 - -```oraset -result = function_name(arg1, arg2, ...); -``` - -### 类定义 - -```oraset -class ClassName { - def method_name(parameter1, parameter2, ...) { - // 代码块 - return value; - } -} -``` - -### 输出 - -使用`show`函数输出内容: - -```oraset -show(`Hello`); -show(`The value is `, x); -``` - -## 示例代码 - -### 基础示例 - -```oraset -// 变量和表达式 -x = 10; -y = 20; -sum = x + y; -show(`Sum: `, sum); - -// 条件语句 -if (sum > 25) { - show(`Sum is greater than 25`); -} else if (sum == 25) { - show(`Sum is exactly 25`); -} else { - show(`Sum is less than 25`); -} - -// 循环语句 -i = 0; -while (i < 5) { - show(`i = `, i); - i += 1; -} -``` - -### 函数示例 - -```oraset -// 函数定义 -def add(a, b) { - return a + b; -} - -def multiply(a, b) { - return a * b; -} - -// 函数调用 -result1 = add(10, 20); -result2 = multiply(5, 6); -show(`10 + 20 = `, result1); -show(`5 * 6 = `, result2); - -// 递归函数 -def factorial(n) { - if (n == 0 || n == 1) { - return 1; - } - return n * factorial(n - 1); -} - -result = factorial(5); -show(`Factorial of 5 is `, result); -``` - -### 类示例 - -```oraset -// 类定义 -class Calculator { - def add(a, b) { - return a + b; - } - - def subtract(a, b) { - return a - b; - } - - def multiply(a, b) { - return a * b; - } - - def divide(a, b) { - return a / b; - } -} - -// 注意:类方法的调用需要通过函数实现 -// 目前版本中,类定义会被存储但不会直接实例化 -``` - -## 项目结构 - -``` -Oraset/ -├── oraset.py # 核心解释器 -├── examples/ # 示例代码 -│ ├── hello.oas # 基础示例 -│ ├── hello.osp # 打包后的基础示例 -│ ├── functions.oas # 函数示例 -│ └── classes.oas # 类示例 -├── README.md # 本教程(中文) -└── README.en_us.md # 本教程(英文) -``` - -## .osp文件说明 - -.osp文件是Oraset的打包文件格式,它是通过对.oas文件进行压缩和编码生成的,具有以下特点: - -- **加密性**:.osp文件使用base64编码和zlib压缩,使得源代码不易被直接查看 -- **跨平台**:.osp文件可以在Windows和Linux系统上直接运行 -- **便捷性**:.osp文件是一个独立的文件,可以方便地分发和使用 - -### 打包.oas文件 - -使用`pack`命令将.oas文件打包成.osp文件: - -```bash -python oraset.py pack -``` - -这将生成一个与.oas文件同名但扩展名为.osp的文件。 - -### 运行.osp文件 - -使用`-osp`参数运行.osp文件: - -```bash -python oraset.py -osp -``` - -这将自动解压和执行.osp文件中的代码。 - -## 开发说明 - -Oraset使用Python实现,主要包含以下组件: - -- **Lexer**:词法分析器,负责将源代码转换为词法单元 -- **Parser**:语法分析器,负责构建抽象语法树(AST) -- **Interpreter**:解释器,负责执行AST并生成结果 - -## 未来计划 - -- 支持更多数据类型(浮点数、布尔值等) -- 完善类的实例化和方法调用 -- 支持模块导入 -- 添加标准库 -- 优化性能 - -## 许可证 - -GNU General Public License v3.0 diff --git a/termux/oraset.py b/termux/oraset.py deleted file mode 100644 index f769852..0000000 --- a/termux/oraset.py +++ /dev/null @@ -1,1424 +0,0 @@ -#!/usr/bin/env python3 - -import sys - -class Token: - def __init__(self, type_, value, line, column): - self.type = type_ - self.value = value - self.line = line - self.column = column - - def __str__(self): - return f"Token({self.type}, {repr(self.value)}, {self.line}, {self.column})" - -class Lexer: - def __init__(self, source): - self.source = source - self.position = 0 - self.line = 1 - self.column = 1 - - def get_next_token(self): - while self.position < len(self.source): - char = self.source[self.position] - - # Skip whitespace - if char.isspace(): - if char == '\n': - self.line += 1 - self.column = 1 - else: - self.column += 1 - self.position += 1 - continue - - # Skip comments (!! for single line, !* ... *! for multi-line) - if char == '!' and self.position + 1 < len(self.source): - # Check for single-line comment (!!) - if self.source[self.position + 1] == '!': - self.position += 2 - self.column += 2 - while self.position < len(self.source) and self.source[self.position] != '\n': - self.position += 1 - self.column += 1 - continue - # Check for multi-line comment (!* ... *!) - elif self.source[self.position + 1] == '*': - self.position += 2 - self.column += 2 - # Skip until closing *! - while self.position + 1 < len(self.source) and not (self.source[self.position] == '*' and self.source[self.position + 1] == '!'): - if self.source[self.position] == '\n': - self.line += 1 - self.column = 1 - else: - self.column += 1 - self.position += 1 - if self.position + 1 < len(self.source): - self.position += 2 - self.column += 2 - continue - # Include directive - elif char == '!' and self.position + 7 < len(self.source) and self.source[self.position:self.position+8] == '!include': - self.position += 8 - self.column += 8 - # Skip whitespace - while self.position < len(self.source) and self.source[self.position].isspace(): - self.position += 1 - self.column += 1 - # Read module name in quotes - if self.position < len(self.source) and self.source[self.position] == "'": - self.position += 1 - self.column += 1 - start = self.position - while self.position < len(self.source) and self.source[self.position] != "'": - self.position += 1 - self.column += 1 - if self.position < len(self.source): - self.position += 1 - self.column += 1 - module = self.source[start:self.position-1] - return Token('INCLUDE_DIRECTIVE', module, self.line, self.column - len(module) - 2) - - # String literal - if char == '`': - start = self.position - self.position += 1 - self.column += 1 - value = '' - while self.position < len(self.source) and self.source[self.position] != '`': - if self.source[self.position] == '\\': - # Handle escape sequences - self.position += 1 - self.column += 1 - if self.position < len(self.source): - if self.source[self.position] == 'n': - value += '\n' - elif self.source[self.position] == 't': - value += '\t' - elif self.source[self.position] == 'r': - value += '\r' - elif self.source[self.position] == '\\': - value += '\\' - elif self.source[self.position] == '`': - value += '`' - else: - # Unknown escape sequence, just add the character - value += self.source[self.position] - self.position += 1 - self.column += 1 - elif self.source[self.position] == '\n': - value += '\n' - self.line += 1 - self.column = 1 - self.position += 1 - else: - value += self.source[self.position] - self.column += 1 - self.position += 1 - if self.position < len(self.source): - self.position += 1 - self.column += 1 - return Token('STRING', value, self.line, self.column - len(value) - 2) - - # Negative number - if char == '-' and self.position + 1 < len(self.source) and self.source[self.position + 1].isdigit(): - start = self.position - self.position += 1 - self.column += 1 - while self.position < len(self.source) and self.source[self.position].isdigit(): - self.position += 1 - self.column += 1 - value = self.source[start:self.position] - return Token('INT', value, self.line, self.column - len(value)) - # Number - elif char.isdigit(): - start = self.position - while self.position < len(self.source) and self.source[self.position].isdigit(): - self.position += 1 - self.column += 1 - value = self.source[start:self.position] - return Token('INT', value, self.line, self.column - len(value)) - - # Identifier or keyword - if char.isalpha() or char == '_': - start = self.position - while self.position < len(self.source) and (self.source[self.position].isalnum() or self.source[self.position] == '_'): - self.position += 1 - self.column += 1 - value = self.source[start:self.position] - if value == 'show': - return Token('KEYWORD_SHOW', value, self.line, self.column - len(value)) - elif value == 'if': - return Token('KEYWORD_IF', value, self.line, self.column - len(value)) - elif value == 'else': - return Token('KEYWORD_ELSE', value, self.line, self.column - len(value)) - elif value == 'while': - return Token('KEYWORD_WHILE', value, self.line, self.column - len(value)) - elif value == 'def': - return Token('KEYWORD_DEF', value, self.line, self.column - len(value)) - elif value == 'class': - return Token('KEYWORD_CLASS', value, self.line, self.column - len(value)) - elif value == 'return': - return Token('KEYWORD_RETURN', value, self.line, self.column - len(value)) - elif value == 'import': - return Token('KEYWORD_IMPORT', value, self.line, self.column - len(value)) - else: - return Token('IDENTIFIER', value, self.line, self.column - len(value)) - - # Operators and punctuation - if char == '+' and self.position + 1 < len(self.source) and self.source[self.position + 1] == '+': - self.position += 2 - self.column += 2 - return Token('PLUS_PLUS', '++', self.line, self.column - 2) - elif char == '-' and self.position + 1 < len(self.source) and self.source[self.position + 1] == '-': - self.position += 2 - self.column += 2 - return Token('MINUS_MINUS', '--', self.line, self.column - 2) - elif char == '+' and self.position + 1 < len(self.source) and self.source[self.position + 1] == '=': - self.position += 2 - self.column += 2 - return Token('PLUS_EQUALS', '+=', self.line, self.column - 2) - elif char == '-' and self.position + 1 < len(self.source) and self.source[self.position + 1] == '=': - self.position += 2 - self.column += 2 - return Token('MINUS_EQUALS', '-=', self.line, self.column - 2) - elif char == '*' and self.position + 1 < len(self.source) and self.source[self.position + 1] == '=': - self.position += 2 - self.column += 2 - return Token('MULTIPLY_EQUALS', '*=', self.line, self.column - 2) - elif char == '/' and self.position + 1 < len(self.source) and self.source[self.position + 1] == '=': - self.position += 2 - self.column += 2 - return Token('DIVIDE_EQUALS', '/=', self.line, self.column - 2) - elif char == '=' and self.position + 1 < len(self.source) and self.source[self.position + 1] == '=': - self.position += 2 - self.column += 2 - return Token('EQUALS', '==', self.line, self.column - 2) - elif char == '=': - self.position += 1 - self.column += 1 - return Token('ASSIGN', '=', self.line, self.column - 1) - elif char == '!' and self.position + 1 < len(self.source) and self.source[self.position + 1] == '=': - self.position += 2 - self.column += 2 - return Token('NOT_EQUALS', '!=', self.line, self.column - 2) - elif char == '@': - self.position += 1 - self.column += 1 - return Token('AT', '@', self.line, self.column - 1) - elif char == '.': - self.position += 1 - self.column += 1 - return Token('DOT', '.', self.line, self.column - 1) - elif char == '<' and self.position + 1 < len(self.source) and self.source[self.position + 1] == '=': - self.position += 2 - self.column += 2 - return Token('LESS_EQUAL', '<=', self.line, self.column - 2) - elif char == '>' and self.position + 1 < len(self.source) and self.source[self.position + 1] == '=': - self.position += 2 - self.column += 2 - return Token('GREATER_EQUAL', '>=', self.line, self.column - 2) - elif char == '&' and self.position + 1 < len(self.source) and self.source[self.position + 1] == '&': - self.position += 2 - self.column += 2 - return Token('AND', '&&', self.line, self.column - 2) - elif char == '|' and self.position + 1 < len(self.source) and self.source[self.position + 1] == '|': - self.position += 2 - self.column += 2 - return Token('OR', '||', self.line, self.column - 2) - elif char == '@': - # Check if it's an include directive - if self.position + 7 < len(self.source) and self.source[self.position+1:self.position+8] == 'include': - # It's an include directive - # Move past '@include' - self.position += 8 - self.column += 8 - # Skip whitespace after include - while self.position < len(self.source) and self.source[self.position].isspace(): - self.position += 1 - self.column += 1 - # Get the module name - if self.position < len(self.source) and (self.source[self.position].isalpha() or self.source[self.position] == '_'): - start = self.position - while self.position < len(self.source) and (self.source[self.position].isalnum() or self.source[self.position] == '_'): - self.position += 1 - module_name = self.source[start:self.position] - self.column += len(module_name) - return Token('INCLUDE_DIRECTIVE', module_name, self.line, self.column - len(module_name)) - # Otherwise, it's an unknown token - self.position += 1 - self.column += 1 - elif char == '!': - # It's a NOT operator - self.position += 1 - self.column += 1 - return Token('NOT', '!', self.line, self.column - 1) - elif char == '+': - self.position += 1 - self.column += 1 - return Token('PLUS', '+', self.line, self.column - 1) - elif char == '-': - self.position += 1 - self.column += 1 - return Token('MINUS', '-', self.line, self.column - 1) - elif char == '*': - self.position += 1 - self.column += 1 - return Token('MULTIPLY', '*', self.line, self.column - 1) - elif char == '/': - self.position += 1 - self.column += 1 - return Token('DIVIDE', '/', self.line, self.column - 1) - elif char == '=': - self.position += 1 - self.column += 1 - return Token('ASSIGN', '=', self.line, self.column - 1) - elif char == '<': - self.position += 1 - self.column += 1 - return Token('LESS', '<', self.line, self.column - 1) - elif char == '>': - self.position += 1 - self.column += 1 - return Token('GREATER', '>', self.line, self.column - 1) - elif char == '(': - self.position += 1 - self.column += 1 - return Token('LPAREN', '(', self.line, self.column - 1) - elif char == ')': - self.position += 1 - self.column += 1 - return Token('RPAREN', ')', self.line, self.column - 1) - elif char == ';': - self.position += 1 - self.column += 1 - return Token('SEMICOLON', ';', self.line, self.column - 1) - elif char == ',': - self.position += 1 - self.column += 1 - return Token('COMMA', ',', self.line, self.column - 1) - elif char == '{': - self.position += 1 - self.column += 1 - return Token('LBRACE', '{', self.line, self.column - 1) - elif char == '}': - self.position += 1 - self.column += 1 - return Token('RBRACE', '}', self.line, self.column - 1) - elif char == '[': - self.position += 1 - self.column += 1 - return Token('LBRACKET', '[', self.line, self.column - 1) - elif char == ']': - self.position += 1 - self.column += 1 - return Token('RBRACKET', ']', self.line, self.column - 1) - - # Unknown character - self.position += 1 - self.column += 1 - - return Token('EOF', None, self.line, self.column) - -class Parser: - def __init__(self, lexer): - self.lexer = lexer - self.current_token = self.lexer.get_next_token() - - def error(self, message): - # For Windows PowerShell compatibility, use simple error messages without ANSI codes - error_msg = f"Syntax Error at line {self.current_token.line}, column {self.current_token.column}: {message}" - # Add more context information - if hasattr(self.lexer, 'source'): - # Get the line where the error occurred - lines = self.lexer.source.split('\n') - if self.current_token.line - 1 < len(lines): - error_line = lines[self.current_token.line - 1] - # Add the error line to the error message - error_msg += f"\nLine {self.current_token.line}: {error_line}" - # Add a pointer to the error position - error_msg += f"\n" + ' ' * (len(f"Line {self.current_token.line}: ") + self.current_token.column - 1) + "^" - # Add specific error messages for common errors - if "Expected SEMICOLON" in message: - error_msg += "\nHint: Missing semicolon at the end of the statement." - elif "Expected LPAREN" in message: - error_msg += "\nHint: Missing opening parenthesis '('." - elif "Expected RPAREN" in message: - error_msg += "\nHint: Missing closing parenthesis ')'." - elif "Expected LBRACE" in message: - error_msg += "\nHint: Missing opening brace '{'." - elif "Expected RBRACE" in message: - error_msg += "\nHint: Missing closing brace '}'." - raise Exception(error_msg) - - def eat(self, token_type): - if self.current_token.type == token_type: - self.current_token = self.lexer.get_next_token() - else: - self.error(f"Expected {token_type}, got {self.current_token.type}") - - def factor(self): - token = self.current_token - if token.type == 'INT': - self.eat('INT') - return {'type': 'literal', 'value': token.value} - elif token.type == 'STRING': - self.eat('STRING') - return {'type': 'literal', 'value': token.value} - elif token.type == 'IDENTIFIER': - # Check if it's a function call or array index - save_position = self.lexer.position - save_line = self.lexer.line - save_column = self.lexer.column - - # Look ahead to see if it's a function call or array index - is_function_call = False - is_array_index = False - pos = save_position - while pos < len(self.lexer.source) and self.lexer.source[pos].isspace(): - pos += 1 - if pos < len(self.lexer.source): - if self.lexer.source[pos] == '(': - is_function_call = True - elif self.lexer.source[pos] == '[': - is_array_index = True - elif self.lexer.source[pos] == '.': - # Handle dot notation (e.g., math.abs) - is_function_call = True - - # Restore the lexer position - self.lexer.position = save_position - self.lexer.line = save_line - self.lexer.column = save_column - - if is_function_call: - # It's a function call (could be with dot notation) - name = self.current_token.value - self.eat('IDENTIFIER') - - # Handle dot notation - while self.current_token.type == 'DOT': - self.eat('DOT') - name += '.' + self.current_token.value - self.eat('IDENTIFIER') - - self.eat('LPAREN') - arguments = [] - if self.current_token.type != 'RPAREN': - arguments.append(self.expression()) - while self.current_token.type == 'COMMA': - self.eat('COMMA') - arguments.append(self.expression()) - self.eat('RPAREN') - return { - 'type': 'function_call', - 'name': name, - 'arguments': arguments - } - elif is_array_index: - # It's an array index - name = self.current_token.value - self.eat('IDENTIFIER') - self.eat('LBRACKET') - index = self.expression() - self.eat('RBRACKET') - return { - 'type': 'array_index', - 'name': name, - 'index': index - } - else: - # It's a simple identifier - self.eat('IDENTIFIER') - return {'type': 'identifier', 'name': token.value} - elif token.type == 'LPAREN': - self.eat('LPAREN') - expr = self.expression() - self.eat('RPAREN') - return expr - else: - self.error(f"Unexpected token {token.type}") - - def statement(self): - if self.current_token.type == 'INCLUDE_DIRECTIVE': - return self.include_directive() - elif self.current_token.type == 'KEYWORD_SHOW': - return self.function_call() - elif self.current_token.type == 'KEYWORD_IF': - return self.if_statement() - elif self.current_token.type == 'KEYWORD_WHILE': - return self.while_statement() - elif self.current_token.type == 'KEYWORD_DEF': - return self.def_statement() - elif self.current_token.type == 'KEYWORD_CLASS': - return self.class_statement() - elif self.current_token.type == 'KEYWORD_RETURN': - return self.return_statement() - elif self.current_token.type == 'KEYWORD_IMPORT': - return self.import_statement() - elif self.current_token.type == 'AT': - return self.import_statement() - elif self.current_token.type == 'IDENTIFIER': - # Check if it's a function call by looking ahead - # We need to save the current position first - save_position = self.lexer.position - save_line = self.lexer.line - save_column = self.lexer.column - - # Look ahead to see if it's a function call - is_function_call = False - pos = save_position - while pos < len(self.lexer.source) and self.lexer.source[pos].isspace(): - pos += 1 - if pos < len(self.lexer.source) and self.lexer.source[pos] == '(': - is_function_call = True - - # Restore the lexer position - self.lexer.position = save_position - self.lexer.line = save_line - self.lexer.column = save_column - - if is_function_call: - # It's a function call (handled in factor) - # The function call is already parsed as part of the expression - # So we just need to parse the expression and add a semicolon - expr = self.expression() - self.eat('SEMICOLON') - return expr - else: - # It's an assignment - return self.assignment() - else: - self.error(f"Unexpected token {self.current_token.type}") - - def term(self): - node = self.factor() - while self.current_token.type in ('MULTIPLY', 'DIVIDE'): - token = self.current_token - if token.type == 'MULTIPLY': - self.eat('MULTIPLY') - else: - self.eat('DIVIDE') - node = { - 'type': 'binary_op', - 'op': token.value, - 'left': node, - 'right': self.factor() - } - return node - - def expression(self): - node = self.logical_or() - return node - - def logical_or(self): - node = self.logical_and() - while self.current_token.type == 'OR': - token = self.current_token - self.eat('OR') - node = { - 'type': 'binary_op', - 'op': token.value, - 'left': node, - 'right': self.logical_and() - } - return node - - def logical_and(self): - node = self.equality() - while self.current_token.type == 'AND': - token = self.current_token - self.eat('AND') - node = { - 'type': 'binary_op', - 'op': token.value, - 'left': node, - 'right': self.equality() - } - return node - - def equality(self): - node = self.comparison() - while self.current_token.type in ('EQUALS', 'NOT_EQUALS'): - token = self.current_token - if token.type == 'EQUALS': - self.eat('EQUALS') - else: - self.eat('NOT_EQUALS') - node = { - 'type': 'binary_op', - 'op': token.value, - 'left': node, - 'right': self.comparison() - } - return node - - def comparison(self): - node = self.term() - while self.current_token.type in ('PLUS', 'MINUS', 'LESS', 'LESS_EQUAL', 'GREATER', 'GREATER_EQUAL'): - token = self.current_token - if token.type == 'PLUS': - self.eat('PLUS') - elif token.type == 'MINUS': - self.eat('MINUS') - elif token.type == 'LESS': - self.eat('LESS') - elif token.type == 'LESS_EQUAL': - self.eat('LESS_EQUAL') - elif token.type == 'GREATER': - self.eat('GREATER') - else: - self.eat('GREATER_EQUAL') - node = { - 'type': 'binary_op', - 'op': token.value, - 'left': node, - 'right': self.term() - } - return node - - def assignment(self): - token = self.current_token - self.eat('IDENTIFIER') - if self.current_token.type == 'ASSIGN': - self.eat('ASSIGN') - value = self.expression() - # Check if there's a semicolon, but don't require it - if self.current_token.type == 'SEMICOLON': - self.eat('SEMICOLON') - return { - 'type': 'assignment', - 'name': token.value, - 'value': value - } - elif self.current_token.type in ('PLUS_EQUALS', 'MINUS_EQUALS', 'MULTIPLY_EQUALS', 'DIVIDE_EQUALS'): - op_token = self.current_token - if op_token.type == 'PLUS_EQUALS': - self.eat('PLUS_EQUALS') - elif op_token.type == 'MINUS_EQUALS': - self.eat('MINUS_EQUALS') - elif op_token.type == 'MULTIPLY_EQUALS': - self.eat('MULTIPLY_EQUALS') - else: - self.eat('DIVIDE_EQUALS') - value = self.expression() - # Check if there's a semicolon, but don't require it - if self.current_token.type == 'SEMICOLON': - self.eat('SEMICOLON') - # Create compound assignment as binary operation - compound_op = op_token.value[0] # Get the first character (e.g., '+' from '+=') - return { - 'type': 'assignment', - 'name': token.value, - 'value': { - 'type': 'binary_op', - 'op': compound_op, - 'left': {'type': 'identifier', 'name': token.value}, - 'right': value - } - } - else: - self.error(f"Expected assignment operator, got {self.current_token.type}") - - def function_call(self): - token = self.current_token - self.eat('KEYWORD_SHOW') - self.eat('LPAREN') - arguments = [] - if self.current_token.type != 'RPAREN': - arguments.append(self.expression()) - while self.current_token.type == 'COMMA': - self.eat('COMMA') - arguments.append(self.expression()) - self.eat('RPAREN') - # Check if there's a semicolon, but don't require it - if self.current_token.type == 'SEMICOLON': - self.eat('SEMICOLON') - return { - 'type': 'function_call', - 'name': token.value, - 'arguments': arguments - } - - def block(self): - self.eat('LBRACE') - statements = [] - while self.current_token.type != 'RBRACE' and self.current_token.type != 'EOF': - statements.append(self.statement()) - self.eat('RBRACE') - return {'type': 'block', 'statements': statements} - - def if_statement(self): - self.eat('KEYWORD_IF') - self.eat('LPAREN') - condition = self.expression() - self.eat('RPAREN') - then_branch = self.block() - else_branch = None - - if self.current_token.type == 'KEYWORD_ELSE': - self.eat('KEYWORD_ELSE') - if self.current_token.type == 'KEYWORD_IF': - else_branch = self.if_statement() - else: - else_branch = self.block() - - return { - 'type': 'if_statement', - 'condition': condition, - 'then_branch': then_branch, - 'else_branch': else_branch - } - - def while_statement(self): - self.eat('KEYWORD_WHILE') - self.eat('LPAREN') - condition = self.expression() - self.eat('RPAREN') - body = self.block() - return { - 'type': 'while_statement', - 'condition': condition, - 'body': body - } - - def return_statement(self): - self.eat('KEYWORD_RETURN') - value = self.expression() - # 检查是否需要分号(在函数体中可能不需要分号) - if self.current_token.type == 'SEMICOLON': - self.eat('SEMICOLON') - return { - 'type': 'return_statement', - 'value': value - } - - def def_statement(self): - self.eat('KEYWORD_DEF') - name = self.current_token.value - self.eat('IDENTIFIER') - self.eat('LPAREN') - parameters = [] - if self.current_token.type != 'RPAREN': - parameters.append(self.current_token.value) - self.eat('IDENTIFIER') - while self.current_token.type == 'COMMA': - self.eat('COMMA') - parameters.append(self.current_token.value) - self.eat('IDENTIFIER') - self.eat('RPAREN') - body = self.block() - return { - 'type': 'def_statement', - 'name': name, - 'parameters': parameters, - 'body': body - } - - def import_statement(self): - # Handle both @library and import library syntax - if self.current_token.type == 'AT': - self.eat('AT') - elif self.current_token.type == 'KEYWORD_IMPORT': - self.eat('KEYWORD_IMPORT') - module = self.current_token.value - self.eat('IDENTIFIER') - # Check if there's a semicolon, but don't require it - if self.current_token.type == 'SEMICOLON': - self.eat('SEMICOLON') - return { - 'type': 'import_statement', - 'module': module - } - - def include_directive(self): - module = self.current_token.value - self.eat('INCLUDE_DIRECTIVE') - # Check if there's a semicolon, but don't require it - if self.current_token.type == 'SEMICOLON': - self.eat('SEMICOLON') - return { - 'type': 'include_directive', - 'module': module - } - - def class_statement(self): - self.eat('KEYWORD_CLASS') - name = self.current_token.value - self.eat('IDENTIFIER') - self.eat('LBRACE') - methods = [] - while self.current_token.type != 'RBRACE' and self.current_token.type != 'EOF': - if self.current_token.type == 'KEYWORD_DEF': - methods.append(self.def_statement()) - else: - self.error(f"Expected function definition in class, got {self.current_token.type}") - self.eat('RBRACE') - return { - 'type': 'class_statement', - 'name': name, - 'methods': methods - } - - def statement(self): - if self.current_token.type == 'INCLUDE_DIRECTIVE': - return self.include_directive() - elif self.current_token.type == 'KEYWORD_SHOW': - return self.function_call() - elif self.current_token.type == 'KEYWORD_IF': - return self.if_statement() - elif self.current_token.type == 'KEYWORD_WHILE': - return self.while_statement() - elif self.current_token.type == 'KEYWORD_DEF': - return self.def_statement() - elif self.current_token.type == 'KEYWORD_CLASS': - return self.class_statement() - elif self.current_token.type == 'KEYWORD_RETURN': - return self.return_statement() - elif self.current_token.type == 'KEYWORD_IMPORT': - return self.import_statement() - elif self.current_token.type == 'AT': - return self.import_statement() - elif self.current_token.type == 'IDENTIFIER': - # Check if it's a function call - if self.lexer.position + 1 < len(self.lexer.source) and self.lexer.source[self.lexer.position] == '(': - # It's a function call - name = self.current_token.value - self.eat('IDENTIFIER') - self.eat('LPAREN') - arguments = [] - if self.current_token.type != 'RPAREN': - arguments.append(self.expression()) - while self.current_token.type == 'COMMA': - self.eat('COMMA') - arguments.append(self.expression()) - self.eat('RPAREN') - self.eat('SEMICOLON') - return { - 'type': 'function_call', - 'name': name, - 'arguments': arguments - } - else: - # It's an assignment - return self.assignment() - else: - self.error(f"Unexpected token {self.current_token.type}") - - def parse(self): - statements = [] - while self.current_token.type != 'EOF': - statements.append(self.statement()) - return {'type': 'block', 'statements': statements} - -class Interpreter: - def __init__(self): - import math - import time - import random - import os - import subprocess - import hashlib - - self.symbols = {} - self.functions = { - # 字符串和数组函数 - 'len': lambda s: str(len(eval(s)) if s.startswith('[') and s.endswith(']') else len(s)), - 'upper': lambda s: s.upper(), - 'lower': lambda s: s.lower(), - 'substring': lambda s, start, end: s[int(start):int(end)], - # 数组函数 - 'arr': lambda *args: str(list(args)), - 'push': lambda arr, item: str(eval(arr) + [item]), - 'pop': lambda arr: str(eval(arr)[:-1]), - 'indexOf': lambda arr, item: str(eval(arr).index(item) if item in eval(arr) else -1), - 'join': lambda arr, sep: sep.join(eval(arr)), - 'reverse': lambda arr: str(list(reversed(eval(arr)))), - 'sort': lambda arr: str(sorted(eval(arr))), - 'slice': lambda arr, start, end: str(eval(arr)[int(start):int(end)]), - # 数学库 - 'math.abs': lambda x: str(abs(float(x))), - 'math.sqrt': lambda x: str(float(x) ** 0.5), - 'math.pow': lambda x, y: str(float(x) ** float(y)), - 'math.sin': lambda x: str(math.sin(math.radians(float(x)))), - 'math.cos': lambda x: str(math.cos(math.radians(float(x)))), - 'math.tan': lambda x: str(math.tan(math.radians(float(x)))), - 'math.asin': lambda x: str(math.degrees(math.asin(float(x)))), - 'math.acos': lambda x: str(math.degrees(math.acos(float(x)))), - 'math.atan': lambda x: str(math.degrees(math.atan(float(x)))), - 'math.pi': lambda: str(math.pi), - 'math.e': lambda: str(math.e), - 'math.max': lambda *args: str(max(float(arg) for arg in args)), - 'math.min': lambda *args: str(min(float(arg) for arg in args)), - 'math.random': lambda: str(random.random()), - 'math.randint': lambda a, b: str(random.randint(int(a), int(b))), - # 日期时间库 - 'time.now': lambda: str(int(time.time())), - 'time.sleep': lambda seconds: (time.sleep(float(seconds)), 'Done')[1], - 'time.format': lambda timestamp, format_str: time.strftime(format_str, time.localtime(float(timestamp))), - # JSON库 - 'json.parse': lambda json_str: str(eval(json_str)), - 'json.stringify': lambda obj: str(obj), - # 加密库 - 'crypto.md5': lambda s: hashlib.md5(s.encode()).hexdigest(), - 'crypto.sha1': lambda s: hashlib.sha1(s.encode()).hexdigest(), - 'crypto.sha256': lambda s: hashlib.sha256(s.encode()).hexdigest(), - # 字符串操作库 - 'string.split': lambda s, sep: str(list(s)) if sep == '' else str(s.split(sep)), - 'string.upper': lambda s: s.upper(), - 'string.lower': lambda s: s.lower(), - 'string.trim': lambda s: s.strip(), - 'string.replace': lambda s, old, new: s.replace(old, new), - 'string.startsWith': lambda s, prefix: str(s.startswith(prefix)), - 'string.endsWith': lambda s, suffix: str(s.endswith(suffix)), - 'string.substring': lambda s, start, end: s[int(start):int(end)], - 'string.indexOf': lambda s, substr: str(s.find(substr)), - 'string.length': lambda s: str(len(s)), - 'string.contains': lambda s, substr: str(substr in s), - 'string.concat': lambda s1, s2: s1 + s2, - 'string.repeat': lambda s, n: s * int(n), - 'string.reverse': lambda s: s[::-1], - 'string.includes': lambda s, substr: str(substr in s), - 'string.lastIndexOf': lambda s, substr: str(s.rfind(substr)), - 'string.padStart': lambda s, length, char: s.rjust(int(length), char), - 'string.padEnd': lambda s, length, char: s.ljust(int(length), char), - # 数组操作库 - 'array.length': lambda arr: str(len(eval(arr))), - 'array.push': lambda arr, item: str(eval(arr) + [item]), - 'array.pop': lambda arr: str(eval(arr)[:-1]), - 'array.shift': lambda arr: str(eval(arr)[1:]), - 'array.unshift': lambda arr, item: str([item] + eval(arr)), - 'array.indexOf': lambda arr, item: str(eval(arr).index(item) if item in eval(arr) else -1), - 'array.lastIndexOf': lambda arr, item: str(len(eval(arr)) - 1 - eval(arr)[::-1].index(item) if item in eval(arr) else -1), - 'array.includes': lambda arr, item: str(item in eval(arr)), - 'array.join': lambda arr, sep: sep.join(eval(arr)), - 'array.reverse': lambda arr: str(list(reversed(eval(arr)))), - 'array.sort': lambda arr: str(sorted(eval(arr))), - 'array.slice': lambda arr, start, end: str(eval(arr)[int(start):int(end)]), - # 工具库 - 'util.random': lambda: str(random.random()), - 'util.randint': lambda a, b: str(random.randint(int(a), int(b))), - 'util.round': lambda x, n=0: str(round(float(x), int(n))), - 'util.abs': lambda x: str(abs(float(x))), - 'util.min': lambda *args: str(min(float(arg) for arg in args)), - 'util.max': lambda *args: str(max(float(arg) for arg in args)), - 'util.sum': lambda *args: str(sum(float(arg) for arg in args)), - 'util.average': lambda *args: str(sum(float(arg) for arg in args) / len(args)) if args else '0', - 'util.isNumber': lambda x: str(x.replace('.', '', 1).replace('-', '', 1).isdigit()), - 'util.isString': lambda x: str(isinstance(x, str)), - 'util.isArray': lambda x: str(x.startswith('[') and x.endswith(']')), - 'util.toNumber': lambda x: str(float(x)), - 'util.toString': lambda x: str(x), - 'util.typeof': lambda x: 'array' if x.startswith('[') and x.endswith(']') else 'number' if x.replace('.', '', 1).replace('-', '', 1).isdigit() else 'string' if isinstance(x, str) else 'unknown', - # 其他函数 - 'input': lambda prompt: input(prompt), - 'int': lambda s: str(int(s)), - 'str': lambda x: str(x), - # 网络请求函数 - 'http_get': self.http_get, - 'http_post': self.http_post, - # 文件操作函数 - 'file_read': self.file_read, - 'file_write': self.file_write, - 'file_exists': self.file_exists, - # 系统函数 - 'system': self.system, - 'get_env': self.get_env - } - self.classes = {} - self.return_value = None - self.modules = {} - - # 模块引用 - self.math = math - self.time = time - self.random = random - self.os = os - self.subprocess = subprocess - self.hashlib = hashlib - - def error(self, message, line=0, column=0): - # For Windows PowerShell compatibility, use simple error messages without ANSI codes - error_msg = "Runtime Error" - if line > 0 and column > 0: - error_msg += f" at line {line}, column {column}" - error_msg += f": {message}" - raise Exception(error_msg) - - def http_get(self, url): - """Send HTTP GET request""" - try: - import urllib.request - import json - with urllib.request.urlopen(url) as response: - data = response.read().decode('utf-8') - return data - except Exception as e: - return f"Error: {str(e)}" - - def http_post(self, url, data): - """Send HTTP POST request""" - try: - import urllib.request - import urllib.parse - import json - data = data.encode('utf-8') - req = urllib.request.Request(url, data=data, method='POST') - req.add_header('Content-Type', 'application/json') - with urllib.request.urlopen(req) as response: - response_data = response.read().decode('utf-8') - return response_data - except Exception as e: - return f"Error: {str(e)}" - - def file_read(self, filename): - """Read file content""" - try: - with open(filename, 'r', encoding='utf-8') as f: - content = f.read() - return content - except Exception as e: - return f"Error: {str(e)}" - - def file_write(self, filename, content): - """Write content to file""" - try: - with open(filename, 'w', encoding='utf-8') as f: - f.write(content) - return "Success" - except Exception as e: - return f"Error: {str(e)}" - - def file_exists(self, filename): - """Check if file exists""" - try: - return str(1 if self.os.path.exists(filename) else 0) - except Exception as e: - return f"Error: {str(e)}" - - def time_now(self): - """Get current time""" - try: - return str(self.time.time()) - except Exception as e: - return f"Error: {str(e)}" - - def time_sleep(self, seconds): - """Sleep for specified seconds""" - try: - self.time.sleep(float(seconds)) - return "Success" - except Exception as e: - return f"Error: {str(e)}" - - def random_int(self, min_val, max_val): - """Generate random integer between min and max""" - try: - return str(self.random.randint(int(min_val), int(max_val))) - except Exception as e: - return f"Error: {str(e)}" - - def random_float(self): - """Generate random float between 0 and 1""" - try: - return str(self.random.random()) - except Exception as e: - return f"Error: {str(e)}" - - def system(self, command): - """Execute system command""" - try: - result = self.subprocess.run(command, shell=True, capture_output=True, text=True) - return result.stdout + result.stderr - except Exception as e: - return f"Error: {str(e)}" - - def get_env(self, var_name): - """Get environment variable""" - try: - value = self.os.environ.get(var_name, "") - return value - except Exception as e: - return f"Error: {str(e)}" - - def evaluate(self, node): - if node['type'] == 'literal': - return node['value'] - elif node['type'] == 'identifier': - if node['name'] in self.symbols: - return self.symbols[node['name']] - else: - raise Exception(f"Undefined variable: {node['name']}") - elif node['type'] == 'array_index': - # Handle array indexing - array_value = self.symbols.get(node['name']) - if array_value is None: - raise Exception(f"Undefined variable: {node['name']}") - # Evaluate the index expression - index_value = self.evaluate(node['index']) - # Convert array string to actual list - try: - array = eval(array_value) - if isinstance(array, list): - index = int(index_value) - if 0 <= index < len(array): - return array[index] - else: - raise Exception(f"Array index out of range: {index}") - else: - raise Exception(f"Variable {node['name']} is not an array") - except Exception as e: - raise Exception(f"Error accessing array: {str(e)}") - elif node['type'] == 'binary_op': - left = self.evaluate(node['left']) - right = self.evaluate(node['right']) - - # Check if both operands are numbers for arithmetic operations - if left.isdigit() and right.isdigit(): - left_num = int(left) - right_num = int(right) - - if node['op'] == '+': - return str(left_num + right_num) - elif node['op'] == '-': - return str(left_num - right_num) - elif node['op'] == '*': - return str(left_num * right_num) - elif node['op'] == '/': - if right_num == 0: - raise Exception("Division by zero") - return str(left_num // right_num) - elif node['op'] == '==': - return str(1 if left_num == right_num else 0) - elif node['op'] == '!=': - return str(1 if left_num != right_num else 0) - elif node['op'] == '<': - return str(1 if left_num < right_num else 0) - elif node['op'] == '<=': - return str(1 if left_num <= right_num else 0) - elif node['op'] == '>': - return str(1 if left_num > right_num else 0) - elif node['op'] == '>=': - return str(1 if left_num >= right_num else 0) - else: - # String operations - if node['op'] == '+': - return left + right - elif node['op'] == '==': - return str(1 if left == right else 0) - elif node['op'] == '!=': - return str(1 if left != right else 0) - else: - raise Exception("Unsupported operation for non-numeric values") - - # Logical operations - if node['op'] == '&&': - return str(1 if (int(left) != 0 and int(right) != 0) else 0) - elif node['op'] == '||': - return str(1 if (int(left) != 0 or int(right) != 0) else 0) - elif node['type'] == 'function_call': - # Handle function calls in expressions - if node['name'] in self.symbols and callable(self.symbols[node['name']]): - # Check if it's a function from a module (like pylang or bflang) - func = self.symbols[node['name']] - args = [self.evaluate(arg) for arg in node['arguments']] - return func(*args) - elif node['name'] in self.functions: - # Check if it's a built-in function (lambda) - func = self.functions[node['name']] - if callable(func): - # Check if it's a library function that requires import - if '.' in node['name']: - # Extract library name from function name (e.g., math.abs -> math) - library_name = node['name'].split('.')[0] - # Check if the library is imported - if library_name not in self.modules: - self.error(f"Library '{library_name}' not imported. Use '@{library_name}' to import it.") - # Check if it's an HTTP function that requires http module - if (node['name'] == 'http_get' or node['name'] == 'http_post') and 'http' not in self.modules: - self.error("HTTP module not imported. Use '@http' to import it.") - # It's a built-in function - args = [self.evaluate(arg) for arg in node['arguments']] - return func(*args) - else: - # It's a user-defined function - # Create local symbol table - local_symbols = self.symbols.copy() - # Assign arguments to parameters - for i, param in enumerate(func['parameters']): - if i < len(node['arguments']): - local_symbols[param] = self.evaluate(node['arguments'][i]) - else: - local_symbols[param] = '0' # Default value - # Save current symbol table and return value - old_symbols = self.symbols - old_return_value = self.return_value - # Set local symbol table - self.symbols = local_symbols - # Reset return value - self.return_value = None - # Execute function body - self.interpret(func['body']) - # Get return value - return_value = self.return_value - # Restore symbol table and return value - self.symbols = old_symbols - self.return_value = old_return_value - # Return the value (ensure it's a string) - if return_value is not None: - return str(return_value) - return return_value - else: - self.error(f"Unknown function: {node['name']}") - else: - raise Exception(f"Unknown node type: {node['type']}") - - def interpret(self, node): - if isinstance(node, list): - for stmt in node: - result = self.interpret(stmt) - if result is not None: - return result - elif node['type'] == 'block': - for stmt in node['statements']: - result = self.interpret(stmt) - if result is not None: - return result - elif node['type'] == 'assignment': - value = self.evaluate(node['value']) - self.symbols[node['name']] = value - elif node['type'] == 'function_call': - if node['name'] == 'show': - for arg in node['arguments']: - value = self.evaluate(arg) - print(value, end=' ') - print() - elif node['name'] in self.symbols and callable(self.symbols[node['name']]): - # Check if it's a function from a module (like pylang or bflang) - func = self.symbols[node['name']] - args = [self.evaluate(arg) for arg in node['arguments']] - result = func(*args) - return result - elif node['name'] in self.functions: - # Check if it's a built-in function (lambda) - func = self.functions[node['name']] - if callable(func): - # Check if it's a library function that requires import - if '.' in node['name']: - # Extract library name from function name (e.g., math.abs -> math) - library_name = node['name'].split('.')[0] - # Check if the library is imported - if library_name not in self.modules: - self.error(f"Library '{library_name}' not imported. Use '@{library_name}' to import it.") - # Check if it's an HTTP function that requires http module - if (node['name'] == 'http_get' or node['name'] == 'http_post') and 'http' not in self.modules: - self.error("HTTP module not imported. Use '@http' to import it.") - # It's a built-in function - args = [self.evaluate(arg) for arg in node['arguments']] - result = func(*args) - return result - else: - # It's a user-defined function - # Create local symbol table - local_symbols = self.symbols.copy() - # Assign arguments to parameters - for i, param in enumerate(func['parameters']): - if i < len(node['arguments']): - local_symbols[param] = self.evaluate(node['arguments'][i]) - else: - local_symbols[param] = '0' # Default value - # Save current symbol table and return value - old_symbols = self.symbols - old_return_value = self.return_value - # Set local symbol table - self.symbols = local_symbols - # Reset return value - self.return_value = None - # Execute function body - self.interpret(func['body']) - # Get return value - return_value = self.return_value - # Restore symbol table and return value - self.symbols = old_symbols - self.return_value = old_return_value - # Return the value (ensure it's a string) - if return_value is not None: - return str(return_value) - return return_value - elif node['name'] in self.functions and 'math.' in node['name']: - # Check if it's a math function - func = self.functions[node['name']] - if callable(func): - # It's a math function - args = [self.evaluate(arg) for arg in node['arguments']] - result = func(*args) - return result - else: - self.error(f"Unknown function: {node['name']}") - elif node['type'] == 'if_statement': - condition = self.evaluate(node['condition']) - if int(condition) != 0: - self.interpret(node['then_branch']) - elif node['else_branch']: - self.interpret(node['else_branch']) - elif node['type'] == 'while_statement': - while True: - condition = self.evaluate(node['condition']) - if int(condition) == 0: - break - self.interpret(node['body']) - # Check for return statement - if self.return_value is not None: - break - elif node['type'] == 'def_statement': - # Store function definition - self.functions[node['name']] = { - 'parameters': node['parameters'], - 'body': node['body'] - } - elif node['type'] == 'class_statement': - # Store class definition - self.classes[node['name']] = { - 'methods': {} - } - for method in node['methods']: - self.classes[node['name']]['methods'][method['name']] = { - 'parameters': method['parameters'], - 'body': method['body'] - } - elif node['type'] == 'return_statement': - # Set return value - self.return_value = self.evaluate(node['value']) - elif node['type'] == 'import_statement': - # Handle import statement - module = node['module'] - self.modules[module] = True - # Here you can add module-specific initialization - if module == 'http': - # HTTP module is already initialized in functions - pass - elif module == 'math': - # Add math functions to the current scope - pass - elif node['type'] == 'include_directive': - # Handle include directive - module = node['module'] - self.modules[module] = True - - # Check if it's a built-in module - if module == 'response': - # Enable HTTP request functions - pass - else: - # Try to load from includes folder - import os - include_path = os.path.join('includes', f'{module}.oraset') - if os.path.exists(include_path): - try: - with open(include_path, 'r', encoding='utf-8') as f: - source = f.read() - # Parse and interpret the included file - lexer = Lexer(source) - parser = Parser(lexer) - ast = parser.parse() - self.interpret(ast) - print(f"Successfully included module: {module}") - except Exception as e: - print(f"Error including module {module}: {e}") - else: - print(f"Warning: Module {module} not found in includes folder") - else: - raise Exception(f"Unknown node type: {node['type']}") - -import base64 -import zlib - -def pack_file(input_file, output_file): - """Pack .oas file into .osp file""" - try: - with open(input_file, 'r', encoding='utf-8') as f: - source = f.read() - - # Compress the source code - compressed = zlib.compress(source.encode('utf-8')) - # Encode as base64 - encoded = base64.b64encode(compressed) - - with open(output_file, 'wb') as f: - f.write(encoded) - - print(f"Successfully packed {input_file} into {output_file}") - return 0 - except Exception as e: - print(f"Error packing file: {e}") - return 1 - -def unpack_file(input_file): - """Unpack .osp file and return the source code""" - try: - with open(input_file, 'rb') as f: - encoded = f.read() - - # Decode from base64 - compressed = base64.b64decode(encoded) - # Decompress - source = zlib.decompress(compressed).decode('utf-8') - - return source - except Exception as e: - print(f"Error unpacking file: {e}") - return None - -def main(): - if len(sys.argv) < 2: - print(f"Usage:") - print(f" {sys.argv[0]} # Run .oas or .oraset file") - print(f" {sys.argv[0]} pack # Pack .oas or .oraset file into .osp") - print(f" {sys.argv[0]} -osp # Run .osp file") - return 1 - - if sys.argv[1] == 'pack': - if len(sys.argv) != 3: - print(f"Usage: {sys.argv[0]} pack ") - return 1 - input_file = sys.argv[2] - output_file = input_file.replace('.oas', '.osp').replace('.oraset', '.osp') - return pack_file(input_file, output_file) - elif sys.argv[1] == '-osp': - if len(sys.argv) != 3: - print(f"Usage: {sys.argv[0]} -osp ") - return 1 - input_file = sys.argv[2] - source = unpack_file(input_file) - if source is None: - return 1 - else: - filename = sys.argv[1] - try: - with open(filename, 'r', encoding='utf-8') as f: - source = f.read() - except FileNotFoundError: - print(f"Error: Could not open file {filename}") - return 1 - - lexer = Lexer(source) - parser = Parser(lexer) - try: - ast = parser.parse() - except Exception as e: - print(f"Error: {e}") - return 1 - - interpreter = Interpreter() - try: - interpreter.interpret(ast) - except Exception as e: - print(f"Error: {e}") - return 1 - - return 0 - -if __name__ == "__main__": - sys.exit(main()) diff --git a/test_file.txt b/test_file.txt new file mode 100644 index 0000000..92ed811 --- /dev/null +++ b/test_file.txt @@ -0,0 +1,2 @@ +Hello, Oraset! +This is a test file. \ No newline at end of file