aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile10
-rw-r--r--README.md4
-rw-r--r--example/example.imp11
-rw-r--r--include/interpreter.h6
-rw-r--r--include/repl.h6
-rw-r--r--src/driver.c49
-rw-r--r--src/interpreter.c46
-rw-r--r--src/parser.y4
-rw-r--r--src/repl.c65
9 files changed, 165 insertions, 36 deletions
diff --git a/Makefile b/Makefile
index 112b109..89eabac 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,6 @@
CC ?= clang
CFLAGS ?= -Wall -Wextra -g
+LDFLAGS ?= -lreadline
BISON ?= bison
FLEX ?= flex
@@ -26,12 +27,12 @@ TARGET := $(BUILD_DIR)/imp
CFLAGS += -I$(INC_DIR) -MMD -MP
DEPS := $(OBJS:.o=.d)
-.PHONY: all clean example
+.PHONY: all clean example repl
all: $(TARGET)
$(TARGET): $(OBJS)
- $(CC) $(CFLAGS) $^ -o $@
+ $(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS)
$(BUILD_DIR):
@mkdir -p $@
@@ -52,7 +53,10 @@ $(LEXER_O): $(LEXER_C)
$(CC) $(CFLAGS) -c $< -o $@
example: $(TARGET)
- ./$(TARGET) example/example.imp
+ ./$(TARGET) -i example/example.imp
+
+repl: $(TARGET)
+ ./$(TARGET)
clean:
@rm -rf $(BUILD_DIR)
diff --git a/README.md b/README.md
index b327668..dc39ac2 100644
--- a/README.md
+++ b/README.md
@@ -5,10 +5,12 @@ A small interpreter of the IMP programming language.
## Build
- `make all` to build interpreter.
+- `make repl` to run repl.
- `make example` to interpret "example/example.imp".
- `make clean` to remove build folder.
## Dependencies
- [flex](https://github.com/westes/flex)
-- [bison](https://www.gnu.org/software/bison) \ No newline at end of file
+- [bison](https://www.gnu.org/software/bison)
+- [readline](https://tiswww.case.edu/php/chet/readline/rltop.html) \ No newline at end of file
diff --git a/example/example.imp b/example/example.imp
index e1ddf22..9db6655 100644
--- a/example/example.imp
+++ b/example/example.imp
@@ -1,4 +1,7 @@
-x := 1;
-while x < 10 do
- x := (x * 2)
-end \ No newline at end of file
+(y := 0;
+var x := 5 in
+ (while x < 10 do
+ x := (x + 1)
+ end;
+ y := x)
+end) \ No newline at end of file
diff --git a/include/interpreter.h b/include/interpreter.h
index 301a3d8..6e60113 100644
--- a/include/interpreter.h
+++ b/include/interpreter.h
@@ -9,5 +9,11 @@
void exec_stmt(hashmap_t context, ASTNode *node);
void context_print(hashmap_t context);
+void context_set(hashmap_t context, const char *name, int value);
+int context_get(hashmap_t context, const char *name);
+
+int exec_file (hashmap_t context, const char *path);
+int exec_str (hashmap_t context, const char *str);
+
#endif \ No newline at end of file
diff --git a/include/repl.h b/include/repl.h
new file mode 100644
index 0000000..804b702
--- /dev/null
+++ b/include/repl.h
@@ -0,0 +1,6 @@
+#ifndef REPL_H
+#define REPL_H
+
+void repl(void);
+
+#endif \ No newline at end of file
diff --git a/src/driver.c b/src/driver.c
index 597d7a8..60d3ce2 100644
--- a/src/driver.c
+++ b/src/driver.c
@@ -1,32 +1,35 @@
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
#include "interpreter.h"
+#include "repl.h"
-extern FILE *yyin;
-extern int yyparse(void);
-extern ASTNode *ast_root;
+static void interpret_file(const char *path) {
+ hashmap_t context = hashmap_create();
+ exec_file(context, path);
+ context_print(context);
+ hashmap_free(context);
+}
int main(int argc, char **argv) {
- if (argc > 1) {
- yyin = fopen(argv[1], "r");
- if (!yyin) {
- perror(argv[1]);
- return EXIT_FAILURE;
+ int opt;
+ const char *script = NULL;
+ while ((opt = getopt(argc, argv, "i:h")) != -1) {
+ switch (opt){
+ case 'i':
+ interpret_file(optarg);
+ return EXIT_SUCCESS;
+ case 'h':
+ default:
+ fprintf(stderr,
+ "Usage: %s [-i program.imp]\n"
+ " -i <program.imp> interpret program and exit\n"
+ " (no args) start REPL\n",
+ argv[0]);
+ return (opt == 'h') ? EXIT_SUCCESS : EXIT_FAILURE;
}
- } else {
- yyin = stdin;
- }
-
- if (yyparse() != 0) {
- fprintf(stderr, "Parsing failed.\n");
- return EXIT_FAILURE;
}
-
- hashmap_t context = hashmap_create();
- exec_stmt(context, ast_root);
- context_print(context);
- hashmap_free(context);
- ast_free(ast_root);
-
+ repl();
return EXIT_SUCCESS;
-}
+} \ No newline at end of file
diff --git a/src/interpreter.c b/src/interpreter.c
index 6b1ebe1..27626a8 100644
--- a/src/interpreter.c
+++ b/src/interpreter.c
@@ -3,11 +3,20 @@
#include <stdio.h>
#include <string.h>
-static void context_set(hashmap_t context, const char *name, int val) {
+typedef void *YY_BUFFER_STATE;
+extern FILE *yyin;
+extern ASTNode *ast_root;
+extern int yyparse(void);
+extern void yyrestart (FILE *);
+extern YY_BUFFER_STATE yy_scan_string(const char *);
+extern void yy_delete_buffer(YY_BUFFER_STATE);
+
+
+void context_set(hashmap_t context, const char *name, int val) {
hashmap_insert(context, name, val);
}
-static int context_get(hashmap_t context, const char *name) {
+int context_get(hashmap_t context, const char *name) {
int *val = hashmap_get(context, name);
if (val) return *val;
return 0;
@@ -111,3 +120,36 @@ void exec_stmt(hashmap_t context, ASTNode *node) {
}
}
}
+
+int exec_file (hashmap_t context, const char *path) {
+ yyin = fopen(path, "r");
+ if (!yyin) {
+ perror(path);
+ return -1;
+ }
+ yyrestart(yyin);
+ if (!yyparse()) {
+ exec_stmt(context, ast_root);
+ ast_free(ast_root);
+ } else {
+ fprintf(stderr, "Parse error in %s\n", path);
+ fclose(yyin);
+ return -1;
+ }
+ fclose(yyin);
+ return 0;
+}
+
+int exec_str (hashmap_t context, const char *str) {
+ YY_BUFFER_STATE buf = yy_scan_string(str);
+ if (!yyparse()) {
+ exec_stmt(context, ast_root);
+ ast_free(ast_root);
+ } else {
+ fprintf(stderr, "Parse error\n");
+ yy_delete_buffer(buf);
+ return -1;
+ }
+ yy_delete_buffer(buf);
+ return 0;
+}
diff --git a/src/parser.y b/src/parser.y
index c1e1d0e..cb3544e 100644
--- a/src/parser.y
+++ b/src/parser.y
@@ -27,7 +27,7 @@ void yyerror(const char *s) {
%token <num> TOKEN_NUMERAL
%token TOKEN_ASSIGN
%token TOKEN_LEFT_PARENTHESIS TOKEN_RIGHT_PARENTHESIS
-%left TOKEN_SEMICOLON
+%token TOKEN_SEMICOLON
%token TOKEN_SKIP
%token TOKEN_IF TOKEN_THEN TOKEN_ELSE TOKEN_END TOKEN_WHILE TOKEN_DO TOKEN_VAR TOKEN_IN
%token TOKEN_PLUS TOKEN_MINUS TOKEN_MULTIPLY
@@ -50,8 +50,6 @@ statement : TOKEN_SKIP
{ $$ = ast_assign($1, $3); }
| TOKEN_LEFT_PARENTHESIS statement TOKEN_SEMICOLON statement TOKEN_RIGHT_PARENTHESIS
{ $$ = ast_seq($2, $4); }
- | statement TOKEN_SEMICOLON statement
- { $$ = ast_seq($1, $3); }
| TOKEN_IF boolean_expression TOKEN_THEN statement TOKEN_ELSE statement TOKEN_END
{ $$ = ast_if($2, $4, $6); }
| TOKEN_WHILE boolean_expression TOKEN_DO statement TOKEN_END
diff --git a/src/repl.c b/src/repl.c
new file mode 100644
index 0000000..9c61b98
--- /dev/null
+++ b/src/repl.c
@@ -0,0 +1,65 @@
+#include "repl.h"
+#include "interpreter.h"
+#include <readline/readline.h>
+#include <readline/history.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static void print_help(void) {
+ puts(
+ "IMP REPL (type IMP statements or commands starting with '%')\n"
+ "Commands:\n"
+ " %quit exit\n"
+ " %run <path/to/file.imp> interpret program\n"
+ " %set <var> <val> set variable\n"
+ " %print [<var>] print variable, or all variables\n"
+ " %help show this message\n");
+}
+
+static void repl_exec_command(hashmap_t context, const char *command) {
+ char *cmd = strtok(command, " \t");
+ if (strcmp(cmd, "%quit") == 0) {
+ exit(EXIT_SUCCESS);
+ } else if (strcmp(cmd, "%help") == 0) {
+ print_help();
+ } else if (strcmp(cmd, "%run") == 0) {
+ char *file = strtok(NULL, " \t");
+ if (file) {
+ if (!exec_file(context, file)) context_print(context);
+ } else {
+ fprintf(stderr, "Usage: %%run <path/to/file.imp>\n");
+ }
+ } else if (strcmp(cmd, "%set") == 0) {
+ char *var = strtok(NULL, " \t");
+ char *val = strtok(NULL, " \t");
+ if (var && val) context_set(context, var, atoi(val));
+ else fprintf(stderr, "Usage: %%set <var> <val>\n");
+ } else if (strcmp(cmd, "%print") == 0) {
+ char *var = strtok(NULL, " \t");
+ if (var) printf("%s = %d\n", var, context_get(context, var));
+ else context_print(context);
+ } else {
+ fprintf(stderr, "Unknown command: %s\n", cmd);
+ }
+}
+
+static void repl_exec_statement(hashmap_t context, const char *statement) {
+ if (!exec_str(context, statement)) context_print(context);
+}
+
+void repl(void) {
+ hashmap_t context = hashmap_create();
+
+ char *line;
+ print_help();
+ while ((line = readline("imp> ")) != NULL) {
+ if (*line) add_history(line);
+ if (line[0] == '%') repl_exec_command(context, line);
+ else if (*line) repl_exec_statement(context, line);
+ free(line);
+ }
+ printf("\n");
+
+ hashmap_free(context);
+} \ No newline at end of file