Skip to content

Instantly share code, notes, and snippets.

@ehzawad
Created February 27, 2025 04:46
Show Gist options
  • Save ehzawad/9992a4cd6c06692981007089db121605 to your computer and use it in GitHub Desktop.
Save ehzawad/9992a4cd6c06692981007089db121605 to your computer and use it in GitHub Desktop.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX_FUNCS 10 // Maximum number of functions supported
#define NAME_LEN 50 // Maximum length of function names
#define STR_LEN 100 // Maximum length of strings to print
// Structure to store function definitions
struct Function {
char name[NAME_LEN]; // Function name
char print_string[STR_LEN]; // String to print when called
};
// Array to hold defined functions
struct Function functions[MAX_FUNCS];
int func_count = 0; // Number of functions defined
int main() {
char line[256]; // Buffer for reading input lines
// Read lines from standard input until EOF
while (fgets(line, sizeof(line), stdin)) {
// Remove trailing newline
line[strcspn(line, "\n")] = 0;
// Check for function definition
if (strncmp(line, "def ", 4) == 0) {
// Extract function name from "def name():"
char *name_start = line + 4; // Skip "def "
char *paren = strchr(name_start, '(');
if (paren) {
int name_len = paren - name_start;
char name[NAME_LEN];
strncpy(name, name_start, name_len);
name[name_len] = '\0'; // Null-terminate
// Read the next line (function body)
if (fgets(line, sizeof(line), stdin)) {
line[strcspn(line, "\n")] = 0;
// Check for indented print statement (exactly 4 spaces)
if (strncmp(line, " print(\"", 11) == 0) {
char *quote_start = line + 11; // Skip " print(\""
char *quote_end = strstr(quote_start, "\")");
if (quote_end) {
int str_len = quote_end - quote_start;
char print_string[STR_LEN];
strncpy(print_string, quote_start, str_len);
print_string[str_len] = '\0'; // Null-terminate
// Store the function
strcpy(functions[func_count].name, name);
strcpy(functions[func_count].print_string, print_string);
func_count++;
}
}
}
}
}
// Check for top-level print statement
else if (strncmp(line, "print(\"", 7) == 0) {
char *quote_start = line + 7; // Skip "print(\""
char *quote_end = strstr(quote_start, "\")");
if (quote_end) {
int str_len = quote_end - quote_start;
char print_string[STR_LEN];
strncpy(print_string, quote_start, str_len);
print_string[str_len] = '\0'; // Null-terminate
printf("%s\n", print_string); // Execute immediately
}
}
// Check for non-indented line (could be a function call or arithmetic expression)
else if (line[0] != ' ' && line[0] != '\0') {
int a, b;
// Try to parse an arithmetic expression of the form "number + number"
// The extra %c is used to ensure no trailing characters exist.
if (sscanf(line, "%d + %d %*c", &a, &b) == 2) {
printf("%d\n", a + b);
}
else if (sscanf(line, "%d - %d %*c", &a, &b) == 2) {
printf("%d\n", a - b);
} else {
// If not an arithmetic expression, treat it as a function call.
char *paren = strchr(line, '(');
if (paren) {
int name_len = paren - line;
char call_name[NAME_LEN];
strncpy(call_name, line, name_len);
call_name[name_len] = '\0'; // Null-terminate
// Find and execute the function if it exists
for (int i = 0; i < func_count; i++) {
if (strcmp(functions[i].name, call_name) == 0) {
printf("%s\n", functions[i].print_string);
break;
}
}
}
}
}
}
return 0;
}
@ehzawad
Copy link
Author

ehzawad commented Feb 27, 2025

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define MAX_LINE 256
#define MAX_STATEMENT 2048
#define MAX_VARS 100
#define MAX_FUNCS 50
#define NAME_LEN 50
#define STR_LEN 100
#define FUNC_BODY_LEN 256

// Value type to support integers (arithmetic) and strings (print literals)
typedef enum {
VAL_INT,
VAL_STRING
} ValueType;

typedef struct {
ValueType type;
int intValue;
char stringValue[STR_LEN];
} Value;

// Variable environment (name-value pairs)
typedef struct {
char name[NAME_LEN];
Value value;
} Variable;

Variable variables[MAX_VARS];
int var_count = 0;

// Function definitions (store function name and its single-statement body)
typedef struct {
char name[NAME_LEN];
char body[FUNC_BODY_LEN]; // for simplicity, the body is a single statement
} FunctionDef;

FunctionDef functions[MAX_FUNCS];
int func_count = 0;

// Forward declaration for lookup_variable to resolve implicit declaration warning.
int lookup_variable(const char *name, Value *result);

// --- Expression Parser (Recursive Descent) ---

const char* skip_whitespace(const char *s);
int parse_expression(const char **s);
int parse_term(const char **s);
int parse_factor(const char **s);

const char* skip_whitespace(const char *s) {
while (*s && isspace(*s)) s++;
return s;
}

// parse_expression: handles addition and subtraction
int parse_expression(const char **s) {
int value = parse_term(s);
*s = skip_whitespace(*s);
while (**s == '+' || **s == '-') {
char op = **s;
(*s)++;
int rhs = parse_term(s);
if (op == '+')
value += rhs;
else
value -= rhs;
*s = skip_whitespace(*s);
}
return value;
}

// parse_term: handles multiplication and division
int parse_term(const char **s) {
int value = parse_factor(s);
*s = skip_whitespace(s);
while (**s == '
' || **s == '/') {
char op = **s;
(s)++;
int rhs = parse_factor(s);
if (op == '
')
value *= rhs;
else {
if (rhs == 0) {
printf("Error: Division by zero.\n");
return 0;
}
value /= rhs;
}
*s = skip_whitespace(*s);
}
return value;
}

// parse_factor: handles numbers, variables, parentheses, and unary minus.
int parse_factor(const char **s) {
*s = skip_whitespace(*s);
int sign = 1;
if (**s == '-') {
sign = -1;
(*s)++;
}
int value = 0;
if (isdigit(**s)) {
value = strtol(*s, (char **)s, 10);
} else if (isalpha(**s)) {
char ident[NAME_LEN];
int i = 0;
while ((isalnum(**s) || **s == '_') && i < NAME_LEN - 1) {
ident[i++] = **s;
(*s)++;
}
ident[i] = '\0';
Value varValue;
if (lookup_variable(ident, &varValue) && varValue.type == VAL_INT)
value = varValue.intValue;
else
printf("Undefined variable or non-integer: %s\n", ident);
} else if (**s == '(') {
(*s)++; // skip '('
value = parse_expression(s);
if (**s == ')')
(*s)++;
else
printf("Expected ')'\n");
}
return sign * value;
}

// Evaluate an arithmetic expression from a string.
int evaluate_arithmetic(const char *expr) {
const char *p = expr;
int result = parse_expression(&p);
return result;
}

// --- Variable and Function Environment Management ---

// Lookup a variable by name; returns 1 if found, and sets result.
int lookup_variable(const char *name, Value *result) {
for (int i = 0; i < var_count; i++) {
if (strcmp(variables[i].name, name) == 0) {
*result = variables[i].value;
return 1;
}
}
return 0;
}

// Set a variable (update if exists, otherwise add).
void set_variable(const char *name, Value value) {
for (int i = 0; i < var_count; i++) {
if (strcmp(variables[i].name, name) == 0) {
variables[i].value = value;
return;
}
}
if (var_count < MAX_VARS) {
strncpy(variables[var_count].name, name, NAME_LEN);
variables[var_count].value = value;
var_count++;
} else {
printf("Error: Variable table full!\n");
}
}

// --- Multi-line Statement Support ---
// Check if the accumulated statement is complete (balanced parentheses and closed quotes).
int is_statement_complete(const char *stmt) {
int paren_count = 0;
int in_quote = 0;
for (int i = 0; stmt[i] != '\0'; i++) {
char c = stmt[i];
if (c == '"') {
in_quote = !in_quote;
} else if (!in_quote) {
if (c == '(')
paren_count++;
else if (c == ')')
paren_count--;
}
}
return (paren_count == 0 && !in_quote);
}

// --- Interpreter Execution ---

// Forward declaration for executing a single statement.
void execute_line(char *line);

// Execute a function call by name.
void execute_function(const char *name) {
for (int i = 0; i < func_count; i++) {
if (strcmp(functions[i].name, name) == 0) {
// For simplicity, a function's body is a single statement.
execute_line(functions[i].body);
return;
}
}
printf("Undefined function: %s\n", name);
}

// Execute a single statement.
void execute_line(char *line) {
// Remove trailing newline.
line[strcspn(line, "\n")] = 0;
char *p = line;
p = (char *)skip_whitespace(p);
if (*p == '\0') return;

// --- Print Statement ---
// Syntax: print(expr) where expr can be a string literal or an arithmetic expression.
if (strncmp(p, "print(", 6) == 0) {
    p += 6; // Skip "print("
    char *end = strchr(p, ')');
    if (end) {
        *end = '\0';
        p = (char *)skip_whitespace(p);
        if (*p == '\"') {
            // String literal: print the characters between the quotes.
            p++; // Skip opening quote.
            char *q = strchr(p, '\"');
            if (q) {
                *q = '\0';
                printf("%s\n", p);
            }
        } else {
            // Evaluate the expression and print the integer result.
            int result = evaluate_arithmetic(p);
            printf("%d\n", result);
        }
    }
    return;
}

// --- Variable Assignment ---
// Syntax: identifier = expression
char *equal_sign = strchr(p, '=');
if (equal_sign) {
    *equal_sign = '\0';
    char varName[NAME_LEN];
    sscanf(p, "%s", varName);
    char *expr = equal_sign + 1;
    expr = (char *)skip_whitespace(expr);
    int result = evaluate_arithmetic(expr);
    Value val;
    val.type = VAL_INT;
    val.intValue = result;
    set_variable(varName, val);
    return;
}

// --- Function Definition ---
// Syntax:
//   def function_name():
//       statement
if (strncmp(p, "def ", 4) == 0) {
    p += 4; // Skip "def "
    char funcName[NAME_LEN];
    sscanf(p, "%[^ (]", funcName); // Read until a space or '('.
    char *colon = strchr(p, ':');
    if (colon) {
        // Read the next line for the function body (expect indentation).
        char bodyLine[MAX_LINE];
        if (fgets(bodyLine, sizeof(bodyLine), stdin)) {
            // Remove leading whitespace from the function body.
            char *body = bodyLine;
            while (*body && isspace(*body)) body++;
            strncpy(functions[func_count].name, funcName, NAME_LEN);
            strncpy(functions[func_count].body, body, FUNC_BODY_LEN);
            func_count++;
        }
    }
    return;
}

// --- Function Call ---
// Syntax: function_name()
char *paren = strchr(p, '(');
if (paren && *(paren+1) == ')') {
    *paren = '\0';
    char funcName[NAME_LEN];
    sscanf(p, "%s", funcName);
    execute_function(funcName);
    return;
}

// --- Arithmetic Expression ---
int result = evaluate_arithmetic(p);
printf("%d\n", result);

}

// --- Main REPL Loop with Multi-line Support ---

int main() {
char line[MAX_LINE];
char statement[MAX_STATEMENT] = "";
printf("Welcome to MiniPy Interpreter.\n");
printf("Type commands (arithmetic, assignment, print, function def/call).\n");
printf("Press Ctrl+D to exit.\n\n");

printf("MiniPy> ");
while (fgets(line, sizeof(line), stdin)) {
    // Append the new line to the current statement buffer.
    if (strlen(statement) + strlen(line) < MAX_STATEMENT) {
        strcat(statement, line);
    } else {
        printf("Error: Statement too long.\n");
        statement[0] = '\0';
        continue;
    }
    
    // Check if the accumulated statement is complete.
    if (is_statement_complete(statement)) {
        execute_line(statement);
        statement[0] = '\0'; // Reset for the next statement.
        printf("MiniPy> ");
    } else {
        // Incomplete statement: prompt for continuation.
        printf("... ");
    }
}

// If any incomplete statement remains at EOF.
if (strlen(statement) > 0) {
    if (is_statement_complete(statement))
        execute_line(statement);
    else
        printf("Error: Incomplete statement at EOF.\n");
}

return 0;

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment