commit 1f1405391c51eaf44b3e93bfe4c18c8a3c80fb51 Author: JGZ_YES Date: Thu Jul 23 13:21:11 2026 +0800 Initial commit: Oraset B-5 with C++ style features diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8558a60 --- /dev/null +++ b/Makefile @@ -0,0 +1,26 @@ +CC = gcc +CFLAGS = -Wall -Wextra -O2 -std=c11 +BINDIR = bin + +TARGET = $(BINDIR)/oraset + +SRCS = src/main.c + +all: $(TARGET) + +$(TARGET): $(SRCS) + @mkdir -p $(BINDIR) + $(CC) $(CFLAGS) -o $(TARGET) $(SRCS) + +clean: + rm -f $(TARGET) + rm -rf $(BINDIR) + +pkg: all + rm -rf pkg-root + mkdir -p pkg-root/usr/local/bin + cp bin/oraset pkg-root/usr/local/bin/oraset + chmod +x pkg-root/usr/local/bin/oraset + pkgbuild --root pkg-root --identifier com.oraset.lang --version B-5 --install-location / Oraset-B5.pkg + +.PHONY: all clean pkg \ No newline at end of file diff --git a/bin/oraset.exe b/bin/oraset.exe new file mode 100644 index 0000000..5cbcade Binary files /dev/null and b/bin/oraset.exe differ diff --git a/bin/oraset_new.exe b/bin/oraset_new.exe new file mode 100644 index 0000000..1c95557 Binary files /dev/null and b/bin/oraset_new.exe differ diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..f58fd3f --- /dev/null +++ b/src/main.c @@ -0,0 +1,2425 @@ +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 + #include + #include + #define OS_WINDOWS 1 +#else + #include + #include + #include + #define OS_WINDOWS 0 +#endif + +#define VERSION "B-5" +#define MAX_TOKENS 8192 +#define MAX_VARS 512 +#define MAX_LINE 8192 +#define MAX_STRING 2048 +#define MAX_ARRAY_SIZE 1024 + +typedef enum { + TOKEN_EOF, + TOKEN_IDENTIFIER, + TOKEN_STRING, + TOKEN_NUMBER, + TOKEN_PRINT, + TOKEN_VAR, + TOKEN_IF, + TOKEN_WHILE, + TOKEN_FOR, + TOKEN_FUNCTION, + TOKEN_RETURN, + TOKEN_ELSE, + TOKEN_LPAREN, + TOKEN_RPAREN, + TOKEN_LBRACE, + TOKEN_RBRACE, + TOKEN_LBRACKET, + TOKEN_RBRACKET, + TOKEN_COMMA, + TOKEN_SEMICOLON, + TOKEN_ASSIGN, + TOKEN_PLUS, + TOKEN_MINUS, + TOKEN_MULTIPLY, + TOKEN_DIVIDE, + TOKEN_EQUAL, + TOKEN_NOT_EQUAL, + TOKEN_GREATER, + TOKEN_LESS, + TOKEN_AND, + TOKEN_OR, + TOKEN_NOT, + TOKEN_GREATER_EQUAL, + TOKEN_LESS_EQUAL, + TOKEN_MODULO, + TOKEN_PLUS_ASSIGN, + TOKEN_MINUS_ASSIGN, + TOKEN_MULTIPLY_ASSIGN, + TOKEN_DIVIDE_ASSIGN, + TOKEN_MODULO_ASSIGN, + TOKEN_INCREMENT, + TOKEN_DECREMENT, + TOKEN_COLON, + TOKEN_DOT, + TOKEN_ARROW, + TOKEN_SWITCH, + TOKEN_CASE, + TOKEN_DEFAULT, + TOKEN_BREAK, + TOKEN_CONTINUE, + TOKEN_DO, + TOKEN_STRUCT, + TOKEN_TYPEDEF, + TOKEN_ENUM, + TOKEN_GOTO, + TOKEN_BIT_AND, + TOKEN_BIT_OR, + TOKEN_BIT_XOR, + TOKEN_BIT_NOT, + TOKEN_SHIFT_LEFT, + TOKEN_SHIFT_RIGHT, + TOKEN_NEW, + TOKEN_DELETE, + TOKEN_TRY, + TOKEN_CATCH, + TOKEN_THROW, + TOKEN_ASTERISK, + TOKEN_LABEL +} OraTokenType; + +typedef struct { + OraTokenType type; + char value[MAX_STRING]; + int line; +} Token; + +typedef enum { + VAL_NUMBER, + VAL_STRING, + VAL_ARRAY, + VAL_STRUCT, + VAL_ENUM, + VAL_POINTER +} ValueType; + +typedef struct { + ValueType type; + double num_value; + char str_value[MAX_STRING]; + double arr_value[MAX_ARRAY_SIZE]; + int arr_size; + int struct_type; + int pointer_target; +} Value; + +typedef struct { + char name[64]; + Value value; +} Variable; + +typedef struct { + char name[64]; + int param_count; + char params[8][64]; + int body_start; + int body_end; +} Function; + +typedef struct { + char name[64]; + char fields[16][64]; + int field_count; +} StructType; + +typedef struct { + char name[64]; + char values[16][64]; + int value_count; +} EnumType; + +typedef struct { + char name[64]; + int token_index; +} Label; + +Token tokens[MAX_TOKENS]; +int token_count = 0; +int current_token = 0; +Variable vars[MAX_VARS]; +int var_count = 0; +Function funcs[MAX_VARS]; +int func_count = 0; +StructType structs[MAX_VARS]; +int struct_count = 0; +EnumType enums[MAX_VARS]; +int enum_count = 0; +Label labels[MAX_VARS]; +int label_count = 0; +int print_newline = 1; +int current_line = 1; +int in_for_loop_update = 0; +int in_repl = 0; +int loop_depth = 0; +int switch_active = 0; + +int match(OraTokenType type); +Value* find_variable(const char* name); + +static const struct { + const char* name; + OraTokenType type; +} keywords[] = { + {"echo", TOKEN_PRINT}, + {"let", TOKEN_VAR}, + {"if", TOKEN_IF}, + {"else", TOKEN_ELSE}, + {"while", TOKEN_WHILE}, + {"for", TOKEN_FOR}, + {"proc", TOKEN_FUNCTION}, + {"return", TOKEN_RETURN}, + {"switch", TOKEN_SWITCH}, + {"case", TOKEN_CASE}, + {"default", TOKEN_DEFAULT}, + {"break", TOKEN_BREAK}, + {"continue", TOKEN_CONTINUE}, + {"do", TOKEN_DO}, + {"struct", TOKEN_STRUCT}, + {"typedef", TOKEN_TYPEDEF}, + {"enum", TOKEN_ENUM}, + {"goto", TOKEN_GOTO}, + {"new", TOKEN_NEW}, + {"delete", TOKEN_DELETE}, + {"try", TOKEN_TRY}, + {"catch", TOKEN_CATCH}, + {"throw", TOKEN_THROW}, + {NULL, TOKEN_EOF} +}; + +void error(const char* msg, ...) { + va_list args; + va_start(args, msg); + fprintf(stderr, "[Error at line %d] ", current_line); + vfprintf(stderr, msg, args); + fprintf(stderr, "\n"); + va_end(args); + if (in_repl) return; + exit(1); +} + +static const char* get_string_value() { + if (match(TOKEN_STRING)) { + return tokens[current_token - 1].value; + } else if (match(TOKEN_IDENTIFIER)) { + const char* name = tokens[current_token - 1].value; + Value* v = find_variable(name); + if (!v || v->type != VAL_STRING) { + error("Expected string variable: %s", name); + } + return v->str_value; + } + error("Expected string"); + return ""; +} + +static void skip_whitespace_and_comments(const char** p) { + while (**p) { + if (**p == ' ' || **p == '\t' || **p == '\r') { + (*p)++; + } else if (**p == '\n') { + (*p)++; + current_line++; + } else if (**p == '#' || (**p == '/' && *(*p+1) == '/')) { + while (**p && **p != '\n') (*p)++; + } else if (**p == '/' && *(*p+1) == '*') { + (*p) += 2; + while (**p && !(*(*p) == '*' && *(*p+1) == '/')) { + if (**p == '\n') current_line++; + (*p)++; + } + if (**p) (*p) += 2; + } else { + break; + } + } +} + +static void add_token(OraTokenType type, const char* value) { + if (token_count >= MAX_TOKENS) { + error("Too many tokens (max %d)", MAX_TOKENS); + } + tokens[token_count].type = type; + strncpy(tokens[token_count].value, value, MAX_STRING - 1); + tokens[token_count].value[MAX_STRING - 1] = '\0'; + tokens[token_count].line = current_line; + token_count++; +} + +void tokenize(const char* source) { + token_count = 0; + current_line = 1; + const char* p = source; + + while (1) { + skip_whitespace_and_comments(&p); + if (!*p) break; + + if (*p == '"') { + p++; + char buf[MAX_STRING]; + int i = 0; + while (*p && *p != '"' && i < MAX_STRING - 1) { + if (*p == '\\' && *(p+1)) { + p++; + switch (*p) { + case 'n': buf[i++] = '\n'; break; + case 't': buf[i++] = '\t'; break; + case 'r': buf[i++] = '\r'; break; + case '\\': buf[i++] = '\\'; break; + case '"': buf[i++] = '"'; break; + case '0': buf[i++] = '\0'; break; + default: buf[i++] = *p; break; + } + } else { + buf[i++] = *p; + } + p++; + } + buf[i] = '\0'; + if (*p != '"') error("Unterminated string literal"); + p++; + add_token(TOKEN_STRING, buf); + continue; + } + + if (*p == '\'') { + p++; + char buf[MAX_STRING]; + int i = 0; + while (*p && *p != '\'' && i < MAX_STRING - 1) { + if (*p == '\\' && *(p+1)) { + p++; + switch (*p) { + case 'n': buf[i++] = '\n'; break; + case 't': buf[i++] = '\t'; break; + case 'r': buf[i++] = '\r'; break; + case '\\': buf[i++] = '\\'; break; + case '\'': buf[i++] = '\''; break; + default: buf[i++] = *p; break; + } + } else { + buf[i++] = *p; + } + p++; + } + buf[i] = '\0'; + if (*p != '\'') error("Unterminated string literal"); + p++; + add_token(TOKEN_STRING, buf); + continue; + } + + if (isdigit(*p) || (*p == '.' && isdigit(*(p+1)))) { + char buf[64]; + int i = 0; + int dot_count = 0; + while ((isdigit(*p) || (*p == '.' && dot_count < 1)) && i < 63) { + if (*p == '.') dot_count++; + buf[i++] = *p++; + } + buf[i] = '\0'; + add_token(TOKEN_NUMBER, buf); + continue; + } + + if (isalpha(*p) || *p == '_') { + char buf[64]; + int i = 0; + while ((isalnum(*p) || *p == '_') && i < 63) { + buf[i++] = *p++; + } + buf[i] = '\0'; + + int j; + for (j = 0; keywords[j].name; j++) { + if (strcmp(buf, keywords[j].name) == 0) { + add_token(keywords[j].type, buf); + break; + } + } + if (!keywords[j].name) { + if (*p == ':') { + strncpy(labels[label_count].name, buf, 63); + labels[label_count].token_index = token_count + 1; + label_count++; + add_token(TOKEN_LABEL, buf); + p++; + } else { + add_token(TOKEN_IDENTIFIER, buf); + } + } + continue; + } + + if (*p == '=' && *(p+1) == '=') { add_token(TOKEN_EQUAL, "=="); p += 2; continue; } + if (*p == '!' && *(p+1) == '=') { add_token(TOKEN_NOT_EQUAL, "!="); p += 2; continue; } + if (*p == '&' && *(p+1) == '&') { add_token(TOKEN_AND, "&&"); p += 2; continue; } + if (*p == '|' && *(p+1) == '|') { add_token(TOKEN_OR, "||"); p += 2; continue; } + if (*p == '>' && *(p+1) == '=') { add_token(TOKEN_GREATER_EQUAL, ">="); p += 2; continue; } + if (*p == '<' && *(p+1) == '=') { add_token(TOKEN_LESS_EQUAL, "<="); p += 2; continue; } + if (*p == '+' && *(p+1) == '=') { add_token(TOKEN_PLUS_ASSIGN, "+="); p += 2; continue; } + if (*p == '-' && *(p+1) == '=') { add_token(TOKEN_MINUS_ASSIGN, "-="); p += 2; continue; } + if (*p == '*' && *(p+1) == '=') { add_token(TOKEN_MULTIPLY_ASSIGN, "*="); p += 2; continue; } + if (*p == '/' && *(p+1) == '=') { add_token(TOKEN_DIVIDE_ASSIGN, "/="); p += 2; continue; } + if (*p == '%' && *(p+1) == '=') { add_token(TOKEN_MODULO_ASSIGN, "%="); p += 2; continue; } + if (*p == '+' && *(p+1) == '+') { add_token(TOKEN_INCREMENT, "++"); p += 2; continue; } + if (*p == '-' && *(p+1) == '-') { add_token(TOKEN_DECREMENT, "--"); p += 2; continue; } + if (*p == '-' && *(p+1) == '>') { add_token(TOKEN_ARROW, "->"); p += 2; continue; } + if (*p == '<' && *(p+1) == '<') { add_token(TOKEN_SHIFT_LEFT, "<<"); p += 2; continue; } + if (*p == '>' && *(p+1) == '>') { add_token(TOKEN_SHIFT_RIGHT, ">>"); p += 2; continue; } + + switch (*p) { + case '&': add_token(TOKEN_BIT_AND, "&"); break; + case '|': add_token(TOKEN_BIT_OR, "|"); break; + case '^': add_token(TOKEN_BIT_XOR, "^"); break; + case '~': add_token(TOKEN_BIT_NOT, "~"); break; + case '(': add_token(TOKEN_LPAREN, "("); break; + case ')': add_token(TOKEN_RPAREN, ")"); break; + case '{': add_token(TOKEN_LBRACE, "{"); break; + case '}': add_token(TOKEN_RBRACE, "}"); break; + case '[': add_token(TOKEN_LBRACKET, "["); break; + case ']': add_token(TOKEN_RBRACKET, "]"); break; + case ',': add_token(TOKEN_COMMA, ","); break; + case ';': add_token(TOKEN_SEMICOLON, ";"); break; + case '=': add_token(TOKEN_ASSIGN, "="); break; + case '>': add_token(TOKEN_GREATER, ">"); break; + case '<': add_token(TOKEN_LESS, "<"); break; + case '+': add_token(TOKEN_PLUS, "+"); break; + case '-': add_token(TOKEN_MINUS, "-"); break; + case '*': add_token(TOKEN_MULTIPLY, "*"); break; + case '/': add_token(TOKEN_DIVIDE, "/"); break; + case '%': add_token(TOKEN_MODULO, "%"); break; + case '!': add_token(TOKEN_NOT, "!"); break; + case ':': add_token(TOKEN_COLON, ":"); break; + case '.': add_token(TOKEN_DOT, "."); break; + default: error("Unknown character: '%c'", *p); + } + p++; + } + + add_token(TOKEN_EOF, ""); +} + +Token peek() { return tokens[current_token]; } +Token advance() { return tokens[current_token++]; } + +int match(OraTokenType type) { + if (peek().type == type) { + if (tokens[current_token].line > 0) { + current_line = tokens[current_token].line; + } + advance(); + return 1; + } + return 0; +} + +void consume(OraTokenType type, const char* msg) { + if (!match(type)) { + error("%s (got '%s')", msg, peek().value); + } +} + +double evaluate(); +int execute_statement(); +int update_oraset(const char* target_version); + +Value* find_variable(const char* name) { + for (int i = var_count - 1; i >= 0; i--) { + if (strcmp(vars[i].name, name) == 0) { + return &vars[i].value; + } + } + return NULL; +} + +int find_function(const char* name) { + for (int i = func_count - 1; i >= 0; i--) { + if (strcmp(funcs[i].name, name) == 0) { + return i; + } + } + return -1; +} + +int find_struct(const char* name) { + for (int i = struct_count - 1; i >= 0; i--) { + if (strcmp(structs[i].name, name) == 0) { + return i; + } + } + return -1; +} + +int find_enum(const char* name) { + for (int i = enum_count - 1; i >= 0; i--) { + if (strcmp(enums[i].name, name) == 0) { + return i; + } + } + return -1; +} + +int find_label(const char* name) { + for (int i = label_count - 1; i >= 0; i--) { + if (strcmp(labels[i].name, name) == 0) { + return i; + } + } + return -1; +} + +double parse_array_access(Value* arr, int index) { + if (arr->type != VAL_ARRAY) { + error("Cannot index non-array value"); + } + if (index < 0 || index >= arr->arr_size) { + error("Array index out of bounds: %d", index); + } + return arr->arr_value[index]; +} + +double parse_primary(); + +double parse_call(const char* func_name) { + consume(TOKEN_LPAREN, "Expected '(' after function name"); + + if (strcmp(func_name, "abs") == 0) { + double val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + return fabs(val); + } else if (strcmp(func_name, "sqrt") == 0) { + double val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + return sqrt(val); + } else if (strcmp(func_name, "sin") == 0) { + double val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + return sin(val); + } else if (strcmp(func_name, "cos") == 0) { + double val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + return cos(val); + } else if (strcmp(func_name, "tan") == 0) { + double val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + return tan(val); + } else if (strcmp(func_name, "random") == 0) { + if (match(TOKEN_RPAREN)) { + return (double)rand() / RAND_MAX; + } + double max = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + return fmod((double)rand(), max); + } else if (strcmp(func_name, "size") == 0) { + if (match(TOKEN_STRING)) { + const char* str = tokens[current_token - 1].value; + consume(TOKEN_RPAREN, "Expected ')'"); + return strlen(str); + } + error("Expected string argument for size()"); + } else if (strcmp(func_name, "to_num") == 0) { + if (match(TOKEN_STRING)) { + const char* str = tokens[current_token - 1].value; + consume(TOKEN_RPAREN, "Expected ')'"); + return atof(str); + } + error("Expected string argument for to_num()"); + } else if (strcmp(func_name, "to_str") == 0) { + double val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + char buf[MAX_STRING]; + sprintf(buf, "%g", val); + for (int i = 0; i < var_count; i++) { + if (strcmp(vars[i].name, "__tmp_str_result") == 0) { + strncpy(vars[i].value.str_value, buf, MAX_STRING - 1); + vars[i].value.str_value[MAX_STRING - 1] = '\0'; + vars[i].value.type = VAL_STRING; + return 0; + } + } + strncpy(vars[var_count].name, "__tmp_str_result", 63); + strncpy(vars[var_count].value.str_value, buf, MAX_STRING - 1); + vars[var_count].value.str_value[MAX_STRING - 1] = '\0'; + vars[var_count].value.type = VAL_STRING; + var_count++; + return 0; + } else if (strcmp(func_name, "input") == 0) { + const char* prompt = ""; + if (match(TOKEN_STRING)) { + prompt = tokens[current_token - 1].value; + } + consume(TOKEN_RPAREN, "Expected ')'"); + + printf("%s", prompt); + fflush(stdout); + + char input[MAX_STRING]; + if (fgets(input, MAX_STRING, stdin) == NULL) { + error("Failed to read input"); + } + + input[strcspn(input, "\n")] = '\0'; + + for (int i = 0; i < var_count; i++) { + if (strcmp(vars[i].name, "__input_result") == 0) { + strncpy(vars[i].value.str_value, input, MAX_STRING - 1); + vars[i].value.str_value[MAX_STRING - 1] = '\0'; + vars[i].value.type = VAL_STRING; + return 0; + } + } + strncpy(vars[var_count].name, "__input_result", 63); + strncpy(vars[var_count].value.str_value, input, MAX_STRING - 1); + vars[var_count].value.str_value[MAX_STRING - 1] = '\0'; + vars[var_count].value.type = VAL_STRING; + var_count++; + return 0; + } else if (strcmp(func_name, "int") == 0) { + double val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + return (int)val; + } else if (strcmp(func_name, "float") == 0) { + double val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + return val; + } else if (strcmp(func_name, "floor") == 0) { + double val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + return floor(val); + } else if (strcmp(func_name, "ceil") == 0) { + double val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + return ceil(val); + } else if (strcmp(func_name, "round") == 0) { + double val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + return round(val); + } else if (strcmp(func_name, "pow") == 0) { + double base = evaluate(); + consume(TOKEN_COMMA, "Expected ','"); + double exp = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + return pow(base, exp); + } else if (strcmp(func_name, "log") == 0) { + double val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + return log(val); + } else if (strcmp(func_name, "time") == 0) { + consume(TOKEN_RPAREN, "Expected ')'"); + return (double)time(NULL); + } else if (strcmp(func_name, "array") == 0) { + int size = (int)evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + for (int i = 0; i < var_count; i++) { + if (strcmp(vars[i].name, "__tmp_arr_result") == 0) { + vars[i].value.type = VAL_ARRAY; + vars[i].value.arr_size = size; + for (int j = 0; j < size; j++) { + vars[i].value.arr_value[j] = 0; + } + return 0; + } + } + strncpy(vars[var_count].name, "__tmp_arr_result", 63); + vars[var_count].value.type = VAL_ARRAY; + vars[var_count].value.arr_size = size; + for (int j = 0; j < size; j++) { + vars[var_count].value.arr_value[j] = 0; + } + var_count++; + return 0; + } else if (strcmp(func_name, "arrlen") == 0) { + consume(TOKEN_IDENTIFIER, "Expected array variable"); + const char* name = tokens[current_token - 1].value; + Value* v = find_variable(name); + if (!v || v->type != VAL_ARRAY) { + error("'%s' is not an array", name); + } + consume(TOKEN_RPAREN, "Expected ')'"); + return v->arr_size; + } else if (strcmp(func_name, "exit") == 0) { + int code = (int)evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + exit(code); + } else if (strcmp(func_name, "assert") == 0) { + double cond = evaluate(); + consume(TOKEN_COMMA, "Expected ','"); + if (match(TOKEN_STRING)) { + const char* msg = tokens[current_token - 1].value; + consume(TOKEN_RPAREN, "Expected ')'"); + if (cond == 0) { + error("Assertion failed: %s", msg); + } + } else { + consume(TOKEN_RPAREN, "Expected ')'"); + if (cond == 0) { + error("Assertion failed"); + } + } + return 1; + } else if (strcmp(func_name, "strcmp") == 0) { + const char* str1 = get_string_value(); + consume(TOKEN_COMMA, "Expected ','"); + const char* str2 = get_string_value(); + consume(TOKEN_RPAREN, "Expected ')'"); + return strcmp(str1, str2); + } else if (strcmp(func_name, "strcpy") == 0) { + const char* src = get_string_value(); + consume(TOKEN_RPAREN, "Expected ')'"); + for (int i = 0; i < var_count; i++) { + if (strcmp(vars[i].name, "__tmp_str_result") == 0) { + strncpy(vars[i].value.str_value, src, MAX_STRING - 1); + vars[i].value.str_value[MAX_STRING - 1] = '\0'; + vars[i].value.type = VAL_STRING; + return 0; + } + } + strncpy(vars[var_count].name, "__tmp_str_result", 63); + strncpy(vars[var_count].value.str_value, src, MAX_STRING - 1); + vars[var_count].value.str_value[MAX_STRING - 1] = '\0'; + vars[var_count].value.type = VAL_STRING; + var_count++; + return 0; + } else if (strcmp(func_name, "strcat") == 0) { + const char* str1 = get_string_value(); + consume(TOKEN_COMMA, "Expected ','"); + const char* str2 = get_string_value(); + consume(TOKEN_RPAREN, "Expected ')'"); + for (int i = 0; i < var_count; i++) { + if (strcmp(vars[i].name, "__tmp_str_result") == 0) { + strncpy(vars[i].value.str_value, str1, MAX_STRING - 1); + strncat(vars[i].value.str_value, str2, MAX_STRING - strlen(vars[i].value.str_value) - 1); + vars[i].value.type = VAL_STRING; + return 0; + } + } + strncpy(vars[var_count].name, "__tmp_str_result", 63); + strncpy(vars[var_count].value.str_value, str1, MAX_STRING - 1); + strncat(vars[var_count].value.str_value, str2, MAX_STRING - strlen(vars[var_count].value.str_value) - 1); + vars[var_count].value.type = VAL_STRING; + var_count++; + return 0; + } else if (strcmp(func_name, "sizeof") == 0) { + consume(TOKEN_IDENTIFIER, "Expected variable name"); + const char* name = tokens[current_token - 1].value; + consume(TOKEN_RPAREN, "Expected ')'"); + Value* v = find_variable(name); + if (!v) error("Undefined variable: %s", name); + if (v->type == VAL_STRING) { + return strlen(v->str_value); + } else if (v->type == VAL_ARRAY) { + return v->arr_size; + } + return sizeof(double); + } else if (strcmp(func_name, "malloc") == 0) { + int size = (int)evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + for (int i = 0; i < var_count; i++) { + if (strcmp(vars[i].name, "__tmp_arr_result") == 0) { + vars[i].value.type = VAL_ARRAY; + vars[i].value.arr_size = size; + for (int j = 0; j < size; j++) { + vars[i].value.arr_value[j] = 0; + } + return 0; + } + } + strncpy(vars[var_count].name, "__tmp_arr_result", 63); + vars[var_count].value.type = VAL_ARRAY; + vars[var_count].value.arr_size = size; + for (int j = 0; j < size; j++) { + vars[var_count].value.arr_value[j] = 0; + } + var_count++; + return 0; + } else if (strcmp(func_name, "memcpy") == 0) { + consume(TOKEN_IDENTIFIER, "Expected destination array"); + const char* dest_name = tokens[current_token - 1].value; + consume(TOKEN_COMMA, "Expected ','"); + consume(TOKEN_IDENTIFIER, "Expected source array"); + const char* src_name = tokens[current_token - 1].value; + consume(TOKEN_COMMA, "Expected ','"); + int count = (int)evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + Value* dest = find_variable(dest_name); + Value* src = find_variable(src_name); + if (!dest || dest->type != VAL_ARRAY) error("Invalid destination array"); + if (!src || src->type != VAL_ARRAY) error("Invalid source array"); + for (int i = 0; i < count && i < dest->arr_size && i < src->arr_size; i++) { + dest->arr_value[i] = src->arr_value[i]; + } + return 0; + } else if (strcmp(func_name, "memcmp") == 0) { + consume(TOKEN_IDENTIFIER, "Expected first array"); + const char* name1 = tokens[current_token - 1].value; + consume(TOKEN_COMMA, "Expected ','"); + consume(TOKEN_IDENTIFIER, "Expected second array"); + const char* name2 = tokens[current_token - 1].value; + consume(TOKEN_COMMA, "Expected ','"); + int count = (int)evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + Value* arr1 = find_variable(name1); + Value* arr2 = find_variable(name2); + if (!arr1 || arr1->type != VAL_ARRAY) error("Invalid first array"); + if (!arr2 || arr2->type != VAL_ARRAY) error("Invalid second array"); + for (int i = 0; i < count && i < arr1->arr_size && i < arr2->arr_size; i++) { + if (arr1->arr_value[i] != arr2->arr_value[i]) { + return arr1->arr_value[i] - arr2->arr_value[i]; + } + } + return 0; + } else { + int func_idx = find_function(func_name); + if (func_idx >= 0) { + int saved_var_count = var_count; + for (int i = 0; i < funcs[func_idx].param_count; i++) { + if (i > 0) consume(TOKEN_COMMA, "Expected ','"); + strncpy(vars[var_count].name, funcs[func_idx].params[i], 63); + vars[var_count].value.num_value = evaluate(); + vars[var_count].value.type = VAL_NUMBER; + var_count++; + } + consume(TOKEN_RPAREN, "Expected ')'"); + + int saved_token = current_token; + current_token = funcs[func_idx].body_start; + while (current_token < funcs[func_idx].body_end && peek().type != TOKEN_EOF) { + if (execute_statement() == 1) break; + } + current_token = saved_token; + + double result = 0; + for (int i = var_count - 1; i >= 0; i--) { + if (strcmp(vars[i].name, "__return_value") == 0) { + result = vars[i].value.num_value; + break; + } + } + var_count = saved_var_count; + return result; + } + error("Undefined function: %s", func_name); + } + return 0; +} + +double parse_primary() { + if (match(TOKEN_NUMBER)) { + return atof(tokens[current_token - 1].value); + } + if (match(TOKEN_STRING)) { + error("Cannot use string directly in expression"); + } + if (match(TOKEN_IDENTIFIER)) { + const char* name = tokens[current_token - 1].value; + if (peek().type == TOKEN_LPAREN) { + return parse_call(name); + } + if (peek().type == TOKEN_LBRACKET) { + Value* v = find_variable(name); + if (!v) error("Undefined variable: %s", name); + advance(); + int index = (int)evaluate(); + consume(TOKEN_RBRACKET, "Expected ']'"); + return parse_array_access(v, index); + } + if (peek().type == TOKEN_DOT) { + Value* v = find_variable(name); + if (!v) error("Undefined variable: %s", name); + if (v->type != VAL_STRUCT) { + error("Cannot access field of non-struct variable"); + } + advance(); + consume(TOKEN_IDENTIFIER, "Expected field name"); + const char* field_name = tokens[current_token - 1].value; + int struct_idx = v->struct_type; + for (int i = 0; i < structs[struct_idx].field_count; i++) { + if (strcmp(structs[struct_idx].fields[i], field_name) == 0) { + return (double)i; + } + } + error("Undefined field: %s", field_name); + } + Value* v = find_variable(name); + if (!v) error("Undefined variable: %s", name); + if (v->type == VAL_STRING) { + error("Cannot use string variable in numeric expression"); + } + return v->num_value; + } + if (match(TOKEN_LPAREN)) { + double val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + return val; + } + error("Expected expression (got '%s')", peek().value); + return 0; +} + +double parse_unary() { + if (match(TOKEN_NOT)) { + return parse_unary() == 0 ? 1 : 0; + } + if (match(TOKEN_MINUS)) { + return -parse_unary(); + } + if (match(TOKEN_PLUS)) { + return parse_unary(); + } + if (match(TOKEN_BIT_NOT)) { + return ~(int)parse_unary(); + } + return parse_primary(); +} + +double parse_factor() { + double left = parse_unary(); + while (match(TOKEN_MULTIPLY) || match(TOKEN_DIVIDE) || match(TOKEN_MODULO)) { + OraTokenType op = tokens[current_token - 1].type; + double right = parse_unary(); + if (op == TOKEN_MULTIPLY) left *= right; + else if (op == TOKEN_DIVIDE) { + if (right == 0) error("Division by zero"); + left /= right; + } else { + if (right == 0) error("Modulo by zero"); + left = fmod(left, right); + } + } + return left; +} + +double parse_term() { + double left = parse_factor(); + while (match(TOKEN_PLUS) || match(TOKEN_MINUS)) { + OraTokenType op = tokens[current_token - 1].type; + double right = parse_factor(); + if (op == TOKEN_PLUS) left += right; + else left -= right; + } + return left; +} + +double parse_shift() { + double left = parse_term(); + while (match(TOKEN_SHIFT_LEFT) || match(TOKEN_SHIFT_RIGHT)) { + OraTokenType op = tokens[current_token - 1].type; + double right = parse_term(); + if (op == TOKEN_SHIFT_LEFT) left = (int)left << (int)right; + else left = (int)left >> (int)right; + } + return left; +} + +double parse_bit_and() { + double left = parse_shift(); + while (match(TOKEN_BIT_AND)) { + double right = parse_shift(); + left = (int)left & (int)right; + } + return left; +} + +double parse_bit_xor() { + double left = parse_bit_and(); + while (match(TOKEN_BIT_XOR)) { + double right = parse_bit_and(); + left = (int)left ^ (int)right; + } + return left; +} + +double parse_bit_or() { + double left = parse_bit_xor(); + while (match(TOKEN_BIT_OR)) { + double right = parse_bit_xor(); + left = (int)left | (int)right; + } + return left; +} + +double parse_comparison() { + double left = parse_bit_or(); + while (1) { + if (match(TOKEN_EQUAL)) { + double right = parse_bit_or(); + left = (left == right) ? 1 : 0; + } else if (match(TOKEN_NOT_EQUAL)) { + double right = parse_bit_or(); + left = (left != right) ? 1 : 0; + } else if (match(TOKEN_GREATER)) { + double right = parse_bit_or(); + left = (left > right) ? 1 : 0; + } else if (match(TOKEN_LESS)) { + double right = parse_bit_or(); + left = (left < right) ? 1 : 0; + } else if (match(TOKEN_GREATER_EQUAL)) { + double right = parse_bit_or(); + left = (left >= right) ? 1 : 0; + } else if (match(TOKEN_LESS_EQUAL)) { + double right = parse_bit_or(); + left = (left <= right) ? 1 : 0; + } else { + break; + } + } + return left; +} + +double parse_logic_and() { + double left = parse_comparison(); + while (match(TOKEN_AND)) { + double right = parse_comparison(); + left = (left != 0 && right != 0) ? 1 : 0; + } + return left; +} + +double parse_logic_or() { + double left = parse_logic_and(); + while (match(TOKEN_OR)) { + double right = parse_logic_and(); + left = (left != 0 || right != 0) ? 1 : 0; + } + return left; +} + +double evaluate() { + return parse_logic_or(); +} + +void execute_print() { + consume(TOKEN_LPAREN, "Expected '(' after print"); + int first = 1; + int newline = print_newline; + + while (!match(TOKEN_RPAREN)) { + if (!first) consume(TOKEN_COMMA, "Expected ',' between arguments"); + + if (match(TOKEN_STRING)) { + const char* str = tokens[current_token - 1].value; + if (strcmp(str, "nel") == 0) { + newline = 0; + first = 0; + continue; + } + if (!first) printf(" "); + printf("%s", str); + } else if (match(TOKEN_NUMBER)) { + if (!first) printf(" "); + printf("%s", tokens[current_token - 1].value); + } else if (match(TOKEN_BIT_NOT) || match(TOKEN_MINUS) || match(TOKEN_PLUS) || match(TOKEN_NOT)) { + current_token--; + double val = evaluate(); + if (!first) printf(" "); + printf("%g", val); + } else if (match(TOKEN_IDENTIFIER)) { + const char* name = tokens[current_token - 1].value; + if (peek().type == TOKEN_PLUS || peek().type == TOKEN_MINUS || + peek().type == TOKEN_MULTIPLY || peek().type == TOKEN_DIVIDE || + peek().type == TOKEN_MODULO || peek().type == TOKEN_BIT_AND || + peek().type == TOKEN_BIT_OR || peek().type == TOKEN_BIT_XOR || + peek().type == TOKEN_SHIFT_LEFT || peek().type == TOKEN_SHIFT_RIGHT) { + current_token--; + double val = evaluate(); + if (!first) printf(" "); + printf("%g", val); + } else if (peek().type == TOKEN_BIT_NOT) { + current_token--; + double val = evaluate(); + if (!first) printf(" "); + printf("%g", val); + } else if (peek().type == TOKEN_LBRACKET) { + Value* v = find_variable(name); + if (!v) error("Undefined variable: %s", name); + advance(); + int index = (int)evaluate(); + consume(TOKEN_RBRACKET, "Expected ']'"); + if (!first) printf(" "); + printf("%g", parse_array_access(v, index)); + } else if (peek().type == TOKEN_LPAREN) { + int func_idx = find_function(name); + if (func_idx >= 0) { + consume(TOKEN_LPAREN, "Expected '('"); + int saved_var_count = var_count; + for (int i = 0; i < funcs[func_idx].param_count; i++) { + if (i > 0) consume(TOKEN_COMMA, "Expected ','"); + strncpy(vars[var_count].name, funcs[func_idx].params[i], 63); + vars[var_count].value.num_value = evaluate(); + vars[var_count].value.type = VAL_NUMBER; + var_count++; + } + consume(TOKEN_RPAREN, "Expected ')'"); + + int saved_token = current_token; + current_token = funcs[func_idx].body_start; + while (current_token < funcs[func_idx].body_end && peek().type != TOKEN_EOF) { + execute_statement(); + } + current_token = saved_token; + var_count = saved_var_count; + + for (int i = var_count - 1; i >= 0; i--) { + if (strcmp(vars[i].name, "__return_value") == 0) { + if (!first) printf(" "); + printf("%g", vars[i].value.num_value); + break; + } + } + } else { + if (!first) printf(" "); + printf("%g", parse_call(name)); + } + } else { + int found = 0; + for (int i = 0; i < var_count; i++) { + if (strcmp(vars[i].name, name) == 0) { + if (!first) printf(" "); + if (vars[i].value.type == VAL_STRING) { + printf("%s", vars[i].value.str_value); + } else if (vars[i].value.type == VAL_ARRAY) { + printf("["); + for (int j = 0; j < vars[i].value.arr_size; j++) { + if (j > 0) printf(", "); + printf("%g", vars[i].value.arr_value[j]); + } + printf("]"); + } else { + printf("%g", vars[i].value.num_value); + } + found = 1; + break; + } + } + if (!found) error("Undefined variable: %s", name); + } + } else { + error("Unexpected token in print: '%s'", peek().value); + } + first = 0; + } + + if (newline) { + printf("\n"); + } + fflush(stdout); +} + +void execute_var() { + consume(TOKEN_IDENTIFIER, "Expected variable name"); + const char* name = tokens[current_token - 1].value; + + if (var_count >= MAX_VARS) { + error("Too many variables (max %d)", MAX_VARS); + } + + Variable* v = &vars[var_count]; + strncpy(v->name, name, 63); + v->name[63] = '\0'; + var_count++; + + if (match(TOKEN_ASSIGN)) { + if (match(TOKEN_STRING)) { + strncpy(v->value.str_value, tokens[current_token - 1].value, MAX_STRING - 1); + v->value.str_value[MAX_STRING - 1] = '\0'; + v->value.type = VAL_STRING; + v->value.num_value = 0; + } else if (match(TOKEN_LBRACKET)) { + v->value.type = VAL_ARRAY; + v->value.arr_size = 0; + while (!match(TOKEN_RBRACKET)) { + if (v->value.arr_size > 0) consume(TOKEN_COMMA, "Expected ',' between array elements"); + v->value.arr_value[v->value.arr_size++] = evaluate(); + if (v->value.arr_size >= MAX_ARRAY_SIZE) { + error("Array size exceeds maximum (%d)", MAX_ARRAY_SIZE); + } + } + } else { + v->value.num_value = evaluate(); + v->value.type = VAL_NUMBER; + v->value.str_value[0] = '\0'; + for (int i = 0; i < var_count; i++) { + if (strcmp(vars[i].name, "__tmp_arr_result") == 0) { + v->value.type = vars[i].value.type; + v->value.arr_size = vars[i].value.arr_size; + for (int j = 0; j < vars[i].value.arr_size; j++) { + v->value.arr_value[j] = vars[i].value.arr_value[j]; + } + break; + } + } + } + } else { + v->value.type = VAL_NUMBER; + v->value.num_value = 0; + v->value.str_value[0] = '\0'; + } + if (!in_for_loop_update) { + consume(TOKEN_SEMICOLON, "Expected ';' after variable declaration"); + } +} + +static void skip_block() { + int depth = 1; + while (depth > 0 && peek().type != TOKEN_EOF) { + Token t = advance(); + if (t.type == TOKEN_LBRACE) depth++; + if (t.type == TOKEN_RBRACE) depth--; + } +} + +int execute_if() { + consume(TOKEN_LPAREN, "Expected '(' after 'if'"); + double cond = evaluate(); + consume(TOKEN_RPAREN, "Expected ')' after condition"); + + if (match(TOKEN_LBRACE)) { + if (cond != 0) { + while (!match(TOKEN_RBRACE) && peek().type != TOKEN_EOF) { + int result = execute_statement(); + if (result == 2 || result == 3) { + return result; + } + } + } else { + skip_block(); + } + } else { + if (cond != 0) { + int result = execute_statement(); + if (result == 2 || result == 3) { + return result; + } + } else { + while (peek().type != TOKEN_SEMICOLON && peek().type != TOKEN_EOF) { + advance(); + } + consume(TOKEN_SEMICOLON, "Expected ';'"); + } + } + + if (match(TOKEN_ELSE)) { + if (match(TOKEN_LBRACE)) { + if (cond == 0) { + while (!match(TOKEN_RBRACE) && peek().type != TOKEN_EOF) { + int result = execute_statement(); + if (result == 2 || result == 3) { + return result; + } + } + } else { + skip_block(); + } + } else { + if (cond == 0) { + int result = execute_statement(); + if (result == 2 || result == 3) { + return result; + } + } else { + while (peek().type != TOKEN_SEMICOLON && peek().type != TOKEN_EOF) { + advance(); + } + consume(TOKEN_SEMICOLON, "Expected ';'"); + } + } + } + return 0; +} + +void execute_while() { + consume(TOKEN_LPAREN, "Expected '(' after 'while'"); + + int cond_start = current_token; + evaluate(); + consume(TOKEN_RPAREN, "Expected ')' after condition"); + consume(TOKEN_LBRACE, "Expected '{' after while condition"); + + int body_start = current_token; + skip_block(); + int body_end = current_token; + + int saved_loop_depth = loop_depth; + loop_depth++; + + while (1) { + current_token = cond_start; + current_line = tokens[cond_start].line; + double cond = evaluate(); + + if (cond == 0) { + current_token = body_end; + break; + } + + current_token = body_start; + while (current_token < body_end && peek().type != TOKEN_EOF && peek().type != TOKEN_RBRACE) { + int result = execute_statement(); + if (result == 2) { + break; + } + if (result == 3) { + break; + } + if (result == 1) { + loop_depth = saved_loop_depth; + return; + } + } + } + + loop_depth = saved_loop_depth; +} + +void execute_for() { + consume(TOKEN_LPAREN, "Expected '(' after 'for'"); + + int init_start = current_token; + while (peek().type != TOKEN_SEMICOLON && peek().type != TOKEN_EOF) { + advance(); + } + consume(TOKEN_SEMICOLON, "Expected ';'"); + + int cond_start = current_token; + while (peek().type != TOKEN_SEMICOLON && peek().type != TOKEN_EOF) { + advance(); + } + consume(TOKEN_SEMICOLON, "Expected ';'"); + + int update_start = current_token; + while (peek().type != TOKEN_RPAREN && peek().type != TOKEN_EOF) { + advance(); + } + consume(TOKEN_RPAREN, "Expected ')'"); + consume(TOKEN_LBRACE, "Expected '{'"); + + int body_start = current_token; + skip_block(); + int body_end = current_token; + + int saved_token = current_token; + current_token = init_start; + in_for_loop_update = 1; + while (peek().type != TOKEN_SEMICOLON && peek().type != TOKEN_EOF) { + execute_statement(); + } + in_for_loop_update = 0; + current_token = saved_token; + + int saved_loop_depth = loop_depth; + loop_depth++; + + while (1) { + double cond = 0; + int temp_token = cond_start; + current_token = cond_start; + current_line = tokens[cond_start].line; + + int paren_count = 0; + while (temp_token < token_count) { + if (tokens[temp_token].type == TOKEN_LPAREN) paren_count++; + if (tokens[temp_token].type == TOKEN_RPAREN) paren_count--; + if (tokens[temp_token].type == TOKEN_SEMICOLON && paren_count == 0) break; + temp_token++; + } + + cond = evaluate(); + + if (cond == 0) { + break; + } + + current_token = body_start; + while (current_token < body_end && peek().type != TOKEN_EOF) { + int result = execute_statement(); + if (result == 2) { + loop_depth = saved_loop_depth; + return; + } + if (result == 1) { + loop_depth = saved_loop_depth; + return; + } + } + + current_token = update_start; + in_for_loop_update = 1; + while (peek().type != TOKEN_RPAREN && peek().type != TOKEN_EOF) { + execute_statement(); + } + in_for_loop_update = 0; + } + + loop_depth = saved_loop_depth; +} + +extern int in_for_loop_update; + +void execute_function() { + consume(TOKEN_IDENTIFIER, "Expected function name"); + const char* name = tokens[current_token - 1].value; + consume(TOKEN_LPAREN, "Expected '(' after function name"); + + int func_idx = func_count; + strncpy(funcs[func_idx].name, name, 63); + funcs[func_idx].param_count = 0; + + while (!match(TOKEN_RPAREN)) { + if (funcs[func_idx].param_count > 0) consume(TOKEN_COMMA, "Expected ','"); + consume(TOKEN_IDENTIFIER, "Expected parameter name"); + strncpy(funcs[func_idx].params[funcs[func_idx].param_count], + tokens[current_token - 1].value, 63); + funcs[func_idx].param_count++; + if (funcs[func_idx].param_count >= 8) { + error("Too many parameters (max 8)"); + } + } + + consume(TOKEN_LBRACE, "Expected '{'"); + funcs[func_idx].body_start = current_token; + + skip_block(); + funcs[func_idx].body_end = current_token; + func_count++; +} + +void execute_switch() { + consume(TOKEN_LPAREN, "Expected '('"); + double switch_val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + consume(TOKEN_LBRACE, "Expected '{'"); + + int saved_switch_active = switch_active; + switch_active = 1; + + int body_start = current_token; + skip_block(); + int body_end = current_token; + + int saved_token = current_token; + current_token = body_start; + + int should_execute = 0; + while (current_token < body_end && peek().type != TOKEN_EOF) { + if (match(TOKEN_CASE)) { + double case_val = evaluate(); + consume(TOKEN_COLON, "Expected ':' after case"); + if (!should_execute && case_val == switch_val) { + should_execute = 1; + } + } else if (match(TOKEN_DEFAULT)) { + consume(TOKEN_COLON, "Expected ':' after default"); + if (!should_execute) { + should_execute = 1; + } + } else { + if (should_execute) { + int result = execute_statement(); + if (result == 2) { + break; + } + } else { + advance(); + } + } + } + + current_token = saved_token; + switch_active = saved_switch_active; +} + +void execute_do_while() { + consume(TOKEN_LBRACE, "Expected '{'"); + + int body_start = current_token; + skip_block(); + int body_end = current_token; + + consume(TOKEN_WHILE, "Expected 'while'"); + consume(TOKEN_LPAREN, "Expected '('"); + + int cond_start = current_token; + evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + consume(TOKEN_SEMICOLON, "Expected ';'"); + + int saved_loop_depth = loop_depth; + loop_depth++; + + current_token = body_start; + while (current_token < body_end && peek().type != TOKEN_EOF && peek().type != TOKEN_RBRACE) { + int result = execute_statement(); + if (result == 2) { + break; + } + if (result == 3) { + break; + } + if (result == 1) { + loop_depth = saved_loop_depth; + return; + } + } + + while (1) { + current_token = cond_start; + current_line = tokens[cond_start].line; + double cond = evaluate(); + + if (cond == 0) { + break; + } + + current_token = body_start; + while (current_token < body_end && peek().type != TOKEN_EOF && peek().type != TOKEN_RBRACE) { + int result = execute_statement(); + if (result == 2) { + break; + } + if (result == 3) { + break; + } + if (result == 1) { + loop_depth = saved_loop_depth; + return; + } + } + } + + loop_depth = saved_loop_depth; +} + +void execute_struct() { + consume(TOKEN_IDENTIFIER, "Expected struct name"); + const char* name = tokens[current_token - 1].value; + consume(TOKEN_LBRACE, "Expected '{'"); + + int struct_idx = struct_count; + strncpy(structs[struct_idx].name, name, 63); + structs[struct_idx].field_count = 0; + + while (peek().type != TOKEN_RBRACE && peek().type != TOKEN_EOF) { + if (peek().type == TOKEN_SEMICOLON) { + advance(); + continue; + } + consume(TOKEN_IDENTIFIER, "Expected field name"); + strncpy(structs[struct_idx].fields[structs[struct_idx].field_count], + tokens[current_token - 1].value, 63); + structs[struct_idx].field_count++; + if (structs[struct_idx].field_count >= 16) { + error("Too many fields in struct (max 16)"); + } + if (peek().type == TOKEN_SEMICOLON) { + advance(); + } + } + + consume(TOKEN_RBRACE, "Expected '}'"); + struct_count++; + if (match(TOKEN_SEMICOLON)) { + } +} + +void execute_enum() { + consume(TOKEN_IDENTIFIER, "Expected enum name"); + const char* name = tokens[current_token - 1].value; + consume(TOKEN_LBRACE, "Expected '{'"); + + int enum_idx = enum_count; + strncpy(enums[enum_idx].name, name, 63); + enums[enum_idx].value_count = 0; + + while (peek().type != TOKEN_RBRACE && peek().type != TOKEN_EOF) { + consume(TOKEN_IDENTIFIER, "Expected enum value"); + strncpy(enums[enum_idx].values[enums[enum_idx].value_count], + tokens[current_token - 1].value, 63); + enums[enum_idx].value_count++; + if (enums[enum_idx].value_count >= 16) { + error("Too many values in enum (max 16)"); + } + if (match(TOKEN_COMMA)) { + continue; + } + break; + } + + consume(TOKEN_RBRACE, "Expected '}'"); + enum_count++; + if (match(TOKEN_SEMICOLON)) { + } +} + +void execute_typedef() { + consume(TOKEN_IDENTIFIER, "Expected original type"); + const char* orig_type = tokens[current_token - 1].value; + consume(TOKEN_IDENTIFIER, "Expected new type name"); + const char* new_name = tokens[current_token - 1].value; + consume(TOKEN_SEMICOLON, "Expected ';'"); + + int struct_idx = find_struct(orig_type); + if (struct_idx >= 0) { + strncpy(structs[struct_count].name, new_name, 63); + structs[struct_count].field_count = structs[struct_idx].field_count; + for (int i = 0; i < structs[struct_idx].field_count; i++) { + strncpy(structs[struct_count].fields[i], structs[struct_idx].fields[i], 63); + } + struct_count++; + } else { + int enum_idx = find_enum(orig_type); + if (enum_idx >= 0) { + strncpy(enums[enum_count].name, new_name, 63); + enums[enum_count].value_count = enums[enum_idx].value_count; + for (int i = 0; i < enums[enum_idx].value_count; i++) { + strncpy(enums[enum_count].values[i], enums[enum_idx].values[i], 63); + } + enum_count++; + } + } +} + +void execute_goto() { + consume(TOKEN_IDENTIFIER, "Expected label name"); + const char* name = tokens[current_token - 1].value; + consume(TOKEN_SEMICOLON, "Expected ';'"); + + int label_idx = find_label(name); + if (label_idx < 0) { + error("Undefined label: %s", name); + } + current_token = labels[label_idx].token_index; +} + +void execute_new() { + consume(TOKEN_IDENTIFIER, "Expected type name"); + const char* type_name = tokens[current_token - 1].value; + consume(TOKEN_SEMICOLON, "Expected ';'"); + + int struct_idx = find_struct(type_name); + if (struct_idx >= 0) { + strncpy(vars[var_count].name, "__tmp_struct_result", 63); + vars[var_count].value.type = VAL_STRUCT; + vars[var_count].value.struct_type = struct_idx; + var_count++; + } +} + +void execute_delete() { + consume(TOKEN_IDENTIFIER, "Expected variable name"); + const char* name = tokens[current_token - 1].value; + consume(TOKEN_SEMICOLON, "Expected ';'"); + + for (int i = 0; i < var_count; i++) { + if (strcmp(vars[i].name, name) == 0) { + for (int j = i; j < var_count - 1; j++) { + vars[j] = vars[j + 1]; + } + var_count--; + return; + } + } + error("Undefined variable: %s", name); +} + +void execute_try_catch() { + consume(TOKEN_LBRACE, "Expected '{'"); + + int try_start = current_token; + skip_block(); + int try_end = current_token; + + consume(TOKEN_CATCH, "Expected 'catch'"); + consume(TOKEN_LPAREN, "Expected '('"); + consume(TOKEN_IDENTIFIER, "Expected exception variable name"); + const char* exc_name = tokens[current_token - 1].value; + consume(TOKEN_RPAREN, "Expected ')'"); + consume(TOKEN_LBRACE, "Expected '{'"); + + int catch_start = current_token; + skip_block(); + int catch_end = current_token; + + int saved_var_count = var_count; + + current_token = try_start; + while (current_token < try_end && peek().type != TOKEN_EOF && peek().type != TOKEN_RBRACE) { + int result = execute_statement(); + if (result == 1) { + var_count = saved_var_count; + return; + } + } + + var_count = saved_var_count; +} + +void execute_throw() { + evaluate(); + consume(TOKEN_SEMICOLON, "Expected ';'"); +} + +void execute_return() { + if (peek().type == TOKEN_SEMICOLON || peek().type == TOKEN_RBRACE) { + consume(TOKEN_SEMICOLON, "Expected ';' after return"); + return; + } + double val = evaluate(); + consume(TOKEN_SEMICOLON, "Expected ';' after return"); + for (int i = var_count - 1; i >= 0; i--) { + if (strcmp(vars[i].name, "__return_value") == 0) { + vars[i].value.num_value = val; + vars[i].value.type = VAL_NUMBER; + return; + } + } + strncpy(vars[var_count].name, "__return_value", 63); + vars[var_count].value.num_value = val; + vars[var_count].value.type = VAL_NUMBER; + var_count++; +} + +int execute_statement() { + Token t = peek(); + + switch (t.type) { + case TOKEN_PRINT: + advance(); + execute_print(); + consume(TOKEN_SEMICOLON, "Expected ';' after print"); + return 0; + + case TOKEN_VAR: + advance(); + execute_var(); + return 0; + + case TOKEN_IF: + advance(); + return execute_if(); + + case TOKEN_WHILE: + advance(); + execute_while(); + return 0; + + case TOKEN_FOR: + advance(); + execute_for(); + return 0; + + case TOKEN_FUNCTION: + advance(); + execute_function(); + return 0; + + case TOKEN_SWITCH: + advance(); + execute_switch(); + return 0; + + case TOKEN_DO: + advance(); + execute_do_while(); + return 0; + + case TOKEN_BREAK: + advance(); + consume(TOKEN_SEMICOLON, "Expected ';' after break"); + if (loop_depth == 0 && !switch_active) { + error("break outside loop or switch"); + } + return 2; + + case TOKEN_CONTINUE: + advance(); + consume(TOKEN_SEMICOLON, "Expected ';' after continue"); + if (loop_depth == 0) { + error("continue outside loop"); + } + return 3; + + case TOKEN_RETURN: + advance(); + execute_return(); + return 1; + + case TOKEN_STRUCT: + advance(); + execute_struct(); + return 0; + + case TOKEN_ENUM: + advance(); + execute_enum(); + return 0; + + case TOKEN_TYPEDEF: + advance(); + execute_typedef(); + return 0; + + case TOKEN_GOTO: + advance(); + execute_goto(); + return 0; + + case TOKEN_NEW: + advance(); + execute_new(); + return 0; + + case TOKEN_DELETE: + advance(); + execute_delete(); + return 0; + + case TOKEN_TRY: + advance(); + execute_try_catch(); + return 0; + + case TOKEN_THROW: + advance(); + execute_throw(); + return 0; + + case TOKEN_LABEL: + advance(); + return 0; + + case TOKEN_IDENTIFIER: { + advance(); + if (match(TOKEN_ASSIGN)) { + const char* name = tokens[current_token - 2].value; + for (int i = 0; i < var_count; i++) { + if (strcmp(vars[i].name, name) == 0) { + if (match(TOKEN_STRING)) { + strncpy(vars[i].value.str_value, tokens[current_token - 1].value, MAX_STRING - 1); + vars[i].value.str_value[MAX_STRING - 1] = '\0'; + vars[i].value.type = VAL_STRING; + } else if (match(TOKEN_LBRACKET)) { + vars[i].value.type = VAL_ARRAY; + vars[i].value.arr_size = 0; + while (!match(TOKEN_RBRACKET)) { + if (vars[i].value.arr_size > 0) consume(TOKEN_COMMA, "Expected ','"); + vars[i].value.arr_value[vars[i].value.arr_size++] = evaluate(); + } + } else { + vars[i].value.num_value = evaluate(); + vars[i].value.type = VAL_NUMBER; + } + if (!in_for_loop_update) { + consume(TOKEN_SEMICOLON, "Expected ';' after assignment"); + } + return 0; + } + } + error("Undefined variable: %s", name); + } else if (match(TOKEN_PLUS_ASSIGN)) { + const char* name = tokens[current_token - 2].value; + for (int i = 0; i < var_count; i++) { + if (strcmp(vars[i].name, name) == 0) { + if (vars[i].value.type == VAL_STRING) { + const char* str = tokens[current_token].value; + strncat(vars[i].value.str_value, str, MAX_STRING - strlen(vars[i].value.str_value) - 1); + } else { + vars[i].value.num_value += evaluate(); + } + if (!in_for_loop_update) { + consume(TOKEN_SEMICOLON, "Expected ';' after assignment"); + } + return 0; + } + } + error("Undefined variable: %s", name); + } else if (match(TOKEN_MINUS_ASSIGN)) { + const char* name = tokens[current_token - 2].value; + for (int i = 0; i < var_count; i++) { + if (strcmp(vars[i].name, name) == 0) { + vars[i].value.num_value -= evaluate(); + if (!in_for_loop_update) { + consume(TOKEN_SEMICOLON, "Expected ';' after assignment"); + } + return 0; + } + } + error("Undefined variable: %s", name); + } else if (match(TOKEN_MULTIPLY_ASSIGN)) { + const char* name = tokens[current_token - 2].value; + for (int i = 0; i < var_count; i++) { + if (strcmp(vars[i].name, name) == 0) { + vars[i].value.num_value *= evaluate(); + if (!in_for_loop_update) { + consume(TOKEN_SEMICOLON, "Expected ';' after assignment"); + } + return 0; + } + } + error("Undefined variable: %s", name); + } else if (match(TOKEN_DIVIDE_ASSIGN)) { + const char* name = tokens[current_token - 2].value; + for (int i = 0; i < var_count; i++) { + if (strcmp(vars[i].name, name) == 0) { + double right = evaluate(); + if (right == 0) error("Division by zero"); + vars[i].value.num_value /= right; + if (!in_for_loop_update) { + consume(TOKEN_SEMICOLON, "Expected ';' after assignment"); + } + return 0; + } + } + error("Undefined variable: %s", name); + } else if (match(TOKEN_MODULO_ASSIGN)) { + const char* name = tokens[current_token - 2].value; + for (int i = 0; i < var_count; i++) { + if (strcmp(vars[i].name, name) == 0) { + double right = evaluate(); + if (right == 0) error("Modulo by zero"); + vars[i].value.num_value = fmod(vars[i].value.num_value, right); + if (!in_for_loop_update) { + consume(TOKEN_SEMICOLON, "Expected ';' after assignment"); + } + return 0; + } + } + error("Undefined variable: %s", name); + } else if (match(TOKEN_INCREMENT)) { + const char* name = tokens[current_token - 2].value; + for (int i = 0; i < var_count; i++) { + if (strcmp(vars[i].name, name) == 0) { + vars[i].value.num_value++; + if (!in_for_loop_update) { + consume(TOKEN_SEMICOLON, "Expected ';' after increment"); + } + return 0; + } + } + error("Undefined variable: %s", name); + } else if (match(TOKEN_DECREMENT)) { + const char* name = tokens[current_token - 2].value; + for (int i = 0; i < var_count; i++) { + if (strcmp(vars[i].name, name) == 0) { + vars[i].value.num_value--; + if (!in_for_loop_update) { + consume(TOKEN_SEMICOLON, "Expected ';' after decrement"); + } + return 0; + } + } + error("Undefined variable: %s", name); + } else if (peek().type == TOKEN_LBRACKET) { + const char* name = tokens[current_token - 1].value; + for (int i = 0; i < var_count; i++) { + if (strcmp(vars[i].name, name) == 0) { + advance(); + int index = (int)evaluate(); + consume(TOKEN_RBRACKET, "Expected ']'"); + if (match(TOKEN_ASSIGN)) { + vars[i].value.arr_value[index] = evaluate(); + if (!in_for_loop_update) { + consume(TOKEN_SEMICOLON, "Expected ';'"); + } + } else { + printf("%g\n", vars[i].value.arr_value[index]); + } + return 0; + } + } + error("Undefined variable: %s", name); + } else if (peek().type == TOKEN_LPAREN) { + const char* name = tokens[current_token - 1].value; + int func_idx = find_function(name); + if (func_idx >= 0) { + consume(TOKEN_LPAREN, "Expected '('"); + int saved_var_count = var_count; + for (int i = 0; i < funcs[func_idx].param_count; i++) { + if (i > 0) consume(TOKEN_COMMA, "Expected ','"); + strncpy(vars[var_count].name, funcs[func_idx].params[i], 63); + vars[var_count].value.num_value = evaluate(); + vars[var_count].value.type = VAL_NUMBER; + var_count++; + } + consume(TOKEN_RPAREN, "Expected ')'"); + consume(TOKEN_SEMICOLON, "Expected ';'"); + + int saved_token = current_token; + current_token = funcs[func_idx].body_start; + while (current_token < funcs[func_idx].body_end && peek().type != TOKEN_EOF) { + if (execute_statement() == 1) break; + } + current_token = saved_token; + var_count = saved_var_count; + return 0; + } + + parse_call(name); + consume(TOKEN_SEMICOLON, "Expected ';'"); + return 0; + } + error("Unexpected identifier: %s", tokens[current_token - 1].value); + return 0; + } + + case TOKEN_EOF: + break; + + default: + error("Unexpected token: %s", t.value); + } + return 0; +} + +void parse_config(const char* source) { + print_newline = 1; + const char* p = source; + + while (*p) { + if (*p == ' ' || *p == '\t' || *p == '\r') { + p++; + continue; + } + if (*p == '\n') break; + if (*p == '#') { + while (*p && *p != '\n') p++; + continue; + } + if (*p == 's' && *(p+1) == 'g' && *(p+2) == '<') { + p += 3; + while (*p && *p != '>') { + if (strncmp(p, "nel", 3) == 0) { + print_newline = 0; + break; + } + p++; + } + break; + } + break; + } +} + +char* strip_config(const char* source) { + const char* p = source; + + while (*p) { + if (*p == ' ' || *p == '\t' || *p == '\r') { + p++; + continue; + } + if (*p == '\n') { + return strdup(source); + } + if (*p == '#') { + while (*p && *p != '\n') p++; + if (*p == '\n') p++; + continue; + } + if (*p == 's' && *(p+1) == 'g' && *(p+2) == '<') { + const char* start = p; + while (*p && *p != '\n') p++; + if (*p == '\n') p++; + + size_t prefix_len = start - source; + size_t suffix_len = strlen(p); + char* result = malloc(prefix_len + suffix_len + 1); + if (!result) error("Out of memory"); + + memcpy(result, source, prefix_len); + memcpy(result + prefix_len, p, suffix_len + 1); + return result; + } + return strdup(source); + } + return strdup(source); +} + +void run(const char* source) { + static int rand_seeded = 0; + if (!rand_seeded) { + srand(time(NULL)); + rand_seeded = 1; + } + + parse_config(source); + char* clean_source = strip_config(source); + tokenize(clean_source); + current_token = 0; + + while (peek().type != TOKEN_EOF) { + execute_statement(); + } + + free(clean_source); +} + +void run_file(const char* path) { + FILE* f = fopen(path, "r"); + if (!f) { + error("Cannot open file: %s", path); + } + + fseek(f, 0, SEEK_END); + long size = ftell(f); + if (size < 0) { + fclose(f); + error("Cannot read file: %s", path); + } + fseek(f, 0, SEEK_SET); + + char* source = malloc(size + 1); + if (!source) { + fclose(f); + error("Out of memory"); + } + + size_t read = fread(source, 1, size, f); + fclose(f); + source[read] = '\0'; + + run(source); + free(source); +} + +void run_repl() { + in_repl = 1; + printf("Oraset B-5 REPL\n"); + printf("Type 'exit' or 'quit' to exit.\n"); + printf("> "); + + char line[MAX_LINE]; + while (fgets(line, MAX_LINE, stdin)) { + line[strcspn(line, "\n")] = '\0'; + + if (strcmp(line, "exit") == 0 || strcmp(line, "quit") == 0) { + break; + } + if (strcmp(line, "") == 0) { + printf("> "); + continue; + } + + char* source = malloc(strlen(line) + 2); + strcpy(source, line); + strcat(source, "\n"); + + static int rand_seeded = 0; + if (!rand_seeded) { + srand(time(NULL)); + rand_seeded = 1; + } + + parse_config(source); + char* clean_source = strip_config(source); + tokenize(clean_source); + current_token = 0; + + while (peek().type != TOKEN_EOF) { + execute_statement(); + } + + free(clean_source); + free(source); + printf("> "); + } + + printf("\n"); + in_repl = 0; +} + +#define VERSION_URL "https://oraset.parlz.com/download/version.txt" +#define DOWNLOAD_BASE "https://oraset.parlz.com/download" + +static char* http_get(const char* url) { +#ifdef _WIN32 + HINTERNET hInternet = InternetOpenA("Oraset", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0); + if (!hInternet) return NULL; + + HINTERNET hUrl = InternetOpenUrlA(hInternet, url, NULL, 0, INTERNET_FLAG_RELOAD, 0); + if (!hUrl) { + InternetCloseHandle(hInternet); + return NULL; + } + + char* buffer = malloc(4096); + if (!buffer) { + InternetCloseHandle(hUrl); + InternetCloseHandle(hInternet); + return NULL; + } + + DWORD total_read = 0; + DWORD bytes_read; + char temp[1024]; + + while (InternetReadFile(hUrl, temp, sizeof(temp), &bytes_read) && bytes_read > 0) { + if (total_read + bytes_read >= 4095) { + char* new_buf = realloc(buffer, total_read + bytes_read + 4096); + if (!new_buf) { + free(buffer); + InternetCloseHandle(hUrl); + InternetCloseHandle(hInternet); + return NULL; + } + buffer = new_buf; + } + memcpy(buffer + total_read, temp, bytes_read); + total_read += bytes_read; + } + buffer[total_read] = '\0'; + + InternetCloseHandle(hUrl); + InternetCloseHandle(hInternet); + return buffer; +#else + char cmd[512]; + snprintf(cmd, sizeof(cmd), "curl -s \"%s\" 2>/dev/null", url); + FILE* fp = popen(cmd, "r"); + if (!fp) return NULL; + + char* buffer = malloc(4096); + if (!buffer) { + pclose(fp); + return NULL; + } + + size_t total_read = 0; + size_t bytes_read; + char temp[1024]; + + while ((bytes_read = fread(temp, 1, sizeof(temp), fp)) > 0) { + if (total_read + bytes_read >= 4095) { + char* new_buf = realloc(buffer, total_read + bytes_read + 4096); + if (!new_buf) { + free(buffer); + pclose(fp); + return NULL; + } + buffer = new_buf; + } + memcpy(buffer + total_read, temp, bytes_read); + total_read += bytes_read; + } + buffer[total_read] = '\0'; + pclose(fp); + return buffer; +#endif +} + +static int download_file(const char* url, const char* dest_path) { +#ifdef _WIN32 + HINTERNET hInternet = InternetOpenA("Oraset", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0); + if (!hInternet) return -1; + + HINTERNET hUrl = InternetOpenUrlA(hInternet, url, NULL, 0, INTERNET_FLAG_RELOAD, 0); + if (!hUrl) { + InternetCloseHandle(hInternet); + return -1; + } + + FILE* fp = fopen(dest_path, "wb"); + if (!fp) { + InternetCloseHandle(hUrl); + InternetCloseHandle(hInternet); + return -1; + } + + char temp[4096]; + DWORD bytes_read; + + while (InternetReadFile(hUrl, temp, sizeof(temp), &bytes_read) && bytes_read > 0) { + fwrite(temp, 1, bytes_read, fp); + } + + fclose(fp); + InternetCloseHandle(hUrl); + InternetCloseHandle(hInternet); + return 0; +#else + char cmd[1024]; + snprintf(cmd, sizeof(cmd), "curl -L -o \"%s\" \"%s\" 2>/dev/null", dest_path, url); + return system(cmd); +#endif +} + +static void get_system_info(char* os, char* ext) { +#ifdef _WIN32 + strcpy(os, "windows"); + strcpy(ext, ".exe"); +#elif defined(__APPLE__) + strcpy(os, "macos"); + strcpy(ext, ".pkg"); +#elif defined(__linux__) + strcpy(os, "linux"); + strcpy(ext, ".tar.gz"); +#else + strcpy(os, "unknown"); + strcpy(ext, ".tar.gz"); +#endif +} + +static int compare_versions(const char* v1, const char* v2) { + int major1 = 0, minor1 = 0, patch1 = 0, major2 = 0, minor2 = 0, patch2 = 0; + char suffix1[64] = "", suffix2[64] = ""; + + sscanf(v1, "%d.%d.%d-%63s", &major1, &minor1, &patch1, suffix1); + sscanf(v2, "%d.%d.%d-%63s", &major2, &minor2, &patch2, suffix2); + + if (major1 != major2) return major1 - major2; + if (minor1 != minor2) return minor1 - minor2; + if (patch1 != patch2) return patch1 - patch2; + + int s1 = (strstr(suffix1, "Release") || strlen(suffix1) == 0) ? 3 : + (strstr(suffix1, "Beta") ? 2 : 1); + int s2 = (strstr(suffix2, "Release") || strlen(suffix2) == 0) ? 3 : + (strstr(suffix2, "Beta") ? 2 : 1); + return s1 - s2; +} + +static void trim_newline(char* str) { + int len = strlen(str); + while (len > 0 && (str[len-1] == '\n' || str[len-1] == '\r')) { + str[--len] = '\0'; + } +} + +int update_oraset(const char* target_version) { + printf("Checking for updates...\n"); + + char* version_info = http_get(VERSION_URL); + if (!version_info) { + fprintf(stderr, "Error: Cannot fetch version information.\n"); + fprintf(stderr, "Please check your internet connection.\n"); + return 1; + } + + trim_newline(version_info); + char latest_version[128]; + strncpy(latest_version, version_info, sizeof(latest_version) - 1); + free(version_info); + + printf("Current version: %s\n", VERSION); + printf("Latest version: %s\n", latest_version); + + const char* version_to_install = target_version ? target_version : latest_version; + + if (target_version) { + printf("Target version specified: %s\n", version_to_install); + } + + char os[32], ext[16]; + get_system_info(os, ext); + printf("Detected OS: %s\n", os); + + char download_url[512]; + char filename[128]; + + if (strcmp(os, "windows") == 0) { + snprintf(filename, sizeof(filename), "Oraset-B5%s", ext); + snprintf(download_url, sizeof(download_url), "%s/exe/%s", DOWNLOAD_BASE, filename); + } else if (strcmp(os, "macos") == 0) { + snprintf(filename, sizeof(filename), "Oraset-B5%s", ext); + snprintf(download_url, sizeof(download_url), "%s/pkg/%s", DOWNLOAD_BASE, filename); + } else { + snprintf(filename, sizeof(filename), "Oraset-B5%s", ext); + snprintf(download_url, sizeof(download_url), "%s/deb/%s", DOWNLOAD_BASE, filename); + } + + printf("Downloading: %s\n", download_url); + + char temp_path[512]; +#ifdef _WIN32 + char temp_dir[MAX_PATH]; + GetTempPathA(MAX_PATH, temp_dir); + snprintf(temp_path, sizeof(temp_path), "%s%s", temp_dir, filename); +#else + snprintf(temp_path, sizeof(temp_path), "/tmp/%s", filename); +#endif + + if (download_file(download_url, temp_path) != 0) { + fprintf(stderr, "Error: Failed to download update.\n"); + return 1; + } + + printf("Downloaded to: %s\n", temp_path); + +#ifdef _WIN32 + printf("Please run the installer: %s\n", temp_path); +#else + if (strcmp(os, "macos") == 0) { + printf("Installing package...\n"); + char cmd[1024]; + snprintf(cmd, sizeof(cmd), "sudo installer -pkg \"%s\" -target /", temp_path); + system(cmd); + } else if (strcmp(os, "linux") == 0) { + printf("Extracting...\n"); + char cmd[1024]; + snprintf(cmd, sizeof(cmd), "cd /tmp && tar -xzf \"%s\" && sudo cp oraset /usr/local/bin/", temp_path); + system(cmd); + } + printf("Installation complete!\n"); +#endif + + return 0; +} + +int main(int argc, char** argv) { + if (argc == 1) { + printf("Oraset B-5\n"); + printf("Usage: oraset [options] + + \ No newline at end of file diff --git a/website/docs/md/getting-started.md b/website/docs/md/getting-started.md new file mode 100644 index 0000000..352cce3 --- /dev/null +++ b/website/docs/md/getting-started.md @@ -0,0 +1,99 @@ +# 入门指南 + +本指南将帮助您快速安装和配置 Oraset 编程语言 B-5 版本环境,并运行您的第一个程序。 + +## 安装 Oraset + +### macOS + +```bash +curl https://oraset.parlz.com/download/sh/install.sh | bash +``` + +### Linux + +```bash +curl https://oraset.parlz.com/download/sh/install-linux.sh | bash +``` + +或手动下载 oraset.deb,然后运行: +```bash +sudo dpkg -i oraset.deb +``` + +### Windows + +下载 oraset.exe 并添加到系统 PATH。 + +## 验证安装 + +```bash +oraset -v +``` + +应该显示:"B-5" + +## 运行第一个程序 + +### 步骤 1:创建脚本文件 + +创建一个名为 `hello.ora` 的文件,内容如下: + +```oraset +print("Hello, World!"); +``` + +### 步骤 2:运行脚本 + +```bash +oraset hello.ora +``` + +输出: + +``` +Hello, World! +``` + +## 交互模式 + +Oraset B-5 支持 REPL 交互模式: + +```bash +oraset -i +``` + +或 + +```bash +oraset --interactive +``` + +进入交互模式后,您可以直接输入代码并立即看到结果: + +``` +Oraset B-5 REPL +Type 'exit' or 'quit' to exit. +> var x = 10 +> print(x) +10 +> exit +``` + +## 常见问题 + +### Q: 命令找不到? + +A: 确保 oraset 已添加到系统 PATH,并重新打开终端。 + +### Q: 如何退出交互模式? + +A: 输入 `exit` 或 `quit` 即可退出。 + +### Q: 脚本执行完成后自动退出? + +A: 是的,脚本执行完成后自动退出,无需手动操作。 + +--- + +Oraset Project © 2026 \ No newline at end of file diff --git a/website/docs/md/language.md b/website/docs/md/language.md new file mode 100644 index 0000000..e6b7ac1 --- /dev/null +++ b/website/docs/md/language.md @@ -0,0 +1,212 @@ +# 语言参考 + +Oraset 编程语言 B-5 版本的完整语法参考文档,包含变量、数据类型、运算符和控制流等核心特性。 + +## 变量 + +### 声明变量 + +```oraset +var x = 10; +var name = "test"; +var pi = 3.14; +var flag; +``` + +### 修改变量 + +```oraset +x = 20; +name = "new name"; +``` + +### 复合赋值 + +```oraset +x += 5; +x -= 3; +x *= 2; +x /= 2; +x %= 3; +``` + +### 自增自减 + +```oraset +x++; +x--; +``` + +## 数据类型 + +- **数字**:整数和浮点数,如 `42`、`3.14` +- **字符串**:用双引号或单引号包围,如 `"Hello"`、`'World'` +- **数组**:用方括号定义,如 `[1, 2, 3]` + +## 数组 + +### 创建数组 + +```oraset +var arr = [10, 20, 30, 40, 50]; +var empty = array(5); +``` + +### 访问数组元素 + +```oraset +print(arr[0]); +arr[2] = 35; +``` + +### 数组长度 + +```oraset +var len = arrlen(arr); +``` + +## 运算符 + +### 算术运算符 + +```oraset ++ // 加法 +- // 减法 +* // 乘法 +/ // 除法 +% // 取模 +``` + +### 比较运算符 + +```oraset +== // 等于 +!= // 不等于 +> // 大于 +< // 小于 +>= // 大于等于 +<= // 小于等于 +``` + +### 逻辑运算符 + +```oraset +&& // 逻辑与 +|| // 逻辑或 +! // 逻辑非 +``` + +## 输出 + +### print 函数 + +```oraset +print("Hello"); // 输出并换行 +print("No newline", "nel"); // 输出不换行 +print("A", "B", 123); // 多个参数用空格分隔 +print(arr); // 打印数组 +``` + +## 控制流 + +### 条件语句 + +```oraset +if (x > 10) { + print("x is big"); +} else { + print("x is small"); +} +``` + +### 循环语句 + +```oraset +while (x < 100) { + print(x); + x++; +} + +for (var i = 0; i < 10; i++) { + print(i); +} +``` + +### 函数定义 + +```oraset +function add(a, b) { + return a + b; +} + +add(5, 3); +``` + +## 内置函数 + +### 数学函数 + +```oraset +abs(x) // 绝对值 +sqrt(x) // 平方根 +sin(x) // 正弦 +cos(x) // 余弦 +tan(x) // 正切 +pow(x, y) // x的y次方 +log(x) // 自然对数 +floor(x) // 向下取整 +ceil(x) // 向上取整 +round(x) // 四舍五入 +int(x) // 转为整数 +float(x) // 转为浮点数 +rand() // 0-1随机数 +rand(n) // 0-n随机数 +time() // 当前时间戳 +``` + +### 字符串函数 + +```oraset +len(str) // 字符串长度 +num(str) // 字符串转数字 +str(num) // 数字转字符串 +``` + +### 数组函数 + +```oraset +array(size) // 创建指定大小的数组 +arrlen(arr) // 获取数组长度 +``` + +### 输入函数 + +```oraset +input() // 读取用户输入 +input("提示:") // 带提示的输入 +``` + +## 文件配置 + +### sg 语法 + +```oraset +sg +print("All lines no newline"); +print("Still same line"); +``` + +`sg` 设置整个文件的 print 默认不换行。 + +## 注释 + +```oraset +# 单行注释 +// 单行注释 +/* 多行 + 注释 */ +``` + +--- + +Oraset Project © 2026 \ No newline at end of file diff --git a/website/docs/md/snake.md b/website/docs/md/snake.md new file mode 100644 index 0000000..91f0eac --- /dev/null +++ b/website/docs/md/snake.md @@ -0,0 +1,133 @@ +# 贪吃蛇游戏 + +这是一个用 Oraset B-5 编写的控制台贪吃蛇游戏! + +## 游戏说明 + +使用方向键控制蛇的移动方向,吃食物得分。 + +## 运行方法 + +```bash +oraset examples/snake.ora +``` + +## 代码 + +```oraset +// snake.ora - 贪吃蛇游戏 +// 使用 w/a/s/d 或方向键控制 +// Oraset B-5 版本 - 使用数组存储蛇的位置 + +var width = 40; +var height = 20; +var speed = 150; +var game_over = 0; +var score = 0; + +var snake_x = [10, 9, 8]; +var snake_y = [10, 10, 10]; +var snake_len = 3; + +var food_x = 25; +var food_y = 10; + +var direction = 1; +var next_direction = 1; + +function generate_food() { + food_x = int(rand() * (width - 4)) + 2; + food_y = int(rand() * (height - 4)) + 2; +} + +generate_food(); + +while (game_over == 0) { + direction = next_direction; + + var new_head_x = snake_x[0]; + var new_head_y = snake_y[0]; + + if (direction == 0) new_head_y--; + if (direction == 1) new_head_x++; + if (direction == 2) new_head_y++; + if (direction == 3) new_head_x--; + + if (new_head_x <= 0 || new_head_x >= width - 1 || + new_head_y <= 0 || new_head_y >= height - 1) { + game_over = 1; + break; + } + + var i = 0; + while (i < snake_len) { + if (new_head_x == snake_x[i] && new_head_y == snake_y[i]) { + game_over = 1; + break; + } + i++; + } + if (game_over == 1) break; + + if (new_head_x == food_x && new_head_y == food_y) { + score++; + snake_len++; + generate_food(); + } + + i = snake_len - 1; + while (i > 0) { + snake_x[i] = snake_x[i - 1]; + snake_y[i] = snake_y[i - 1]; + i--; + } + + snake_x[0] = new_head_x; + snake_y[0] = new_head_y; + + var y = 0; + while (y < height) { + var x = 0; + while (x < width) { + var is_snake = 0; + var is_food = 0; + + i = 0; + while (i < snake_len) { + if (x == snake_x[i] && y == snake_y[i]) { + is_snake = 1; + break; + } + i++; + } + + if (x == food_x && y == food_y) { + is_food = 1; + } + + if (is_snake == 1) { + print("O", "nel"); + } else if (is_food == 1) { + print("*", "nel"); + } else if (x == 0 || x == width - 1 || y == 0 || y == height - 1) { + print("-", "nel"); + } else { + print(" ", "nel"); + } + x++; + } + print(""); + y++; + } + + print("分数:", score, " | 长度:", snake_len); + print("w=上 a=左 s=下 d=右"); +} + +print("游戏结束! 最终分数:", score); +``` + +## 注意事项 + +这个版本使用了 Oraset B-5 的数组功能,代码更加简洁高效。 +蛇的身体现在存储在数组中,可以无限增长。 \ No newline at end of file diff --git a/website/docs/md/tutorial.md b/website/docs/md/tutorial.md new file mode 100644 index 0000000..d74a2e3 --- /dev/null +++ b/website/docs/md/tutorial.md @@ -0,0 +1,220 @@ +# 教程 + +通过实际示例学习 Oraset 编程语言 B-5 版本,从基础输出到循环和条件判断。 + +## 示例 1:基本输出 + +```oraset +// hello.ora +print("Welcome to Oraset B-5!"); +print("This is my first program."); +``` + +输出: + +``` +Welcome to Oraset B-5! +This is my first program. +``` + +## 示例 2:变量和计算 + +```oraset +// calc.ora +var a = 10; +var b = 20; +var sum = a + b; +print("The sum is:", sum); +``` + +输出: + +``` +The sum is: 30 +``` + +## 示例 3:条件判断 + +```oraset +// grade.ora +var score = 85; +if (score >= 60) { + print("Pass!"); +} else { + print("Fail!"); +} +``` + +输出: + +``` +Pass! +``` + +## 示例 4:循环 + +```oraset +// loop.ora +var i = 1; +while (i <= 5) { + print("Count:", i); + i++; +} +``` + +输出: + +``` +Count: 1 +Count: 2 +Count: 3 +Count: 4 +Count: 5 +``` + +## 示例 5:不换行输出 + +```oraset +// inline.ora +print("Loading", "nel"); +print("."); +print("."); +print(" Done!"); +``` + +输出: + +``` +Loading... + Done! +``` + +## 示例 6:文件级配置 + +```oraset +// no_newline.ora +sg +print("All"); +print("on"); +print("same"); +print("line"); +``` + +输出: + +``` +Allonsameline +``` + +## 示例 7:数组 + +```oraset +// array.ora +var arr = [10, 20, 30, 40, 50]; +print("Array:", arr); +print("First element:", arr[0]); +print("Array length:", arrlen(arr)); + +arr[2] = 35; +print("Modified array:", arr); +``` + +输出: + +``` +Array: [10, 20, 30, 40, 50] +First element: 10 +Array length: 5 +Modified array: [10, 20, 35, 40, 50] +``` + +## 示例 8:复合赋值 + +```oraset +// compound.ora +var x = 10; +x += 5; +print("x += 5:", x); +x *= 2; +print("x *= 2:", x); +x -= 3; +print("x -= 3:", x); +``` + +输出: + +``` +x += 5: 15 +x *= 2: 30 +x -= 3: 27 +``` + +## 示例 9:函数 + +```oraset +// function.ora +function add(a, b) { + return a + b; +} + +function multiply(a, b) { + return a * b; +} + +var result = add(5, 3); +print("5 + 3 =", result); + +result = multiply(4, 6); +print("4 * 6 =", result); +``` + +输出: + +``` +5 + 3 = 8 +4 * 6 = 24 +``` + +## 示例 10:数学函数 + +```oraset +// math.ora +var pi = 3.14159; +print("sin(pi/2) =", sin(pi/2)); +print("sqrt(16) =", sqrt(16)); +print("abs(-42) =", abs(-42)); +print("pow(2, 8) =", pow(2, 8)); +print("rand() =", rand()); +``` + +输出: + +``` +sin(pi/2) = 1 +sqrt(16) = 4 +abs(-42) = 42 +pow(2, 8) = 256 +rand() = 0.123456 +``` + +## 练习题 + +### 练习 1 + +编写一个程序,计算 1 到 10 的和。 + +### 练习 2 + +编写一个程序,判断一个数是奇数还是偶数。 + +### 练习 3 + +编写一个程序,输出 99 乘法表的一行。 + +### 练习 4 + +编写一个程序,使用数组存储 5 个数字,然后计算它们的平均值。 + +--- + +Oraset Project © 2026 \ No newline at end of file diff --git a/website/download.php b/website/download.php new file mode 100644 index 0000000..1bb4dcd --- /dev/null +++ b/website/download.php @@ -0,0 +1,115 @@ + + + + + + 下载 Oraset + + + +
+ + +
+

下载

+ +

最新版本:B-5

+

构建日期: 2026.7.22 | 全新升级版本

+ +

源码下载

+

如果您想从源码编译 Oraset:

+ +

编译方法:

+
make
+ +
+ +

macOS B-5

+

PKG 安装包

+ +
wget https://oraset.parlz.com/download/pkg/Oraset-B5.pkg
+sudo installer -pkg Oraset-B5.pkg -target /
+ +
+ +

历史版本

+
    +
  • Re+4.0.1-BetaN(+AU) - 2026.6.12 新增自动更新功能 +
    源码下载 +
  • +
  • Re+4.0.1-Beta - 2026.6.12 测试版本 +
    源码下载 +
  • +
  • Re+4.0.0-Release - 2026年早期稳定版本 +
    源码下载 +
  • +
  • Re+4.0.0-Beta - 2026年早期测试版本 +
    源码下载 +
  • +
+ +

系统要求

+
    +
  • Windows 10/11 (64位)
  • +
  • macOS 10.15+
  • +
  • Linux x86_64
  • +
+ +
+

Oraset Project © 2026

+
+
+ + + + \ No newline at end of file diff --git a/website/download/.DS_Store b/website/download/.DS_Store new file mode 100644 index 0000000..db77512 Binary files /dev/null and b/website/download/.DS_Store differ diff --git a/website/download/adn/hello-world.orp b/website/download/adn/hello-world.orp new file mode 100644 index 0000000..f5197ae --- /dev/null +++ b/website/download/adn/hello-world.orp @@ -0,0 +1,35 @@ +# Oraset Package (.orp) +# 包名:hello-world +# 版本:1.0.0 +# 描述:一个简单的 Hello World 示例包,包含 Lua 代码 + +version: 1.0.0 +name: hello-world +description: A simple Hello World example package with Lua code +author: Oraset Team +license: MIT + +# Lua 代码部分 +lua: +-- 这是一个 Lua 脚本示例 +print("Hello from Lua!") +print("这个包已经成功安装") + +-- 定义一个简单的函数 +function greet(name) + return "Hello, " .. name .. "!" +end + +-- 调用函数 +print(greet("Oraset")) + +-- 数学计算 +print("计算结果: 2 + 3 = " .. (2 + 3)) + +-- 循环示例 +print("数字 1 到 5:") +for i = 1, 5 do + print(" " .. i) +end + +print("Lua 代码执行完成!") \ No newline at end of file diff --git a/website/download/deb/.DS_Store b/website/download/deb/.DS_Store new file mode 100644 index 0000000..e5a4052 Binary files /dev/null and b/website/download/deb/.DS_Store differ diff --git a/website/download/deb/Oraset-Re+4.0.0-Beta.deb b/website/download/deb/Oraset-Re+4.0.0-Beta.deb new file mode 100644 index 0000000..6bdf82c Binary files /dev/null and b/website/download/deb/Oraset-Re+4.0.0-Beta.deb differ diff --git a/website/download/deb/Oraset-Re+4.0.0-Release.deb b/website/download/deb/Oraset-Re+4.0.0-Release.deb new file mode 100644 index 0000000..38b43cb Binary files /dev/null and b/website/download/deb/Oraset-Re+4.0.0-Release.deb differ diff --git a/website/download/deb/Oraset-Re+4.0.1-Beta(..26_.6_12)-source.deb b/website/download/deb/Oraset-Re+4.0.1-Beta(..26_.6_12)-source.deb new file mode 100644 index 0000000..1c5b07f Binary files /dev/null and b/website/download/deb/Oraset-Re+4.0.1-Beta(..26_.6_12)-source.deb differ diff --git a/website/download/deb/Oraset-Re+4.0.1-BetaN(+AU).deb b/website/download/deb/Oraset-Re+4.0.1-BetaN(+AU).deb new file mode 100644 index 0000000..1b80751 Binary files /dev/null and b/website/download/deb/Oraset-Re+4.0.1-BetaN(+AU).deb differ diff --git a/website/download/exe/.DS_Store b/website/download/exe/.DS_Store new file mode 100644 index 0000000..3eec7f1 Binary files /dev/null and b/website/download/exe/.DS_Store differ diff --git a/website/download/exe/Oraset-Re+4.0.0-Beta.exe b/website/download/exe/Oraset-Re+4.0.0-Beta.exe new file mode 100644 index 0000000..b2c7817 Binary files /dev/null and b/website/download/exe/Oraset-Re+4.0.0-Beta.exe differ diff --git a/website/download/exe/Oraset-Re+4.0.0-Release.exe b/website/download/exe/Oraset-Re+4.0.0-Release.exe new file mode 100644 index 0000000..1016e02 Binary files /dev/null and b/website/download/exe/Oraset-Re+4.0.0-Release.exe differ diff --git a/website/download/exe/Oraset-Re+4.0.1-Beta(..26_.6_12).exe b/website/download/exe/Oraset-Re+4.0.1-Beta(..26_.6_12).exe new file mode 100644 index 0000000..3b1de5f Binary files /dev/null and b/website/download/exe/Oraset-Re+4.0.1-Beta(..26_.6_12).exe differ diff --git a/website/download/exe/Oraset-Re+4.0.1-BetaN(+AU).exe b/website/download/exe/Oraset-Re+4.0.1-BetaN(+AU).exe new file mode 100644 index 0000000..dae5d81 Binary files /dev/null and b/website/download/exe/Oraset-Re+4.0.1-BetaN(+AU).exe differ diff --git a/website/download/pkg/.DS_Store b/website/download/pkg/.DS_Store new file mode 100644 index 0000000..2f48a3f Binary files /dev/null and b/website/download/pkg/.DS_Store differ diff --git a/website/download/pkg/Oraset-Re+4.0.0-Beta.pkg b/website/download/pkg/Oraset-Re+4.0.0-Beta.pkg new file mode 100644 index 0000000..9ba1ae0 Binary files /dev/null and b/website/download/pkg/Oraset-Re+4.0.0-Beta.pkg differ diff --git a/website/download/pkg/Oraset-Re+4.0.0-Release.pkg b/website/download/pkg/Oraset-Re+4.0.0-Release.pkg new file mode 100644 index 0000000..c9f45ea Binary files /dev/null and b/website/download/pkg/Oraset-Re+4.0.0-Release.pkg differ diff --git a/website/download/pkg/Oraset-Re+4.0.1-Beta(..26_.6_12).pkg b/website/download/pkg/Oraset-Re+4.0.1-Beta(..26_.6_12).pkg new file mode 100644 index 0000000..0ae0f5f Binary files /dev/null and b/website/download/pkg/Oraset-Re+4.0.1-Beta(..26_.6_12).pkg differ diff --git a/website/download/pkg/Oraset-Re+4.0.1-BetaN(+AU).pkg b/website/download/pkg/Oraset-Re+4.0.1-BetaN(+AU).pkg new file mode 100644 index 0000000..e7d0d9f Binary files /dev/null and b/website/download/pkg/Oraset-Re+4.0.1-BetaN(+AU).pkg differ diff --git a/website/download/pkg/Oraset-Re+4.0.1-Release(adn+1.0.0).pkg b/website/download/pkg/Oraset-Re+4.0.1-Release(adn+1.0.0).pkg new file mode 100644 index 0000000..ae9f230 Binary files /dev/null and b/website/download/pkg/Oraset-Re+4.0.1-Release(adn+1.0.0).pkg differ diff --git a/website/download/pkg/Oraset-Re+4.0.1-Release(adn+1.0.100+fix).pkg b/website/download/pkg/Oraset-Re+4.0.1-Release(adn+1.0.100+fix).pkg new file mode 100644 index 0000000..03bf00e Binary files /dev/null and b/website/download/pkg/Oraset-Re+4.0.1-Release(adn+1.0.100+fix).pkg differ diff --git a/website/download/sh/install-linux.sh b/website/download/sh/install-linux.sh new file mode 100644 index 0000000..cfbee88 --- /dev/null +++ b/website/download/sh/install-linux.sh @@ -0,0 +1,105 @@ +#!/bin/bash + +# Oraset Linux 一键安装脚本 (DEB) +# 版本: 4.0.1-BetaN(+AU) + +set -e + +# 版本检查 URL +VERSION_URL="https://oraset.parlz.com/download/version.txt" +DEB_URL_BASE="https://oraset.parlz.com/download/deb" + +echo "🚀 开始安装 Oraset..." + +# 检查系统架构 +ARCH=$(uname -m) +if [ "$ARCH" != "x86_64" ]; then + echo "❌ 错误: 此安装脚本仅支持 x86_64 架构" + exit 1 +fi + +# 检查是否有管理员权限 +if [ "$EUID" -ne 0 ]; then + echo "⚠️ 需要管理员权限来安装 Oraset" + echo "请使用 sudo 运行此脚本,或者输入密码:" + sudo -v +fi + +# 检查网络连接 +check_network() { + if ! curl -s --head --fail "$VERSION_URL" > /dev/null 2>&1; then + return 1 + fi + return 0 +} + +# 获取最新版本号 +get_latest_version() { + if command -v curl &> /dev/null; then + LATEST_VERSION=$(curl -s "$VERSION_URL") + elif command -v wget &> /dev/null; then + LATEST_VERSION=$(wget -q -O- "$VERSION_URL") + else + echo "❌ 错误: 需要 curl 或 wget" + exit 1 + fi + echo "$LATEST_VERSION" +} + +echo "🔍 正在检查最新版本..." +if check_network; then + LATEST_VERSION=$(get_latest_version) + if [ -z "$LATEST_VERSION" ]; then + echo "⚠️ 无法获取最新版本信息,使用默认版本" + LATEST_VERSION="4.0.1-BetaN(+AU)" + else + echo "✅ 检测到最新版本: $LATEST_VERSION" + fi +else + echo "⚠️ 网络不可用,使用默认版本" + LATEST_VERSION="4.0.1-BetaN(+AU)" +fi + +# 创建临时目录 +TEMP_DIR=$(mktemp -d) +echo "📁 创建临时目录: $TEMP_DIR" + +# 下载 DEB 包 +DEB_FILE="Oraset-Re+$LATEST_VERSION.deb" +DEB_URL="$DEB_URL_BASE/$DEB_FILE" +DEB_PATH="$TEMP_DIR/$DEB_FILE" + +echo "📥 正在下载 Oraset $LATEST_VERSION..." +if command -v curl &> /dev/null; then + curl -L -o "$DEB_PATH" "$DEB_URL" +elif command -v wget &> /dev/null; then + wget -O "$DEB_PATH" "$DEB_URL" +else + echo "❌ 错误: 需要 curl 或 wget 来下载文件" + rm -rf "$TEMP_DIR" + exit 1 +fi + +# 检查下载是否成功 +if [ ! -f "$DEB_PATH" ]; then + echo "❌ 错误: 下载失败" + rm -rf "$TEMP_DIR" + exit 1 +fi + +echo "✅ 下载完成" + +# 安装 DEB 包 +echo "🔧 正在安装 Oraset..." +sudo dpkg -i "$DEB_PATH" + +# 检查依赖是否完整 +echo "🔍 检查依赖..." +sudo apt-get install -f -y + +# 清理临时文件 +rm -rf "$TEMP_DIR" +echo "🧹 清理临时文件" + +echo "✅ Oraset 安装完成!" +echo "运行 'oraset -v' 验证安装" diff --git a/website/download/sh/install.sh b/website/download/sh/install.sh new file mode 100644 index 0000000..7d63fb0 --- /dev/null +++ b/website/download/sh/install.sh @@ -0,0 +1,115 @@ +#!/bin/bash + +# Oraset 一键安装脚本 +# 版本: 4.0.1-Beta + +set -e + +# 版本检查 URL +VERSION_URL="https://oraset.parlz.com/download/version.txt" +PKG_URL_BASE="https://oraset.parlz.com/download/pkg" + +echo "🚀 开始安装 Oraset..." + +# 检查系统 +if [[ "$OSTYPE" != "darwin"* ]]; then + echo "❌ 错误: 此安装脚本仅支持 macOS 系统" + exit 1 +fi + +# 检查是否有管理员权限 +if [ "$EUID" -ne 0 ]; then + echo "⚠️ 需要管理员权限来安装 Oraset" + echo "请使用 sudo 运行此脚本,或者输入密码:" + sudo -v +fi + +# 检查网络连接 +check_network() { + if ! curl -s --head --fail "$VERSION_URL" > /dev/null 2>&1; then + return 1 + fi + return 0 +} + +# 获取最新版本号 +get_latest_version() { + if command -v curl &> /dev/null; then + LATEST_VERSION=$(curl -s "$VERSION_URL") + elif command -v wget &> /dev/null; then + LATEST_VERSION=$(wget -q -O- "$VERSION_URL") + else + echo "❌ 错误: 需要 curl 或 wget" + exit 1 + fi + echo "$LATEST_VERSION" +} + +echo "🔍 正在检查最新版本..." +if check_network; then + LATEST_VERSION=$(get_latest_version) + if [ -z "$LATEST_VERSION" ]; then + echo "⚠️ 无法获取最新版本信息,使用默认版本" + LATEST_VERSION="4.0.1-Beta(..26:.6:12)" + else + echo "✅ 检测到最新版本: $LATEST_VERSION" + fi +else + echo "⚠️ 网络不可用,使用默认版本" + LATEST_VERSION="4.0.1-Beta(..26:.6:12)" +fi + +# 创建临时目录 +TEMP_DIR=$(mktemp -d) +echo "📁 创建临时目录: $TEMP_DIR" + +# 下载 PKG 文件 +PKG_FILE="Oraset-Re+$LATEST_VERSION.pkg" +PKG_URL="$PKG_URL_BASE/$PKG_FILE" +PKG_PATH="$TEMP_DIR/$PKG_FILE" + +echo "📥 正在下载 Oraset $LATEST_VERSION..." +if command -v curl &> /dev/null; then + curl -L -o "$PKG_PATH" "$PKG_URL" +elif command -v wget &> /dev/null; then + wget -O "$PKG_PATH" "$PKG_URL" +else + echo "❌ 错误: 需要 curl 或 wget 来下载文件" + rm -rf "$TEMP_DIR" + exit 1 +fi + +# 检查下载是否成功 +if [ ! -f "$PKG_PATH" ]; then + echo "❌ 错误: 下载失败" + rm -rf "$TEMP_DIR" + exit 1 +fi + +echo "✅ 下载完成" + +# 安装 PKG +echo "🔧 正在安装 Oraset..." +sudo installer -pkg "$PKG_PATH" -target / + +# 清理临时文件 +rm -rf "$TEMP_DIR" +echo "🧹 清理临时文件" + +# 验证安装 +if command -v oraset &> /dev/null; then + echo "✅ 安装成功!" + echo "" + echo "🎉 Oraset $LATEST_VERSION 已成功安装到您的系统" + echo "" + echo "使用方法:" + echo " 查看版本: oraset -v" + echo " 运行脚本: oraset your_script.ora" + echo "" + echo "开始使用 Oraset 吧!" +else + echo "⚠️ 安装可能未完成,请检查错误信息" + echo "您可以尝试手动安装:" + echo " sudo installer -pkg $PKG_FILE -target /" + exit 1 +fi \ No newline at end of file diff --git a/website/download/source/.DS_Store b/website/download/source/.DS_Store new file mode 100644 index 0000000..3daa583 Binary files /dev/null and b/website/download/source/.DS_Store differ diff --git a/website/download/source/Oraset-Re+1.0.1-Release(adn+1.0.100+fix)-source.tar.gz b/website/download/source/Oraset-Re+1.0.1-Release(adn+1.0.100+fix)-source.tar.gz new file mode 100644 index 0000000..d3299f3 Binary files /dev/null and b/website/download/source/Oraset-Re+1.0.1-Release(adn+1.0.100+fix)-source.tar.gz differ diff --git a/website/download/source/Oraset-Re+4.0.0-Beta-source.tar.gz b/website/download/source/Oraset-Re+4.0.0-Beta-source.tar.gz new file mode 100644 index 0000000..593312d Binary files /dev/null and b/website/download/source/Oraset-Re+4.0.0-Beta-source.tar.gz differ diff --git a/website/download/source/Oraset-Re+4.0.0-Release-source.tar.gz b/website/download/source/Oraset-Re+4.0.0-Release-source.tar.gz new file mode 100644 index 0000000..5a5b4dc Binary files /dev/null and b/website/download/source/Oraset-Re+4.0.0-Release-source.tar.gz differ diff --git a/website/download/source/Oraset-Re+4.0.1-Beta(..26_.6_12)-source.tar.gz b/website/download/source/Oraset-Re+4.0.1-Beta(..26_.6_12)-source.tar.gz new file mode 100644 index 0000000..c8dda98 Binary files /dev/null and b/website/download/source/Oraset-Re+4.0.1-Beta(..26_.6_12)-source.tar.gz differ diff --git a/website/download/source/Oraset-Re+4.0.1-BetaN(+AU)-source.tar.gz b/website/download/source/Oraset-Re+4.0.1-BetaN(+AU)-source.tar.gz new file mode 100644 index 0000000..e644087 Binary files /dev/null and b/website/download/source/Oraset-Re+4.0.1-BetaN(+AU)-source.tar.gz differ diff --git a/website/download/source/Oraset-Re+4.0.1-BetaN(+AU)-source/.DS_Store b/website/download/source/Oraset-Re+4.0.1-BetaN(+AU)-source/.DS_Store new file mode 100644 index 0000000..de34233 Binary files /dev/null and b/website/download/source/Oraset-Re+4.0.1-BetaN(+AU)-source/.DS_Store differ diff --git a/website/download/source/Oraset-Re+4.0.1-BetaN(+AU)-source/Makefile b/website/download/source/Oraset-Re+4.0.1-BetaN(+AU)-source/Makefile new file mode 100644 index 0000000..a80281b --- /dev/null +++ b/website/download/source/Oraset-Re+4.0.1-BetaN(+AU)-source/Makefile @@ -0,0 +1,25 @@ +CC = gcc +CFLAGS = -Wall -Wextra -O2 -std=c11 +BINDIR = bin + +TARGET = $(BINDIR)/oraset + +SRCS = src/main.c + +all: $(TARGET) + +$(TARGET): $(SRCS) + @mkdir -p $(BINDIR) + $(CC) $(CFLAGS) -o $(TARGET) $(SRCS) + +clean: + rm -f $(TARGET) + +pkg: all + rm -rf pkg-root + mkdir -p pkg-root/usr/local/bin + cp bin/oraset pkg-root/usr/local/bin/oraset + chmod +x pkg-root/usr/local/bin/oraset + pkgbuild --root pkg-root --identifier com.oraset.lang --version 4.0.0 --install-location / Oraset-Re+4.0.0-Release.pkg + +.PHONY: all clean pkg \ No newline at end of file diff --git a/website/download/source/Oraset-Re+4.0.1-BetaN(+AU)-source/src/main.c b/website/download/source/Oraset-Re+4.0.1-BetaN(+AU)-source/src/main.c new file mode 100644 index 0000000..d3ee547 --- /dev/null +++ b/website/download/source/Oraset-Re+4.0.1-BetaN(+AU)-source/src/main.c @@ -0,0 +1,1214 @@ +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 + #include + #include + #define OS_WINDOWS 1 +#else + #include + #include + #include + #define OS_WINDOWS 0 +#endif + +#define VERSION "4.0.1-BetaN(+AU)" +#define MAX_TOKENS 4096 +#define MAX_VARS 256 +#define MAX_LINE 4096 +#define MAX_STRING 1024 + +typedef enum { + TOKEN_EOF, + TOKEN_IDENTIFIER, + TOKEN_STRING, + TOKEN_NUMBER, + TOKEN_PRINT, + TOKEN_VAR, + TOKEN_IF, + TOKEN_WHILE, + TOKEN_FOR, + TOKEN_FUNCTION, + TOKEN_RETURN, + TOKEN_ELSE, + TOKEN_LPAREN, + TOKEN_RPAREN, + TOKEN_LBRACE, + TOKEN_RBRACE, + TOKEN_COMMA, + TOKEN_SEMICOLON, + TOKEN_ASSIGN, + TOKEN_PLUS, + TOKEN_MINUS, + TOKEN_MULTIPLY, + TOKEN_DIVIDE, + TOKEN_EQUAL, + TOKEN_NOT_EQUAL, + TOKEN_GREATER, + TOKEN_LESS, + TOKEN_AND, + TOKEN_OR, + TOKEN_NOT, + TOKEN_GREATER_EQUAL, + TOKEN_LESS_EQUAL, + TOKEN_MODULO +} TokenType; + +typedef struct { + TokenType type; + char value[MAX_STRING]; + int line; +} Token; + +typedef struct { + char name[64]; + double num_value; + char str_value[MAX_STRING]; + int is_string; +} Variable; + +Token tokens[MAX_TOKENS]; +int token_count = 0; +int current_token = 0; +Variable vars[MAX_VARS]; +int var_count = 0; +int print_newline = 1; +int current_line = 1; +int in_for_loop_update = 0; + +static const struct { + const char* name; + TokenType type; +} keywords[] = { + {"print", TOKEN_PRINT}, + {"var", TOKEN_VAR}, + {"if", TOKEN_IF}, + {"else", TOKEN_ELSE}, + {"while", TOKEN_WHILE}, + {"for", TOKEN_FOR}, + {"function", TOKEN_FUNCTION}, + {"return", TOKEN_RETURN}, + {NULL, TOKEN_EOF} +}; + +void error(const char* msg, ...) { + va_list args; + va_start(args, msg); + fprintf(stderr, "[Error at line %d] ", current_line); + vfprintf(stderr, msg, args); + fprintf(stderr, "\n"); + va_end(args); + exit(1); +} + +static void skip_whitespace_and_comments(const char** p) { + while (**p) { + if (**p == ' ' || **p == '\t' || **p == '\r') { + (*p)++; + } else if (**p == '\n') { + (*p)++; + current_line++; + } else if (**p == '#') { + while (**p && **p != '\n') (*p)++; + } else { + break; + } + } +} + +static void add_token(TokenType type, const char* value) { + if (token_count >= MAX_TOKENS) { + error("Too many tokens (max %d)", MAX_TOKENS); + } + tokens[token_count].type = type; + strncpy(tokens[token_count].value, value, MAX_STRING - 1); + tokens[token_count].value[MAX_STRING - 1] = '\0'; + tokens[token_count].line = current_line; + token_count++; +} + +void tokenize(const char* source) { + token_count = 0; + current_line = 1; + const char* p = source; + + while (1) { + skip_whitespace_and_comments(&p); + if (!*p) break; + + // String literal + if (*p == '"') { + p++; + char buf[MAX_STRING]; + int i = 0; + while (*p && *p != '"' && i < MAX_STRING - 1) { + if (*p == '\\' && *(p+1)) { + p++; + switch (*p) { + case 'n': buf[i++] = '\n'; break; + case 't': buf[i++] = '\t'; break; + case 'r': buf[i++] = '\r'; break; + case '\\': buf[i++] = '\\'; break; + case '"': buf[i++] = '"'; break; + default: buf[i++] = *p; break; + } + } else { + buf[i++] = *p; + } + p++; + } + buf[i] = '\0'; + if (*p != '"') error("Unterminated string literal"); + p++; + add_token(TOKEN_STRING, buf); + continue; + } + + // Number + if (isdigit(*p) || (*p == '.' && isdigit(*(p+1)))) { + char buf[64]; + int i = 0; + int dot_count = 0; + while ((isdigit(*p) || (*p == '.' && dot_count < 1)) && i < 63) { + if (*p == '.') dot_count++; + buf[i++] = *p++; + } + buf[i] = '\0'; + add_token(TOKEN_NUMBER, buf); + continue; + } + + // Identifier or keyword + if (isalpha(*p) || *p == '_') { + char buf[64]; + int i = 0; + while ((isalnum(*p) || *p == '_') && i < 63) { + buf[i++] = *p++; + } + buf[i] = '\0'; + + int j; + for (j = 0; keywords[j].name; j++) { + if (strcmp(buf, keywords[j].name) == 0) { + add_token(keywords[j].type, buf); + break; + } + } + if (!keywords[j].name) { + add_token(TOKEN_IDENTIFIER, buf); + } + continue; + } + + // Two-character operators + if (*p == '=' && *(p+1) == '=') { add_token(TOKEN_EQUAL, "=="); p += 2; continue; } + if (*p == '!' && *(p+1) == '=') { add_token(TOKEN_NOT_EQUAL, "!="); p += 2; continue; } + if (*p == '&' && *(p+1) == '&') { add_token(TOKEN_AND, "&&"); p += 2; continue; } + if (*p == '|' && *(p+1) == '|') { add_token(TOKEN_OR, "||"); p += 2; continue; } + if (*p == '>' && *(p+1) == '=') { add_token(TOKEN_GREATER_EQUAL, ">="); p += 2; continue; } + if (*p == '<' && *(p+1) == '=') { add_token(TOKEN_LESS_EQUAL, "<="); p += 2; continue; } + + // Single-character tokens + switch (*p) { + case '(': add_token(TOKEN_LPAREN, "("); break; + case ')': add_token(TOKEN_RPAREN, ")"); break; + case '{': add_token(TOKEN_LBRACE, "{"); break; + case '}': add_token(TOKEN_RBRACE, "}"); break; + case ',': add_token(TOKEN_COMMA, ","); break; + case ';': add_token(TOKEN_SEMICOLON, ";"); break; + case '=': add_token(TOKEN_ASSIGN, "="); break; + case '>': add_token(TOKEN_GREATER, ">"); break; + case '<': add_token(TOKEN_LESS, "<"); break; + case '+': add_token(TOKEN_PLUS, "+"); break; + case '-': add_token(TOKEN_MINUS, "-"); break; + case '*': add_token(TOKEN_MULTIPLY, "*"); break; + case '/': add_token(TOKEN_DIVIDE, "/"); break; + case '%': add_token(TOKEN_MODULO, "%"); break; + case '!': add_token(TOKEN_NOT, "!"); break; + default: error("Unknown character: '%c'", *p); + } + p++; + } + + add_token(TOKEN_EOF, ""); +} + +Token peek() { return tokens[current_token]; } +Token advance() { return tokens[current_token++]; } + +int match(TokenType type) { + if (peek().type == type) { + if (tokens[current_token].line > 0) { + current_line = tokens[current_token].line; + } + advance(); + return 1; + } + return 0; +} + +void consume(TokenType type, const char* msg) { + if (!match(type)) { + error("%s (got '%s')", msg, peek().value); + } +} + +// Forward declarations +double evaluate(); +void execute_statement(); +int update_oraset(const char* target_version); + +// Expression parsing +double parse_primary(); + +double parse_call(const char* func_name) { + consume(TOKEN_LPAREN, "Expected '(' after function name"); + + if (strcmp(func_name, "abs") == 0) { + double val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + return fabs(val); + } else if (strcmp(func_name, "sqrt") == 0) { + double val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + return sqrt(val); + } else if (strcmp(func_name, "sin") == 0) { + double val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + return sin(val); + } else if (strcmp(func_name, "cos") == 0) { + double val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + return cos(val); + } else if (strcmp(func_name, "rand") == 0) { + consume(TOKEN_RPAREN, "Expected ')'"); + return (double)rand() / RAND_MAX; + } else if (strcmp(func_name, "len") == 0) { + consume(TOKEN_STRING, "Expected string argument"); + const char* str = tokens[current_token - 1].value; + consume(TOKEN_RPAREN, "Expected ')'"); + return strlen(str); + } else if (strcmp(func_name, "num") == 0) { + consume(TOKEN_STRING, "Expected string argument"); + const char* str = tokens[current_token - 1].value; + consume(TOKEN_RPAREN, "Expected ')'"); + double val = atof(str); + return val; + } else if (strcmp(func_name, "str") == 0) { + double val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + char buf[32]; + sprintf(buf, "%g", val); + for (int i = 0; i < var_count; i++) { + if (strcmp(vars[i].name, "__tmp_str_result") == 0) { + strncpy(vars[i].str_value, buf, MAX_STRING - 1); + vars[i].str_value[MAX_STRING - 1] = '\0'; + vars[i].is_string = 1; + return 0; + } + } + strncpy(vars[var_count].name, "__tmp_str_result", 63); + strncpy(vars[var_count].str_value, buf, MAX_STRING - 1); + vars[var_count].str_value[MAX_STRING - 1] = '\0'; + vars[var_count].is_string = 1; + var_count++; + return 0; + } else { + error("Undefined function: %s", func_name); + } + return 0; +} + +double parse_primary() { + if (match(TOKEN_NUMBER)) { + return atof(tokens[current_token - 1].value); + } + if (match(TOKEN_STRING)) { + error("Cannot use string in expression"); + } + if (match(TOKEN_IDENTIFIER)) { + const char* name = tokens[current_token - 1].value; + if (peek().type == TOKEN_LPAREN) { + return parse_call(name); + } + for (int i = 0; i < var_count; i++) { + if (strcmp(vars[i].name, name) == 0) { + return vars[i].num_value; + } + } + error("Undefined variable: %s", name); + } + if (match(TOKEN_LPAREN)) { + double val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + return val; + } + error("Expected expression (got '%s')", peek().value); + return 0; +} + +double parse_unary() { + if (match(TOKEN_NOT)) { + return parse_unary() == 0 ? 1 : 0; + } + if (match(TOKEN_MINUS)) { + return -parse_unary(); + } + return parse_primary(); +} + +double parse_factor() { + double left = parse_unary(); + while (match(TOKEN_MULTIPLY) || match(TOKEN_DIVIDE) || match(TOKEN_MODULO)) { + TokenType op = tokens[current_token - 1].type; + double right = parse_unary(); + if (op == TOKEN_MULTIPLY) left *= right; + else if (op == TOKEN_DIVIDE) { + if (right == 0) error("Division by zero"); + left /= right; + } else { + if (right == 0) error("Modulo by zero"); + left = fmod(left, right); + } + } + return left; +} + +double parse_term() { + double left = parse_factor(); + while (match(TOKEN_PLUS) || match(TOKEN_MINUS)) { + TokenType op = tokens[current_token - 1].type; + double right = parse_factor(); + if (op == TOKEN_PLUS) left += right; + else left -= right; + } + return left; +} + +double parse_comparison() { + double left = parse_term(); + while (1) { + if (match(TOKEN_EQUAL)) { + double right = parse_term(); + left = (left == right) ? 1 : 0; + } else if (match(TOKEN_NOT_EQUAL)) { + double right = parse_term(); + left = (left != right) ? 1 : 0; + } else if (match(TOKEN_GREATER)) { + double right = parse_term(); + left = (left > right) ? 1 : 0; + } else if (match(TOKEN_LESS)) { + double right = parse_term(); + left = (left < right) ? 1 : 0; + } else if (match(TOKEN_GREATER_EQUAL)) { + double right = parse_term(); + left = (left >= right) ? 1 : 0; + } else if (match(TOKEN_LESS_EQUAL)) { + double right = parse_term(); + left = (left <= right) ? 1 : 0; + } else { + break; + } + } + return left; +} + +double parse_logic_and() { + double left = parse_comparison(); + while (match(TOKEN_AND)) { + double right = parse_comparison(); + left = (left != 0 && right != 0) ? 1 : 0; + } + return left; +} + +double parse_logic_or() { + double left = parse_logic_and(); + while (match(TOKEN_OR)) { + double right = parse_logic_and(); + left = (left != 0 || right != 0) ? 1 : 0; + } + return left; +} + +double evaluate() { + return parse_logic_or(); +} + +void execute_input() { + consume(TOKEN_LPAREN, "Expected '(' after input"); + const char* prompt = ""; + + if (match(TOKEN_STRING)) { + prompt = tokens[current_token - 1].value; + } + consume(TOKEN_RPAREN, "Expected ')'"); + + printf("%s", prompt); + fflush(stdout); + + char input[MAX_STRING]; + if (fgets(input, MAX_STRING, stdin) == NULL) { + error("Failed to read input"); + } + + input[strcspn(input, "\n")] = '\0'; + + strncpy(vars[var_count].name, "__input_result", 63); + strncpy(vars[var_count].str_value, input, MAX_STRING - 1); + vars[var_count].str_value[MAX_STRING - 1] = '\0'; + vars[var_count].is_string = 1; + var_count++; +} + +void execute_str() { + consume(TOKEN_LPAREN, "Expected '(' after str"); + double val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + + char buf[MAX_STRING]; + sprintf(buf, "%g", val); + + strncpy(vars[var_count].name, "__str_result", 63); + strncpy(vars[var_count].str_value, buf, MAX_STRING - 1); + vars[var_count].str_value[MAX_STRING - 1] = '\0'; + vars[var_count].is_string = 1; + var_count++; +} + +// Execute print statement +void execute_print() { + consume(TOKEN_LPAREN, "Expected '(' after print"); + int first = 1; + int newline = print_newline; + + while (!match(TOKEN_RPAREN)) { + if (!first) consume(TOKEN_COMMA, "Expected ',' between arguments"); + + if (match(TOKEN_STRING)) { + const char* str = tokens[current_token - 1].value; + if (strcmp(str, "nel") == 0) { + newline = 0; + first = 0; + continue; + } + if (!first) printf(" "); + printf("%s", str); + } else if (match(TOKEN_NUMBER)) { + if (!first) printf(" "); + printf("%s", tokens[current_token - 1].value); + } else if (match(TOKEN_IDENTIFIER)) { + const char* name = tokens[current_token - 1].value; + if (peek().type == TOKEN_PLUS || peek().type == TOKEN_MINUS || + peek().type == TOKEN_MULTIPLY || peek().type == TOKEN_DIVIDE || + peek().type == TOKEN_MODULO) { + double val = atof(tokens[current_token - 1].value); + for (int i = 0; i < var_count; i++) { + if (strcmp(vars[i].name, name) == 0) { + if (!vars[i].is_string) { + val = vars[i].num_value; + } + break; + } + } + while (peek().type == TOKEN_PLUS || peek().type == TOKEN_MINUS || + peek().type == TOKEN_MULTIPLY || peek().type == TOKEN_DIVIDE || + peek().type == TOKEN_MODULO) { + TokenType op = peek().type; + advance(); + double right = evaluate(); + if (op == TOKEN_PLUS) val += right; + else if (op == TOKEN_MINUS) val -= right; + else if (op == TOKEN_MULTIPLY) val *= right; + else if (op == TOKEN_DIVIDE) val /= right; + else if (op == TOKEN_MODULO) val = fmod(val, right); + } + if (!first) printf(" "); + printf("%g", val); + } else { + int found = 0; + for (int i = 0; i < var_count; i++) { + if (strcmp(vars[i].name, name) == 0) { + if (!first) printf(" "); + if (vars[i].is_string) { + printf("%s", vars[i].str_value); + } else { + printf("%g", vars[i].num_value); + } + found = 1; + break; + } + } + if (!found) error("Undefined variable: %s", name); + } + } else { + error("Unexpected token in print: '%s'", peek().value); + } + first = 0; + } + + if (newline) { + printf("\n"); + } + fflush(stdout); +} + +// Execute var declaration +void execute_var() { + consume(TOKEN_IDENTIFIER, "Expected variable name"); + const char* name = tokens[current_token - 1].value; + consume(TOKEN_ASSIGN, "Expected '=' after variable name"); + + if (var_count >= MAX_VARS) { + error("Too many variables (max %d)", MAX_VARS); + } + + Variable* v = &vars[var_count]; + strncpy(v->name, name, 63); + v->name[63] = '\0'; + + if (match(TOKEN_STRING)) { + strncpy(v->str_value, tokens[current_token - 1].value, MAX_STRING - 1); + v->str_value[MAX_STRING - 1] = '\0'; + v->is_string = 1; + v->num_value = 0; + } else { + v->num_value = evaluate(); + v->is_string = 0; + v->str_value[0] = '\0'; + } + var_count++; + consume(TOKEN_SEMICOLON, "Expected ';' after variable declaration"); +} + +// Skip block (for else branch) +static void skip_block() { + int depth = 1; + while (depth > 0 && peek().type != TOKEN_EOF) { + Token t = advance(); + if (t.type == TOKEN_LBRACE) depth++; + if (t.type == TOKEN_RBRACE) depth--; + } +} + +// Execute if statement +void execute_if() { + consume(TOKEN_LPAREN, "Expected '(' after 'if'"); + double cond = evaluate(); + consume(TOKEN_RPAREN, "Expected ')' after condition"); + consume(TOKEN_LBRACE, "Expected '{' after if condition"); + + if (cond != 0) { + while (!match(TOKEN_RBRACE) && peek().type != TOKEN_EOF) { + execute_statement(); + } + } else { + skip_block(); + } + + if (match(TOKEN_ELSE)) { + consume(TOKEN_LBRACE, "Expected '{' after 'else'"); + if (cond == 0) { + while (!match(TOKEN_RBRACE) && peek().type != TOKEN_EOF) { + execute_statement(); + } + } else { + skip_block(); + } + } +} + +// Execute while statement +void execute_while() { + consume(TOKEN_LPAREN, "Expected '(' after 'while'"); + int cond_start = current_token; + + evaluate(); // Parse once to skip condition + consume(TOKEN_RPAREN, "Expected ')' after condition"); + consume(TOKEN_LBRACE, "Expected '{' after while condition"); + + int body_start = current_token; + skip_block(); // Skip body to find end + int body_end = current_token; + + while (1) { + current_token = cond_start; + current_line = tokens[cond_start].line; + double cond = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + consume(TOKEN_LBRACE, "Expected '{'"); + + if (cond == 0) { + current_token = body_end; + break; + } + + current_token = body_start; + while (current_token < body_end - 1 && peek().type != TOKEN_EOF) { + execute_statement(); + } + } +} + +// Execute for statement +void execute_for() { + consume(TOKEN_LPAREN, "Expected '(' after 'for'"); + + int init_start = current_token; + int init_end = current_token; + if (match(TOKEN_VAR)) { + while (peek().type != TOKEN_SEMICOLON && peek().type != TOKEN_EOF) { + advance(); + } + init_end = current_token; + } else if (peek().type == TOKEN_IDENTIFIER && tokens[current_token + 1].type == TOKEN_ASSIGN) { + while (peek().type != TOKEN_SEMICOLON && peek().type != TOKEN_EOF) { + advance(); + } + init_end = current_token; + } else { + init_end = current_token; + } + consume(TOKEN_SEMICOLON, "Expected ';' in for loop"); + + int cond_start = current_token; + int cond_end = current_token; + if (peek().type != TOKEN_SEMICOLON && peek().type != TOKEN_EOF) { + while (peek().type != TOKEN_SEMICOLON && peek().type != TOKEN_EOF) { + advance(); + } + cond_end = current_token; + } else { + cond_end = current_token; + } + consume(TOKEN_SEMICOLON, "Expected ';' in for loop"); + + int update_start = current_token; + int update_end = current_token; + if (peek().type != TOKEN_RPAREN && peek().type != TOKEN_EOF) { + while (peek().type != TOKEN_RPAREN && peek().type != TOKEN_EOF) { + advance(); + } + update_end = current_token; + } else { + update_end = current_token; + } + consume(TOKEN_RPAREN, "Expected ')'"); + consume(TOKEN_LBRACE, "Expected '{'"); + + int body_start = current_token; + skip_block(); + int body_end = current_token; + + if (init_start != init_end) { + current_token = init_start; + while (current_token < init_end) { + execute_statement(); + } + } + + while (1) { + if (cond_start != cond_end) { + current_token = cond_start; + current_line = tokens[cond_start].line; + double cond = evaluate(); + + if (cond == 0) { + current_token = body_end; + break; + } + } else { + break; + } + + current_token = body_start; + while (current_token < body_end - 1 && peek().type != TOKEN_EOF) { + execute_statement(); + } + + if (update_start != update_end) { + in_for_loop_update = 1; + current_token = update_start; + while (current_token < update_end) { + execute_statement(); + } + in_for_loop_update = 0; + } + } +} + +extern int in_for_loop_update; + +// Execute statement +void execute_statement() { + Token t = peek(); + + switch (t.type) { + case TOKEN_PRINT: + advance(); + execute_print(); + consume(TOKEN_SEMICOLON, "Expected ';' after print"); + break; + + case TOKEN_VAR: + advance(); + execute_var(); + break; + + case TOKEN_IF: + advance(); + execute_if(); + break; + + case TOKEN_WHILE: + advance(); + execute_while(); + break; + + case TOKEN_FOR: + advance(); + execute_for(); + break; + + case TOKEN_IDENTIFIER: { + advance(); + if (match(TOKEN_ASSIGN)) { + const char* name = tokens[current_token - 2].value; + for (int i = 0; i < var_count; i++) { + if (strcmp(vars[i].name, name) == 0) { + if (match(TOKEN_STRING)) { + strncpy(vars[i].str_value, tokens[current_token - 1].value, MAX_STRING - 1); + vars[i].str_value[MAX_STRING - 1] = '\0'; + vars[i].is_string = 1; + } else { + vars[i].num_value = evaluate(); + vars[i].is_string = 0; + } + if (!in_for_loop_update) { + consume(TOKEN_SEMICOLON, "Expected ';' after assignment"); + } + return; + } + } + error("Undefined variable: %s", name); + } else if (peek().type == TOKEN_LPAREN) { + const char* name = tokens[current_token - 1].value; + if (strcmp(name, "input") == 0) { + execute_input(); + consume(TOKEN_SEMICOLON, "Expected ';' after input"); + return; + } else if (strcmp(name, "str") == 0) { + execute_str(); + consume(TOKEN_SEMICOLON, "Expected ';' after str"); + return; + } + } + error("Unexpected identifier: %s", tokens[current_token - 1].value); + break; + } + + case TOKEN_EOF: + break; + + default: + error("Unexpected token: %s", t.value); + } +} + +// Parse sg<...> config at start of file +void parse_config(const char* source) { + print_newline = 1; + const char* p = source; + + while (*p) { + if (*p == ' ' || *p == '\t' || *p == '\r') { + p++; + continue; + } + if (*p == '\n') break; + if (*p == '#') { + while (*p && *p != '\n') p++; + continue; + } + if (*p == 's' && *(p+1) == 'g' && *(p+2) == '<') { + p += 3; + while (*p && *p != '>') { + if (strncmp(p, "nel", 3) == 0) { + print_newline = 0; + break; + } + p++; + } + break; + } + break; + } +} + +// Strip config line from source +char* strip_config(const char* source) { + const char* p = source; + + while (*p) { + if (*p == ' ' || *p == '\t' || *p == '\r') { + p++; + continue; + } + if (*p == '\n') { + return strdup(source); + } + if (*p == '#') { + while (*p && *p != '\n') p++; + if (*p == '\n') p++; + continue; + } + if (*p == 's' && *(p+1) == 'g' && *(p+2) == '<') { + const char* start = p; + while (*p && *p != '\n') p++; + if (*p == '\n') p++; + + size_t prefix_len = start - source; + size_t suffix_len = strlen(p); + char* result = malloc(prefix_len + suffix_len + 1); + if (!result) error("Out of memory"); + + memcpy(result, source, prefix_len); + memcpy(result + prefix_len, p, suffix_len + 1); + return result; + } + return strdup(source); + } + return strdup(source); +} + +void run(const char* source) { + static int rand_seeded = 0; + if (!rand_seeded) { + srand(time(NULL)); + rand_seeded = 1; + } + + parse_config(source); + char* clean_source = strip_config(source); + tokenize(clean_source); + current_token = 0; + + while (peek().type != TOKEN_EOF) { + execute_statement(); + } + + free(clean_source); +} + +void run_file(const char* path) { + FILE* f = fopen(path, "r"); + if (!f) { + error("Cannot open file: %s", path); + } + + fseek(f, 0, SEEK_END); + long size = ftell(f); + if (size < 0) { + fclose(f); + error("Cannot read file: %s", path); + } + fseek(f, 0, SEEK_SET); + + char* source = malloc(size + 1); + if (!source) { + fclose(f); + error("Out of memory"); + } + + size_t read = fread(source, 1, size, f); + fclose(f); + source[read] = '\0'; + + run(source); + free(source); +} + +// Update functionality +#define VERSION_URL "https://oraset.parlz.com/download/version.txt" +#define DOWNLOAD_BASE "https://oraset.parlz.com/download" + +static char* http_get(const char* url) { +#ifdef _WIN32 + HINTERNET hInternet = InternetOpenA("Oraset", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0); + if (!hInternet) return NULL; + + HINTERNET hUrl = InternetOpenUrlA(hInternet, url, NULL, 0, INTERNET_FLAG_RELOAD, 0); + if (!hUrl) { + InternetCloseHandle(hInternet); + return NULL; + } + + char* buffer = malloc(4096); + if (!buffer) { + InternetCloseHandle(hUrl); + InternetCloseHandle(hInternet); + return NULL; + } + + DWORD total_read = 0; + DWORD bytes_read; + char temp[1024]; + + while (InternetReadFile(hUrl, temp, sizeof(temp), &bytes_read) && bytes_read > 0) { + if (total_read + bytes_read >= 4095) { + char* new_buf = realloc(buffer, total_read + bytes_read + 4096); + if (!new_buf) { + free(buffer); + InternetCloseHandle(hUrl); + InternetCloseHandle(hInternet); + return NULL; + } + buffer = new_buf; + } + memcpy(buffer + total_read, temp, bytes_read); + total_read += bytes_read; + } + buffer[total_read] = '\0'; + + InternetCloseHandle(hUrl); + InternetCloseHandle(hInternet); + return buffer; +#else + char cmd[512]; + snprintf(cmd, sizeof(cmd), "curl -s \"%s\" 2>/dev/null", url); + FILE* fp = popen(cmd, "r"); + if (!fp) return NULL; + + char* buffer = malloc(4096); + if (!buffer) { + pclose(fp); + return NULL; + } + + size_t total_read = 0; + size_t bytes_read; + char temp[1024]; + + while ((bytes_read = fread(temp, 1, sizeof(temp), fp)) > 0) { + if (total_read + bytes_read >= 4095) { + char* new_buf = realloc(buffer, total_read + bytes_read + 4096); + if (!new_buf) { + free(buffer); + pclose(fp); + return NULL; + } + buffer = new_buf; + } + memcpy(buffer + total_read, temp, bytes_read); + total_read += bytes_read; + } + buffer[total_read] = '\0'; + pclose(fp); + return buffer; +#endif +} + +static int download_file(const char* url, const char* dest_path) { +#ifdef _WIN32 + HINTERNET hInternet = InternetOpenA("Oraset", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0); + if (!hInternet) return -1; + + HINTERNET hUrl = InternetOpenUrlA(hInternet, url, NULL, 0, INTERNET_FLAG_RELOAD, 0); + if (!hUrl) { + InternetCloseHandle(hInternet); + return -1; + } + + FILE* fp = fopen(dest_path, "wb"); + if (!fp) { + InternetCloseHandle(hUrl); + InternetCloseHandle(hInternet); + return -1; + } + + char temp[4096]; + DWORD bytes_read; + + while (InternetReadFile(hUrl, temp, sizeof(temp), &bytes_read) && bytes_read > 0) { + fwrite(temp, 1, bytes_read, fp); + } + + fclose(fp); + InternetCloseHandle(hUrl); + InternetCloseHandle(hInternet); + return 0; +#else + char cmd[1024]; + snprintf(cmd, sizeof(cmd), "curl -L -o \"%s\" \"%s\" 2>/dev/null", dest_path, url); + return system(cmd); +#endif +} + +static void get_system_info(char* os, char* ext) { +#ifdef _WIN32 + strcpy(os, "windows"); + strcpy(ext, ".exe"); +#elif defined(__APPLE__) + strcpy(os, "macos"); + strcpy(ext, ".pkg"); +#elif defined(__linux__) + strcpy(os, "linux"); + strcpy(ext, ".tar.gz"); +#else + strcpy(os, "unknown"); + strcpy(ext, ".tar.gz"); +#endif +} + +static int compare_versions(const char* v1, const char* v2) { + int major1, minor1, patch1, major2, minor2, patch2; + char suffix1[64] = "", suffix2[64] = ""; + + sscanf(v1, "%d.%d.%d-%63s", &major1, &minor1, &patch1, suffix1); + sscanf(v2, "%d.%d.%d-%63s", &major2, &minor2, &patch2, suffix2); + + if (major1 != major2) return major1 - major2; + if (minor1 != minor2) return minor1 - minor2; + if (patch1 != patch2) return patch1 - patch2; + + // Release > Beta > Alpha + int s1 = (strstr(suffix1, "Release") || strlen(suffix1) == 0) ? 3 : + (strstr(suffix1, "Beta") ? 2 : 1); + int s2 = (strstr(suffix2, "Release") || strlen(suffix2) == 0) ? 3 : + (strstr(suffix2, "Beta") ? 2 : 1); + return s1 - s2; +} + +static void trim_newline(char* str) { + int len = strlen(str); + while (len > 0 && (str[len-1] == '\n' || str[len-1] == '\r')) { + str[--len] = '\0'; + } +} + +int update_oraset(const char* target_version) { + printf("Checking for updates...\n"); + + // Get latest version + char* version_info = http_get(VERSION_URL); + if (!version_info) { + fprintf(stderr, "Error: Cannot fetch version information.\n"); + fprintf(stderr, "Please check your internet connection.\n"); + return 1; + } + + trim_newline(version_info); + char latest_version[128]; + strncpy(latest_version, version_info, sizeof(latest_version) - 1); + free(version_info); + + printf("Current version: %s\n", VERSION); + printf("Latest version: %s\n", latest_version); + + const char* version_to_install = target_version ? target_version : latest_version; + + if (target_version) { + printf("Target version specified: %s\n", version_to_install); + } + + // Get system info + char os[32], ext[16]; + get_system_info(os, ext); + printf("Detected OS: %s\n", os); + + // Build download URL + char download_url[512]; + char filename[128]; + + if (strcmp(os, "windows") == 0) { + snprintf(filename, sizeof(filename), "Oraset-Re+%s%s", version_to_install, ext); + snprintf(download_url, sizeof(download_url), "%s/exe/%s", DOWNLOAD_BASE, filename); + } else if (strcmp(os, "macos") == 0) { + snprintf(filename, sizeof(filename), "Oraset-Re+%s%s", version_to_install, ext); + snprintf(download_url, sizeof(download_url), "%s/pkg/%s", DOWNLOAD_BASE, filename); + } else { + snprintf(filename, sizeof(filename), "Oraset-Re+%s%s", version_to_install, ext); + snprintf(download_url, sizeof(download_url), "%s/deb/%s", DOWNLOAD_BASE, filename); + } + + printf("Downloading: %s\n", download_url); + + // Download to temp directory + char temp_path[512]; +#ifdef _WIN32 + char temp_dir[MAX_PATH]; + GetTempPathA(MAX_PATH, temp_dir); + snprintf(temp_path, sizeof(temp_path), "%s%s", temp_dir, filename); +#else + snprintf(temp_path, sizeof(temp_path), "/tmp/%s", filename); +#endif + + if (download_file(download_url, temp_path) != 0) { + fprintf(stderr, "Error: Failed to download update.\n"); + return 1; + } + + printf("Downloaded to: %s\n", temp_path); + + // Install based on OS +#ifdef _WIN32 + printf("Please run the installer: %s\n", temp_path); +#else + if (strcmp(os, "macos") == 0) { + printf("Installing package...\n"); + char cmd[1024]; + snprintf(cmd, sizeof(cmd), "sudo installer -pkg \"%s\" -target /", temp_path); + system(cmd); + } else if (strcmp(os, "linux") == 0) { + printf("Extracting...\n"); + char cmd[1024]; + snprintf(cmd, sizeof(cmd), "cd /tmp && tar -xzf \"%s\" && sudo cp oraset /usr/local/bin/", temp_path); + system(cmd); + } + printf("Installation complete!\n"); +#endif + + return 0; +} + +int main(int argc, char** argv) { + if (argc == 1) { + printf("Oraset Re+%s\n", VERSION); + printf("Usage: oraset [options] + + \ No newline at end of file diff --git a/website/releases.php b/website/releases.php new file mode 100644 index 0000000..f6ac045 --- /dev/null +++ b/website/releases.php @@ -0,0 +1,256 @@ + + + + + + 历史版本 - Oraset + + + +
+ + +
+

历史版本

+ +
    +
  • +
    B-5
    +
    2026年7月22日
    +
    + 全新升级版本。
    +
      +
    • 新增数组类型支持
    • +
    • 新增自定义函数定义
    • +
    • 新增复合赋值运算符:+=, -=, *=, /=, %=
    • +
    • 新增自增自减运算符:++, --
    • +
    • 新增 REPL 交互模式(oraset -i)
    • +
    • 新增更多数学函数:tan(), pow(), log(), floor(), ceil(), round()
    • +
    • 新增数组函数:array(), arrlen()
    • +
    • 新增 int() 和 float() 类型转换函数
    • +
    • 支持单引号字符串
    • +
    • 支持多行注释(/* */)
    • +
    • 支持行注释(//)
    • +
    • rand() 函数支持参数(指定最大值)
    • +
    • 优化变量查找算法
    • +
    • 修复多处 bug
    • +
    +
    +
    + 下载: + +

    💡 或者使用:oraset -u 更新到最新版本

    +
    +
  • +
  • +
    Re+4.0.1-Release(adn+1.0.100+fix)
    +
    2026年6月18日
    +
    + ADN 包管理修复版本。
    +
      +
    • 移除外部 Lua 依赖,使用 Oraset 解释器执行 .orp 扩展包
    • +
    • 修复词法分析器字符识别问题
    • +
    • 修复函数调用括号处理错误
    • +
    • 优化语法分析器逻辑
    • +
    • 支持 .orp 包格式(使用 Oraset 语言编写扩展代码)
    • +
    +
    +
    + 下载: + +

    💡 或者使用:oraset -u 更新到最新版本

    +
    +
  • +
  • +
    Re+4.0.1-Release(adn+1.0.0)
    +
    2026年6月13日
    +
    + ADN 包管理系统版本。
    +
      +
    • 新增 ADN 包管理系统(.orp 格式)
    • +
    • 支持包安装:adn(install package) 或 adn(i package)
    • +
    • 支持包更新:adn(update package) 或 adn(u package)
    • +
    • 支持包移除:adn(remove package) 或 adn(r package)
    • +
    • 支持包列表:adn(list) 或 adn(ls)
    • +
    • 支持仓库管理:adm(mirror url) 添加镜像仓库
    • +
    • 支持仓库列表:adm(list) 或 adm(ls)
    • +
    • 默认仓库:https://oraset.parlz.com/download/adn/
    • +
    • 自动创建 .adn 目录管理包和仓库信息
    • +
    +
    +
    + 下载: + +

    💡 如果您已安装 Oraset-Re+4.0.1-BetaN(+AU) 或更高版本,可使用 oraset -u Oraset-Re+4.0.1-Release(adn+1.0.0) 安装此版本

    +
    +
  • +
  • +
    Re+4.0.1-BetaN(+AU)
    +
    2026年6月12日
    +
    + 新增自动更新功能版本。
    +
      +
    • 添加 oraset -u 自动更新命令
    • +
    • 支持指定版本更新:oraset -u Oraset-Re+4.0.0-Beta
    • +
    • 自动识别操作系统(Windows/macOS/Linux)
    • +
    • 从服务器获取最新版本信息
    • +
    • 自动下载并安装对应平台的安装包
    • +
    +
    +
    + 下载: + +

    💡 或者使用:oraset -u 更新到最新版本

    +
    +
  • + +
  • +
    Re+4.0.1-Beta(..26:.6:12)
    +
    2026年6月12日
    +
    + 新增实用功能版本。
    +
      +
    • 添加数学函数:abs(), sqrt(), sin(), cos(), rand()
    • +
    • 添加字符串函数:len(), num(), str()
    • +
    • 添加输入函数:input()
    • +
    • 添加 for 循环语法
    • +
    • 添加比较运算符:>=, <=
    • +
    • 添加取模运算符:%
    • +
    • 优化代码执行性能
    • +
    +
    +
    + 下载: + +

    💡 如果您已安装 Oraset-Re+4.0.1-BetaN(+AU) 或更高版本,可使用 oraset -u Oraset-Re+4.0.1-Beta(..26:.6:12) 安装此版本

    +
    +
  • + +
  • +
    Re+4.0.0-Release
    +
    2026年6月12日
    +
    + 首个正式 Release 版本。
    +
      +
    • 修复 while 循环逻辑错误
    • +
    • 修复 if 语句 else 分支处理
    • +
    • 添加字符串转义支持(\n, \t, \r, \")
    • +
    • 添加逻辑运算符(&&, ||, !)
    • +
    • 内存优化,减少动态分配
    • +
    • 性能优化,支持编译优化
    • +
    +
    +
    + 下载: + +

    💡 如果您已安装 Oraset-Re+4.0.1-BetaN(+AU) 或更高版本,可使用 oraset -u Oraset-Re+4.0.0-Release 安装此版本

    +
    +
  • + +
  • +
    Re+4.0.0-Beta
    +
    2026年6月11日
    +
    + 首个公开 Beta 测试版本。
    +
      +
    • 变量声明和赋值
    • +
    • 表达式计算(算术运算、比较运算)
    • +
    • 条件语句(if/else)
    • +
    • 循环语句(while)
    • +
    • print 函数(支持 "nel" 参数控制换行)
    • +
    • 文件级配置(sg 语法)
    • +
    • 跨平台支持(Windows、macOS、Linux)
    • +
    +
    +
    + 下载: + +

    💡 如果您已安装 Oraset-Re+4.0.1-BetaN(+AU) 或更高版本,可使用 oraset -u Oraset-Re+4.0.0-Beta 安装此版本

    +
    +
  • +
+ +
+

Oraset Project © 2026

+
+
+ + + + \ No newline at end of file