DEADSOFTWARE

Что-то там тестировалось с массивами...
[dsw-obn.git] / oberon.c
index 0aa8d45bf1304f6a95fe211480359d30afa7c9ef..f4a711f5da3156912e43afb4bc8887dd076936a3 100644 (file)
--- a/oberon.c
+++ b/oberon.c
@@ -3,6 +3,7 @@
 #include <stdarg.h>
 #include <ctype.h>
 #include <string.h>
+#include <assert.h>
 
 #include "oberon.h"
 #include "generator.h"
@@ -38,7 +39,18 @@ enum {
        MOD,
        AND,
        NOT,
-       PROCEDURE
+       PROCEDURE,
+       COMMA,
+       RETURN,
+       CONST,
+       TYPE,
+       ARRAY,
+       OF,
+       LBRACE,
+       RBRACE,
+       RECORD,
+       POINTER,
+       TO
 };
 
 // =======================================================================
@@ -60,6 +72,33 @@ oberon_error(oberon_context_t * ctx, const char * fmt, ...)
        exit(1);
 }
 
+static oberon_type_t *
+oberon_new_type_ptr(int class)
+{
+       oberon_type_t * x = malloc(sizeof *x);
+       memset(x, 0, sizeof *x);
+       x -> class = class;
+       return x;
+}
+
+static oberon_type_t *
+oberon_new_type_integer(int size)
+{
+       oberon_type_t * x;
+       x = oberon_new_type_ptr(OBERON_TYPE_INTEGER);
+       x -> size = size;
+       return x;
+}
+
+static oberon_type_t *
+oberon_new_type_boolean(int size)
+{
+       oberon_type_t * x;
+       x = oberon_new_type_ptr(OBERON_TYPE_BOOLEAN);
+       x -> size = size;
+       return x;
+}
+
 // =======================================================================
 //   TABLE
 // ======================================================================= 
@@ -112,6 +151,30 @@ oberon_define_object(oberon_scope_t * scope, char * name, int class)
        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;
+       x -> next = field;
+}
+
 static oberon_object_t *
 oberon_find_object_in_list(oberon_object_t * list, char * name)
 {
@@ -124,7 +187,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;
 
@@ -135,7 +198,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);
        }
@@ -143,15 +206,35 @@ oberon_find_object(oberon_scope_t * scope, char * name)
        return result;
 }
 
-static void
+static oberon_object_t *
+oberon_find_field(oberon_context_t * ctx, oberon_type_t * rec, char * name)
+{
+       oberon_object_t * x = rec -> decl;
+       for(int i = 0; i < rec -> num_decl; i++)
+       {
+               if(strcmp(x -> name, name) == 0)
+               {
+                       return x;
+               }
+               x = x -> next;
+       }
+
+       oberon_error(ctx, "field not defined");
+
+       return NULL;
+}
+
+static oberon_object_t *
 oberon_define_type(oberon_scope_t * scope, char * name, oberon_type_t * type)
 {
        oberon_object_t * id;
        id = oberon_define_object(scope, name, OBERON_CLASS_TYPE);
        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)
 {
@@ -163,14 +246,15 @@ oberon_find_type(oberon_scope_t * scope, char * name)
 
        return x -> type;
 }
+*/
 
-static void
-oberon_define_var(oberon_scope_t * scope, char * name, oberon_type_t * 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, OBERON_CLASS_VAR);
+       var = oberon_define_object(scope, name, class);
        var -> type = type;
-       oberon_generator_init_var(scope -> ctx, var);
+       return var;
 }
 
 /*
@@ -189,11 +273,11 @@ oberon_find_var(oberon_scope_t * scope, char * name)
 */
 
 static oberon_object_t *
-oberon_define_proc(oberon_scope_t * scope, char * name)
+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);
-       oberon_generator_init_proc(scope -> ctx, proc);
+       proc -> type = signature;
        return proc;
 }
 
@@ -279,6 +363,38 @@ oberon_read_ident(oberon_context_t * ctx)
        {
                ctx -> token = PROCEDURE;
        }
+       else if(strcmp(ident, "RETURN") == 0)
+       {
+               ctx -> token = RETURN;
+       }
+       else if(strcmp(ident, "CONST") == 0)
+       {
+               ctx -> token = CONST;
+       }
+       else if(strcmp(ident, "TYPE") == 0)
+       {
+               ctx -> token = TYPE;
+       }
+       else if(strcmp(ident, "ARRAY") == 0)
+       {
+               ctx -> token = ARRAY;
+       }
+       else if(strcmp(ident, "OF") == 0)
+       {
+               ctx -> token = OF;
+       }
+       else if(strcmp(ident, "RECORD") == 0)
+       {
+               ctx -> token = RECORD;
+       }
+       else if(strcmp(ident, "POINTER") == 0)
+       {
+               ctx -> token = POINTER;
+       }
+       else if(strcmp(ident, "TO") == 0)
+       {
+               ctx -> token = TO;
+       }
 }
 
 static void
@@ -399,6 +515,18 @@ oberon_read_symbol(oberon_context_t * ctx)
                        ctx -> token = NOT;
                        oberon_get_char(ctx);
                        break;
+               case ',':
+                       ctx -> token = COMMA;
+                       oberon_get_char(ctx);
+                       break;
+               case '[':
+                       ctx -> token = LBRACE;
+                       oberon_get_char(ctx);
+                       break;
+               case ']':
+                       ctx -> token = RBRACE;
+                       oberon_get_char(ctx);
+                       break;
                default:
                        oberon_error(ctx, "invalid char");
                        break;
@@ -433,6 +561,7 @@ 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 void oberon_type(oberon_context_t * ctx, oberon_type_t ** type);
 
 static oberon_expr_t *
 oberon_new_operator(int op, oberon_type_t * result, oberon_expr_t * left, oberon_expr_t * right)
@@ -498,31 +627,277 @@ oberon_make_unary_op(oberon_context_t * ctx, int token, oberon_expr_t * a)
        return expr;
 }
 
+static void
+oberon_expr_list(oberon_context_t * ctx, int * num_expr, oberon_expr_t ** first)
+{
+       oberon_expr_t * last;
+
+       *num_expr = 1;
+       *first = last = oberon_expr(ctx);
+       while(ctx -> token == COMMA)
+       {
+               oberon_assert_token(ctx, COMMA);
+               oberon_expr_t * current;
+               current = oberon_expr(ctx);
+               last -> next = current;
+               last = current;
+               *num_expr += 1;
+       }
+}
+
 static oberon_expr_t *
-oberon_factor(oberon_context_t * ctx)
+oberon_autocast_to(oberon_context_t * ctx, oberon_expr_t * expr, oberon_type_t * pref)
+{
+       if(pref -> class != expr -> result -> class)
+       {
+               oberon_error(ctx, "incompatible types");
+       }
+
+
+       if(pref -> class == OBERON_TYPE_INTEGER)
+       {
+               if(expr -> result -> class > pref -> class)
+               {
+                       oberon_error(ctx, "incompatible size");
+               }
+       }
+       else if(pref -> class == OBERON_TYPE_RECORD)
+       {
+               if(expr -> result != pref)
+               {
+                       printf("oberon_autocast_to: rec %p != %p\n", expr -> result, pref);
+                       oberon_error(ctx, "incompatible record types");
+               }
+       }
+
+       // TODO cast
+
+       return expr;
+}
+
+static void
+oberon_autocast_call(oberon_context_t * ctx, oberon_expr_t * desig)
+{
+       if(desig -> is_item == 0)
+       {
+               oberon_error(ctx, "expected item");
+       }
+
+       if(desig -> item.mode != MODE_CALL)
+       {
+               oberon_error(ctx, "expected mode CALL");
+       }
+
+       if(desig -> item.var -> class != OBERON_CLASS_PROC)
+       {
+               oberon_error(ctx, "only procedures can be called");
+       }
+
+       oberon_type_t * fn = desig -> item.var -> type;
+       int num_args = desig -> item.num_args;
+       int num_decl = fn -> num_decl;
+
+       if(num_args < num_decl)
+       {
+               oberon_error(ctx, "too few arguments");
+       }
+       else if(num_args > num_decl)
+       {
+               oberon_error(ctx, "too many arguments");
+       }
+
+       oberon_expr_t * arg = desig -> item.args;
+       oberon_object_t * param = fn -> decl;
+       for(int i = 0; i < num_args; i++)
+       {
+               oberon_autocast_to(ctx, arg, param -> type);
+               arg = arg -> next;
+               param = param -> next;
+       }
+}
+
+#define ISEXPR(x) \
+       (((x) == PLUS) \
+       || ((x) == MINUS) \
+       || ((x) == IDENT) \
+       || ((x) == INTEGER) \
+       || ((x) == LPAREN) \
+       || ((x) == NOT) \
+       || ((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)
+{
+       assert(desig -> is_item == 1);
+
+       if(desig -> item.mode != MODE_VAR)
+       {
+               oberon_error(ctx, "not MODE_VAR");
+       }
+
+       int class = desig -> item.var -> class;
+       switch(class)
+       {
+               case OBERON_CLASS_VAR:
+               case OBERON_CLASS_VAR_PARAM:
+               case OBERON_CLASS_PARAM:
+                       break;
+               default:
+                       oberon_error(ctx, "not variable");
+                       break;
+       }
+
+       oberon_type_t * type = desig -> item.var -> type;
+       if(type -> class != OBERON_TYPE_ARRAY)
+       {
+               oberon_error(ctx, "not array");
+       }
+
+//     int dim = desig -> item.var -> type -> dim;
+//     if(num_indexes != dim)
+//     {
+//             oberon_error(ctx, "dimesions not matched");
+//     }
+
+       oberon_type_t * base = desig -> item.var -> type -> base;
+
+       oberon_expr_t * selector;
+       selector = oberon_new_item(MODE_INDEX, base);
+       selector -> item.parent = (oberon_item_t *) desig;
+       selector -> item.num_args = num_indexes;
+       selector -> item.args = indexes;
+
+       return selector;
+}
+
+static oberon_expr_t *
+oberon_make_record_selector(oberon_context_t * ctx, oberon_expr_t * expr, char * name)
+{
+       assert(expr -> is_item == 1);
+
+       int class = expr -> result -> class;
+       if(class != OBERON_TYPE_RECORD)
+       {
+               oberon_error(ctx, "not record");
+       }
+
+       oberon_type_t * rec = expr -> result;
+
+       oberon_object_t * field;
+       field = oberon_find_field(ctx, rec, name);
+
+       oberon_expr_t * selector;
+       selector = oberon_new_item(MODE_FIELD, field -> type);
+       selector -> item.var = field;
+       selector -> item.parent = (oberon_item_t *) expr;
+
+       return selector;
+}
+
+static oberon_expr_t *
+oberon_designator(oberon_context_t * ctx)
 {
        char * name;
        oberon_object_t * var;
        oberon_expr_t * expr;
 
+       name = oberon_assert_ident(ctx);
+       var = oberon_find_object(ctx -> decl, name, 1);
+
+       switch(var -> class)
+       {
+               case OBERON_CLASS_CONST:
+                       // TODO copy value
+                       expr = (oberon_expr_t *) var -> value;
+                       break;
+               case OBERON_CLASS_VAR:
+               case OBERON_CLASS_VAR_PARAM:
+               case OBERON_CLASS_PARAM:
+                       expr = oberon_new_item(MODE_VAR, var -> type);
+                       break;
+               case OBERON_CLASS_PROC:
+                       expr = oberon_new_item(MODE_CALL, var -> type);
+                       break;
+               default:
+                       oberon_error(ctx, "invalid designator");
+                       break;
+       }
+       expr -> item.var = var;
+
+       while(ISSELECTOR(ctx -> token))
+       {
+               switch(ctx -> token)
+               {
+                       case DOT:
+                               oberon_assert_token(ctx, DOT);
+                               name = oberon_assert_ident(ctx);
+                               expr = oberon_make_record_selector(ctx, expr, name);
+                               break;
+                       case LBRACE:
+                               oberon_assert_token(ctx, LBRACE);
+                               int num_indexes = 0;
+                               oberon_expr_t * indexes = NULL;
+                               oberon_expr_list(ctx, &num_indexes, &indexes);
+                               oberon_assert_token(ctx, RBRACE);
+                               expr = oberon_make_array_selector(ctx, expr, num_indexes, indexes);
+                               break;
+                       default:
+                               oberon_error(ctx, "oberon_designator: wat");
+                               break;
+               }
+       }
+       return expr;
+}
+
+static oberon_expr_t *
+oberon_opt_proc_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;
+               oberon_expr_t * arguments = NULL;
+
+               if(ISEXPR(ctx -> token))
+               {
+                       oberon_expr_list(ctx, &num_args, &arguments);
+               }
+
+               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);
+
+               oberon_autocast_call(ctx, expr);
+       }
+
+       return expr;
+}
+
+static oberon_expr_t *
+oberon_factor(oberon_context_t * ctx)
+{
+       oberon_expr_t * expr;
+
        switch(ctx -> token)
        {
                case IDENT:
-                       name = oberon_assert_ident(ctx);
-                       var = oberon_find_object(ctx -> decl, name);
-                       if(var -> class == OBERON_CLASS_VAR)
-                       {
-                               expr = oberon_new_item(MODE_VAR, var -> type);
-                       }
-                       else if(var -> class == OBERON_CLASS_PROC)
-                       {
-                               expr = oberon_new_item(MODE_CALL, var -> type);
-                       }
-                       else
-                       {
-                               oberon_error(ctx, "invalid desinator");
-                       }
-                       expr -> item.var = var;
+                       expr = oberon_designator(ctx);
+                       expr = oberon_opt_proc_parens(ctx, expr);
                        break;
                case INTEGER:
                        expr = oberon_new_item(MODE_INTEGER, ctx -> int_type);
@@ -606,14 +981,12 @@ oberon_make_bin_op(oberon_context_t * ctx, int token, oberon_expr_t * a, oberon_
        oberon_expr_t * expr;
        oberon_type_t * result;
 
-       oberon_autocast_binary_op(ctx, a -> result, b -> result, &result);
-
        if(ITMAKESBOOLEAN(token))
        {
                if(ITUSEONLYINTEGER(token))
                {
                        if(a -> result -> class != OBERON_TYPE_INTEGER
-                               && b -> result -> class != OBERON_TYPE_INTEGER)
+                               || b -> result -> class != OBERON_TYPE_INTEGER)
                        {
                                oberon_error(ctx, "used only with integer types");
                        }
@@ -621,12 +994,14 @@ oberon_make_bin_op(oberon_context_t * ctx, int token, oberon_expr_t * a, oberon_
                else if(ITUSEONLYBOOLEAN(token))
                {
                        if(a -> result -> class != OBERON_TYPE_BOOLEAN
-                               && b -> result -> class != OBERON_TYPE_BOOLEAN)
+                               || b -> result -> class != OBERON_TYPE_BOOLEAN)
                        {
                                oberon_error(ctx, "used only with boolean type");
                        }
                }
 
+               result = ctx -> bool_type;
+
                if(token == EQUAL)
                {
                        expr = oberon_new_operator(OP_EQ, result, a, b);
@@ -666,6 +1041,8 @@ oberon_make_bin_op(oberon_context_t * ctx, int token, oberon_expr_t * a, oberon_
        }
        else
        {
+               oberon_autocast_binary_op(ctx, a -> result, b -> result, &result);
+
                if(token == PLUS)
                {
                        expr = oberon_new_operator(OP_ADD, result, a, b);
@@ -779,6 +1156,20 @@ oberon_expr(oberon_context_t * ctx)
        return expr;
 }
 
+static oberon_item_t *
+oberon_const_expr(oberon_context_t * ctx)
+{
+       oberon_expr_t * expr;
+       expr = oberon_expr(ctx);
+
+       if(expr -> is_item == 0)
+       {
+               oberon_error(ctx, "const expression are required");
+       }
+
+       return (oberon_item_t *) expr;
+}
+
 // =======================================================================
 //   PARSER
 // =======================================================================
@@ -810,43 +1201,134 @@ oberon_assert_ident(oberon_context_t * 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 -> decl, name);
-       return type;
-}
-
 static void
 oberon_var_decl(oberon_context_t * ctx)
 {
-       char * name = oberon_assert_ident(ctx);
+       char * name;
+       oberon_type_t * type;
+       type = oberon_new_type_ptr(OBERON_TYPE_VOID);
+
+       name = oberon_assert_ident(ctx);
        oberon_assert_token(ctx, COLON);
-       oberon_type_t * type = oberon_type(ctx);
-       oberon_define_var(ctx -> decl, name, type);
+       oberon_type(ctx, &type);
+       oberon_define_var(ctx -> decl, OBERON_CLASS_VAR, name, type);
 }
 
-static void
-oberon_make_procedure_begin(oberon_context_t * ctx, char * name)
+static oberon_object_t *
+oberon_make_param(oberon_context_t * ctx, int token, char * name, oberon_type_t * type)
 {
-       oberon_object_t * proc;
-       proc = oberon_define_proc(ctx -> decl, name);
+       oberon_object_t * param;
 
-       oberon_open_scope(ctx);
+       if(token == VAR)
+       {
+               param = oberon_define_var(ctx -> decl, OBERON_CLASS_VAR_PARAM, name, type);
+       }
+       else if(token == IDENT)
+       {
+               param = oberon_define_var(ctx -> decl, OBERON_CLASS_PARAM, name, type);
+       }
+       else
+       {
+               oberon_error(ctx, "oberon_make_param: wat");
+       }
 
-       oberon_generate_begin_proc(ctx, proc);
+       return param;
 }
 
-static void
-oberon_make_procedure_end(oberon_context_t * ctx)
+static oberon_object_t *
+oberon_fp_section(oberon_context_t * ctx, int * num_decl)
 {
-       oberon_generate_end_proc(ctx);
+       int modifer_token = ctx -> token;
+       if(ctx -> token == VAR)
+       {
+               oberon_read_token(ctx);
+       }
 
-       oberon_close_scope(ctx -> decl);
-}
+       char * name;
+       name = oberon_assert_ident(ctx);
 
-static void
+       oberon_assert_token(ctx, COLON);
+
+       oberon_type_t * type;
+       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);
+
+       *num_decl += 1;
+       return first;
+}
+
+#define ISFPSECTION \
+       ((ctx -> token == VAR) || (ctx -> token == IDENT))
+
+static void
+oberon_formal_pars(oberon_context_t * ctx, oberon_type_t * signature)
+{
+       oberon_assert_token(ctx, LPAREN);
+
+       if(ISFPSECTION)
+       {
+               signature -> decl = oberon_fp_section(ctx, &signature -> num_decl);
+               while(ctx -> token == SEMICOLON)
+               {
+                       oberon_assert_token(ctx, SEMICOLON);
+                       oberon_fp_section(ctx, &signature -> num_decl);
+               }
+       }
+
+       oberon_assert_token(ctx, RPAREN);
+
+       if(ctx -> token == COLON)
+       {
+               oberon_assert_token(ctx, COLON);
+               oberon_type(ctx, &signature -> base);
+       }
+}
+
+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)
+       {
+               oberon_formal_pars(ctx, signature);
+       }
+}
+
+static void
+oberon_make_return(oberon_context_t * ctx, oberon_expr_t * expr)
+{
+       if(ctx -> result_type -> class == OBERON_TYPE_VOID)
+       {
+               if(expr != NULL)
+               {
+                       oberon_error(ctx, "procedure has no result type");
+               }
+       }
+       else
+       {
+               if(expr == NULL)
+               {
+                       oberon_error(ctx, "procedure requires expression on result");
+               }
+
+               oberon_autocast_to(ctx, expr, ctx -> result_type);
+       }
+
+       ctx -> has_return = 1;
+
+       oberon_generate_return(ctx, expr);
+}
+
+static void
 oberon_proc_decl(oberon_context_t * ctx)
 {
        oberon_assert_token(ctx, PROCEDURE);
@@ -854,84 +1336,573 @@ oberon_proc_decl(oberon_context_t * ctx)
        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_new_type_ptr(OBERON_TYPE_VOID);
+       oberon_opt_formal_pars(ctx, &signature);
+
+       oberon_object_t * proc;
+       proc = oberon_define_proc(this_proc_def_scope, name, signature);
+
+       ctx -> result_type = signature -> base;
+       ctx -> has_return = 0;
+
        oberon_assert_token(ctx, SEMICOLON);
 
-       oberon_make_procedure_begin(ctx, name);
+       oberon_generate_begin_proc(ctx, proc);
+
+       // TODO declarations
+
        if(ctx -> token == BEGIN)
        {
                oberon_assert_token(ctx, BEGIN);
                oberon_statement_seq(ctx);
        }
-       oberon_make_procedure_end(ctx);
 
        oberon_assert_token(ctx, END);
        char * name2 = oberon_assert_ident(ctx);
-
        if(strcmp(name2, name) != 0)
        {
                oberon_error(ctx, "procedure name not matched");
        }
+
+       if(signature -> base -> class == OBERON_TYPE_VOID)
+       {
+               oberon_make_return(ctx, NULL);
+       }
+
+       if(ctx -> 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_decl_seq(oberon_context_t * ctx)
+oberon_const_decl(oberon_context_t * ctx)
 {
-       if(ctx -> token == VAR)
+       char * name;
+       oberon_item_t * value;
+       oberon_object_t * constant;
+
+       name = oberon_assert_ident(ctx);
+       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_item_t * size, oberon_type_t * base, oberon_type_t ** type)
+{
+       if(size -> mode != MODE_INTEGER)
        {
-               oberon_assert_token(ctx, VAR);
-               while(ctx -> token == IDENT)
+               oberon_error(ctx, "requires integer constant");
+       }
+
+       oberon_type_t * arr;
+       arr = *type;
+       arr -> class = OBERON_TYPE_ARRAY;
+       arr -> size = size -> integer;
+       arr -> base = base;
+}
+
+static void
+oberon_field_list(oberon_context_t * ctx, oberon_type_t * rec)
+{
+       if(ctx -> token == IDENT)
+       {
+               char * name;
+               oberon_type_t * type;
+               type = oberon_new_type_ptr(OBERON_TYPE_VOID);
+
+               name = oberon_assert_ident(ctx);
+               oberon_assert_token(ctx, COLON);
+               oberon_type(ctx, &type);
+               oberon_define_field(ctx, rec, name, type);
+       }
+}
+
+static void
+oberon_qualident_type(oberon_context_t * ctx, oberon_type_t ** type)
+{
+       char * name;
+       oberon_object_t * to;
+
+       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);
+               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_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);
+
+               oberon_item_t * size;
+               size = oberon_const_expr(ctx);
+
+               oberon_assert_token(ctx, OF);
+
+               oberon_type_t * base;
+               base = oberon_new_type_ptr(OBERON_TYPE_VOID);
+               oberon_type(ctx, &base);
+
+               oberon_make_array_type(ctx, size, base, type);
+       }
+       else if(ctx -> token == RECORD)
+       {
+               oberon_type_t * rec;
+               rec = *type;
+               rec -> class = OBERON_TYPE_RECORD;
+               oberon_object_t * list = malloc(sizeof *list);
+               memset(list, 0, sizeof *list);
+               rec -> num_decl = 0;
+               rec -> base = NULL;
+               rec -> decl = list;
+
+               oberon_assert_token(ctx, RECORD);
+               oberon_field_list(ctx, rec);
+               while(ctx -> token == SEMICOLON)
                {
-                       oberon_var_decl(ctx);
                        oberon_assert_token(ctx, SEMICOLON);
+                       oberon_field_list(ctx, rec);
                }
+               oberon_assert_token(ctx, END);
+
+               rec -> decl = rec -> decl -> next;
+               *type = rec;
        }
+       else if(ctx -> token == POINTER)
+       {
+               oberon_assert_token(ctx, POINTER);
+               oberon_assert_token(ctx, TO);
 
-       if(ctx -> token == PROCEDURE)
+               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_proc_decl(ctx);
-               oberon_assert_token(ctx, SEMICOLON);
+               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_assign(oberon_context_t * ctx, oberon_expr_t * src, oberon_expr_t * dst)
+oberon_type_decl(oberon_context_t * ctx)
+{
+       char * name;
+       oberon_object_t * newtype;
+       oberon_type_t * type;
+
+       name = oberon_assert_ident(ctx);
+
+       newtype = oberon_find_object(ctx -> decl, name, 0);
+       if(newtype == NULL)
+       {
+               newtype = oberon_define_object(ctx -> decl, name, OBERON_CLASS_TYPE);
+               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");
+               }
+       }
+
+       oberon_assert_token(ctx, EQUAL);
+
+       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:
+                       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(src -> result -> class != dst -> result -> class)
+       if(type -> class != OBERON_TYPE_RECORD)
        {
-               oberon_error(ctx, "incompatible assignment types");
+               return;
        }
 
-       if(dst -> result -> class == OBERON_TYPE_INTEGER)
+       int num_fields = type -> num_decl;
+       oberon_object_t * field = type -> decl;
+       for(int i = 0; i < num_fields; i++)
        {
-               if((dst -> result -> size) < (src -> result -> size))
+               if(field -> type -> class == OBERON_TYPE_POINTER)
                {
-                       oberon_error(ctx, "incompatible assignment type size");
+                       oberon_initialize_type(ctx, field -> type);
                }
+
+               oberon_initialize_object(ctx, field);
+               field = field -> next;
        }
 
-       oberon_generate_assign(ctx, src, dst);
+       oberon_generator_init_record(ctx, type);
 }
 
 static void
-oberon_make_call(oberon_context_t * ctx, oberon_expr_t * desig)
+oberon_initialize_type(oberon_context_t * ctx, oberon_type_t * type)
 {
-       if(desig -> is_item == 0)
+       if(type -> class == OBERON_TYPE_VOID)
        {
-               oberon_error(ctx, "expected item");
+               oberon_error(ctx, "undeclarated type");
        }
 
-       if(desig -> item.mode != MODE_CALL)
+       if(type -> initialized)
        {
-               oberon_error(ctx, "expected mode CALL");
+               return;
        }
 
-       if(desig -> item.var -> class != OBERON_CLASS_PROC)
+       type -> initialized = 1;
+
+       if(type -> class == OBERON_TYPE_POINTER)
        {
-               oberon_error(ctx, "only procedures can be called");
+               if(type -> base -> class == OBERON_TYPE_RECORD)
+               {
+                       oberon_generator_init_type(ctx, type -> base);
+                       oberon_generator_init_type(ctx, type);
+               }
+               else
+               {
+                       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;
+               }               
 
-       // TODO check arguments
+               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)
+{
+       printf("oberon_initialize_object: name %s class %i\n", x -> name, x -> class);
+       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:
+                       break;
+               default:
+                       oberon_error(ctx, "oberon_prevent_recursive_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_decl_seq(oberon_context_t * ctx)
+{
+       if(ctx -> token == CONST)
+       {
+               oberon_assert_token(ctx, CONST);
+               while(ctx -> token == IDENT)
+               {
+                       oberon_const_decl(ctx);
+                       oberon_assert_token(ctx, SEMICOLON);
+               }
+       }
+
+       if(ctx -> token == TYPE)
+       {
+               oberon_assert_token(ctx, TYPE);
+               while(ctx -> token == IDENT)
+               {
+                       oberon_type_decl(ctx);
+                       oberon_assert_token(ctx, SEMICOLON);
+               }
+       }
+
+       if(ctx -> token == VAR)
+       {
+               oberon_assert_token(ctx, VAR);
+               while(ctx -> token == IDENT)
+               {
+                       oberon_var_decl(ctx);
+                       oberon_assert_token(ctx, SEMICOLON);
+               }
+       }
+
+       oberon_prevent_recursive_decl(ctx);
+       oberon_initialize_decl(ctx);
+
+       while(ctx -> token == PROCEDURE)
+       {
+               oberon_proc_decl(ctx);
+               oberon_assert_token(ctx, SEMICOLON);
+       }
+}
+
+static void
+oberon_assign(oberon_context_t * ctx, oberon_expr_t * src, oberon_expr_t * dst)
+{
+       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);
 }
 
@@ -943,7 +1914,7 @@ oberon_statement(oberon_context_t * ctx)
 
        if(ctx -> token == IDENT)
        {
-               item1 = oberon_expr(ctx);
+               item1 = oberon_designator(ctx);
                if(ctx -> token == ASSIGN)
                {
                        oberon_assert_token(ctx, ASSIGN);
@@ -952,9 +1923,24 @@ oberon_statement(oberon_context_t * ctx)
                }
                else
                {
+                       item1 = oberon_opt_proc_parens(ctx, item1);
                        oberon_make_call(ctx, item1);
                }
        }
+       else if(ctx -> token == RETURN)
+       {
+               oberon_assert_token(ctx, RETURN);
+               if(ISEXPR(ctx -> token))
+               {
+                       oberon_expr_t * expr;
+                       expr = oberon_expr(ctx);
+                       oberon_make_return(ctx, expr);
+               }
+               else
+               {
+                       oberon_make_return(ctx, NULL);
+               }
+       }
 }
 
 static void
@@ -1003,40 +1989,16 @@ oberon_parse_module(oberon_context_t * ctx)
 //   LIBRARY
 // =======================================================================
 
-static oberon_type_t *
-oberon_new_type_ptr(int class)
-{
-       oberon_type_t * x = malloc(sizeof *x);
-       memset(x, 0, sizeof *x);
-       x -> class = class;
-       return x;
-}
-
-static oberon_type_t *
-oberon_new_type_integer(int size)
-{
-       oberon_type_t * x;
-       x = oberon_new_type_ptr(OBERON_TYPE_INTEGER);
-       x -> size = size;
-       return x;
-}
-
-static oberon_type_t *
-oberon_new_type_boolean(int size)
-{
-       oberon_type_t * x;
-       x = oberon_new_type_ptr(OBERON_TYPE_BOOLEAN);
-       x -> size = size;
-       return x;
-}
-
 static void
 register_default_types(oberon_context_t * ctx)
 {
-       ctx -> int_type = oberon_new_type_integer(sizeof(int));
-       ctx -> bool_type = oberon_new_type_boolean(sizeof(int));
+       ctx -> void_type = oberon_new_type_ptr(OBERON_TYPE_VOID);
+       oberon_generator_init_type(ctx, ctx -> void_type);
 
+       ctx -> int_type = oberon_new_type_integer(sizeof(int));
        oberon_define_type(ctx -> world_scope, "INTEGER", ctx -> int_type);
+
+       ctx -> bool_type = oberon_new_type_boolean(sizeof(int));
        oberon_define_type(ctx -> world_scope, "BOOLEAN", ctx -> bool_type);
 }