DEADSOFTWARE

Исправлен экспорт полей и экспорт для "только чтения"
[dsw-obn.git] / oberon.c
index bd5498bbccc06ee9a4ac7ff684ca9f2532149bf9..b56a458a6c1e979a743c9d4139b5df3044f1cdc3 100644 (file)
--- a/oberon.c
+++ b/oberon.c
@@ -48,7 +48,12 @@ enum {
        OF,
        LBRACE,
        RBRACE,
-       RECORD
+       RECORD,
+       POINTER,
+       TO,
+       UPARROW,
+       NIL,
+       IMPORT
 };
 
 // =======================================================================
@@ -104,16 +109,19 @@ oberon_new_type_boolean(int size)
 static oberon_scope_t *
 oberon_open_scope(oberon_context_t * ctx)
 {
-       oberon_scope_t * scope = malloc(sizeof *scope);
-       memset(scope, 0, sizeof *scope);
-
-       oberon_object_t * list = malloc(sizeof *list);
-       memset(list, 0, sizeof *list);
+       oberon_scope_t * scope = calloc(1, sizeof *scope);
+       oberon_object_t * list = calloc(1, sizeof *list);
 
        scope -> ctx = ctx;
        scope -> list = list;
        scope -> up = ctx -> decl;
 
+       if(scope -> up)
+       {
+               scope -> parent = scope -> up -> parent;
+               scope -> local = scope -> up -> local;
+       }
+
        ctx -> decl = scope;
        return scope;
 }
@@ -126,7 +134,7 @@ oberon_close_scope(oberon_scope_t * scope)
 }
 
 static oberon_object_t *
-oberon_define_object(oberon_scope_t * scope, char * name, int class)
+oberon_define_object(oberon_scope_t * scope, char * name, int class, int export, int read_only)
 {
        oberon_object_t * x = scope -> list;
        while(x -> next && strcmp(x -> next -> name, name) != 0)
@@ -143,38 +151,17 @@ oberon_define_object(oberon_scope_t * scope, char * name, int class)
        memset(newvar, 0, sizeof *newvar);
        newvar -> name = name;
        newvar -> class = class;
+       newvar -> export = export;
+       newvar -> read_only = read_only;
+       newvar -> local = scope -> local;
+       newvar -> parent = scope -> parent;
+       newvar -> module = scope -> ctx -> mod;
 
        x -> next = newvar;
 
        return newvar;
 }
 
-static void
-oberon_define_field(oberon_context_t * ctx, oberon_type_t * rec, char *  name, oberon_type_t * type)
-{
-       oberon_object_t * x = rec -> decl;
-       while(x -> next && strcmp(x -> next -> name, name) != 0)
-       {
-               x = x -> next;
-       }
-
-       if(x -> next)
-       {
-               oberon_error(ctx, "multiple definition");
-       }
-
-       oberon_object_t * field = malloc(sizeof *field);
-       memset(field, 0, sizeof *field);
-       field -> name = name;
-       field -> class = OBERON_CLASS_FIELD;
-       field -> type = type;
-
-       rec -> num_decl += 1;
-       oberon_generator_init_var(ctx, field);
-
-       x -> next = field;
-}
-
 static oberon_object_t *
 oberon_find_object_in_list(oberon_object_t * list, char * name)
 {
@@ -187,7 +174,7 @@ oberon_find_object_in_list(oberon_object_t * list, char * name)
 }
 
 static oberon_object_t *
-oberon_find_object(oberon_scope_t * scope, char * name)
+oberon_find_object(oberon_scope_t * scope, char * name, int check_it)
 {
        oberon_object_t * result = NULL;
 
@@ -198,7 +185,7 @@ oberon_find_object(oberon_scope_t * scope, char * name)
                s = s -> up;
        }
 
-       if(result == NULL)
+       if(check_it && result == NULL)
        {
                oberon_error(scope -> ctx, "undefined ident %s", name);
        }
@@ -225,62 +212,15 @@ oberon_find_field(oberon_context_t * ctx, oberon_type_t * rec, char * name)
 }
 
 static oberon_object_t *
-oberon_define_type(oberon_scope_t * scope, char * name, oberon_type_t * type)
+oberon_define_type(oberon_scope_t * scope, char * name, oberon_type_t * type, int export)
 {
        oberon_object_t * id;
-       id = oberon_define_object(scope, name, OBERON_CLASS_TYPE);
+       id = oberon_define_object(scope, name, OBERON_CLASS_TYPE, export, 0);
        id -> type = type;
        oberon_generator_init_type(scope -> ctx, type);
        return id;
 }
 
-static oberon_type_t *
-oberon_find_type(oberon_scope_t * scope, char * name)
-{
-       oberon_object_t * x = oberon_find_object(scope, name);
-       if(x -> class != OBERON_CLASS_TYPE)
-       {
-               oberon_error(scope -> ctx, "%s not a type", name);
-       }
-
-       return x -> type;
-}
-
-static oberon_object_t *
-oberon_define_var(oberon_scope_t * scope, int class, char * name, oberon_type_t * type)
-{
-       oberon_object_t * var;
-       var = oberon_define_object(scope, name, class);
-       var -> type = type;
-       oberon_generator_init_var(scope -> ctx, var);
-       return var;
-}
-
-/*
-static oberon_object_t *
-oberon_find_var(oberon_scope_t * scope, char * name)
-{
-       oberon_object_t * x = oberon_find_object(scope, name);
-
-       if(x -> class != OBERON_CLASS_VAR)
-       {
-               oberon_error(scope -> ctx, "%s not a var", name);
-       }
-
-       return x;
-}
-*/
-
-static oberon_object_t *
-oberon_define_proc(oberon_scope_t * scope, char * name, oberon_type_t * signature)
-{
-       oberon_object_t * proc;
-       proc = oberon_define_object(scope, name, OBERON_CLASS_PROC);
-       proc -> type = signature;
-       oberon_generator_init_proc(scope -> ctx, proc);
-       return proc;
-}
-
 // =======================================================================
 //   SCANER
 // ======================================================================= 
@@ -387,6 +327,22 @@ oberon_read_ident(oberon_context_t * ctx)
        {
                ctx -> token = RECORD;
        }
+       else if(strcmp(ident, "POINTER") == 0)
+       {
+               ctx -> token = POINTER;
+       }
+       else if(strcmp(ident, "TO") == 0)
+       {
+               ctx -> token = TO;
+       }
+       else if(strcmp(ident, "NIL") == 0)
+       {
+               ctx -> token = NIL;
+       }
+       else if(strcmp(ident, "IMPORT") == 0)
+       {
+               ctx -> token = IMPORT;
+       }
 }
 
 static void
@@ -519,6 +475,10 @@ oberon_read_symbol(oberon_context_t * ctx)
                        ctx -> token = RBRACE;
                        oberon_get_char(ctx);
                        break;
+               case '^':
+                       ctx -> token = UPARROW;
+                       oberon_get_char(ctx);
+                       break;
                default:
                        oberon_error(ctx, "invalid char");
                        break;
@@ -553,7 +513,8 @@ static void oberon_expect_token(oberon_context_t * ctx, int token);
 static oberon_expr_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_type_t * oberon_type(oberon_context_t * ctx);
+static void oberon_type(oberon_context_t * ctx, oberon_type_t ** type);
+static oberon_item_t * oberon_const_expr(oberon_context_t * ctx);
 
 static oberon_expr_t *
 oberon_new_operator(int op, oberon_type_t * result, oberon_expr_t * left, oberon_expr_t * right)
@@ -564,6 +525,7 @@ oberon_new_operator(int op, oberon_type_t * result, oberon_expr_t * left, oberon
 
        operator -> is_item = 0;
        operator -> result = result;
+       operator -> read_only = 1;
        operator -> op = op;
        operator -> left = left;
        operator -> right = right;
@@ -572,7 +534,7 @@ oberon_new_operator(int op, oberon_type_t * result, oberon_expr_t * left, oberon
 }
 
 static oberon_expr_t *
-oberon_new_item(int mode, oberon_type_t * result)
+oberon_new_item(int mode, oberon_type_t * result, int read_only)
 {
        oberon_item_t * item;
         item = malloc(sizeof *item);
@@ -580,6 +542,7 @@ oberon_new_item(int mode, oberon_type_t * result)
 
        item -> is_item = 1;
        item -> result = result;
+       item -> read_only = read_only;
        item -> mode = mode;
 
        return (oberon_expr_t *)item;
@@ -620,7 +583,7 @@ oberon_make_unary_op(oberon_context_t * ctx, int token, oberon_expr_t * a)
 }
 
 static void
-oberon_expr_list(oberon_context_t * ctx, int * num_expr, oberon_expr_t ** first)
+oberon_expr_list(oberon_context_t * ctx, int * num_expr, oberon_expr_t ** first, int const_expr)
 {
        oberon_expr_t * last;
 
@@ -630,7 +593,16 @@ oberon_expr_list(oberon_context_t * ctx, int * num_expr, oberon_expr_t ** first)
        {
                oberon_assert_token(ctx, COMMA);
                oberon_expr_t * current;
-               current = oberon_expr(ctx);
+
+               if(const_expr)
+               {
+                       current = (oberon_expr_t *) oberon_const_expr(ctx);
+               }
+               else
+               {
+                       current = oberon_expr(ctx);
+               }
+
                last -> next = current;
                last = current;
                *num_expr += 1;
@@ -645,7 +617,6 @@ oberon_autocast_to(oberon_context_t * ctx, oberon_expr_t * expr, oberon_type_t *
                oberon_error(ctx, "incompatible types");
        }
 
-
        if(pref -> class == OBERON_TYPE_INTEGER)
        {
                if(expr -> result -> class > pref -> class)
@@ -661,6 +632,16 @@ oberon_autocast_to(oberon_context_t * ctx, oberon_expr_t * expr, oberon_type_t *
                        oberon_error(ctx, "incompatible record types");
                }
        }
+       else if(pref -> class == OBERON_TYPE_POINTER)
+       {
+               if(expr -> result -> base != pref -> base)
+               {
+                       if(expr -> result -> base -> class != OBERON_TYPE_VOID)
+                       {
+                               oberon_error(ctx, "incompatible pointer types");
+                       }
+               }
+       }
 
        // TODO cast
 
@@ -680,7 +661,7 @@ oberon_autocast_call(oberon_context_t * ctx, oberon_expr_t * desig)
                oberon_error(ctx, "expected mode CALL");
        }
 
-       if(desig -> item.var -> class != OBERON_CLASS_PROC)
+       if(desig -> item.var -> type -> class != OBERON_TYPE_PROCEDURE)
        {
                oberon_error(ctx, "only procedures can be called");
        }
@@ -702,12 +683,132 @@ oberon_autocast_call(oberon_context_t * ctx, oberon_expr_t * desig)
        oberon_object_t * param = fn -> decl;
        for(int i = 0; i < num_args; i++)
        {
+               if(param -> class == OBERON_CLASS_VAR_PARAM)
+               {
+                       if(arg -> is_item)
+                       {
+                               switch(arg -> item.mode)
+                               {
+                                       case MODE_VAR:
+                                       case MODE_INDEX:
+                                       case MODE_FIELD:
+                                       // Допустимо разыменование?
+                                       //case MODE_DEREF:
+                                               break;
+                                       default:
+                                               oberon_error(ctx, "var-parameter accept only variables");
+                                               break;
+                               }
+                       }
+               }
                oberon_autocast_to(ctx, arg, param -> type);
                arg = arg -> next;
                param = param -> next;
        }
 }
 
+static oberon_expr_t *
+oberon_make_call_func(oberon_context_t * ctx, oberon_object_t * proc, int num_args, oberon_expr_t * list_args)
+{
+       switch(proc -> class)
+       {
+               case OBERON_CLASS_PROC:
+                       if(proc -> class != OBERON_CLASS_PROC)
+                       {
+                               oberon_error(ctx, "not a procedure");
+                       }
+                       break;
+               case OBERON_CLASS_VAR:
+               case OBERON_CLASS_VAR_PARAM:
+               case OBERON_CLASS_PARAM:
+                       if(proc -> type -> class != OBERON_TYPE_PROCEDURE)
+                       {
+                               oberon_error(ctx, "not a procedure");
+                       }
+                       break;
+               default:
+                       oberon_error(ctx, "not a procedure");
+                       break;
+       }
+
+       oberon_expr_t * call;
+
+       if(proc -> sysproc)
+       {
+               if(proc -> genfunc == NULL)
+               {
+                       oberon_error(ctx, "not a function-procedure");
+               }
+
+               call = proc -> genfunc(ctx, num_args, list_args);
+       }
+       else
+       {
+               if(proc -> type -> base -> class == OBERON_TYPE_VOID)
+               {
+                       oberon_error(ctx, "attempt to call procedure in expression");
+               }
+
+               call = oberon_new_item(MODE_CALL, proc -> type -> base, 1);
+               call -> item.var = proc;
+               call -> item.num_args = num_args;
+               call -> item.args = list_args;
+               oberon_autocast_call(ctx, call);
+       }
+
+       return call;
+}
+
+static void
+oberon_make_call_proc(oberon_context_t * ctx, oberon_object_t * proc, int num_args, oberon_expr_t * list_args)
+{
+       switch(proc -> class)
+       {
+               case OBERON_CLASS_PROC:
+                       if(proc -> class != OBERON_CLASS_PROC)
+                       {
+                               oberon_error(ctx, "not a procedure");
+                       }
+                       break;
+               case OBERON_CLASS_VAR:
+               case OBERON_CLASS_VAR_PARAM:
+               case OBERON_CLASS_PARAM:
+                       if(proc -> type -> class != OBERON_TYPE_PROCEDURE)
+                       {
+                               oberon_error(ctx, "not a procedure");
+                       }
+                       break;
+               default:
+                       oberon_error(ctx, "not a procedure");
+                       break;
+       }
+
+       if(proc -> sysproc)
+       {
+               if(proc -> genproc == NULL)
+               {
+                       oberon_error(ctx, "requres non-typed procedure");
+               }
+
+               proc -> genproc(ctx, num_args, list_args);
+       }
+       else
+       {
+               if(proc -> type -> base -> class != OBERON_TYPE_VOID)
+               {
+                       oberon_error(ctx, "attempt to call function as non-typed procedure");
+               }
+
+               oberon_expr_t * call;
+               call = oberon_new_item(MODE_CALL, proc -> type -> base, 1);
+               call -> item.var = proc;
+               call -> item.num_args = num_args;
+               call -> item.args = list_args;
+               oberon_autocast_call(ctx, call);
+               oberon_generate_call_proc(ctx, call);
+       }
+}
+
 #define ISEXPR(x) \
        (((x) == PLUS) \
        || ((x) == MINUS) \
@@ -718,51 +819,65 @@ oberon_autocast_call(oberon_context_t * ctx, oberon_expr_t * desig)
        || ((x) == TRUE) \
        || ((x) == FALSE))
 
-#define ISSELECTOR(x) \
-       (((x) == LBRACE) \
-       || ((x) == DOT))
-
 static oberon_expr_t *
-oberon_make_array_selector(oberon_context_t * ctx, oberon_expr_t * desig, int num_indexes, oberon_expr_t * indexes)
+oberno_make_dereferencing(oberon_context_t * ctx, oberon_expr_t * expr)
 {
-       assert(desig -> is_item == 1);
-
-       if(desig -> item.mode != MODE_VAR)
+       if(expr -> result -> class != OBERON_TYPE_POINTER)
        {
-               oberon_error(ctx, "not MODE_VAR");
+               oberon_error(ctx, "not a pointer");
        }
 
-       int class = desig -> item.var -> class;
-       switch(class)
+       assert(expr -> is_item);
+
+       oberon_expr_t * selector;
+       selector = oberon_new_item(MODE_DEREF, expr -> result -> base, expr -> read_only);
+       selector -> item.parent = (oberon_item_t *) expr;
+
+       return selector;
+}
+
+static oberon_expr_t *
+oberon_make_array_selector(oberon_context_t * ctx, oberon_expr_t * desig, oberon_expr_t * index)
+{
+       if(desig -> result -> class == OBERON_TYPE_POINTER)
        {
-               case OBERON_CLASS_VAR:
-               case OBERON_CLASS_VAR_PARAM:
-               case OBERON_CLASS_PARAM:
-                       break;
-               default:
-                       oberon_error(ctx, "not variable");
-                       break;
+               desig = oberno_make_dereferencing(ctx, desig);
        }
 
-       oberon_type_t * type = desig -> item.var -> type;
-       if(type -> class != OBERON_TYPE_ARRAY)
+       assert(desig -> is_item);
+
+       if(desig -> result -> class != OBERON_TYPE_ARRAY)
        {
                oberon_error(ctx, "not array");
        }
 
-       int dim = desig -> item.var -> type -> dim;
-       if(num_indexes != dim)
+       oberon_type_t * base;
+       base = desig -> result -> base;
+
+       if(index -> result -> class != OBERON_TYPE_INTEGER)
        {
-               oberon_error(ctx, "dimesions not matched");
+               oberon_error(ctx, "index must be integer");
        }
 
-       oberon_type_t * base = desig -> item.var -> type -> base;
+       // Статическая проверка границ массива
+       if(index -> is_item)
+       {
+               if(index -> item.mode == MODE_INTEGER)
+               {
+                       int arr_size = desig -> result -> size;
+                       int index_int = index -> item.integer;
+                       if(index_int < 0 || index_int > arr_size - 1)
+                       {
+                               oberon_error(ctx, "not in range (dimension size 0..%i)", arr_size - 1);
+                       }
+               }
+       }
 
        oberon_expr_t * selector;
-       selector = oberon_new_item(MODE_INDEX, base);
+       selector = oberon_new_item(MODE_INDEX, base, desig -> read_only);
        selector -> item.parent = (oberon_item_t *) desig;
-       selector -> item.num_args = num_indexes;
-       selector -> item.args = indexes;
+       selector -> item.num_args = 1;
+       selector -> item.args = index;
 
        return selector;
 }
@@ -770,10 +885,14 @@ oberon_make_array_selector(oberon_context_t * ctx, oberon_expr_t * desig, int nu
 static oberon_expr_t *
 oberon_make_record_selector(oberon_context_t * ctx, oberon_expr_t * expr, char * name)
 {
+       if(expr -> result -> class == OBERON_TYPE_POINTER)
+       {
+               expr = oberno_make_dereferencing(ctx, expr);
+       }
+
        assert(expr -> is_item == 1);
 
-       int class = expr -> result -> class;
-       if(class != OBERON_TYPE_RECORD)
+       if(expr -> result -> class != OBERON_TYPE_RECORD)
        {
                oberon_error(ctx, "not record");
        }
@@ -783,14 +902,69 @@ oberon_make_record_selector(oberon_context_t * ctx, oberon_expr_t * expr, char *
        oberon_object_t * field;
        field = oberon_find_field(ctx, rec, name);
 
+       if(field -> export == 0)
+       {
+               if(field -> module != ctx -> mod)
+               {
+                       oberon_error(ctx, "field not exported");
+               }
+       }
+
+       int read_only = 0;
+       if(field -> read_only)
+       {
+               if(field -> module != ctx -> mod)
+               {
+                       read_only = 1;
+               }
+       }
+
        oberon_expr_t * selector;
-       selector = oberon_new_item(MODE_FIELD, field -> type);
+       selector = oberon_new_item(MODE_FIELD, field -> type, read_only);
        selector -> item.var = field;
        selector -> item.parent = (oberon_item_t *) expr;
 
        return selector;
 }
 
+#define ISSELECTOR(x) \
+       (((x) == LBRACE) \
+       || ((x) == DOT) \
+       || ((x) == UPARROW))
+
+static oberon_object_t *
+oberon_qualident(oberon_context_t * ctx, char ** xname, int check)
+{
+       char * name;
+       oberon_object_t * x;
+
+       name = oberon_assert_ident(ctx);
+       x = oberon_find_object(ctx -> decl, name, check);
+
+       if(x != NULL)
+       {
+               if(x -> class == OBERON_CLASS_MODULE)
+               {
+                       oberon_assert_token(ctx, DOT);
+                       name = oberon_assert_ident(ctx);
+                       /* Наличие объектов в левых модулях всегда проверяется */
+                       x = oberon_find_object(x -> module -> decl, name, 1);
+
+                       if(x -> export == 0)
+                       {
+                               oberon_error(ctx, "not exported");
+                       }
+               }
+       }
+
+       if(xname)
+       {
+               *xname = name;
+       }
+
+       return x;
+}
+
 static oberon_expr_t *
 oberon_designator(oberon_context_t * ctx)
 {
@@ -798,8 +972,16 @@ oberon_designator(oberon_context_t * ctx)
        oberon_object_t * var;
        oberon_expr_t * expr;
 
-       name = oberon_assert_ident(ctx);
-       var = oberon_find_object(ctx -> decl, name);
+       var = oberon_qualident(ctx, NULL, 1);
+
+       int read_only = 0;
+       if(var -> read_only)
+       {
+               if(var -> module != ctx -> mod)
+               {
+                       read_only = 1;
+               }
+       }
 
        switch(var -> class)
        {
@@ -810,10 +992,10 @@ oberon_designator(oberon_context_t * ctx)
                case OBERON_CLASS_VAR:
                case OBERON_CLASS_VAR_PARAM:
                case OBERON_CLASS_PARAM:
-                       expr = oberon_new_item(MODE_VAR, var -> type);
+                       expr = oberon_new_item(MODE_VAR, var -> type, read_only);
                        break;
                case OBERON_CLASS_PROC:
-                       expr = oberon_new_item(MODE_CALL, var -> type);
+                       expr = oberon_new_item(MODE_VAR, var -> type, 1);
                        break;
                default:
                        oberon_error(ctx, "invalid designator");
@@ -834,9 +1016,18 @@ oberon_designator(oberon_context_t * ctx)
                                oberon_assert_token(ctx, LBRACE);
                                int num_indexes = 0;
                                oberon_expr_t * indexes = NULL;
-                               oberon_expr_list(ctx, &num_indexes, &indexes);
+                               oberon_expr_list(ctx, &num_indexes, &indexes, 0);
                                oberon_assert_token(ctx, RBRACE);
-                               expr = oberon_make_array_selector(ctx, expr, num_indexes, indexes);
+
+                               for(int i = 0; i < num_indexes; i++)
+                               {
+                                       expr = oberon_make_array_selector(ctx, expr, indexes);
+                                       indexes = indexes -> next;
+                               }
+                               break;
+                       case UPARROW:
+                               oberon_assert_token(ctx, UPARROW);
+                               expr = oberno_make_dereferencing(ctx, expr);
                                break;
                        default:
                                oberon_error(ctx, "oberon_designator: wat");
@@ -847,17 +1038,13 @@ oberon_designator(oberon_context_t * ctx)
 }
 
 static oberon_expr_t *
-oberon_opt_proc_parens(oberon_context_t * ctx, oberon_expr_t * expr)
+oberon_opt_func_parens(oberon_context_t * ctx, oberon_expr_t * expr)
 {
        assert(expr -> is_item == 1);
 
+       /* Если есть скобки - значит вызов. Если нет, то передаём указатель. */
        if(ctx -> token == LPAREN)
        {
-               if(expr -> result -> class != OBERON_TYPE_PROCEDURE)
-               {
-                       oberon_error(ctx, "not a procedure");
-               }
-
                oberon_assert_token(ctx, LPAREN);
 
                int num_args = 0;
@@ -865,21 +1052,41 @@ oberon_opt_proc_parens(oberon_context_t * ctx, oberon_expr_t * expr)
 
                if(ISEXPR(ctx -> token))
                {
-                       oberon_expr_list(ctx, &num_args, &arguments);
+                       oberon_expr_list(ctx, &num_args, &arguments, 0);
                }
 
-               expr -> result = expr -> item.var -> type -> base;
-               expr -> item.mode = MODE_CALL;
-               expr -> item.num_args = num_args;
-               expr -> item.args = arguments;
-               oberon_assert_token(ctx, RPAREN);
+               expr = oberon_make_call_func(ctx, expr -> item.var, num_args, arguments);
 
-               oberon_autocast_call(ctx, expr);
+               oberon_assert_token(ctx, RPAREN);
        }
 
        return expr;
 }
 
+static void
+oberon_opt_proc_parens(oberon_context_t * ctx, oberon_expr_t * expr)
+{
+       assert(expr -> is_item == 1);
+
+       int num_args = 0;
+       oberon_expr_t * arguments = NULL;
+
+       if(ctx -> token == LPAREN)
+       {
+               oberon_assert_token(ctx, LPAREN);
+
+               if(ISEXPR(ctx -> token))
+               {
+                       oberon_expr_list(ctx, &num_args, &arguments, 0);
+               }
+
+               oberon_assert_token(ctx, RPAREN);
+       }
+
+       /* Вызов происходит даже без скобок */
+       oberon_make_call_proc(ctx, expr -> item.var, num_args, arguments);
+}
+
 static oberon_expr_t *
 oberon_factor(oberon_context_t * ctx)
 {
@@ -889,20 +1096,20 @@ oberon_factor(oberon_context_t * ctx)
        {
                case IDENT:
                        expr = oberon_designator(ctx);
-                       expr = oberon_opt_proc_parens(ctx, expr);
+                       expr = oberon_opt_func_parens(ctx, expr);
                        break;
                case INTEGER:
-                       expr = oberon_new_item(MODE_INTEGER, ctx -> int_type);
+                       expr = oberon_new_item(MODE_INTEGER, ctx -> int_type, 1);
                        expr -> item.integer = ctx -> integer;
                        oberon_assert_token(ctx, INTEGER);
                        break;
                case TRUE:
-                       expr = oberon_new_item(MODE_BOOLEAN, ctx -> bool_type);
+                       expr = oberon_new_item(MODE_BOOLEAN, ctx -> bool_type, 1);
                        expr -> item.boolean = 1;
                        oberon_assert_token(ctx, TRUE);
                        break;
                case FALSE:
-                       expr = oberon_new_item(MODE_BOOLEAN, ctx -> bool_type);
+                       expr = oberon_new_item(MODE_BOOLEAN, ctx -> bool_type, 1);
                        expr -> item.boolean = 0;
                        oberon_assert_token(ctx, FALSE);
                        break;
@@ -916,6 +1123,10 @@ oberon_factor(oberon_context_t * ctx)
                        expr = oberon_factor(ctx);
                        expr = oberon_make_unary_op(ctx, NOT, expr);
                        break;
+               case NIL:
+                       oberon_assert_token(ctx, NIL);
+                       expr = oberon_new_item(MODE_NIL, ctx -> void_ptr_type, 1);
+                       break;
                default:
                        oberon_error(ctx, "invalid expression");
        }
@@ -1166,7 +1377,9 @@ oberon_const_expr(oberon_context_t * ctx)
 //   PARSER
 // =======================================================================
 
+static void oberon_decl_seq(oberon_context_t * ctx);
 static void oberon_statement_seq(oberon_context_t * ctx);
+static void oberon_initialize_decl(oberon_context_t * ctx);
 
 static void
 oberon_expect_token(oberon_context_t * ctx, int token)
@@ -1193,169 +1406,122 @@ oberon_assert_ident(oberon_context_t * ctx)
        return ident;
 }
 
-static oberon_type_t *
-oberon_make_array_type(oberon_context_t * ctx, int dim, oberon_item_t * size, oberon_type_t * base)
+static void
+oberon_def(oberon_context_t * ctx, int * export, int * read_only)
 {
-       assert(dim == 1);
-       oberon_type_t * newtype;
-
-       if(size -> mode != MODE_INTEGER)
+       switch(ctx -> token)
        {
-               oberon_error(ctx, "requires integer constant");
+               case STAR:
+                       oberon_assert_token(ctx, STAR);
+                       *export = 1;
+                       *read_only = 0;
+                       break;
+               case MINUS:
+                       oberon_assert_token(ctx, MINUS);
+                       *export = 1;
+                       *read_only = 1;
+                       break;
+               default:
+                       *export = 0;
+                       *read_only = 0;
+                       break;
        }
+}
 
-       newtype = oberon_new_type_ptr(OBERON_TYPE_ARRAY);
-       newtype -> dim = dim;
-       newtype -> size = size -> integer;
-       newtype -> base = base;
-       oberon_generator_init_type(ctx, newtype);
+static oberon_object_t *
+oberon_ident_def(oberon_context_t * ctx, int class)
+{
+       char * name;
+       int export;
+       int read_only;
+       oberon_object_t * x;
+
+       name = oberon_assert_ident(ctx);
+       oberon_def(ctx, &export, &read_only);
 
-       return newtype;
+       x = oberon_define_object(ctx -> decl, name, class, export, read_only);
+       return x;
 }
 
 static void
-oberon_field_list(oberon_context_t * ctx, oberon_type_t * rec)
+oberon_ident_list(oberon_context_t * ctx, int class, int * num, oberon_object_t ** list)
 {
-       if(ctx -> token == IDENT)
-       {
-               char * name;
-               oberon_type_t * type;
-               name = oberon_assert_ident(ctx);
-               oberon_assert_token(ctx, COLON);
-               type = oberon_type(ctx);
-               oberon_define_field(ctx, rec, name, type);
-       }
-}
-
-static oberon_type_t * oberon_opt_formal_pars(oberon_context_t * ctx, int class);
-
-static oberon_type_t *
-oberon_type(oberon_context_t * ctx)
-{
-       oberon_type_t * type;
-
-       if(ctx -> token == IDENT)
-       {
-               char * name = oberon_assert_ident(ctx);
-               type = oberon_find_type(ctx -> decl, name);
-       }
-       else if(ctx -> token == ARRAY)
-       {
-               oberon_assert_token(ctx, ARRAY);
-               oberon_item_t * size = oberon_const_expr(ctx);
-               oberon_assert_token(ctx, OF);
-               oberon_type_t * base = oberon_type(ctx);
-               type = oberon_make_array_type(ctx, 1, size, base);
-       }
-       else if(ctx -> token == RECORD)
-       {
-               type = oberon_new_type_ptr(OBERON_TYPE_RECORD);
-               oberon_object_t * list = malloc(sizeof *list);
-               memset(list, 0, sizeof *list);
-               type -> num_decl = 0;
-               type -> base = NULL;
-               type -> decl = list;
-
-               oberon_assert_token(ctx, RECORD);
-               oberon_field_list(ctx, type);
-               while(ctx -> token == SEMICOLON)
-               {
-                       oberon_assert_token(ctx, SEMICOLON);
-                       oberon_field_list(ctx, type);
-               }
-               oberon_assert_token(ctx, END);
-
-               type -> decl = type -> decl -> next;
-               oberon_generator_init_type(ctx, type);
-       }
-       else if(ctx -> token == PROCEDURE)
-       {
-               oberon_assert_token(ctx, PROCEDURE);
-               type = oberon_opt_formal_pars(ctx, OBERON_TYPE_PROCEDURE);
-       }
-       else
+       *num = 1;
+       *list = oberon_ident_def(ctx, class);
+       while(ctx -> token == COMMA)
        {
-               oberon_error(ctx, "invalid type declaration");
+               oberon_assert_token(ctx, COMMA);
+               oberon_ident_def(ctx, class);
+               *num += 1;
        }
-
-       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 -> decl, OBERON_CLASS_VAR, name, type);
-}
+       int num;
+       oberon_object_t * list;
+       oberon_type_t * type;
+       type = oberon_new_type_ptr(OBERON_TYPE_VOID);
 
-static oberon_object_t *
-oberon_make_param(oberon_context_t * ctx, int token, char * name, oberon_type_t * type)
-{
-       oberon_object_t * param;
+       oberon_ident_list(ctx, OBERON_CLASS_VAR, &num, &list);
+       oberon_assert_token(ctx, COLON);
+       oberon_type(ctx, &type);
 
-       if(token == VAR)
+       oberon_object_t * var = list;
+       for(int i = 0; i < num; i++)
        {
-               param = oberon_define_var(ctx -> decl, OBERON_CLASS_VAR_PARAM, name, type);
+               var -> type = type;
+               var = var -> next;
        }
-       else if(token == IDENT)
-       {
-               param = oberon_define_var(ctx -> decl, OBERON_CLASS_PARAM, name, type);
-       }
-       else
-       {
-               oberon_error(ctx, "oberon_make_param: wat");
-       }
-
-       return param;
 }
 
 static oberon_object_t *
 oberon_fp_section(oberon_context_t * ctx, int * num_decl)
 {
-       int modifer_token = ctx -> token;
+       int class = OBERON_CLASS_PARAM;
        if(ctx -> token == VAR)
        {
                oberon_read_token(ctx);
+               class = OBERON_CLASS_VAR_PARAM;
        }
 
-       char * name;
-       name = oberon_assert_ident(ctx);
+       int num;
+       oberon_object_t * list;
+       oberon_ident_list(ctx, class, &num, &list);
 
        oberon_assert_token(ctx, COLON);
 
        oberon_type_t * type;
-       type = oberon_type(ctx);
+       type = oberon_new_type_ptr(OBERON_TYPE_VOID);
+       oberon_type(ctx, &type);
 
-       oberon_object_t * first;
-       first = oberon_make_param(ctx, modifer_token, name, type);
+       oberon_object_t * param = list;
+       for(int i = 0; i < num; i++)
+       {
+               param -> type = type;
+               param = param -> next;
+       }
 
-       *num_decl += 1;
-       return first;
+       *num_decl += num;
+       return list;
 }
 
 #define ISFPSECTION \
        ((ctx -> token == VAR) || (ctx -> token == IDENT))
 
-static oberon_type_t *
-oberon_formal_pars(oberon_context_t * ctx)
+static void
+oberon_formal_pars(oberon_context_t * ctx, oberon_type_t * signature)
 {
-       oberon_type_t * tp;
-       tp = oberon_new_type_ptr(OBERON_TYPE_PROCEDURE);
-       tp -> num_decl = 0;
-       tp -> base = ctx -> void_type;
-       tp -> decl = NULL;
-
        oberon_assert_token(ctx, LPAREN);
 
        if(ISFPSECTION)
        {
-               tp -> decl = oberon_fp_section(ctx, &tp -> num_decl);
+               signature -> decl = oberon_fp_section(ctx, &signature -> num_decl);
                while(ctx -> token == SEMICOLON)
                {
                        oberon_assert_token(ctx, SEMICOLON);
-                       oberon_fp_section(ctx, &tp -> num_decl);
+                       oberon_fp_section(ctx, &signature -> num_decl);
                }
        }
 
@@ -1364,38 +1530,68 @@ oberon_formal_pars(oberon_context_t * ctx)
        if(ctx -> token == COLON)
        {
                oberon_assert_token(ctx, COLON);
-               tp -> base = oberon_type(ctx);
-       }
 
-       oberon_generator_init_type(ctx, tp);
-       return tp;
+               oberon_object_t * typeobj;
+               typeobj = oberon_qualident(ctx, NULL, 1);
+               if(typeobj -> class != OBERON_CLASS_TYPE)
+               {
+                       oberon_error(ctx, "function result is not type");
+               }
+               signature -> base = typeobj -> type;
+       }
 }
 
-static oberon_type_t *
-oberon_opt_formal_pars(oberon_context_t * ctx, int class)
+static void
+oberon_opt_formal_pars(oberon_context_t * ctx, oberon_type_t ** type)
 {
        oberon_type_t * signature;
+       signature = *type;
+       signature -> class = OBERON_TYPE_PROCEDURE;
+       signature -> num_decl = 0;
+       signature -> base = ctx -> void_type;
+       signature -> decl = NULL;
 
        if(ctx -> token == LPAREN)
        {
-               signature = oberon_formal_pars(ctx);
+               oberon_formal_pars(ctx, signature);
        }
-       else
+}
+
+static void
+oberon_compare_signatures(oberon_context_t * ctx, oberon_type_t * a, oberon_type_t * b)
+{
+       if(a -> num_decl != b -> num_decl)
        {
-               signature = oberon_new_type_ptr(class);
-               signature -> num_decl = 0;
-               signature -> base = ctx -> void_type;
-               signature -> decl = NULL;
-               oberon_generator_init_type(ctx, signature);
+               oberon_error(ctx, "number parameters not matched");
        }
 
-       return signature;
+       int num_param = a -> num_decl;
+       oberon_object_t * param_a = a -> decl;
+       oberon_object_t * param_b = b -> decl;
+       for(int i = 0; i < num_param; i++)
+       {
+               if(strcmp(param_a -> name, param_b -> name) != 0)
+               {
+                       oberon_error(ctx, "param %i name not matched", i + 1);
+               }
+
+               if(param_a -> type != param_b -> type)
+               {
+                       oberon_error(ctx, "param %i type not matched", i + 1);
+               }
+
+               param_a = param_a -> next;
+               param_b = param_b -> next;
+       }
 }
 
 static void
 oberon_make_return(oberon_context_t * ctx, oberon_expr_t * expr)
 {
-       if(ctx -> result_type -> class == OBERON_TYPE_VOID)
+       oberon_object_t * proc = ctx -> decl -> parent;
+       oberon_type_t * result_type = proc -> type -> base;
+
+       if(result_type -> class == OBERON_TYPE_VOID)
        {
                if(expr != NULL)
                {
@@ -1409,40 +1605,25 @@ oberon_make_return(oberon_context_t * ctx, oberon_expr_t * expr)
                        oberon_error(ctx, "procedure requires expression on result");
                }
 
-               oberon_autocast_to(ctx, expr, ctx -> result_type);
+               oberon_autocast_to(ctx, expr, result_type);
        }
 
-       ctx -> has_return = 1;
+       proc -> has_return = 1;
 
        oberon_generate_return(ctx, expr);
 }
 
 static void
-oberon_proc_decl(oberon_context_t * ctx)
+oberon_proc_decl_body(oberon_context_t * ctx, oberon_object_t * proc)
 {
-       oberon_assert_token(ctx, PROCEDURE);
-
-       char * name;
-       name = oberon_assert_ident(ctx);
-
-       oberon_scope_t * this_proc_def_scope = ctx -> decl;
-       oberon_open_scope(ctx);
-
-       oberon_type_t * signature;
-       signature = oberon_opt_formal_pars(ctx, OBERON_TYPE_PROCEDURE);
-
-       oberon_object_t * proc;
-       proc = oberon_define_proc(this_proc_def_scope, name, signature);
+       oberon_assert_token(ctx, SEMICOLON);
 
-       ctx -> result_type = signature -> base;
-       ctx -> has_return = 0;
+       ctx -> decl = proc -> scope;
 
-       oberon_assert_token(ctx, SEMICOLON);
+       oberon_decl_seq(ctx);
 
        oberon_generate_begin_proc(ctx, proc);
 
-       // TODO declarations
-
        if(ctx -> token == BEGIN)
        {
                oberon_assert_token(ctx, BEGIN);
@@ -1450,55 +1631,620 @@ oberon_proc_decl(oberon_context_t * ctx)
        }
 
        oberon_assert_token(ctx, END);
-       char * name2 = oberon_assert_ident(ctx);
-       if(strcmp(name2, name) != 0)
+       char * name = oberon_assert_ident(ctx);
+       if(strcmp(name, proc -> name) != 0)
        {
                oberon_error(ctx, "procedure name not matched");
        }
 
-       if(signature -> base -> class == OBERON_TYPE_VOID)
+       if(proc -> type -> base -> class == OBERON_TYPE_VOID
+               && proc -> has_return == 0)
        {
                oberon_make_return(ctx, NULL);
        }
 
-       if(ctx -> has_return == 0)
+       if(proc -> has_return == 0)
        {
                oberon_error(ctx, "procedure requires return");
        }
-       ctx -> result_type = NULL;
 
        oberon_generate_end_proc(ctx);
        oberon_close_scope(ctx -> decl);
 }
 
 static void
-oberon_const_decl(oberon_context_t * ctx)
+oberon_proc_decl(oberon_context_t * ctx)
 {
+       oberon_assert_token(ctx, PROCEDURE);
+
+       int forward = 0;
+       if(ctx -> token == UPARROW)
+       {
+               oberon_assert_token(ctx, UPARROW);
+               forward = 1;
+       }
+
        char * name;
+       int export;
+       int read_only;
+       name = oberon_assert_ident(ctx);
+       oberon_def(ctx, &export, &read_only);
+
+       oberon_scope_t * proc_scope;
+       proc_scope = oberon_open_scope(ctx);
+       ctx -> decl -> local = 1;
+
+       oberon_type_t * signature;
+       signature = oberon_new_type_ptr(OBERON_TYPE_VOID);
+       oberon_opt_formal_pars(ctx, &signature);        
+
+       oberon_initialize_decl(ctx);
+       oberon_generator_init_type(ctx, signature);
+       oberon_close_scope(ctx -> decl);
+
+       oberon_object_t * proc;
+       proc = oberon_find_object(ctx -> decl, name, 0);
+       if(proc != NULL)
+       {
+               if(proc -> class != OBERON_CLASS_PROC)
+               {
+                       oberon_error(ctx, "mult definition");
+               }
+
+               if(forward == 0)
+               {
+                       if(proc -> linked)
+                       {
+                               oberon_error(ctx, "mult procedure definition");
+                       }
+               }
+
+               if(proc -> export != export || proc -> read_only != read_only)
+               {
+                       oberon_error(ctx, "export type not matched");
+               }
+
+               oberon_compare_signatures(ctx, proc -> type, signature);
+       }
+       else
+       {
+               proc = oberon_define_object(ctx -> decl, name, OBERON_CLASS_PROC, export, read_only);
+               proc -> type = signature;
+               proc -> scope = proc_scope;
+               oberon_generator_init_proc(ctx, proc);
+       }
+
+       proc -> scope -> parent = proc;
+
+       if(forward == 0)
+       {
+               proc -> linked = 1;
+               oberon_proc_decl_body(ctx, proc);
+       }
+}
+
+static void
+oberon_const_decl(oberon_context_t * ctx)
+{
        oberon_item_t * value;
        oberon_object_t * constant;
 
-       name = oberon_assert_ident(ctx);
+       constant = oberon_ident_def(ctx, OBERON_CLASS_CONST);
        oberon_assert_token(ctx, EQUAL);
        value = oberon_const_expr(ctx);
-
-       constant = oberon_define_object(ctx -> decl, name, OBERON_CLASS_CONST);
        constant -> value = value;
 }
 
+static void
+oberon_make_array_type(oberon_context_t * ctx, oberon_expr_t * size, oberon_type_t * base, oberon_type_t ** type)
+{
+       if(size -> is_item == 0)
+       {
+               oberon_error(ctx, "requires constant");
+       }
+
+       if(size -> item.mode != MODE_INTEGER)
+       {
+               oberon_error(ctx, "requires integer constant");
+       }
+
+       oberon_type_t * arr;
+       arr = *type;
+       arr -> class = OBERON_TYPE_ARRAY;
+       arr -> size = size -> item.integer;
+       arr -> base = base;
+}
+
+static void
+oberon_field_list(oberon_context_t * ctx, oberon_type_t * rec)
+{
+       if(ctx -> token == IDENT)
+       {
+               int num;
+               oberon_object_t * list;
+               oberon_type_t * type;
+               type = oberon_new_type_ptr(OBERON_TYPE_VOID);
+
+               oberon_ident_list(ctx, OBERON_CLASS_FIELD, &num, &list);
+               oberon_assert_token(ctx, COLON);
+               oberon_type(ctx, &type);
+
+               oberon_object_t * field = list;
+               for(int i = 0; i < num; i++)
+               {
+                       field -> type = type;
+                       field = field -> next;
+               }
+
+               rec -> num_decl += num;
+       }
+}
+
+static void
+oberon_qualident_type(oberon_context_t * ctx, oberon_type_t ** type)
+{
+       char * name;
+       oberon_object_t * to;
+
+       to = oberon_qualident(ctx, &name, 0);
+
+       //name = oberon_assert_ident(ctx);
+       //to = oberon_find_object(ctx -> decl, name, 0);
+
+       if(to != NULL)
+       {
+               if(to -> class != OBERON_CLASS_TYPE)
+               {
+                       oberon_error(ctx, "not a type");
+               }
+       }
+       else
+       {
+               to = oberon_define_object(ctx -> decl, name, OBERON_CLASS_TYPE, 0, 0);
+               to -> type = oberon_new_type_ptr(OBERON_TYPE_VOID);
+       }
+
+       *type = to -> type;
+}
+
+static void oberon_opt_formal_pars(oberon_context_t * ctx, oberon_type_t ** type);
+
+/*
+ * Правило граматики "type". Указатель type должен указывать на существующий объект!
+ */
+
+static void
+oberon_make_multiarray(oberon_context_t * ctx, oberon_expr_t * sizes, oberon_type_t * base, oberon_type_t ** type)
+{
+       if(sizes == NULL)
+       {
+               *type = base;
+               return;
+       }
+
+       oberon_type_t * dim;
+       dim = oberon_new_type_ptr(OBERON_TYPE_VOID);
+
+       oberon_make_multiarray(ctx, sizes -> next, base, &dim);
+
+       oberon_make_array_type(ctx, sizes, dim, type);
+}
+
+static void
+oberon_type(oberon_context_t * ctx, oberon_type_t ** type)
+{
+       if(ctx -> token == IDENT)
+       {
+               oberon_qualident_type(ctx, type);
+       }
+       else if(ctx -> token == ARRAY)
+       {
+               oberon_assert_token(ctx, ARRAY);
+
+               int num_sizes = 0;
+               oberon_expr_t * sizes;
+               oberon_expr_list(ctx, &num_sizes, &sizes, 1);
+
+               oberon_assert_token(ctx, OF);
+
+               oberon_type_t * base;
+               base = oberon_new_type_ptr(OBERON_TYPE_VOID);
+               oberon_type(ctx, &base);
+
+               oberon_make_multiarray(ctx, sizes, base, type);
+       }
+       else if(ctx -> token == RECORD)
+       {
+               oberon_type_t * rec;
+               rec = *type;
+               rec -> class = OBERON_TYPE_RECORD;
+
+               oberon_scope_t * record_scope;
+               record_scope = oberon_open_scope(ctx);
+               // TODO parent object
+               //record_scope -> parent = NULL;
+               record_scope -> local = 1;
+
+               oberon_assert_token(ctx, RECORD);
+               oberon_field_list(ctx, rec);
+               while(ctx -> token == SEMICOLON)
+               {
+                       oberon_assert_token(ctx, SEMICOLON);
+                       oberon_field_list(ctx, rec);
+               }
+               oberon_assert_token(ctx, END);
+
+               rec -> decl = record_scope -> list -> next;
+               oberon_close_scope(record_scope);
+
+               *type = rec;
+       }
+       else if(ctx -> token == POINTER)
+       {
+               oberon_assert_token(ctx, POINTER);
+               oberon_assert_token(ctx, TO);
+
+               oberon_type_t * base;
+               base = oberon_new_type_ptr(OBERON_TYPE_VOID);
+               oberon_type(ctx, &base);
+
+               oberon_type_t * ptr;
+               ptr = *type;
+               ptr -> class = OBERON_TYPE_POINTER;
+               ptr -> base = base;
+       }
+       else if(ctx -> token == PROCEDURE)
+       {
+               oberon_open_scope(ctx);
+               oberon_assert_token(ctx, PROCEDURE);
+               oberon_opt_formal_pars(ctx, type);
+               oberon_close_scope(ctx -> decl);
+       }
+       else
+       {
+               oberon_error(ctx, "invalid type declaration");
+       }
+}
+
 static void
 oberon_type_decl(oberon_context_t * ctx)
 {
        char * name;
        oberon_object_t * newtype;
        oberon_type_t * type;
+       int export;
+       int read_only;
 
        name = oberon_assert_ident(ctx);
+       oberon_def(ctx, &export, &read_only);
+
+       newtype = oberon_find_object(ctx -> decl, name, 0);
+       if(newtype == NULL)
+       {
+               newtype = oberon_define_object(ctx -> decl, name, OBERON_CLASS_TYPE, export, read_only);
+               newtype -> type = oberon_new_type_ptr(OBERON_TYPE_VOID);
+               assert(newtype -> type);
+       }
+       else
+       {
+               if(newtype -> class != OBERON_CLASS_TYPE)
+               {
+                       oberon_error(ctx, "mult definition");
+               }
+
+               if(newtype -> linked)
+               {
+                       oberon_error(ctx, "mult definition - already linked");
+               }
+
+               newtype -> export = export;
+               newtype -> read_only = read_only;
+       }
+
        oberon_assert_token(ctx, EQUAL);
-       type = oberon_type(ctx);
 
-       newtype = oberon_define_object(ctx -> decl, name, OBERON_CLASS_TYPE);
+       type = newtype -> type;
+       oberon_type(ctx, &type);
+
+       if(type -> class == OBERON_TYPE_VOID)
+       {
+               oberon_error(ctx, "recursive alias declaration");
+       }
+
        newtype -> type = type;
+       newtype -> linked = 1;
+}
+
+static void oberon_prevent_recursive_object(oberon_context_t * ctx, oberon_object_t * x);
+static void oberon_prevent_recursive_type(oberon_context_t * ctx, oberon_type_t * type);
+
+static void
+oberon_prevent_recursive_pointer(oberon_context_t * ctx, oberon_type_t * type)
+{
+       if(type -> class != OBERON_TYPE_POINTER
+               && type -> class != OBERON_TYPE_ARRAY)
+       {
+               return;
+       }
+
+       if(type -> recursive)
+       {
+               oberon_error(ctx, "recursive pointer declaration");
+       }
+
+       if(type -> base -> class == OBERON_TYPE_POINTER)
+       {
+               oberon_error(ctx, "attempt to make pointer to pointer");
+       }
+
+       type -> recursive = 1;
+
+       oberon_prevent_recursive_pointer(ctx, type -> base);
+
+       type -> recursive = 0;
+}
+
+static void
+oberon_prevent_recursive_record(oberon_context_t * ctx, oberon_type_t * type)
+{
+       if(type -> class != OBERON_TYPE_RECORD)
+       {
+               return;
+       }
+
+       if(type -> recursive)
+       {
+               oberon_error(ctx, "recursive record declaration");
+       }
+
+       type -> recursive = 1;
+
+       int num_fields = type -> num_decl;
+       oberon_object_t * field = type -> decl;
+       for(int i = 0; i < num_fields; i++)
+       {
+               oberon_prevent_recursive_object(ctx, field);
+               field = field -> next;
+       }
+
+       type -> recursive = 0;  
+}
+static void
+oberon_prevent_recursive_procedure(oberon_context_t * ctx, oberon_type_t * type)
+{
+       if(type -> class != OBERON_TYPE_PROCEDURE)
+       {
+               return;
+       }
+
+       if(type -> recursive)
+       {
+               oberon_error(ctx, "recursive procedure declaration");
+       }
+
+       type -> recursive = 1;
+
+       int num_fields = type -> num_decl;
+       oberon_object_t * field = type -> decl;
+       for(int i = 0; i < num_fields; i++)
+       {
+               oberon_prevent_recursive_object(ctx, field);
+               field = field -> next;
+       }
+
+       type -> recursive = 0;  
+}
+
+static void
+oberon_prevent_recursive_array(oberon_context_t * ctx, oberon_type_t * type)
+{
+       if(type -> class != OBERON_TYPE_ARRAY)
+       {
+               return;
+       }
+
+       if(type -> recursive)
+       {
+               oberon_error(ctx, "recursive array declaration");
+       }
+
+       type -> recursive = 1;
+
+       oberon_prevent_recursive_type(ctx, type -> base);
+
+       type -> recursive = 0;
+}
+
+static void
+oberon_prevent_recursive_type(oberon_context_t * ctx, oberon_type_t * type)
+{
+       if(type -> class == OBERON_TYPE_POINTER)
+       {
+               oberon_prevent_recursive_pointer(ctx, type);
+       }
+       else if(type -> class == OBERON_TYPE_RECORD)
+       {
+               oberon_prevent_recursive_record(ctx, type);
+       }
+       else if(type -> class == OBERON_TYPE_ARRAY)
+       {
+               oberon_prevent_recursive_array(ctx, type);
+       }
+       else if(type -> class == OBERON_TYPE_PROCEDURE)
+       {
+               oberon_prevent_recursive_procedure(ctx, type);
+       }
+}
+
+static void
+oberon_prevent_recursive_object(oberon_context_t * ctx, oberon_object_t * x)
+{
+       switch(x -> class)
+       {
+               case OBERON_CLASS_VAR:
+               case OBERON_CLASS_TYPE:
+               case OBERON_CLASS_PARAM:
+               case OBERON_CLASS_VAR_PARAM:
+               case OBERON_CLASS_FIELD:
+                       oberon_prevent_recursive_type(ctx, x -> type);
+                       break;
+               case OBERON_CLASS_CONST:
+               case OBERON_CLASS_PROC:
+               case OBERON_CLASS_MODULE:
+                       break;
+               default:
+                       oberon_error(ctx, "oberon_prevent_recursive_object: wat");
+                       break;
+       }
+}
+
+static void
+oberon_prevent_recursive_decl(oberon_context_t * ctx)
+{
+       oberon_object_t * x = ctx -> decl -> list -> next;
+
+       while(x)
+       {
+               oberon_prevent_recursive_object(ctx, x);
+               x = x -> next;
+       }
+}
+
+static void oberon_initialize_object(oberon_context_t * ctx, oberon_object_t * x);
+static void oberon_initialize_type(oberon_context_t * ctx, oberon_type_t * type);
+
+static void
+oberon_initialize_record_fields(oberon_context_t * ctx, oberon_type_t * type)
+{
+       if(type -> class != OBERON_TYPE_RECORD)
+       {
+               return;
+       }
+
+       int num_fields = type -> num_decl;
+       oberon_object_t * field = type -> decl;
+       for(int i = 0; i < num_fields; i++)
+       {
+               if(field -> type -> class == OBERON_TYPE_POINTER)
+               {
+                       oberon_initialize_type(ctx, field -> type);
+               }
+
+               oberon_initialize_object(ctx, field);
+               field = field -> next;
+       }
+
+       oberon_generator_init_record(ctx, type);
+}
+
+static void
+oberon_initialize_type(oberon_context_t * ctx, oberon_type_t * type)
+{
+       if(type -> class == OBERON_TYPE_VOID)
+       {
+               oberon_error(ctx, "undeclarated type");
+       }
+
+       if(type -> initialized)
+       {
+               return;
+       }
+
+       type -> initialized = 1;
+
+       if(type -> class == OBERON_TYPE_POINTER)
+       {
+               oberon_initialize_type(ctx, type -> base);
+               oberon_generator_init_type(ctx, type);
+       }
+       else if(type -> class == OBERON_TYPE_ARRAY)
+       {
+               oberon_initialize_type(ctx, type -> base);              
+               oberon_generator_init_type(ctx, type);
+       }
+       else if(type -> class == OBERON_TYPE_RECORD)
+       {
+               oberon_generator_init_type(ctx, type);
+               oberon_initialize_record_fields(ctx, type);
+       }
+       else if(type -> class == OBERON_TYPE_PROCEDURE)
+       {
+               int num_fields = type -> num_decl;
+               oberon_object_t * field = type -> decl;
+               for(int i = 0; i < num_fields; i++)
+               {
+                       oberon_initialize_object(ctx, field);
+                       field = field -> next;
+               }               
+
+               oberon_generator_init_type(ctx, type);
+       }
+       else
+       {
+               oberon_generator_init_type(ctx, type);
+       }
+}
+
+static void
+oberon_initialize_object(oberon_context_t * ctx, oberon_object_t * x)
+{
+       if(x -> initialized)
+       {
+               return;
+       }
+
+       x -> initialized = 1;
+
+       switch(x -> class)
+       {
+               case OBERON_CLASS_TYPE:
+                       oberon_initialize_type(ctx, x -> type);
+                       break;
+               case OBERON_CLASS_VAR:
+               case OBERON_CLASS_PARAM:
+               case OBERON_CLASS_VAR_PARAM:
+               case OBERON_CLASS_FIELD:
+                       oberon_initialize_type(ctx, x -> type);
+                       oberon_generator_init_var(ctx, x);
+                       break;
+               case OBERON_CLASS_CONST:
+               case OBERON_CLASS_PROC:
+               case OBERON_CLASS_MODULE:
+                       break;
+               default:
+                       oberon_error(ctx, "oberon_initialize_object: wat");
+                       break;
+       }
+}
+
+static void
+oberon_initialize_decl(oberon_context_t * ctx)
+{
+       oberon_object_t * x = ctx -> decl -> list;
+
+       while(x -> next)
+       {
+               oberon_initialize_object(ctx, x -> next);
+               x = x -> next;
+       }       
+}
+
+static void
+oberon_prevent_undeclarated_procedures(oberon_context_t * ctx)
+{
+       oberon_object_t * x = ctx -> decl -> list;
+
+       while(x -> next)
+       {
+               if(x -> next -> class == OBERON_CLASS_PROC)
+               {
+                       if(x -> next -> linked == 0)
+                       {
+                               oberon_error(ctx, "unresolved forward declaration");
+                       }
+               }
+               x = x -> next;
+       }       
 }
 
 static void
@@ -1534,27 +2280,30 @@ oberon_decl_seq(oberon_context_t * ctx)
                }
        }
 
+       oberon_prevent_recursive_decl(ctx);
+       oberon_initialize_decl(ctx);
+
        while(ctx -> token == PROCEDURE)
        {
                oberon_proc_decl(ctx);
                oberon_assert_token(ctx, SEMICOLON);
        }
+
+       oberon_prevent_undeclarated_procedures(ctx);
 }
 
 static void
 oberon_assign(oberon_context_t * ctx, oberon_expr_t * src, oberon_expr_t * dst)
 {
+       if(dst -> read_only)
+       {
+               oberon_error(ctx, "read-only destination");
+       }
+
        oberon_autocast_to(ctx, src, dst -> result);
        oberon_generate_assign(ctx, src, dst);
 }
 
-static void
-oberon_make_call(oberon_context_t * ctx, oberon_expr_t * desig)
-{
-       oberon_autocast_call(ctx, desig);
-       oberon_generate_call_proc(ctx, desig);
-}
-
 static void
 oberon_statement(oberon_context_t * ctx)
 {
@@ -1572,8 +2321,7 @@ oberon_statement(oberon_context_t * ctx)
                }
                else
                {
-                       item1 = oberon_opt_proc_parens(ctx, item1);
-                       oberon_make_call(ctx, item1);
+                       oberon_opt_proc_parens(ctx, item1);
                }
        }
        else if(ctx -> token == RETURN)
@@ -1603,10 +2351,74 @@ oberon_statement_seq(oberon_context_t * ctx)
        }
 }
 
+static void
+oberon_import_module(oberon_context_t * ctx, char * alias, char * name)
+{
+       oberon_module_t * m = ctx -> module_list;
+       while(m && strcmp(m -> name, name) != 0)
+       {
+               m = m -> next;
+       }
+
+       if(m == NULL)
+       {
+               const char * code;
+               code = ctx -> import_module(name);
+               if(code == NULL)
+               {
+                       oberon_error(ctx, "no such module");
+               }
+
+               m = oberon_compile_module(ctx, code);
+               assert(m);
+       }
+
+       if(m -> ready == 0)
+       {
+               oberon_error(ctx, "cyclic module import");
+       }
+
+       oberon_object_t * ident;
+       ident = oberon_define_object(ctx -> decl, alias, OBERON_CLASS_MODULE, 0, 0);
+       ident -> module = m;
+}
+
+static void
+oberon_import_decl(oberon_context_t * ctx)
+{
+       char * alias;
+       char * name;
+
+       alias = name = oberon_assert_ident(ctx);
+       if(ctx -> token == ASSIGN)
+       {
+               oberon_assert_token(ctx, ASSIGN);
+               name = oberon_assert_ident(ctx);
+       }
+
+       oberon_import_module(ctx, alias, name);
+}
+
+static void
+oberon_import_list(oberon_context_t * ctx)
+{
+       oberon_assert_token(ctx, IMPORT);
+
+       oberon_import_decl(ctx);
+       while(ctx -> token == COMMA)
+       {
+               oberon_assert_token(ctx, COMMA);
+               oberon_import_decl(ctx);
+       }
+
+       oberon_assert_token(ctx, SEMICOLON);
+}
+
 static void
 oberon_parse_module(oberon_context_t * ctx)
 {
-       char *name1, *name2;
+       char * name1;
+       char * name2;
        oberon_read_token(ctx);
 
        oberon_assert_token(ctx, MODULE);
@@ -1614,15 +2426,20 @@ oberon_parse_module(oberon_context_t * ctx)
        oberon_assert_token(ctx, SEMICOLON);
        ctx -> mod -> name = name1;
 
+       if(ctx -> token == IMPORT)
+       {
+               oberon_import_list(ctx);
+       }
+
        oberon_decl_seq(ctx);
 
+       oberon_generate_begin_module(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_generate_end_module(ctx);
 
        oberon_assert_token(ctx, END);
        name2 = oberon_assert_ident(ctx);
@@ -1644,26 +2461,73 @@ register_default_types(oberon_context_t * ctx)
        ctx -> void_type = oberon_new_type_ptr(OBERON_TYPE_VOID);
        oberon_generator_init_type(ctx, ctx -> void_type);
 
+       ctx -> void_ptr_type = oberon_new_type_ptr(OBERON_TYPE_POINTER);
+       ctx -> void_ptr_type -> base = ctx -> void_type;
+       oberon_generator_init_type(ctx, ctx -> void_ptr_type);
+
        ctx -> int_type = oberon_new_type_integer(sizeof(int));
-       oberon_define_type(ctx -> world_scope, "INTEGER", ctx -> int_type);
+       oberon_define_type(ctx -> world_scope, "INTEGER", ctx -> int_type, 1);
 
        ctx -> bool_type = oberon_new_type_boolean(sizeof(int));
-       oberon_define_type(ctx -> world_scope, "BOOLEAN", ctx -> bool_type);
+       oberon_define_type(ctx -> world_scope, "BOOLEAN", ctx -> bool_type, 1);
+}
+
+static void
+oberon_new_intrinsic(oberon_context_t * ctx, char * name, GenerateFuncCallback f, GenerateProcCallback p)
+{
+       oberon_object_t * proc;
+       proc = oberon_define_object(ctx -> decl, name, OBERON_CLASS_PROC, 1, 0);
+       proc -> sysproc = 1;
+       proc -> genfunc = f;
+       proc -> genproc = p;
+       proc -> type = oberon_new_type_ptr(OBERON_TYPE_PROCEDURE);
+}
+
+static oberon_expr_t *
+oberon_make_abs_call(oberon_context_t * ctx, int num_args, oberon_expr_t * list_args)
+{
+       if(num_args < 1)
+       {
+               oberon_error(ctx, "too few arguments");
+       }
+
+       if(num_args > 1)
+       {
+               oberon_error(ctx, "too mach arguments");
+       }
+
+       oberon_expr_t * arg;
+       arg = list_args;
+
+       oberon_type_t * result_type;
+       result_type = arg -> result;
+       
+       if(result_type -> class != OBERON_TYPE_INTEGER)
+       {
+               oberon_error(ctx, "ABS accepts only integers");
+       }
+
+
+       oberon_expr_t * expr;
+       expr = oberon_new_operator(OP_ABS, result_type, arg, NULL);
+       return expr;
 }
 
 oberon_context_t *
-oberon_create_context()
+oberon_create_context(ModuleImportCallback import_module)
 {
-       oberon_context_t * ctx = malloc(sizeof *ctx);
-       memset(ctx, 0, sizeof *ctx);
+       oberon_context_t * ctx = calloc(1, sizeof *ctx);
 
        oberon_scope_t * world_scope;
        world_scope = oberon_open_scope(ctx);
        ctx -> world_scope = world_scope;
 
+       ctx -> import_module = import_module;
+
        oberon_generator_init_context(ctx);
 
-       register_default_types(ctx);    
+       register_default_types(ctx);
+       oberon_new_intrinsic(ctx, "ABS", oberon_make_abs_call, NULL);
 
        return ctx;
 }
@@ -1676,21 +2540,41 @@ oberon_destroy_context(oberon_context_t * ctx)
 }
 
 oberon_module_t *
-oberon_compile_module(oberon_context_t * ctx, const char * code)
+oberon_compile_module(oberon_context_t * ctx, const char * newcode)
 {
-       oberon_module_t * mod = malloc(sizeof *mod);
-       memset(mod, 0, sizeof *mod);
-       ctx -> mod = mod;
+       const char * code = ctx -> code;
+       int code_index = ctx -> code_index;
+       char c = ctx -> c;
+       int token = ctx -> token;
+       char * string = ctx -> string;
+       int integer = ctx -> integer;
+       oberon_scope_t * decl = ctx -> decl;
+       oberon_module_t * mod = ctx -> mod;
 
        oberon_scope_t * module_scope;
        module_scope = oberon_open_scope(ctx);
-       mod -> decl = module_scope;
 
-       oberon_init_scaner(ctx, code);
+       oberon_module_t * module;
+       module = calloc(1, sizeof *module);
+       module -> decl = module_scope;
+       module -> next = ctx -> module_list;
+
+       ctx -> mod = module;
+       ctx -> module_list = module;
+
+       oberon_init_scaner(ctx, newcode);
        oberon_parse_module(ctx);
 
-       oberon_generate_code(ctx);
+       module -> ready = 1;
+
+       ctx -> code = code;
+       ctx -> code_index = code_index;
+       ctx -> c = c;
+       ctx -> token = token;
+       ctx -> string = string;
+       ctx -> integer = integer;
+       ctx -> decl = decl;
+       ctx -> mod = mod;
 
-       ctx -> mod = NULL;
-       return mod;
+       return module;
 }