Search code examples
cvariablesinterpreter

How to make an interpreter store variables


I’ve been trying to make a little programming language but I can’t figure out how to make my interpreter store and recall variables

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

#define MAX_VARIABLES 10

// Structure to store variables
struct Variable {
    char name[50];
    char value[256];
};

// Array to store variables
struct Variable variables[MAX_VARIABLES];
int variableCount = 0;

// Function to find a variable by name
struct Variable *findVariable(const char *name) {
    for (int i = 0; i < variableCount; ++i) {
        if (strcmp(variables[i].name, name) == 0) {
            return &variables[i];
        }
    }
    return NULL; // Variable not found
}

// Function to interpret commands
void interpretCommand(const char *command) {
    if (strncmp(command, "console:write->", 15) == 0) {
        // Extract the message within double quotes
        const char *messageStart = strchr(command, '"');
        const char *messageEnd = strrchr(command, '"');

        if (messageStart != NULL && messageEnd != NULL && messageStart < messageEnd) {
            // Print the message, replacing variables if present
            for (const char *p = messageStart + 1; p < messageEnd; ++p) {
                if (*p == '*') {
                    ++p; // Move past '*'
                    const char *varStart = p;
                    while (*p != '*' && p < messageEnd) {
                        ++p;
                    }

                    char varName[50];
                    strncpy(varName, varStart, p - varStart);
                    varName[p - varStart] = '\0';

                    struct Variable *variable = findVariable(varName);
                    if (variable != NULL) {
                        printf("%s", variable->value);
                    } else {
                        printf("Undefined variable: %s", varName);
                    }
                } else {
                    putchar(*p);
                }
            }
            putchar('\n');
        } else {
            printf("Invalid message format\n");
        }
    } else if (strncmp(command, "console:read->", 14) == 0) {
      // Extract the prompt within double quotes
      const char *promptStart = strchr(command, '"');
      const char *promptEnd = strrchr(command, '"');

      if (promptStart != NULL && promptEnd != NULL && promptStart < promptEnd) {
          // Print the prompt and read user input
          printf("%.*s", (int)(promptEnd - promptStart - 1), promptStart + 1);

          // Read user input
          char userInput[256]; // Adjust size as needed
          fgets(userInput, sizeof(userInput), stdin);

      } else {
          printf("Invalid prompt format\n");
      }
    } else {
        // Check if the command starts with "variableName->"
        char *arrow = strstr(command, "->");
        if (arrow != NULL) {
            *arrow = '\0'; // Separate variable name and value

            // Find or create a variable with the given name
            struct Variable *variable = findVariable(command);
            if (variable == NULL) {
                if (variableCount < MAX_VARIABLES) {
                    strcpy(variables[variableCount].name, command);
                    variable = &variables[variableCount++];
                } else {
                    printf("Maximum number of variables reached\n");
                    return;
                }
            }

            // Copy the value to the variable
            strcpy(variable->value, arrow + 2);
        } else {
            printf("Unknown command\n");
        }
    }
}

int main() {
    // Open the file
    FILE *file = fopen("King.roar", "r");

    if (file == NULL) {
        perror("Error opening file");
        return 1;
    }

    char line[256]; // Assuming a maximum line length of 255 characters

    // Read and interpret each line from the file
    while (fgets(line, sizeof(line), file) != NULL) {
        // Remove newline character if present
        size_t len = strlen(line);
        if (len > 0 && line[len - 1] == '\n') {
            line[len - 1] = '\0';
        }

        // Interpret the command
        interpretCommand(line);
    }

    // Close the file
    fclose(file);

    return 0;
}

And this is my sample code stored in King.roar:

console:read->"Enter your name: "->name
console:read->"Enter your age: "->age
console:write->"Hello *name*!"
console:write->"You are *age* years old!"

For some reason I only get undefined variable I don’t know if the data isn’t being stored or it’s the recall that’s not working

I tried looking up on the internet but I didn’t find anything about it. Thanks for helping !


Solution

  • The code to store a value into a variable seems to be

    variable_name->value
    

    But in the file you seem to use a different syntax:

    console:read->"Enter your name: "->name
    

    It seems more consistent to use

    value->variable_name
    

    and use a current value that will be set by the console methods and other syntaxes.

    Here is a modified version:

    #include <errno.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define MAX_VARIABLES 10
    
    // Structure to store variables
    struct Variable {
        char name[50];
        char value[256];
    };
    
    // Array to store variables
    struct Variable variables[MAX_VARIABLES];
    int variableCount = 0;
    
    // Function to find a variable by name
    struct Variable *findVariable(const char *name, size_t len) {
        for (int i = 0; i < variableCount; ++i) {
            if (memcmp(variables[i].name, name, len) == 0
            &&  variables[i].name[len] == '\0') {
                return &variables[i];
            }
        }
        return NULL; // Variable not found
    }
    
    void console_write(const char *s, size_t len) {
        // Print the message, replacing variables if present
        for (const char *p = s, *end = s + len; p < end; p++) {
            if (*p == '*' && ++p < end && *p != '*') {
                const char *varName = p;
                while (p < end && *p != '*') {
                    ++p;
                }
                size_t nameLen = p - varName;
                struct Variable *variable = findVariable(varName, nameLen);
                if (variable != NULL) {
                    printf("%s", variable->value);
                } else {
                    fprintf(stderr, "Undefined variable: %.*s\n", (int)nameLen, varName);
                }
            } else {
                putchar(*p);
            }
        }
    }
    
    // Function to interpret commands
    int interpretCommand(const char *line) {
        char current_value[250];
    
        *current_value = '\0';
        for (const char *command = line; *command;) {
            if (strncmp(command, "console:write->", 15) == 0) {
                // Extract the message within double quotes
                const char *messageStart = strchr(command, '"');
                const char *messageEnd = strrchr(command, '"');
                if (messageStart == NULL || messageEnd == NULL || messageStart >= messageEnd) {
                    fprintf(stderr, "Invalid message format\n");
                    return 1;
                }
                console_write(messageStart + 1, messageEnd - messageStart - 1);
                putchar('\n');
                command = messageEnd + 1;
            } else
            if (strncmp(command, "console:read->", 14) == 0) {
                // Extract the prompt within double quotes
                const char *promptStart = strchr(command, '"');
                const char *promptEnd = strrchr(command, '"');
                if (promptStart == NULL || promptEnd == NULL || promptStart >= promptEnd) {
                    fprintf(stderr, "Invalid prompt format\n");
                    return 1;
                }
                // Print the prompt and read user input
                console_write(promptStart + 1, promptEnd - promptStart - 1);
                // Read user input
                fgets(current_value, sizeof(current_value), stdin);
                size_t len = strlen(current_value);
                if (len > 0 && current_value[len - 1] == '\n')
                    current_value[--len] = '\0';
                command = promptEnd + 1;
            } else
            if (!strncmp(command, "->", 2)) {
                // ->variableName
                const char *name = command + 2;
                size_t len = strlen(name);
                struct Variable *variable = findVariable(name, len);
                if (variable == NULL) {
                    if (len >= sizeof(variable->name)) {
                        fprintf(stderr, "Variable name too long: %.*s\n", (int)len, name);
                        return 1;
                    }
                    if (variableCount >= MAX_VARIABLES) {
                        fprintf(stderr, "Maximum number of variables reached\n");
                        return 1;
                    }
                    memcpy(variables[variableCount].name, name, len);
                    variables[variableCount].name[len] = '\0';
                    variable = &variables[variableCount++];
                }
                strcpy(variable->value, current_value);
                command = name + len;
            } else
            if (*command == '\"') {
                // string value
                const char *valueStart = command;
                const char *valueEnd = strrchr(command, '"');
                if (valueStart >= valueEnd) {
                    fprintf(stderr, "Unterminated value string: %s\n", command);
                    return 1;
                }
                size_t len = valueEnd - valueStart - 1;
                if (len >= sizeof(current_value)) {
                    fprintf(stderr, "Value string too long: %.*s\n", (int)len + 1, valueStart);
                    return 1;
                }
                memcpy(current_value, valueStart + 1, len);
                current_value[len] = '\0';
                command = valueEnd + 1;
            } else {
                char *arrow = strstr(command, "->");
                if (arrow != NULL) {
                    size_t len = arrow - command;
                    struct Variable *variable = findVariable(command, len);
                    if (variable == NULL) {
                        fprintf(stderr, "Unknown variable: %.*s\n", (int)len, command);
                        return 1;
                    }
                    strcpy(current_value, variable->value);
                } else {
                    fprintf(stderr, "Unknown command: %s\n", command);
                }
            }
        }
        return 0;
    }
    
    int main(int argc, char *argv[]) {
        // Open the file
        const char *filename = argc > 1 ? argv[1] : "King.roar";
        FILE *file = fopen(filename, "r");
        if (file == NULL) {
            fprintf(stderr, "Error opening file %s: %s", filename, strerror(errno));
            return 1;
        }
    
        char line[256]; // Assuming a maximum line length of 255 characters
    
        // Read and interpret each line from the file
        while (fgets(line, sizeof(line), file) != NULL) {
            // Remove newline character if present
            size_t len = strlen(line);
            if (len > 0 && line[len - 1] == '\n') {
                line[len - 1] = '\0';
            }
    
            // Interpret the command
            if (interpretCommand(line))
                break;
        }
    
        // Close the file
        fclose(file);
    
        return 0;
    }