Files
Claro/src/claro.c
T
2026-02-17 23:14:16 +01:00

1463 lines
47 KiB
C

/*
* CLARO - Programming for Everyone
* An accessible scripting language for people with learning disabilities
*
* Design principles:
* - CAPITALIZED keywords for clarity
* - Natural English-like syntax
* - Helpful error messages
* - Visual feedback with colors
* - Game development ready
* - Portable ANSI C
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include <time.h>
#ifdef _WIN32
#define strcasecmp _stricmp
#else
#include <strings.h>
#endif
/* ============================================================================
* CONFIGURATION
* ============================================================================ */
#define MAX_LINE_LENGTH 512
#define MAX_VARIABLES 200
#define MAX_FUNCTIONS 100
#define MAX_CODE_LINES 2000
#define MAX_PARAMS 10
#define MAX_SPRITES 50
/* ANSI color codes */
#define COLOR_RESET "\x1b[0m"
#define COLOR_GREEN "\x1b[32m"
#define COLOR_RED "\x1b[31m"
#define COLOR_YELLOW "\x1b[33m"
#define COLOR_BLUE "\x1b[34m"
#define COLOR_MAGENTA "\x1b[35m"
#define COLOR_CYAN "\x1b[36m"
/* High contrast mode */
#define HC_RESET "\x1b[0m"
#define HC_GREEN "\x1b[97;42m"
#define HC_RED "\x1b[97;41m"
#define HC_YELLOW "\x1b[30;43m"
/* ============================================================================
* DATA STRUCTURES
* ============================================================================ */
typedef enum {
TYPE_NUMBER,
TYPE_TEXT,
TYPE_YES_NO /* Boolean - friendlier for beginners */
} VarType;
typedef struct {
char name[MAX_LINE_LENGTH];
VarType type;
union {
double number;
char text[MAX_LINE_LENGTH];
int yes_no; /* 1 = YES, 0 = NO */
} value;
} Variable;
typedef struct {
char name[MAX_LINE_LENGTH];
char params[MAX_PARAMS][MAX_LINE_LENGTH];
int param_count;
char code[MAX_CODE_LINES][MAX_LINE_LENGTH];
int code_line_count;
} Function;
typedef struct {
char name[MAX_LINE_LENGTH];
int x, y;
int width, height;
int visible;
char image[MAX_LINE_LENGTH];
} Sprite;
typedef struct {
Variable vars[MAX_VARIABLES];
int var_count;
Function funcs[MAX_FUNCTIONS];
int func_count;
Sprite sprites[MAX_SPRITES];
int sprite_count;
int high_contrast;
int line_number;
int in_function;
double return_value;
int should_return;
VarType return_type;
char return_text[MAX_LINE_LENGTH];
char current_function[MAX_LINE_LENGTH];
int call_depth;
/* Debug / step mode */
int debug_mode;
} Runtime;
static Runtime runtime;
/* ============================================================================
* UTILITY FUNCTIONS
* ============================================================================ */
char *trim(char *str) {
char *end;
while (isspace((unsigned char)*str)) str++;
if (*str == 0) return str;
end = str + strlen(str) - 1;
while (end > str && isspace((unsigned char)*end)) end--;
end[1] = '\0';
return str;
}
void to_upper(char *str) {
for (; *str; ++str) *str = toupper((unsigned char)*str);
}
void print_colored(const char *color, const char *msg) {
if (runtime.high_contrast) {
if (strcmp(color, COLOR_RED) == 0) printf("%s%s%s", HC_RED, msg, HC_RESET);
else if (strcmp(color, COLOR_GREEN) == 0) printf("%s%s%s", HC_GREEN, msg, HC_RESET);
else if (strcmp(color, COLOR_YELLOW) == 0) printf("%s%s%s", HC_YELLOW, msg, HC_RESET);
else printf("%s", msg);
} else {
printf("%s%s%s", color, msg, COLOR_RESET);
}
}
/* ============================================================================
* ERROR HANDLING
* ============================================================================ */
void error_message(const char *message, const char *help) {
print_colored(COLOR_RED, "\n*** OOPS! ***\n");
printf("Line %d: %s\n", runtime.line_number, message);
if (runtime.call_depth > 0 && strlen(runtime.current_function) > 0) {
print_colored(COLOR_MAGENTA, "In function: ");
printf("%s\n", runtime.current_function);
}
if (help && strlen(help) > 0) {
print_colored(COLOR_YELLOW, "Tip: ");
printf("%s\n\n", help);
}
}
/* ============================================================================
* SPELL-CHECK / "DID YOU MEAN?" HELPER
* ============================================================================ */
static int levenshtein(const char *s1, const char *s2) {
int l1 = (int)strlen(s1), l2 = (int)strlen(s2);
int dp[l1+1][l2+1];
for (int i = 0; i <= l1; i++) dp[i][0] = i;
for (int j = 0; j <= l2; j++) dp[0][j] = j;
for (int i = 1; i <= l1; i++)
for (int j = 1; j <= l2; j++) {
int cost = (toupper(s1[i-1]) == toupper(s2[j-1])) ? 0 : 1;
dp[i][j] = dp[i-1][j]+1;
if (dp[i][j-1]+1 < dp[i][j]) dp[i][j] = dp[i][j-1]+1;
if (dp[i-1][j-1]+cost < dp[i][j]) dp[i][j] = dp[i-1][j-1]+cost;
}
return dp[l1][l2];
}
void suggest_command(const char *bad_cmd) {
static const char *commands[] = {
"SAY","SET","ASK","IF","ELSE","ENDIF",
"REPEAT","UNTIL","DO","TIMES","DONE",
"TEACH","LEARNED","CALL","RETURN",
"WAIT","HELP","EXIT","VARS","DEBUG",
"CREATE","MOVE","SHOW","HIDE",
NULL
};
int best_dist = 999;
const char *best = NULL;
char upper[MAX_LINE_LENGTH];
strncpy(upper, bad_cmd, MAX_LINE_LENGTH-1);
to_upper(upper);
for (int i = 0; commands[i]; i++) {
int d = levenshtein(upper, commands[i]);
if (d < best_dist && d <= 2) { best_dist = d; best = commands[i]; }
}
if (best) {
print_colored(COLOR_YELLOW, "Did you mean: ");
print_colored(COLOR_GREEN, best);
printf("?\n");
}
}
/* ============================================================================
* VARIABLE STORE
* ============================================================================ */
Variable *find_var(const char *name) {
for (int i = 0; i < runtime.var_count; i++)
if (strcasecmp(runtime.vars[i].name, name) == 0)
return &runtime.vars[i];
return NULL;
}
Variable *get_or_create_var(const char *name) {
Variable *v = find_var(name);
if (v) return v;
if (runtime.var_count >= MAX_VARIABLES) {
error_message("Too many variables!", "Remove some variables you no longer need.");
return NULL;
}
v = &runtime.vars[runtime.var_count++];
memset(v, 0, sizeof(Variable));
strncpy(v->name, name, MAX_LINE_LENGTH-1);
return v;
}
void set_number(const char *name, double val) {
Variable *v = get_or_create_var(name);
if (!v) return;
v->type = TYPE_NUMBER;
v->value.number = val;
}
void set_text(const char *name, const char *val) {
Variable *v = get_or_create_var(name);
if (!v) return;
v->type = TYPE_TEXT;
strncpy(v->value.text, val, MAX_LINE_LENGTH-1);
}
void set_yes_no(const char *name, int val) {
Variable *v = get_or_create_var(name);
if (!v) return;
v->type = TYPE_YES_NO;
v->value.yes_no = val;
}
/* ============================================================================
* EXPRESSION EVALUATOR
* ============================================================================ */
/* -------------------------------------------------
* Number expressions
* Supports:
* - +, -, *, / with normal precedence
* - parentheses: ( ... )
* - variables (NUMBER / YES-NO treated as 0/1)
* - functions: SQRT(x), ABS(x), ROUND(x), FLOOR(x), CEIL(x)
* - RANDOM n (1..n)
* ------------------------------------------------- */
typedef struct {
const char *s;
int pos;
int had_error;
} NumParser;
static void np_skip_ws(NumParser *p) {
while (p->s[p->pos] && isspace((unsigned char)p->s[p->pos])) p->pos++;
}
static int np_peek(NumParser *p) {
np_skip_ws(p);
return (unsigned char)p->s[p->pos];
}
static int np_match(NumParser *p, char c) {
np_skip_ws(p);
if (p->s[p->pos] == c) { p->pos++; return 1; }
return 0;
}
static void np_set_error(NumParser *p, const char *msg, const char *tip) {
if (!p->had_error) {
error_message(msg, tip);
p->had_error = 1;
}
}
static double np_parse_expr(NumParser *p); /* forward */
static int np_parse_identifier(NumParser *p, char out[MAX_LINE_LENGTH]) {
np_skip_ws(p);
int i = 0;
if (!isalpha((unsigned char)p->s[p->pos]) && p->s[p->pos] != '_') return 0;
while (p->s[p->pos] && (isalnum((unsigned char)p->s[p->pos]) || p->s[p->pos] == '_')) {
if (i < MAX_LINE_LENGTH-1) out[i++] = p->s[p->pos];
p->pos++;
}
out[i] = '\0';
return 1;
}
static double np_parse_factor(NumParser *p) {
np_skip_ws(p);
/* unary + / - */
if (np_match(p, '+')) return np_parse_factor(p);
if (np_match(p, '-')) return -np_parse_factor(p);
/* parenthesis */
if (np_match(p, '(')) {
double v = np_parse_expr(p);
if (!np_match(p, ')')) {
np_set_error(p, "Missing ) in math", "Check your parentheses, like: (2 + 3) * 4");
}
return v;
}
/* number literal */
{
np_skip_ws(p);
char *endptr = NULL;
double v = strtod(p->s + p->pos, &endptr);
if (endptr && endptr != (p->s + p->pos)) {
p->pos = (int)(endptr - p->s);
return v;
}
}
/* identifier: variable or function */
char name[MAX_LINE_LENGTH];
if (np_parse_identifier(p, name)) {
char upper[MAX_LINE_LENGTH];
strncpy(upper, name, MAX_LINE_LENGTH-1);
upper[MAX_LINE_LENGTH-1] = '\0';
to_upper(upper);
/* RANDOM n (space separated) */
if (strcmp(upper, "RANDOM") == 0) {
double n = np_parse_factor(p);
if (n <= 0) { np_set_error(p, "RANDOM needs a positive number", "Try: RANDOM 10"); return 0; }
return (double)(rand() % (int)n) + 1;
}
/* function call NAME(expr) */
if (np_match(p, '(')) {
double a = np_parse_expr(p);
if (!np_match(p, ')')) {
np_set_error(p, "Missing ) after function", "Example: SQRT(16)");
}
if (strcmp(upper,"SQRT") == 0) return sqrt(a);
if (strcmp(upper,"ABS") == 0) return fabs(a);
if (strcmp(upper,"ROUND") == 0) return round(a);
if (strcmp(upper,"FLOOR") == 0) return floor(a);
if (strcmp(upper,"CEIL") == 0) return ceil(a);
np_set_error(p, "Unknown math function", "Try: SQRT(x), ABS(x), ROUND(x), FLOOR(x), CEIL(x)");
return 0;
}
/* variable lookup */
Variable *v = find_var(name);
if (v) {
if (v->type == TYPE_NUMBER) return v->value.number;
if (v->type == TYPE_YES_NO) return v->value.yes_no;
np_set_error(p, "That value is not a number", "Use a NUMBER variable in math, like: SET score TO 10");
return 0;
}
np_set_error(p, "Unknown number or variable", "Tip: Use VARS to see your variables");
return 0;
}
/* nothing matched */
np_set_error(p, "I couldn't understand that math", "Example: 2 + 3 * 4 or (2 + 3) * 4");
return 0;
}
static double np_parse_term(NumParser *p) {
double v = np_parse_factor(p);
for (;;) {
if (np_match(p, '*')) {
v *= np_parse_factor(p);
} else if (np_match(p, '/')) {
double r = np_parse_factor(p);
if (r == 0) {
np_set_error(p, "Divide by zero", "Make sure the right side is not 0");
return 0;
}
v /= r;
} else {
break;
}
}
return v;
}
static double np_parse_expr(NumParser *p) {
double v = np_parse_term(p);
for (;;) {
if (np_match(p, '+')) {
v += np_parse_term(p);
} else if (np_match(p, '-')) {
v -= np_parse_term(p);
} else {
break;
}
}
return v;
}
double eval_number_expr(const char *expr_in) {
NumParser p = { expr_in, 0, 0 };
double v = np_parse_expr(&p);
np_skip_ws(&p);
if (!p.had_error && p.s[p.pos] != '\0') {
error_message("Extra text in math", "Tip: check for extra words or symbols");
return 0;
}
return v;
}
int eval_yes_no_expr(const char *expr_in) {
char expr[MAX_LINE_LENGTH];
strncpy(expr, expr_in, MAX_LINE_LENGTH-1);
char *e = trim(expr);
if (strcasecmp(e,"YES")==0 || strcasecmp(e,"TRUE")==0) return 1;
if (strcasecmp(e,"NO")==0 || strcasecmp(e,"FALSE")==0) return 0;
Variable *v = find_var(e);
if (v && v->type == TYPE_YES_NO) return v->value.yes_no;
char left[MAX_LINE_LENGTH], op[MAX_LINE_LENGTH], right[MAX_LINE_LENGTH];
/* Try "left OP right" */
if (sscanf(e, "%s %s %[^\n]", left, op, right) == 3) {
to_upper(op);
if (strcmp(op,"IS")==0 || strcmp(op,"EQUALS")==0) {
/* String comparison */
Variable *lv = find_var(left);
if (lv && lv->type == TYPE_TEXT) {
char rstr[MAX_LINE_LENGTH];
strncpy(rstr, right, MAX_LINE_LENGTH-1);
if (rstr[0]=='"') { memmove(rstr,rstr+1,strlen(rstr)); }
size_t rl = strlen(rstr);
if (rl>0 && rstr[rl-1]=='"') rstr[rl-1]='\0';
return strcasecmp(lv->value.text, rstr) == 0;
}
return fabs(eval_number_expr(left) - eval_number_expr(right)) < 0.0001;
}
if (strcmp(op,"BIGGER")==0 || strcmp(op,"GREATER")==0)
return eval_number_expr(left) > eval_number_expr(right);
if (strcmp(op,"SMALLER")==0 || strcmp(op,"LESS")==0)
return eval_number_expr(left) < eval_number_expr(right);
if (strcmp(op,"NOT")==0 && (strcmp(right,"IS")==0 || strcmp(right,"EQUALS")==0)) {
/* "x NOT IS y" -> not equal */
char r2[MAX_LINE_LENGTH];
sscanf(e, "%*s %*s %*s %[^\n]", r2);
return fabs(eval_number_expr(left) - eval_number_expr(r2)) >= 0.0001;
}
}
/* If it's just a variable holding a number, treat nonzero as YES */
if (v && v->type == TYPE_NUMBER) return v->value.number != 0;
return 0;
}
/* ============================================================================
* COMMAND IMPLEMENTATIONS
* ============================================================================ */
void execute_line(char *line);
void execute_code(char code[][MAX_LINE_LENGTH], int line_count);
void execute_block(char code[][MAX_LINE_LENGTH], int line_count, int start, int end);
/* SAY */
void cmd_say(char *args) {
args = trim(args);
/* Strip surrounding quotes */
if ((args[0]=='"' || args[0]=='\'') ) {
size_t len = strlen(args);
if (len>1 && (args[len-1]=='"' || args[len-1]=='\'')) {
args[len-1] = '\0';
args++;
}
}
Variable *v = find_var(args);
if (v) {
if (v->type == TYPE_NUMBER) {
if (floor(v->value.number) == v->value.number)
printf("%.0f\n", v->value.number);
else
printf("%g\n", v->value.number);
} else if (v->type == TYPE_TEXT) {
printf("%s\n", v->value.text);
} else {
printf("%s\n", v->value.yes_no ? "YES" : "NO");
}
} else {
printf("%s\n", args);
}
}
/* SET */
void cmd_set(char *args) {
char name[MAX_LINE_LENGTH];
char value[MAX_LINE_LENGTH];
/* Accept "SET name TO value" (case-insensitive TO) */
char *to_pos = NULL;
char upper_args[MAX_LINE_LENGTH];
strncpy(upper_args, args, MAX_LINE_LENGTH-1);
to_upper(upper_args);
to_pos = strstr(upper_args, " TO ");
if (!to_pos) {
error_message("Could not understand SET command",
"Try: SET score TO 100");
return;
}
int name_len = (int)(to_pos - upper_args);
strncpy(name, args, name_len);
name[name_len] = '\0';
trim(name);
strncpy(value, args + name_len + 4, MAX_LINE_LENGTH-1);
trim(value);
/* Quoted string? */
if (value[0]=='"' || value[0]=='\'') {
size_t len = strlen(value);
if (len>1 && (value[len-1]=='"' || value[len-1]=='\'')) {
value[len-1] = '\0';
set_text(name, value+1);
return;
}
}
/* YES/NO */
if (strcasecmp(value,"YES")==0 || strcasecmp(value,"TRUE")==0) { set_yes_no(name,1); return; }
if (strcasecmp(value,"NO")==0 || strcasecmp(value,"FALSE")==0) { set_yes_no(name,0); return; }
/* Number / expression */
set_number(name, eval_number_expr(value));
}
/* ASK */
void cmd_ask(char *args) {
char name[MAX_LINE_LENGTH], prompt[MAX_LINE_LENGTH];
if (sscanf(args, "%s %[^\n]", name, prompt) >= 1) {
if (strlen(prompt) > 0)
printf("%s ", prompt);
char input[MAX_LINE_LENGTH];
if (fgets(input, sizeof(input), stdin)) {
input[strcspn(input,"\n")] = 0;
trim(input);
char *endptr;
double num = strtod(input, &endptr);
if (*endptr == '\0')
set_number(name, num);
else
set_text(name, input);
}
} else {
error_message("Could not understand ASK command",
"Try: ASK name What is your name?");
}
}
/* WAIT */
void cmd_wait(char *args) {
double seconds = eval_number_expr(args);
if (seconds > 0) {
clock_t start = clock();
while ((double)(clock()-start)/CLOCKS_PER_SEC < seconds);
}
}
/* VARS - display all variables */
void cmd_vars(void) {
if (runtime.var_count == 0) {
printf("No variables set yet.\n");
return;
}
print_colored(COLOR_CYAN, "=== Variables ===\n");
for (int i = 0; i < runtime.var_count; i++) {
Variable *v = &runtime.vars[i];
printf(" %-20s = ", v->name);
if (v->type == TYPE_NUMBER) {
if (floor(v->value.number)==v->value.number)
printf("%.0f\n", v->value.number);
else
printf("%g\n", v->value.number);
} else if (v->type == TYPE_TEXT) {
printf("\"%s\"\n", v->value.text);
} else {
printf("%s\n", v->value.yes_no ? "YES" : "NO");
}
}
}
/* HELP */
void cmd_help(char *topic) {
topic = trim(topic);
to_upper(topic);
if (strlen(topic) == 0 || strcmp(topic,"ALL")==0) {
print_colored(COLOR_GREEN, "\n=== CLARO Commands ===\n\n");
printf("Showing / Asking:\n");
printf(" SAY \"text\" - Show a message\n");
printf(" SAY variable - Show a variable's value\n");
printf(" ASK var What? - Ask the user a question\n\n");
printf("Variables:\n");
printf(" SET var TO value - Remember something\n");
printf(" VARS - Show all variables\n\n");
printf("Making Decisions:\n");
printf(" IF condition - Check something\n");
printf(" ...\n");
printf(" ELSE - Otherwise\n");
printf(" ...\n");
printf(" ENDIF - End the IF block\n\n");
printf("Repeating:\n");
printf(" DO 5 TIMES - Repeat 5 times\n");
printf(" ...\n");
printf(" DONE\n\n");
printf(" REPEAT 5 - Old-style (still works)\n");
printf(" ...\n");
printf(" UNTIL\n\n");
printf("Functions (Reusable Blocks):\n");
printf(" TEACH name TAKES a, b - Teach CLARO a new skill\n");
printf(" ...\n");
printf(" RETURN value - Send a value back\n");
printf(" LEARNED - End the TEACH block\n\n");
printf(" CALL name WITH a, b - Use a function (stores RESULT)\n\n");
printf("Sprites (Game Objects):\n");
printf(" CREATE SPRITE name - Create a game character\n");
printf(" MOVE SPRITE name TO x y\n");
printf(" SHOW SPRITE name\n");
printf(" HIDE SPRITE name\n\n");
printf("Other:\n");
printf(" WAIT 2 - Pause for 2 seconds\n");
printf(" DEBUG ON/OFF - Toggle debug mode\n");
printf(" HELP SAY - Help on a specific command\n");
printf(" EXIT / QUIT - Exit CLARO\n\n");
printf("Comparisons in IF:\n");
printf(" IS / EQUALS - Equal?\n");
printf(" BIGGER / GREATER - First one larger?\n");
printf(" SMALLER / LESS - First one smaller?\n\n");
} else if (strcmp(topic,"SAY")==0) {
printf("\nSAY - Show a message or variable\n");
printf(" SAY \"Hello!\"\n");
printf(" SAY score\n");
} else if (strcmp(topic,"SET")==0) {
printf("\nSET - Store a value in a variable\n");
printf(" SET score TO 100\n");
printf(" SET name TO \"Alice\"\n");
printf(" SET score TO score + 10\n");
} else if (strcmp(topic,"ASK")==0) {
printf("\nASK - Get input from the user\n");
printf(" ASK name What is your name?\n");
printf(" ASK age How old are you?\n");
} else if (strcmp(topic,"IF")==0) {
printf("\nIF - Make a decision\n");
printf(" IF score IS 100\n");
printf(" SAY \"Perfect!\"\n");
printf(" ELSE\n");
printf(" SAY \"Try again!\"\n");
printf(" ENDIF\n");
} else if (strcmp(topic,"REPEAT")==0 || strcmp(topic,"DO")==0) {
printf("\nDO / REPEAT - Repeat a block\n");
printf(" DO 5 TIMES\n");
printf(" SAY \"Hello!\"\n");
printf(" DONE\n");
} else if (strcmp(topic,"TEACH")==0) {
printf("\nTEACH - Define a reusable function\n");
printf(" TEACH greet TAKES name\n");
printf(" SAY \"Hello \"\n");
printf(" SAY name\n");
printf(" RETURN 1\n");
printf(" LEARNED\n");
} else if (strcmp(topic,"CALL")==0) {
printf("\nCALL - Run a function you taught CLARO\n");
printf(" CALL greet WITH \"Sam\"\n");
printf(" SAY RESULT\n");
printf("\nTip: If you have more than one value, separate them with commas.\n");
} else if (strcmp(topic,"RETURN")==0) {
printf("\nRETURN - Send a value back from a function\n");
printf(" RETURN 42\n");
printf(" RETURN \"Done!\"\n");
printf(" RETURN YES\n");
} else {
printf("No help found for '%s'. Type HELP for all commands.\n", topic);
}
}
/* SPRITE commands */
Sprite *find_sprite(const char *name) {
for (int i = 0; i < runtime.sprite_count; i++)
if (strcasecmp(runtime.sprites[i].name, name) == 0)
return &runtime.sprites[i];
return NULL;
}
void cmd_create_sprite(char *args) {
char name[MAX_LINE_LENGTH];
sscanf(args, "%s", name);
if (find_sprite(name)) {
printf("Sprite '%s' already exists.\n", name); return;
}
if (runtime.sprite_count >= MAX_SPRITES) {
error_message("Too many sprites!", "Remove some sprites first."); return;
}
Sprite *s = &runtime.sprites[runtime.sprite_count++];
memset(s, 0, sizeof(Sprite));
strncpy(s->name, name, MAX_LINE_LENGTH-1);
s->visible = 1; s->width = 32; s->height = 32;
printf("Sprite '%s' created.\n", name);
}
void cmd_move_sprite(char *args) {
/* MOVE SPRITE name TO x y */
char name[MAX_LINE_LENGTH]; int x, y;
if (sscanf(args, "%s TO %d %d", name, &x, &y) == 3 ||
sscanf(args, "%s to %d %d", name, &x, &y) == 3) {
Sprite *s = find_sprite(name);
if (!s) { error_message("Sprite not found", "Use CREATE SPRITE first"); return; }
s->x = x; s->y = y;
printf("Moved '%s' to (%d, %d)\n", name, x, y);
} else {
error_message("Could not understand MOVE SPRITE",
"Try: MOVE SPRITE player TO 100 200");
}
}
void cmd_show_sprite(char *args) {
char name[MAX_LINE_LENGTH]; sscanf(args, "%s", name);
Sprite *s = find_sprite(name);
if (!s) { error_message("Sprite not found","Use CREATE SPRITE first"); return; }
s->visible = 1; printf("Sprite '%s' is now visible.\n", name);
}
void cmd_hide_sprite(char *args) {
char name[MAX_LINE_LENGTH]; sscanf(args, "%s", name);
Sprite *s = find_sprite(name);
if (!s) { error_message("Sprite not found","Use CREATE SPRITE first"); return; }
s->visible = 0; printf("Sprite '%s' is now hidden.\n", name);
}
/* ============================================================================
* USER FUNCTIONS (TEACH / CALL / RETURN)
* ============================================================================ */
Function *find_func(const char *name) {
for (int i = 0; i < runtime.func_count; i++)
if (strcasecmp(runtime.funcs[i].name, name) == 0)
return &runtime.funcs[i];
return NULL;
}
static void clear_function(Function *f) {
memset(f, 0, sizeof(Function));
}
static void upsert_function(const Function *src) {
Function *existing = find_func(src->name);
if (existing) {
*existing = *src;
return;
}
if (runtime.func_count >= MAX_FUNCTIONS) {
error_message("Too many functions!", "Tip: keep your program small, or reuse functions you already have.");
return;
}
runtime.funcs[runtime.func_count++] = *src;
}
static int is_word(const char *w, const char *kw) { return strcasecmp(w, kw) == 0; }
/* Parse: TEACH name [TAKES a, b, c] */
static void parse_teach_header(const char *rest, Function *out) {
clear_function(out);
char tmp[MAX_LINE_LENGTH];
strncpy(tmp, rest, MAX_LINE_LENGTH-1);
tmp[MAX_LINE_LENGTH-1] = '\0';
char *p = trim(tmp);
/* name */
char name[MAX_LINE_LENGTH] = {0};
sscanf(p, "%s", name);
strncpy(out->name, name, MAX_LINE_LENGTH-1);
/* move past name */
p = p + strlen(name);
p = trim(p);
out->param_count = 0;
if (*p == 0) return;
char kw[MAX_LINE_LENGTH] = {0};
sscanf(p, "%s", kw);
if (!is_word(kw, "TAKES") && !is_word(kw, "WITH")) return;
p = trim(p + strlen(kw));
if (*p == 0) return;
/* split by commas, otherwise treat as space-separated simple names */
if (strchr(p, ',')) {
for (char *tok = strtok(p, ",");
tok && out->param_count < MAX_PARAMS;
tok = strtok(NULL, ",")) {
tok = trim(tok);
if (*tok == 0) continue;
/* take first word as param name */
char param[MAX_LINE_LENGTH] = {0};
sscanf(tok, "%s", param);
strncpy(out->params[out->param_count++], param, MAX_LINE_LENGTH-1);
}
} else {
/* space-separated */
for (char *tok = strtok(p, " \t");
tok && out->param_count < MAX_PARAMS;
tok = strtok(NULL, " \t")) {
tok = trim(tok);
if (*tok == 0) continue;
strncpy(out->params[out->param_count++], tok, MAX_LINE_LENGTH-1);
}
}
}
/* Scan a code buffer and register any TEACH blocks (functions). */
void parse_functions(char code[][MAX_LINE_LENGTH], int line_count) {
for (int i = 0; i < line_count; i++) {
char line_copy[MAX_LINE_LENGTH];
strncpy(line_copy, code[i], MAX_LINE_LENGTH-1);
line_copy[MAX_LINE_LENGTH-1] = '\0';
char *l = trim(line_copy);
if (strlen(l)==0 || l[0]=='#') continue;
char cmd[MAX_LINE_LENGTH];
sscanf(l, "%s", cmd);
to_upper(cmd);
if (strcmp(cmd, "TEACH") != 0) continue;
Function f;
char *rest = trim(l + strlen(cmd));
parse_teach_header(rest, &f);
if (strlen(f.name) == 0) {
runtime.line_number = i + 1;
error_message("TEACH needs a function name", "Try: TEACH greet TAKES name");
continue;
}
/* collect lines until LEARNED */
int start = i + 1;
int end = start;
while (end < line_count) {
char w[MAX_LINE_LENGTH]; w[0] = '\0';
sscanf(trim(code[end]), "%s", w);
to_upper(w);
if (strcmp(w, "LEARNED") == 0) break;
end++;
}
if (end >= line_count) {
runtime.line_number = i + 1;
error_message("TEACH without LEARNED", "Add LEARNED at the end of your TEACH block");
continue;
}
f.code_line_count = 0;
for (int j = start; j < end && f.code_line_count < MAX_CODE_LINES; j++) {
strncpy(f.code[f.code_line_count], code[j], MAX_LINE_LENGTH-1);
f.code[f.code_line_count][MAX_LINE_LENGTH-1] = '\0';
f.code_line_count++;
}
upsert_function(&f);
/* skip past this function block */
i = end;
}
}
static void set_result_from_return(void) {
if (runtime.return_type == TYPE_TEXT) {
set_text("RESULT", runtime.return_text);
} else if (runtime.return_type == TYPE_YES_NO) {
set_yes_no("RESULT", runtime.return_value != 0.0);
} else {
set_number("RESULT", runtime.return_value);
}
}
static void parse_value(const char *expr, VarType *out_type, double *out_num, char *out_text) {
char tmp[MAX_LINE_LENGTH];
strncpy(tmp, expr, MAX_LINE_LENGTH-1);
tmp[MAX_LINE_LENGTH-1] = '\0';
char *t = trim(tmp);
if (strlen(t) == 0) { *out_type = TYPE_NUMBER; *out_num = 0; out_text[0] = '\0'; return; }
/* quoted text */
if ((t[0] == '"' && t[strlen(t)-1] == '"') || (t[0] == '\'' && t[strlen(t)-1] == '\'')) {
*out_type = TYPE_TEXT;
t[strlen(t)-1] = '\0';
strncpy(out_text, t+1, MAX_LINE_LENGTH-1);
out_text[MAX_LINE_LENGTH-1] = '\0';
return;
}
/* YES/NO */
char up[MAX_LINE_LENGTH];
strncpy(up, t, MAX_LINE_LENGTH-1);
up[MAX_LINE_LENGTH-1] = '\0';
to_upper(up);
if (strcmp(up, "YES") == 0 || strcmp(up, "NO") == 0) {
*out_type = TYPE_YES_NO;
*out_num = (strcmp(up, "YES") == 0) ? 1.0 : 0.0;
out_text[0] = '\0';
return;
}
/* variable reference (text/bool) */
Variable *v = find_var(t);
if (v && v->type == TYPE_TEXT) {
*out_type = TYPE_TEXT;
strncpy(out_text, v->value.text, MAX_LINE_LENGTH-1);
out_text[MAX_LINE_LENGTH-1] = '\0';
return;
}
if (v && v->type == TYPE_YES_NO) {
*out_type = TYPE_YES_NO;
*out_num = v->value.yes_no ? 1.0 : 0.0;
out_text[0] = '\0';
return;
}
/* fallback: number expression */
*out_type = TYPE_NUMBER;
*out_num = eval_number_expr(t);
out_text[0] = '\0';
}
/* CALL name [WITH arg1, arg2, ...] */
void cmd_call(char *args) {
char tmp[MAX_LINE_LENGTH];
strncpy(tmp, args, MAX_LINE_LENGTH-1);
tmp[MAX_LINE_LENGTH-1] = '\0';
char *t = trim(tmp);
if (strlen(t) == 0) {
error_message("CALL needs a function name", "Try: CALL greet WITH \"Sam\"");
return;
}
char fname[MAX_LINE_LENGTH] = {0};
sscanf(t, "%s", fname);
Function *f = find_func(fname);
if (!f) {
char msg[MAX_LINE_LENGTH];
snprintf(msg, sizeof(msg), "I don't know how to '%s' yet.", fname);
error_message(msg, "Tip: Define it first with TEACH ... LEARNED");
return;
}
/* move past name */
t = trim(t + strlen(fname));
/* optional WITH/USING */
char kw[MAX_LINE_LENGTH] = {0};
char *arg_str = t;
if (strlen(arg_str) > 0) {
sscanf(arg_str, "%s", kw);
to_upper(kw);
if (strcmp(kw,"WITH")==0 || strcmp(kw,"USING")==0) {
arg_str = trim(arg_str + strlen(kw));
}
}
/* parse args */
char argbuf[MAX_LINE_LENGTH];
strncpy(argbuf, arg_str, MAX_LINE_LENGTH-1);
argbuf[MAX_LINE_LENGTH-1] = '\0';
char *ab = trim(argbuf);
char *argv[MAX_PARAMS];
int argc = 0;
if (strlen(ab) == 0) {
argc = 0;
} else if (strchr(ab, ',')) {
for (char *tok = strtok(ab, ",");
tok && argc < MAX_PARAMS;
tok = strtok(NULL, ",")) {
tok = trim(tok);
if (*tok == 0) continue;
argv[argc++] = tok;
}
} else {
/* single argument (may contain spaces / expressions) */
argv[argc++] = ab;
}
if (argc != f->param_count) {
char msg[MAX_LINE_LENGTH];
snprintf(msg, sizeof(msg), "CALL %s needs %d value(s), but you gave %d.",
f->name, f->param_count, argc);
error_message(msg, "Tip: Use commas: CALL name WITH a, b, \"text\"");
return;
}
/* Save previous param variables so we can restore them after the call */
Variable saved[MAX_PARAMS];
int had_saved[MAX_PARAMS];
memset(saved, 0, sizeof(saved));
memset(had_saved, 0, sizeof(had_saved));
for (int i = 0; i < f->param_count; i++) {
Variable *v = find_var(f->params[i]);
if (v) { had_saved[i] = 1; saved[i] = *v; }
}
/* Assign params */
for (int i = 0; i < f->param_count; i++) {
VarType ttype;
double tnum;
char ttext[MAX_LINE_LENGTH];
parse_value(argv[i], &ttype, &tnum, ttext);
if (ttype == TYPE_TEXT) set_text(f->params[i], ttext);
else if (ttype == TYPE_YES_NO) set_yes_no(f->params[i], (tnum != 0.0));
else set_number(f->params[i], tnum);
}
/* Call */
int prev_in_func = runtime.in_function;
int prev_should_return = runtime.should_return;
VarType prev_return_type = runtime.return_type;
double prev_return_value = runtime.return_value;
char prev_return_text[MAX_LINE_LENGTH];
strncpy(prev_return_text, runtime.return_text, MAX_LINE_LENGTH-1);
prev_return_text[MAX_LINE_LENGTH-1] = '\0';
runtime.in_function = 1;
runtime.should_return = 0;
runtime.return_type = TYPE_NUMBER;
runtime.return_value = 0;
runtime.return_text[0] = '\0';
runtime.call_depth++;
strncpy(runtime.current_function, f->name, MAX_LINE_LENGTH-1);
runtime.current_function[MAX_LINE_LENGTH-1] = '\0';
execute_code(f->code, f->code_line_count);
runtime.call_depth--;
if (runtime.call_depth == 0) runtime.current_function[0] = '\0';
/* Restore param variables */
for (int i = 0; i < f->param_count; i++) {
if (had_saved[i]) {
Variable *v = get_or_create_var(saved[i].name);
if (v) *v = saved[i];
}
}
/* publish return as RESULT */
set_result_from_return();
/* restore caller context flags */
runtime.in_function = prev_in_func;
runtime.should_return = prev_should_return;
/* keep returned value in runtime, but restore caller's return storage if nested call */
if (runtime.call_depth > 0) {
runtime.return_type = prev_return_type;
runtime.return_value = prev_return_value;
strncpy(runtime.return_text, prev_return_text, MAX_LINE_LENGTH-1);
runtime.return_text[MAX_LINE_LENGTH-1] = '\0';
}
}
void cmd_return(char *expr) {
if (!runtime.in_function) {
error_message("RETURN can only be used inside TEACH ... LEARNED", "Tip: RETURN ends a function early.");
return;
}
VarType ttype;
double tnum;
char ttext[MAX_LINE_LENGTH];
parse_value(expr, &ttype, &tnum, ttext);
runtime.return_type = ttype;
if (ttype == TYPE_TEXT) {
strncpy(runtime.return_text, ttext, MAX_LINE_LENGTH-1);
runtime.return_text[MAX_LINE_LENGTH-1] = '\0';
runtime.return_value = 0;
} else {
runtime.return_value = tnum;
runtime.return_text[0] = '\0';
}
runtime.should_return = 1;
/* also set RESULT inside the function (helpful for beginners) */
set_result_from_return();
}
/* ============================================================================
* IF / REPEAT / DO TIMES COMMANDS
* ============================================================================ */
/* Find matching ELSE and ENDIF for an IF at position start */
void find_if_structure(char code[][MAX_LINE_LENGTH], int line_count, int start,
int *else_line, int *endif_line) {
int depth = 1;
*else_line = -1;
*endif_line = -1;
for (int i = start+1; i < line_count; i++) {
char w[MAX_LINE_LENGTH]; sscanf(trim(code[i]), "%s", w); to_upper(w);
if (strcmp(w,"IF")==0) depth++;
if (strcmp(w,"ENDIF")==0) { depth--; if (depth==0) { *endif_line = i; return; } }
if (strcmp(w,"ELSE")==0 && depth==1) *else_line = i;
}
}
void cmd_if(char *cond, char code[][MAX_LINE_LENGTH], int line_count, int *line) {
int else_line, endif_line;
find_if_structure(code, line_count, *line, &else_line, &endif_line);
if (endif_line < 0) { error_message("IF without ENDIF","Add ENDIF after your IF block"); return; }
int result = eval_yes_no_expr(cond);
if (result) {
int end = (else_line >= 0) ? else_line : endif_line;
execute_block(code, line_count, *line + 1, end);
} else if (else_line >= 0) {
execute_block(code, line_count, else_line + 1, endif_line);
}
*line = endif_line;
}
void cmd_repeat(char *args, char code[][MAX_LINE_LENGTH], int line_count, int *line) {
int count = (int)eval_number_expr(args);
if (count <= 0) { error_message("REPEAT count must be a positive number","Try: REPEAT 5"); return; }
int start = *line + 1;
int end = start;
int depth = 1;
while (end < line_count && depth > 0) {
char w[MAX_LINE_LENGTH]; sscanf(trim(code[end]), "%s", w); to_upper(w);
if (strcmp(w,"REPEAT")==0) depth++;
if (strcmp(w,"UNTIL")==0) depth--;
if (depth > 0) end++;
}
if (depth != 0) { error_message("REPEAT without UNTIL","Add UNTIL after your REPEAT block"); return; }
for (int i = 0; i < count; i++) {
execute_block(code, line_count, start, end);
}
*line = end;
}
void cmd_do_times(char *count_str, char code[][MAX_LINE_LENGTH], int line_count, int *line) {
int count = (int)eval_number_expr(count_str);
if (count <= 0) { error_message("DO count must be a positive number","Try: DO 5 TIMES"); return; }
int start = *line + 1;
int end = start;
int depth = 1;
while (end < line_count && depth > 0) {
char w[MAX_LINE_LENGTH]; sscanf(trim(code[end]), "%s", w); to_upper(w);
if (strcmp(w,"DO")==0) depth++;
if (strcmp(w,"DONE")==0) depth--;
if (depth > 0) end++;
}
if (depth != 0) { error_message("DO without DONE","Add DONE after your DO block"); return; }
for (int i = 0; i < count; i++) {
execute_block(code, line_count, start, end);
}
*line = end;
}
/* ============================================================================
* LINE EXECUTOR
* ============================================================================ */
void execute_line(char *raw_line) {
char line[MAX_LINE_LENGTH];
strncpy(line, raw_line, MAX_LINE_LENGTH-1);
char *l = trim(line);
if (strlen(l) == 0) return;
if (l[0] == '#') return; /* comment */
/* Debug step */
if (runtime.debug_mode) {
print_colored(COLOR_CYAN, "[DEBUG] ");
printf("Line %d: %s\n", runtime.line_number, l);
}
char cmd[MAX_LINE_LENGTH];
sscanf(l, "%s", cmd);
to_upper(cmd);
char *rest = l + strlen(cmd);
rest = trim(rest);
/* ---- Commands ---- */
if (strcmp(cmd,"SAY")==0) { cmd_say(rest); return; }
if (strcmp(cmd,"SET")==0) { cmd_set(rest); return; }
if (strcmp(cmd,"ASK")==0) { cmd_ask(rest); return; }
if (strcmp(cmd,"WAIT")==0) { cmd_wait(rest); return; }
if (strcmp(cmd,"VARS")==0) { cmd_vars(); return; }
if (strcmp(cmd,"CALL")==0) { cmd_call(rest); return; }
if (strcmp(cmd,"RETURN")==0){ cmd_return(rest); return; }
if (strcmp(cmd,"HELP")==0) { cmd_help(rest); return; }
if (strcmp(cmd,"EXIT")==0 || strcmp(cmd,"QUIT")==0) { exit(0); }
if (strcmp(cmd,"DEBUG")==0) {
to_upper(rest);
runtime.debug_mode = (strcmp(rest,"ON")==0) ? 1 : 0;
printf("Debug mode: %s\n", runtime.debug_mode ? "ON" : "OFF");
return;
}
/* Sprite commands */
if (strcmp(cmd,"CREATE")==0) {
char sub[MAX_LINE_LENGTH]; sscanf(rest, "%s", sub); to_upper(sub);
if (strcmp(sub,"SPRITE")==0) { cmd_create_sprite(rest + strlen(sub) + 1); return; }
}
if (strcmp(cmd,"MOVE")==0) {
char sub[MAX_LINE_LENGTH]; sscanf(rest, "%s", sub); to_upper(sub);
if (strcmp(sub,"SPRITE")==0) { cmd_move_sprite(rest + strlen(sub) + 1); return; }
}
if (strcmp(cmd,"SHOW")==0) {
char sub[MAX_LINE_LENGTH]; sscanf(rest, "%s", sub); to_upper(sub);
if (strcmp(sub,"SPRITE")==0) { cmd_show_sprite(rest + strlen(sub) + 1); return; }
}
if (strcmp(cmd,"HIDE")==0) {
char sub[MAX_LINE_LENGTH]; sscanf(rest, "%s", sub); to_upper(sub);
if (strcmp(sub,"SPRITE")==0) { cmd_hide_sprite(rest + strlen(sub) + 1); return; }
}
/* Unknown command */
char msg[MAX_LINE_LENGTH];
snprintf(msg, sizeof(msg), "Unknown command: %s", cmd);
error_message(msg, "");
suggest_command(cmd);
}
/* ============================================================================
* MULTI-LINE EXECUTOR
* ============================================================================ */
void execute_block(char code[][MAX_LINE_LENGTH], int line_count, int start, int end) {
if (start < 0) start = 0;
if (end > line_count) end = line_count;
for (int i = start; i < end; i++) {
runtime.line_number = i + 1;
char line_copy[MAX_LINE_LENGTH];
strncpy(line_copy, code[i], MAX_LINE_LENGTH-1);
line_copy[MAX_LINE_LENGTH-1] = '\0';
char *l = trim(line_copy);
if (strlen(l)==0 || l[0]=='#') continue;
char cmd[MAX_LINE_LENGTH]; sscanf(l, "%s", cmd); to_upper(cmd);
char *rest = trim(l + strlen(cmd));
if (strcmp(cmd,"TEACH")==0) {
/* Skip function definition blocks during normal execution */
int end = i + 1;
while (end < line_count) {
char w[MAX_LINE_LENGTH]; w[0] = '\0';
sscanf(trim(code[end]), "%s", w);
to_upper(w);
if (strcmp(w, "LEARNED") == 0) break;
end++;
}
if (end >= line_count) {
error_message("TEACH without LEARNED", "Add LEARNED at the end of your TEACH block");
return;
}
i = end;
continue;
}
if (strcmp(cmd,"IF")==0) {
/* Find the THEN keyword if present, strip it */
char cond[MAX_LINE_LENGTH]; strncpy(cond, rest, MAX_LINE_LENGTH-1);
cond[MAX_LINE_LENGTH-1] = '\0';
char *then = strstr(cond," THEN"); if (then) *then = '\0';
to_upper(cond);
cmd_if(cond, code, line_count, &i);
continue;
}
if (strcmp(cmd,"ELSE")==0 || strcmp(cmd,"ENDIF")==0 ||
strcmp(cmd,"UNTIL")==0 || strcmp(cmd,"DONE")==0 || strcmp(cmd,"LEARNED")==0) continue;
if (strcmp(cmd,"REPEAT")==0) { cmd_repeat(rest, code, line_count, &i); continue; }
if (strcmp(cmd,"DO")==0) {
/* DO n TIMES */
char count_s[MAX_LINE_LENGTH], times_kw[MAX_LINE_LENGTH];
count_s[0] = '\0'; times_kw[0] = '\0';
sscanf(rest, "%s %s", count_s, times_kw);
cmd_do_times(count_s, code, line_count, &i);
continue;
}
execute_line(code[i]);
if (runtime.should_return) return;
}
}
void execute_code(char code[][MAX_LINE_LENGTH], int line_count) {
/* First, learn any TEACH blocks in this code buffer */
parse_functions(code, line_count);
execute_block(code, line_count, 0, line_count);
}
/* ============================================================================
* MAIN ENTRY POINTS
* ============================================================================ */
void run_interactive(void) {
print_colored(COLOR_GREEN, "\n=== Welcome to CLARO! ===\n\n");
printf("A programming language designed for everyone.\n");
printf("Type HELP for a list of commands. Type EXIT to quit.\n");
printf("Tip: IF/DO/REPEAT can be typed as multi-line blocks here.\n\n");
char input[MAX_LINE_LENGTH];
/* REPL block buffer */
char block[MAX_CODE_LINES][MAX_LINE_LENGTH];
int block_count = 0;
int stack_depth = 0;
while (1) {
if (stack_depth == 0) {
print_colored(COLOR_MAGENTA, "claro> ");
} else {
print_colored(COLOR_MAGENTA, "....> ");
}
if (!fgets(input, sizeof(input), stdin)) break;
input[strcspn(input,"\n")] = 0;
char tmp[MAX_LINE_LENGTH];
strncpy(tmp, input, MAX_LINE_LENGTH-1);
tmp[MAX_LINE_LENGTH-1] = '\0';
char *t = trim(tmp);
if (strlen(t) == 0) continue;
/* If we're not currently in a block, decide whether to start one */
if (stack_depth == 0) {
char first[MAX_LINE_LENGTH];
sscanf(t, "%s", first); to_upper(first);
if (strcmp(first,"IF")==0 || strcmp(first,"DO")==0 || strcmp(first,"REPEAT")==0 || strcmp(first,"TEACH")==0) {
block_count = 0;
stack_depth = 0;
} else {
runtime.line_number = 0;
execute_line(input);
continue;
}
}
/* Add line to block buffer */
if (block_count >= MAX_CODE_LINES) {
error_message("Block too long", "Tip: keep blocks shorter, or run from a file");
block_count = 0;
stack_depth = 0;
continue;
}
strncpy(block[block_count], input, MAX_LINE_LENGTH-1);
block[block_count][MAX_LINE_LENGTH-1] = '\0';
block_count++;
/* Update block nesting stack by keywords */
char kw[MAX_LINE_LENGTH];
kw[0] = '\0';
sscanf(t, "%s", kw); to_upper(kw);
if (strcmp(kw,"IF")==0) stack_depth++;
if (strcmp(kw,"DO")==0) stack_depth++;
if (strcmp(kw,"REPEAT")==0) stack_depth++;
if (strcmp(kw,"TEACH")==0) stack_depth++;
if (strcmp(kw,"ENDIF")==0) stack_depth--;
if (strcmp(kw,"DONE")==0) stack_depth--;
if (strcmp(kw,"UNTIL")==0) stack_depth--;
if (strcmp(kw,"LEARNED")==0) stack_depth--;
if (stack_depth < 0) {
error_message("Unexpected block ending", "Tip: ENDIF closes IF, DONE closes DO, UNTIL closes REPEAT, LEARNED closes TEACH");
block_count = 0;
stack_depth = 0;
continue;
}
/* If stack is empty, execute the whole collected block */
if (stack_depth == 0) {
runtime.line_number = 0;
execute_code(block, block_count);
block_count = 0;
}
}
}
void run_file(const char *filename) {
FILE *file = fopen(filename, "r");
if (!file) {
print_colored(COLOR_RED, "Error: ");
printf("Could not open file '%s'\n", filename);
return;
}
char code[MAX_CODE_LINES][MAX_LINE_LENGTH];
int count = 0;
while (fgets(code[count], MAX_LINE_LENGTH, file) && count < MAX_CODE_LINES) {
code[count][strcspn(code[count],"\n")] = 0;
count++;
}
fclose(file);
execute_code(code, count);
}
int main(int argc, char *argv[]) {
memset(&runtime, 0, sizeof(Runtime));
srand((unsigned)time(NULL));
if (argc > 1) {
if (strcmp(argv[1],"-h")==0 || strcmp(argv[1],"--high-contrast")==0) {
runtime.high_contrast = 1;
if (argc > 2) run_file(argv[2]);
else run_interactive();
} else {
run_file(argv[1]);
}
} else {
run_interactive();
}
return 0;
}