diff --git a/README.md b/README.md index bd6a796..c2b45df 100644 --- a/README.md +++ b/README.md @@ -28,12 +28,14 @@ A modern, C++-style scripting language designed for ease of use and powerful fun - Logical: `&&`, `||`, `!` - Compound: `+=`, `-=`, `*=`, `/=`, `%=` - Increment/Decrement: `++`, `--` +- Ternary: `condition ? true_value : false_value` ### Built-in Functions -- **Mathematical**: `abs`, `sqrt`, `sin`, `cos`, `tan`, `pow`, `log`, `floor`, `ceil`, `round` -- **String**: `strcmp`, `strcpy`, `strcat`, `sizeof` -- **Array**: `arrlen`, `memcpy`, `memcmp`, `malloc` -- **Utility**: `echo`, `input`, `time`, `random`, `exit`, `assert` +- **Mathematical**: `abs`, `sqrt`, `sin`, `cos`, `tan`, `asin`, `acos`, `atan`, `atan2`, `pow`, `log`, `log10`, `exp`, `floor`, `ceil`, `round` +- **String**: `strcmp`, `strcpy`, `strcat`, `strlen`, `strchr`, `strstr`, `tolower`, `toupper`, `sizeof` +- **Array**: `arrlen`, `memcpy`, `memcmp`, `malloc`, `push`, `pop`, `insert`, `remove` +- **File IO**: `fopen`, `fclose`, `fread`, `fwrite`, `fgets`, `fputs` +- **System**: `echo`, `input`, `time`, `random`, `exit`, `assert`, `system`, `getenv`, `sleep` ## Getting Started diff --git a/bin/oraset.exe b/bin/oraset.exe index 8d8db0d..86b6068 100644 Binary files a/bin/oraset.exe and b/bin/oraset.exe differ diff --git a/src/main.c b/src/main.c index f58fd3f..ab34f72 100644 --- a/src/main.c +++ b/src/main.c @@ -92,7 +92,8 @@ typedef enum { TOKEN_CATCH, TOKEN_THROW, TOKEN_ASTERISK, - TOKEN_LABEL + TOKEN_LABEL, + TOKEN_QUESTION } OraTokenType; typedef struct { @@ -409,6 +410,7 @@ void tokenize(const char* source) { case '!': add_token(TOKEN_NOT, "!"); break; case ':': add_token(TOKEN_COLON, ":"); break; case '.': add_token(TOKEN_DOT, "."); break; + case '?': add_token(TOKEN_QUESTION, "?"); break; default: error("Unknown character: '%c'", *p); } p++; @@ -786,6 +788,271 @@ double parse_call(const char* func_name) { } } return 0; + } else if (strcmp(func_name, "fopen") == 0) { + const char* filename = get_string_value(); + consume(TOKEN_COMMA, "Expected ','"); + const char* mode = get_string_value(); + consume(TOKEN_RPAREN, "Expected ')'"); + for (int i = 0; i < var_count; i++) { + if (strcmp(vars[i].name, "__tmp_file_result") == 0) { + FILE* fp = fopen(filename, mode); + vars[i].value.num_value = (double)(intptr_t)fp; + vars[i].value.type = VAL_NUMBER; + return 0; + } + } + strncpy(vars[var_count].name, "__tmp_file_result", 63); + FILE* fp = fopen(filename, mode); + vars[var_count].value.num_value = (double)(intptr_t)fp; + vars[var_count].value.type = VAL_NUMBER; + var_count++; + return 0; + } else if (strcmp(func_name, "fclose") == 0) { + double fp_val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + FILE* fp = (FILE*)(intptr_t)fp_val; + return (double)fclose(fp); + } else if (strcmp(func_name, "fgets") == 0) { + double fp_val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + FILE* fp = (FILE*)(intptr_t)fp_val; + char buf[MAX_STRING]; + if (fgets(buf, MAX_STRING, fp)) { + 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, "fputs") == 0) { + const char* str = get_string_value(); + consume(TOKEN_COMMA, "Expected ','"); + double fp_val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + FILE* fp = (FILE*)(intptr_t)fp_val; + return (double)fputs(str, fp); + } else if (strcmp(func_name, "fread") == 0) { + consume(TOKEN_IDENTIFIER, "Expected array variable"); + const char* name = tokens[current_token - 1].value; + consume(TOKEN_COMMA, "Expected ','"); + int count = (int)evaluate(); + consume(TOKEN_COMMA, "Expected ','"); + double fp_val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + FILE* fp = (FILE*)(intptr_t)fp_val; + Value* arr = find_variable(name); + if (!arr) error("Undefined variable: %s", name); + if (arr->type != VAL_ARRAY) { + arr->type = VAL_ARRAY; + arr->arr_size = 0; + } + double buf[MAX_ARRAY_SIZE]; + size_t read = fread(buf, sizeof(double), count, fp); + for (int i = 0; i < read && arr->arr_size + i < MAX_ARRAY_SIZE; i++) { + arr->arr_value[arr->arr_size + i] = buf[i]; + } + arr->arr_size += read; + return (double)read; + } else if (strcmp(func_name, "fwrite") == 0) { + consume(TOKEN_IDENTIFIER, "Expected array variable"); + const char* name = tokens[current_token - 1].value; + consume(TOKEN_COMMA, "Expected ','"); + int count = (int)evaluate(); + consume(TOKEN_COMMA, "Expected ','"); + double fp_val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + FILE* fp = (FILE*)(intptr_t)fp_val; + Value* arr = find_variable(name); + if (!arr || arr->type != VAL_ARRAY) error("Invalid array"); + size_t written = fwrite(arr->arr_value, sizeof(double), count, fp); + return (double)written; + } else if (strcmp(func_name, "strlen") == 0) { + const char* str = get_string_value(); + consume(TOKEN_RPAREN, "Expected ')'"); + return strlen(str); + } else if (strcmp(func_name, "strchr") == 0) { + const char* str = get_string_value(); + consume(TOKEN_COMMA, "Expected ','"); + double ch = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + const char* found = strchr(str, (int)ch); + return found ? (double)(found - str) : -1; + } else if (strcmp(func_name, "strstr") == 0) { + const char* str = get_string_value(); + consume(TOKEN_COMMA, "Expected ','"); + const char* substr = get_string_value(); + consume(TOKEN_RPAREN, "Expected ')'"); + const char* found = strstr(str, substr); + return found ? (double)(found - str) : -1; + } else if (strcmp(func_name, "tolower") == 0) { + const char* str = 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, str, MAX_STRING - 1); + for (int j = 0; vars[i].value.str_value[j]; j++) { + vars[i].value.str_value[j] = tolower(vars[i].value.str_value[j]); + } + vars[i].value.type = VAL_STRING; + return 0; + } + } + strncpy(vars[var_count].name, "__tmp_str_result", 63); + strncpy(vars[var_count].value.str_value, str, MAX_STRING - 1); + for (int j = 0; vars[var_count].value.str_value[j]; j++) { + vars[var_count].value.str_value[j] = tolower(vars[var_count].value.str_value[j]); + } + vars[var_count].value.type = VAL_STRING; + var_count++; + return 0; + } else if (strcmp(func_name, "toupper") == 0) { + const char* str = 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, str, MAX_STRING - 1); + for (int j = 0; vars[i].value.str_value[j]; j++) { + vars[i].value.str_value[j] = toupper(vars[i].value.str_value[j]); + } + vars[i].value.type = VAL_STRING; + return 0; + } + } + strncpy(vars[var_count].name, "__tmp_str_result", 63); + strncpy(vars[var_count].value.str_value, str, MAX_STRING - 1); + for (int j = 0; vars[var_count].value.str_value[j]; j++) { + vars[var_count].value.str_value[j] = toupper(vars[var_count].value.str_value[j]); + } + vars[var_count].value.type = VAL_STRING; + var_count++; + return 0; + } else if (strcmp(func_name, "asin") == 0) { + double val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + return asin(val); + } else if (strcmp(func_name, "acos") == 0) { + double val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + return acos(val); + } else if (strcmp(func_name, "atan") == 0) { + double val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + return atan(val); + } else if (strcmp(func_name, "atan2") == 0) { + double y = evaluate(); + consume(TOKEN_COMMA, "Expected ','"); + double x = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + return atan2(y, x); + } else if (strcmp(func_name, "log10") == 0) { + double val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + return log10(val); + } else if (strcmp(func_name, "exp") == 0) { + double val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + return exp(val); + } else if (strcmp(func_name, "system") == 0) { + const char* cmd = get_string_value(); + consume(TOKEN_RPAREN, "Expected ')'"); + return (double)system(cmd); + } else if (strcmp(func_name, "getenv") == 0) { + const char* name = get_string_value(); + consume(TOKEN_RPAREN, "Expected ')'"); + const char* val = getenv(name); + if (val) { + for (int i = 0; i < var_count; i++) { + if (strcmp(vars[i].name, "__tmp_str_result") == 0) { + strncpy(vars[i].value.str_value, val, 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, val, 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, "sleep") == 0) { + double seconds = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + #ifdef OS_WINDOWS + Sleep((DWORD)(seconds * 1000)); + #else + sleep((unsigned int)seconds); + #endif + return 0; + } else if (strcmp(func_name, "push") == 0) { + consume(TOKEN_IDENTIFIER, "Expected array variable"); + const char* name = tokens[current_token - 1].value; + consume(TOKEN_COMMA, "Expected ','"); + double val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + Value* arr = find_variable(name); + if (!arr) error("Undefined variable: %s", name); + if (arr->type != VAL_ARRAY) { + arr->type = VAL_ARRAY; + arr->arr_size = 0; + } + if (arr->arr_size >= MAX_ARRAY_SIZE) { + error("Array size exceeds maximum (%d)", MAX_ARRAY_SIZE); + } + arr->arr_value[arr->arr_size++] = val; + return 0; + } else if (strcmp(func_name, "pop") == 0) { + consume(TOKEN_IDENTIFIER, "Expected array variable"); + const char* name = tokens[current_token - 1].value; + consume(TOKEN_RPAREN, "Expected ')'"); + Value* arr = find_variable(name); + if (!arr || arr->type != VAL_ARRAY || arr->arr_size == 0) { + error("Cannot pop from empty array"); + } + return arr->arr_value[--arr->arr_size]; + } else if (strcmp(func_name, "insert") == 0) { + consume(TOKEN_IDENTIFIER, "Expected array variable"); + const char* name = tokens[current_token - 1].value; + consume(TOKEN_COMMA, "Expected ','"); + int index = (int)evaluate(); + consume(TOKEN_COMMA, "Expected ','"); + double val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + Value* arr = find_variable(name); + if (!arr || arr->type != VAL_ARRAY) error("Invalid array"); + if (index < 0 || index > arr->arr_size) error("Array index out of bounds"); + if (arr->arr_size >= MAX_ARRAY_SIZE) error("Array full"); + for (int i = arr->arr_size; i > index; i--) { + arr->arr_value[i] = arr->arr_value[i - 1]; + } + arr->arr_value[index] = val; + arr->arr_size++; + return 0; + } else if (strcmp(func_name, "remove") == 0) { + consume(TOKEN_IDENTIFIER, "Expected array variable"); + const char* name = tokens[current_token - 1].value; + consume(TOKEN_COMMA, "Expected ','"); + int index = (int)evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + Value* arr = find_variable(name); + if (!arr || arr->type != VAL_ARRAY) error("Invalid array"); + if (index < 0 || index >= arr->arr_size) error("Array index out of bounds"); + double result = arr->arr_value[index]; + for (int i = index; i < arr->arr_size - 1; i++) { + arr->arr_value[i] = arr->arr_value[i + 1]; + } + arr->arr_size--; + return result; } else { int func_idx = find_function(func_name); if (func_idx >= 0) { @@ -1002,8 +1269,19 @@ double parse_logic_or() { return left; } +double parse_ternary() { + double cond = parse_logic_or(); + if (match(TOKEN_QUESTION)) { + double true_val = parse_ternary(); + consume(TOKEN_COLON, "Expected ':' in ternary expression"); + double false_val = parse_ternary(); + return cond != 0 ? true_val : false_val; + } + return cond; +} + double evaluate() { - return parse_logic_or(); + return parse_ternary(); } void execute_print() { diff --git a/test_io.txt b/test_io.txt new file mode 100644 index 0000000..557db03 --- /dev/null +++ b/test_io.txt @@ -0,0 +1 @@ +Hello World diff --git a/test_new.ora b/test_new.ora new file mode 100644 index 0000000..96c51b4 --- /dev/null +++ b/test_new.ora @@ -0,0 +1,72 @@ +echo("=== 新功能测试 ==="); + +echo(""); +echo("=== 三元运算符测试 ==="); +let a = 10; +let b = 20; +let max = (a > b) ? a : b; +echo("max =", max); +let min = (a < b) ? a : b; +echo("min =", min); + +echo(""); +echo("=== 文件IO测试 ==="); +fopen("test_io.txt", "w"); +fputs("Hello World\n", __tmp_file_result); +fclose(__tmp_file_result); + +fopen("test_io.txt", "r"); +fgets(__tmp_file_result); +echo("读取第一行:", __tmp_str_result); +fclose(__tmp_file_result); + +echo(""); +echo("=== 字符串函数测试 ==="); +let s = "Hello World"; +echo("strlen(s) =", strlen(s)); +echo("strchr(s, 'W') =", strchr(s, 87)); +echo("strstr(s, 'World') =", strstr(s, "World")); + +toupper(s); +echo("toupper:", __tmp_str_result); + +tolower(__tmp_str_result); +echo("tolower:", __tmp_str_result); + +echo(""); +echo("=== 数学函数测试 ==="); +echo("sin(0) =", sin(0)); +echo("cos(0) =", cos(0)); +echo("asin(0) =", asin(0)); +echo("acos(1) =", acos(1)); +echo("atan(0) =", atan(0)); +echo("atan2(1, 0) =", atan2(1, 0)); +echo("log10(100) =", log10(100)); +echo("exp(1) =", exp(1)); + +echo(""); +echo("=== 数组操作测试 ==="); +let arr = [1, 2, 3]; +echo("初始数组:", arr); + +push(arr, 4); +echo("push后:", arr); + +let popped = pop(arr); +echo("pop出:", popped); +echo("pop后:", arr); + +insert(arr, 1, 99); +echo("insert后:", arr); + +let removed = remove(arr, 1); +echo("remove出:", removed); +echo("remove后:", arr); + +echo(""); +echo("=== 系统函数测试 ==="); +getenv("PATH"); +echo("PATH长度:", strlen(__tmp_str_result)); + +echo(""); +echo("所有新功能测试完成!"); \ No newline at end of file diff --git a/website/download/exe/Oraset-B-5.exe b/website/download/exe/Oraset-B-5.exe index 8d8db0d..86b6068 100644 Binary files a/website/download/exe/Oraset-B-5.exe and b/website/download/exe/Oraset-B-5.exe differ diff --git a/website/download/source/Oraset-B-5-source/src/main.c b/website/download/source/Oraset-B-5-source/src/main.c index f58fd3f..ab34f72 100644 --- a/website/download/source/Oraset-B-5-source/src/main.c +++ b/website/download/source/Oraset-B-5-source/src/main.c @@ -92,7 +92,8 @@ typedef enum { TOKEN_CATCH, TOKEN_THROW, TOKEN_ASTERISK, - TOKEN_LABEL + TOKEN_LABEL, + TOKEN_QUESTION } OraTokenType; typedef struct { @@ -409,6 +410,7 @@ void tokenize(const char* source) { case '!': add_token(TOKEN_NOT, "!"); break; case ':': add_token(TOKEN_COLON, ":"); break; case '.': add_token(TOKEN_DOT, "."); break; + case '?': add_token(TOKEN_QUESTION, "?"); break; default: error("Unknown character: '%c'", *p); } p++; @@ -786,6 +788,271 @@ double parse_call(const char* func_name) { } } return 0; + } else if (strcmp(func_name, "fopen") == 0) { + const char* filename = get_string_value(); + consume(TOKEN_COMMA, "Expected ','"); + const char* mode = get_string_value(); + consume(TOKEN_RPAREN, "Expected ')'"); + for (int i = 0; i < var_count; i++) { + if (strcmp(vars[i].name, "__tmp_file_result") == 0) { + FILE* fp = fopen(filename, mode); + vars[i].value.num_value = (double)(intptr_t)fp; + vars[i].value.type = VAL_NUMBER; + return 0; + } + } + strncpy(vars[var_count].name, "__tmp_file_result", 63); + FILE* fp = fopen(filename, mode); + vars[var_count].value.num_value = (double)(intptr_t)fp; + vars[var_count].value.type = VAL_NUMBER; + var_count++; + return 0; + } else if (strcmp(func_name, "fclose") == 0) { + double fp_val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + FILE* fp = (FILE*)(intptr_t)fp_val; + return (double)fclose(fp); + } else if (strcmp(func_name, "fgets") == 0) { + double fp_val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + FILE* fp = (FILE*)(intptr_t)fp_val; + char buf[MAX_STRING]; + if (fgets(buf, MAX_STRING, fp)) { + 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, "fputs") == 0) { + const char* str = get_string_value(); + consume(TOKEN_COMMA, "Expected ','"); + double fp_val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + FILE* fp = (FILE*)(intptr_t)fp_val; + return (double)fputs(str, fp); + } else if (strcmp(func_name, "fread") == 0) { + consume(TOKEN_IDENTIFIER, "Expected array variable"); + const char* name = tokens[current_token - 1].value; + consume(TOKEN_COMMA, "Expected ','"); + int count = (int)evaluate(); + consume(TOKEN_COMMA, "Expected ','"); + double fp_val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + FILE* fp = (FILE*)(intptr_t)fp_val; + Value* arr = find_variable(name); + if (!arr) error("Undefined variable: %s", name); + if (arr->type != VAL_ARRAY) { + arr->type = VAL_ARRAY; + arr->arr_size = 0; + } + double buf[MAX_ARRAY_SIZE]; + size_t read = fread(buf, sizeof(double), count, fp); + for (int i = 0; i < read && arr->arr_size + i < MAX_ARRAY_SIZE; i++) { + arr->arr_value[arr->arr_size + i] = buf[i]; + } + arr->arr_size += read; + return (double)read; + } else if (strcmp(func_name, "fwrite") == 0) { + consume(TOKEN_IDENTIFIER, "Expected array variable"); + const char* name = tokens[current_token - 1].value; + consume(TOKEN_COMMA, "Expected ','"); + int count = (int)evaluate(); + consume(TOKEN_COMMA, "Expected ','"); + double fp_val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + FILE* fp = (FILE*)(intptr_t)fp_val; + Value* arr = find_variable(name); + if (!arr || arr->type != VAL_ARRAY) error("Invalid array"); + size_t written = fwrite(arr->arr_value, sizeof(double), count, fp); + return (double)written; + } else if (strcmp(func_name, "strlen") == 0) { + const char* str = get_string_value(); + consume(TOKEN_RPAREN, "Expected ')'"); + return strlen(str); + } else if (strcmp(func_name, "strchr") == 0) { + const char* str = get_string_value(); + consume(TOKEN_COMMA, "Expected ','"); + double ch = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + const char* found = strchr(str, (int)ch); + return found ? (double)(found - str) : -1; + } else if (strcmp(func_name, "strstr") == 0) { + const char* str = get_string_value(); + consume(TOKEN_COMMA, "Expected ','"); + const char* substr = get_string_value(); + consume(TOKEN_RPAREN, "Expected ')'"); + const char* found = strstr(str, substr); + return found ? (double)(found - str) : -1; + } else if (strcmp(func_name, "tolower") == 0) { + const char* str = 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, str, MAX_STRING - 1); + for (int j = 0; vars[i].value.str_value[j]; j++) { + vars[i].value.str_value[j] = tolower(vars[i].value.str_value[j]); + } + vars[i].value.type = VAL_STRING; + return 0; + } + } + strncpy(vars[var_count].name, "__tmp_str_result", 63); + strncpy(vars[var_count].value.str_value, str, MAX_STRING - 1); + for (int j = 0; vars[var_count].value.str_value[j]; j++) { + vars[var_count].value.str_value[j] = tolower(vars[var_count].value.str_value[j]); + } + vars[var_count].value.type = VAL_STRING; + var_count++; + return 0; + } else if (strcmp(func_name, "toupper") == 0) { + const char* str = 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, str, MAX_STRING - 1); + for (int j = 0; vars[i].value.str_value[j]; j++) { + vars[i].value.str_value[j] = toupper(vars[i].value.str_value[j]); + } + vars[i].value.type = VAL_STRING; + return 0; + } + } + strncpy(vars[var_count].name, "__tmp_str_result", 63); + strncpy(vars[var_count].value.str_value, str, MAX_STRING - 1); + for (int j = 0; vars[var_count].value.str_value[j]; j++) { + vars[var_count].value.str_value[j] = toupper(vars[var_count].value.str_value[j]); + } + vars[var_count].value.type = VAL_STRING; + var_count++; + return 0; + } else if (strcmp(func_name, "asin") == 0) { + double val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + return asin(val); + } else if (strcmp(func_name, "acos") == 0) { + double val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + return acos(val); + } else if (strcmp(func_name, "atan") == 0) { + double val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + return atan(val); + } else if (strcmp(func_name, "atan2") == 0) { + double y = evaluate(); + consume(TOKEN_COMMA, "Expected ','"); + double x = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + return atan2(y, x); + } else if (strcmp(func_name, "log10") == 0) { + double val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + return log10(val); + } else if (strcmp(func_name, "exp") == 0) { + double val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + return exp(val); + } else if (strcmp(func_name, "system") == 0) { + const char* cmd = get_string_value(); + consume(TOKEN_RPAREN, "Expected ')'"); + return (double)system(cmd); + } else if (strcmp(func_name, "getenv") == 0) { + const char* name = get_string_value(); + consume(TOKEN_RPAREN, "Expected ')'"); + const char* val = getenv(name); + if (val) { + for (int i = 0; i < var_count; i++) { + if (strcmp(vars[i].name, "__tmp_str_result") == 0) { + strncpy(vars[i].value.str_value, val, 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, val, 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, "sleep") == 0) { + double seconds = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + #ifdef OS_WINDOWS + Sleep((DWORD)(seconds * 1000)); + #else + sleep((unsigned int)seconds); + #endif + return 0; + } else if (strcmp(func_name, "push") == 0) { + consume(TOKEN_IDENTIFIER, "Expected array variable"); + const char* name = tokens[current_token - 1].value; + consume(TOKEN_COMMA, "Expected ','"); + double val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + Value* arr = find_variable(name); + if (!arr) error("Undefined variable: %s", name); + if (arr->type != VAL_ARRAY) { + arr->type = VAL_ARRAY; + arr->arr_size = 0; + } + if (arr->arr_size >= MAX_ARRAY_SIZE) { + error("Array size exceeds maximum (%d)", MAX_ARRAY_SIZE); + } + arr->arr_value[arr->arr_size++] = val; + return 0; + } else if (strcmp(func_name, "pop") == 0) { + consume(TOKEN_IDENTIFIER, "Expected array variable"); + const char* name = tokens[current_token - 1].value; + consume(TOKEN_RPAREN, "Expected ')'"); + Value* arr = find_variable(name); + if (!arr || arr->type != VAL_ARRAY || arr->arr_size == 0) { + error("Cannot pop from empty array"); + } + return arr->arr_value[--arr->arr_size]; + } else if (strcmp(func_name, "insert") == 0) { + consume(TOKEN_IDENTIFIER, "Expected array variable"); + const char* name = tokens[current_token - 1].value; + consume(TOKEN_COMMA, "Expected ','"); + int index = (int)evaluate(); + consume(TOKEN_COMMA, "Expected ','"); + double val = evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + Value* arr = find_variable(name); + if (!arr || arr->type != VAL_ARRAY) error("Invalid array"); + if (index < 0 || index > arr->arr_size) error("Array index out of bounds"); + if (arr->arr_size >= MAX_ARRAY_SIZE) error("Array full"); + for (int i = arr->arr_size; i > index; i--) { + arr->arr_value[i] = arr->arr_value[i - 1]; + } + arr->arr_value[index] = val; + arr->arr_size++; + return 0; + } else if (strcmp(func_name, "remove") == 0) { + consume(TOKEN_IDENTIFIER, "Expected array variable"); + const char* name = tokens[current_token - 1].value; + consume(TOKEN_COMMA, "Expected ','"); + int index = (int)evaluate(); + consume(TOKEN_RPAREN, "Expected ')'"); + Value* arr = find_variable(name); + if (!arr || arr->type != VAL_ARRAY) error("Invalid array"); + if (index < 0 || index >= arr->arr_size) error("Array index out of bounds"); + double result = arr->arr_value[index]; + for (int i = index; i < arr->arr_size - 1; i++) { + arr->arr_value[i] = arr->arr_value[i + 1]; + } + arr->arr_size--; + return result; } else { int func_idx = find_function(func_name); if (func_idx >= 0) { @@ -1002,8 +1269,19 @@ double parse_logic_or() { return left; } +double parse_ternary() { + double cond = parse_logic_or(); + if (match(TOKEN_QUESTION)) { + double true_val = parse_ternary(); + consume(TOKEN_COLON, "Expected ':' in ternary expression"); + double false_val = parse_ternary(); + return cond != 0 ? true_val : false_val; + } + return cond; +} + double evaluate() { - return parse_logic_or(); + return parse_ternary(); } void execute_print() {