#include #include #include #include #include #include "oberon.h" #include "generator.h" enum { EOF_ = 0, IDENT, MODULE, SEMICOLON, END, DOT, VAR, COLON, BEGIN, ASSIGN, INTEGER, TRUE, FALSE, LPAREN, RPAREN }; // ======================================================================= // UTILS // ======================================================================= void oberon_error(oberon_context_t * ctx, const char * fmt, ...) { va_list ptr; va_start(ptr, fmt); fprintf(stderr, "error: "); vfprintf(stderr, fmt, ptr); fprintf(stderr, "\n"); fprintf(stderr, " code_index = %i\n", ctx -> code_index); fprintf(stderr, " c = %c\n", ctx -> c); fprintf(stderr, " token = %i\n", ctx -> token); va_end(ptr); exit(1); } static int oberon_item_to_type_class(oberon_context_t * ctx, oberon_item_t * item) { int class; switch(item -> mode) { case MODE_INTEGER: class = OBERON_TYPE_INTEGER; break; case MODE_BOOLEAN: class = OBERON_TYPE_BOOLEAN; break; case MODE_VAR: class = item -> var -> type -> class; break; default: oberon_error(ctx, "oberon_item_to_type_class: wat"); break; } return class; } static void oberon_autocast_to(oberon_context_t * ctx, oberon_item_t * from, oberon_item_t * to) { int from_class = oberon_item_to_type_class(ctx, from); int to_class = oberon_item_to_type_class(ctx, to); if(from_class != to_class) { oberon_error(ctx, "oberon_autocast_to: types not matched %i -> %i", from_class, to_class); } } // ======================================================================= // TABLE // ======================================================================= static oberon_type_t * oberon_find_type(oberon_context_t * ctx, char * name) { oberon_type_t * x = ctx -> types; while(x -> next && strcmp(x -> next -> name, name) != 0) { x = x -> next; } return x -> next; } static oberon_var_t * oberon_find_var(oberon_context_t * ctx, char * name) { oberon_var_t * x = ctx -> mod -> vars; while(x -> next && strcmp(x -> next -> name, name) != 0) { x = x -> next; } return x -> next; } static void oberon_define_var(oberon_context_t * ctx, char * name, oberon_type_t * type) { oberon_var_t * x = ctx -> mod -> vars; while(x -> next && strcmp(x -> next -> name, name) != 0) { x = x -> next; } if(x -> next) { oberon_error(ctx, "already defined"); } oberon_var_t * newvar = malloc(sizeof *newvar); memset(newvar, 0, sizeof *newvar); newvar -> name = name; newvar -> type = type; oberon_generator_init_var(ctx, newvar); x -> next = newvar; } // ======================================================================= // SCANER // ======================================================================= static void oberon_get_char(oberon_context_t * ctx) { ctx -> code_index += 1; ctx -> c = ctx -> code[ctx -> code_index]; } static void oberon_init_scaner(oberon_context_t * ctx, const char * code) { ctx -> code = code; ctx -> code_index = 0; ctx -> c = ctx -> code[ctx -> code_index]; } static void oberon_read_ident(oberon_context_t * ctx) { int len = 0; int i = ctx -> code_index; int c = ctx -> code[i]; while(isalnum(c)) { i += 1; len += 1; c = ctx -> code[i]; } char * ident = malloc(len + 1); memcpy(ident, &ctx->code[ctx->code_index], len); ident[len] = 0; ctx -> code_index = i; ctx -> c = ctx -> code[i]; ctx -> string = ident; ctx -> token = IDENT; if(strcmp(ident, "MODULE") == 0) { ctx -> token = MODULE; } else if(strcmp(ident, "END") == 0) { ctx -> token = END; } else if(strcmp(ident, "VAR") == 0) { ctx -> token = VAR; } else if(strcmp(ident, "BEGIN") == 0) { ctx -> token = BEGIN; } else if(strcmp(ident, "TRUE") == 0) { ctx -> token = TRUE; } else if(strcmp(ident, "FALSE") == 0) { ctx -> token = FALSE; } } static void oberon_read_integer(oberon_context_t * ctx) { int len = 0; int i = ctx -> code_index; int c = ctx -> code[i]; while(isdigit(c)) { i += 1; len += 1; c = ctx -> code[i]; } char * ident = malloc(len + 2); memcpy(ident, &ctx->code[ctx->code_index], len); ident[len + 1] = 0; ctx -> code_index = i; ctx -> c = ctx -> code[i]; ctx -> string = ident; ctx -> integer = atoi(ident); ctx -> token = INTEGER; } static void oberon_skip_space(oberon_context_t * ctx) { while(isspace(ctx -> c)) { oberon_get_char(ctx); } } static void oberon_read_symbol(oberon_context_t * ctx) { int c = ctx -> c; switch(c) { case 0: ctx -> token = EOF_; break; case ';': ctx -> token = SEMICOLON; oberon_get_char(ctx); break; case ':': ctx -> token = COLON; oberon_get_char(ctx); if(ctx -> c == '=') { ctx -> token = ASSIGN; oberon_get_char(ctx); } break; case '.': ctx -> token = DOT; oberon_get_char(ctx); break; case '(': ctx -> token = LPAREN; oberon_get_char(ctx); break; case ')': ctx -> token = RPAREN; oberon_get_char(ctx); break; default: oberon_error(ctx, "invalid char"); break; } } static void oberon_read_token(oberon_context_t * ctx) { oberon_skip_space(ctx); int c = ctx -> c; if(isalpha(c)) { oberon_read_ident(ctx); } else if(isdigit(c)) { oberon_read_integer(ctx); } else { oberon_read_symbol(ctx); } } // ======================================================================= // EXPR // ======================================================================= static void oberon_expect_token(oberon_context_t * ctx, int token); static oberon_item_t * oberon_expr(oberon_context_t * ctx); static void oberon_assert_token(oberon_context_t * ctx, int token); static char * oberon_assert_ident(oberon_context_t * ctx); static oberon_item_t * oberon_factor(oberon_context_t * ctx) { char * name; oberon_var_t * var; oberon_item_t * item = malloc(sizeof *item); memset(item, 0, sizeof *item); switch(ctx -> token) { case IDENT: name = oberon_assert_ident(ctx); var = oberon_find_var(ctx, name); if(var == NULL) { oberon_error(ctx, "undefined variable %s", name); } item -> mode = MODE_VAR; item -> var = var; break; case INTEGER: item -> mode = MODE_INTEGER; item -> integer = ctx -> integer; oberon_assert_token(ctx, INTEGER); break; case TRUE: item -> mode = MODE_BOOLEAN; item -> boolean = 1; oberon_assert_token(ctx, TRUE); break; case FALSE: item -> mode = MODE_BOOLEAN; item -> boolean = 0; oberon_assert_token(ctx, FALSE); break; case LPAREN: oberon_assert_token(ctx, LPAREN); item = oberon_expr(ctx); oberon_assert_token(ctx, RPAREN); break; default: oberon_error(ctx, "invalid expression"); } return item; } static oberon_item_t * oberon_expr(oberon_context_t * ctx) { return oberon_factor(ctx); } // ======================================================================= // PARSER // ======================================================================= static void oberon_expect_token(oberon_context_t * ctx, int token) { if(ctx -> token != token) { oberon_error(ctx, "unexpected token %i (%i)", ctx -> token, token); } } static void oberon_assert_token(oberon_context_t * ctx, int token) { oberon_expect_token(ctx, token); oberon_read_token(ctx); } static char * oberon_assert_ident(oberon_context_t * ctx) { oberon_expect_token(ctx, IDENT); char * ident = ctx -> string; oberon_read_token(ctx); return ident; } static oberon_type_t * oberon_type(oberon_context_t * ctx) { char * name = oberon_assert_ident(ctx); oberon_type_t * type = oberon_find_type(ctx, name); if(type == NULL) { oberon_error(ctx, "undefined type"); } return type; } static void oberon_var_decl(oberon_context_t * ctx) { char * name = oberon_assert_ident(ctx); oberon_assert_token(ctx, COLON); oberon_type_t * type = oberon_type(ctx); oberon_define_var(ctx, name, type); } static void oberon_decl_seq(oberon_context_t * ctx) { if(ctx -> token == VAR) { oberon_assert_token(ctx, VAR); while(ctx -> token == IDENT) { oberon_var_decl(ctx); oberon_assert_token(ctx, SEMICOLON); } } } static void oberon_assign(oberon_context_t * ctx, oberon_item_t * src, oberon_item_t * dst) { if(dst -> mode == MODE_INTEGER) { oberon_error(ctx, "invalid assignment"); } oberon_autocast_to(ctx, src, dst); oberon_generate_assign(ctx, src, dst); } static void oberon_statement(oberon_context_t * ctx) { oberon_item_t * item1; oberon_item_t * item2; if(ctx -> token == IDENT) { item1 = oberon_expr(ctx); oberon_assert_token(ctx, ASSIGN); item2 = oberon_expr(ctx); oberon_assign(ctx, item2, item1); } } static void oberon_statement_seq(oberon_context_t * ctx) { oberon_statement(ctx); while(ctx -> token == SEMICOLON) { oberon_assert_token(ctx, SEMICOLON); oberon_statement(ctx); } } static void oberon_parse_module(oberon_context_t * ctx) { char *name1, *name2; oberon_read_token(ctx); oberon_assert_token(ctx, MODULE); name1 = oberon_assert_ident(ctx); oberon_assert_token(ctx, SEMICOLON); ctx -> mod -> name = name1; oberon_decl_seq(ctx); if(ctx -> token == BEGIN) { oberon_assert_token(ctx, BEGIN); oberon_generate_begin_module(ctx); oberon_statement_seq(ctx); oberon_generate_end_module(ctx); } oberon_assert_token(ctx, END); name2 = oberon_assert_ident(ctx); oberon_assert_token(ctx, DOT); if(strcmp(name1, name2) != 0) { oberon_error(ctx, "module name not matched"); } } // ======================================================================= // LIBRARY // ======================================================================= oberon_context_t * oberon_create_context() { oberon_context_t * ctx = malloc(sizeof *ctx); memset(ctx, 0, sizeof *ctx); oberon_type_t * types = malloc(sizeof *types); memset(types, 0, sizeof *types); oberon_generator_init_context(ctx); ctx -> types = types; return ctx; } void oberon_destroy_context(oberon_context_t * ctx) { oberon_generator_destroy_context(ctx); free(ctx); } oberon_module_t * oberon_compile_module(oberon_context_t * ctx, const char * code) { oberon_module_t * mod = malloc(sizeof *mod); memset(mod, 0, sizeof *mod); oberon_var_t * vars = malloc(sizeof *vars); memset(vars, 0, sizeof *vars); ctx -> mod = mod; ctx -> mod -> vars = vars; oberon_init_scaner(ctx, code); oberon_parse_module(ctx); oberon_generate_code(ctx); return mod; } void oberon_register_global_type(oberon_context_t * ctx, oberon_type_t * type) { oberon_type_t * x = ctx -> types; while(x -> next && strcmp(x -> next -> name, type -> name) != 0) { x = x -> next; } if(x -> next) { oberon_error(ctx, "already defined"); } // TODO: copy type name (not a pointer) oberon_type_t * newtype = malloc(sizeof *newtype); memcpy(newtype, type, sizeof *newtype); newtype -> next = NULL; oberon_generator_init_type(ctx, newtype); x -> next = newtype; }