CODESAMPLE

Interpreter - C

Share on:

The Interpreter pattern defines a way to evaluate a language, typically a simple one, given its grammar. This example implements a basic arithmetic expression interpreter for integers and the operations addition, subtraction, multiplication, and division. The expression is represented as a string, and the interpreter parses and evaluates it. The implementation uses a recursive descent parser, building an expression tree internally (though the tree is not explicitly defined as a data structure, the recursion achieves the same effect). C’s function pointer capabilities, combined with its procedural nature, make it suitable for straightforward interpreter implementations like this.

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

// Token types
typedef enum {
    TOKEN_NUMBER,
    TOKEN_PLUS,
    TOKEN_MINUS,
    TOKEN_MULTIPLY,
    TOKEN_DIVIDE,
    TOKEN_EOF
} TokenType;

// Token structure
typedef struct {
    TokenType type;
    int value;
    char* str; // Store the original string representation of the token
} Token;

// Global variables
char* expression;
int current_pos = 0;

// Function prototypes
Token get_next_token();
int parse_expression();
int parse_term();
int parse_factor();

// Get the next token from the expression string
Token get_next_token() {
    Token token;
    while (expression[current_pos] != '\0' && isspace(expression[current_pos])) {
        current_pos++;
    }

    if (expression[current_pos] == '\0') {
        token.type = TOKEN_EOF;
        return token;
    }

    if (isdigit(expression[current_pos])) {
        token.type = TOKEN_NUMBER;
        char* start = &expression[current_pos];
        while (isdigit(expression[current_pos])) {
            current_pos++;
        }
        token.value = atoi(start);
        token.str = strdup(start); // Duplicate the string for later use
        return token;
    }

    switch (expression[current_pos]) {
        case '+':
            token.type = TOKEN_PLUS;
            token.str = "+";
            current_pos++;
            return token;
        case '-':
            token.type = TOKEN_MINUS;
            token.str = "-";
            current_pos++;
            return token;
        case '*':
            token.type = TOKEN_MULTIPLY;
            token.str = "*";
            current_pos++;
            return token;
        case '/':
            token.type = TOKEN_DIVIDE;
            token.str = "/";
            current_pos++;
            return token;
        default:
            fprintf(stderr, "Error: Invalid character '%c' at position %d\n", expression[current_pos], current_pos);
            exit(1);
    }
}


// Grammar:
// expression : term ((PLUS | MINUS) term)*
// term       : factor ((MULTIPLY | DIVIDE) factor)*
// factor     : NUMBER
int parse_expression() {
    int result = parse_term();

    while (1) {
        Token token = get_next_token();
        if (token.type == TOKEN_PLUS) {
            result += parse_term();
        } else if (token.type == TOKEN_MINUS) {
            result -= parse_term();
        } else {
            // Put back the token -- necessary as it will be consumed by evaluate()
            current_pos -= strlen(token.str); 
            if(token.type != TOKEN_EOF){
                current_pos--; // Adjust for potential whitespace before token.str
            }
            
            break;
        }
    }
    return result;
}

int parse_term() {
    int result = parse_factor();

    while (1) {
        Token token = get_next_token();
        if (token.type == TOKEN_MULTIPLY) {
            result *= parse_factor();
        } else if (token.type == TOKEN_DIVIDE) {
            int divisor = parse_factor();
            if (divisor == 0) {
                fprintf(stderr, "Error: Division by zero\n");
                exit(1);
            }
            result /= divisor;
        } else {
            current_pos -= strlen(token.str);
            if(token.type != TOKEN_EOF){
                current_pos--; // Adjust for potential whitespace before token.str
            }
            
            break;
        }
    }
    return result;
}


int parse_factor() {
    Token token = get_next_token();
    if (token.type == TOKEN_NUMBER) {
        return token.value;
    } else {
        fprintf(stderr, "Error: Expected a number, got '%s' at position %d\n", token.str, current_pos - strlen(token.str));
        exit(1);
    }
}

int main() {
    expression = "1 + 2 * 3 - 4 / 2";
    current_pos = 0;
    int result = parse_expression();
    printf("Result: %d\n", result);

    expression = "10 / 2 - 3";
    current_pos = 0;
    result = parse_expression();
    printf("Result: %d\n", result);
    
    expression = "2 * (3 + 4)"; // Not supported, but shows limitation
    current_pos = 0; //reset position.
    printf("Note: Parentheses are currently not supported.\n");
    
    return 0;
}