DEADSOFTWARE

Запрещён возврат массива или записи функцией
[dsw-obn.git] / src / oberon.c
index 5a93489b35ed197ee2ac10b262a95c042583d146..59a5c3d5fa7f6f11dc9e65e6b1974f59fca12770 100644 (file)
@@ -85,6 +85,9 @@ enum {
 //   UTILS
 // ======================================================================= 
 
+static void
+oberon_make_copy_call(oberon_context_t * ctx, int num_args, oberon_expr_t * list_args);
+
 static void
 oberon_error(oberon_context_t * ctx, const char * fmt, ...)
 {
@@ -1055,6 +1058,11 @@ oberon_check_record_compatibility(oberon_context_t * ctx, oberon_type_t * from,
 static void
 oberon_check_dst(oberon_context_t * ctx, oberon_expr_t * dst)
 {
+       if(dst -> read_only)
+       {
+               oberon_error(ctx, "read-only destination");
+       }
+
        if(dst -> is_item == false)
        {
                oberon_error(ctx, "not variable");
@@ -1453,7 +1461,7 @@ oberon_make_record_selector(oberon_context_t * ctx, oberon_expr_t * expr, char *
                }
        }
 
-       int read_only = 0;
+       int read_only = expr -> read_only;
        if(field -> read_only)
        {
                if(field -> module != ctx -> mod)
@@ -1573,11 +1581,53 @@ oberon_qualident_expr(oberon_context_t * ctx)
        return expr;
 }
 
+static void
+oberon_check_type_guard(oberon_context_t * ctx, oberon_expr_t * expr, oberon_type_t * type)
+{
+       /* Охрана типа применима, если */
+       /*   1. v - параметр-переменная типа запись, или v - указатель, и если */
+       /*   2. T - расширение статического типа v */
+
+       if(expr -> is_item
+               && expr -> item.mode == MODE_VAR
+               && expr -> item.var -> class == OBERON_CLASS_VAR_PARAM)
+       {
+               // accept
+       }
+       else if(expr -> result -> class == OBERON_TYPE_POINTER
+               || expr -> result -> class == OBERON_TYPE_RECORD)
+       {
+               // accept
+       }
+       else
+       {
+               oberon_error(ctx, "guard type used only with var-param or pointers");
+       }
+
+       oberon_check_record_compatibility(ctx, type, expr -> result);
+}
+
+static oberon_expr_t *
+oberon_make_type_guard(oberon_context_t * ctx, oberon_expr_t * expr, oberon_object_t * objtype)
+{
+       oberon_type_t * type;
+
+       if(objtype -> class != OBERON_CLASS_TYPE)
+       {
+               oberon_error(ctx, "must be type");
+       }
+       type = objtype -> type;
+
+       oberon_check_type_guard(ctx, expr, type);
+       return oberno_make_record_cast(ctx, expr, objtype -> type);
+}
+
 static oberon_expr_t *
 oberon_designator(oberon_context_t * ctx)
 {
        char * name;
        oberon_expr_t * expr;
+       oberon_object_t * objtype;
 
        expr = oberon_qualident_expr(ctx);
 
@@ -1609,13 +1659,9 @@ oberon_designator(oberon_context_t * ctx)
                                break;
                        case LPAREN:
                                oberon_assert_token(ctx, LPAREN);
-                               oberon_object_t * objtype = oberon_qualident(ctx, NULL, 1);
-                               if(objtype -> class != OBERON_CLASS_TYPE)
-                               {
-                                       oberon_error(ctx, "must be type");
-                               }
+                               objtype = oberon_qualident(ctx, NULL, true);
                                oberon_assert_token(ctx, RPAREN);
-                               expr = oberno_make_record_cast(ctx, expr, objtype -> type);
+                               expr = oberon_make_type_guard(ctx, expr, objtype);
                                break;
                        default:
                                oberon_error(ctx, "oberon_designator: wat");
@@ -1827,15 +1873,6 @@ oberon_factor(oberon_context_t * ctx)
        return expr;
 }
 
-#define ITMAKESBOOLEAN(x) \
-       (((x) >= EQUAL && (x) <= GEQ) || ((x) == OR) || ((x) == AND))
-
-#define ITUSEONLYINTEGER(x) \
-       ((x) >= LESS && (x) <= GEQ)
-
-#define ITUSEONLYBOOLEAN(x) \
-       (((x) == OR) || ((x) == AND))
-
 static void
 oberon_autocast_to_real(oberon_context_t * ctx, oberon_expr_t ** e)
 {
@@ -1857,6 +1894,49 @@ oberon_autocast_to_real(oberon_context_t * ctx, oberon_expr_t ** e)
        }
 }
 
+static bool
+oberon_is_numeric_type(oberon_type_t * t)
+{
+       return (t -> class == OBERON_TYPE_INTEGER) || (t -> class == OBERON_TYPE_REAL);
+}
+
+static bool
+oberon_is_char_type(oberon_type_t * t)
+{
+       return (t -> class == OBERON_TYPE_CHAR);
+}
+
+static bool
+oberon_is_string_type(oberon_type_t * t)
+{
+       return (t -> class == OBERON_TYPE_STRING)
+               || (t -> class == OBERON_TYPE_ARRAY && t -> base -> class == OBERON_TYPE_CHAR);
+}
+
+static bool
+oberon_is_boolean_type(oberon_type_t * t)
+{
+       return (t -> class == OBERON_TYPE_BOOLEAN);
+}
+
+static bool
+oberon_is_set_type(oberon_type_t * t)
+{
+       return (t -> class == OBERON_TYPE_SET);
+}
+
+static bool
+oberon_is_pointer_type(oberon_type_t * t)
+{
+       return (t -> class == OBERON_TYPE_POINTER) || (t -> class == OBERON_TYPE_NIL);
+}
+
+static bool
+oberon_is_procedure_type(oberon_type_t * t)
+{
+       return (t -> class == OBERON_TYPE_POINTER) || (t -> class == OBERON_TYPE_NIL);
+}
+
 static oberon_expr_t *
 oberon_make_bin_op(oberon_context_t * ctx, int token, oberon_expr_t * a, oberon_expr_t * b)
 {
@@ -1881,65 +1961,81 @@ oberon_make_bin_op(oberon_context_t * ctx, int token, oberon_expr_t * a, oberon_
        }
        else if(token == IS)
        {
-               oberon_type_t * v = a -> result;
-               if(v -> class == OBERON_TYPE_POINTER)
-               {
-                       v = v -> base;
-                       if(v -> class != OBERON_TYPE_RECORD)
-                       {
-                               oberon_error(ctx, "must be record");
-                       }
-               }
-               else if(v -> class != OBERON_TYPE_RECORD)
-               {
-                       oberon_error(ctx, "must be record");
-               }               
-
                if(b -> is_item == false || b -> item.mode != MODE_TYPE)
                {
                        oberon_error(ctx, "requires type");
                }
 
-               oberon_type_t * t = b -> result;
-               if(t -> class == OBERON_TYPE_POINTER)
-               {
-                       t = t -> base;
-                       if(t -> class != OBERON_TYPE_RECORD)
-                       {
-                               oberon_error(ctx, "must be record");
-                       }
-               }
-               else if(t -> class != OBERON_TYPE_RECORD)
-               {
-                       oberon_error(ctx, "must be record");
-               }
-
                result = ctx -> bool_type;
+               oberon_check_type_guard(ctx, a, b -> result);
                expr = oberon_new_operator(OP_IS, result, a, b);
        }
-       else if(ITMAKESBOOLEAN(token))
+       else if((token >= EQUAL && token <= GEQ) || token == OR || token == AND)
        {
-               if(ITUSEONLYINTEGER(token))
+               if(token >= LESS && token <= GEQ)
                {
-                       if(a -> result -> class == OBERON_TYPE_INTEGER
-                               || b -> result -> class == OBERON_TYPE_INTEGER
-                               || a -> result -> class == OBERON_TYPE_REAL
-                               || b -> result -> class == OBERON_TYPE_REAL)
+                       if(oberon_is_numeric_type(a -> result) && oberon_is_numeric_type(b -> result))
+                       {
+                               // accept
+                       }
+                       else if(oberon_is_char_type(a -> result) && oberon_is_char_type(b -> result))
+                       {
+                               // accept
+                       }
+                       else if(oberon_is_string_type(a -> result) && oberon_is_string_type(b -> result))
                        {
                                // accept
                        }
                        else
                        {
-                               oberon_error(ctx, "used only with numeric types");
+                               oberon_error(ctx, "invalid comparation");
                        }
                }
-               else if(ITUSEONLYBOOLEAN(token))
+               else if(token == EQUAL || token == NEQ)
                {
-                       if(a -> result -> class != OBERON_TYPE_BOOLEAN
-                               || b -> result -> class != OBERON_TYPE_BOOLEAN)
+                       if(oberon_is_numeric_type(a -> result) && oberon_is_numeric_type(b -> result))
+                       {
+                               // accept
+                       }
+                       else if(oberon_is_char_type(a -> result) && oberon_is_char_type(b -> result))
+                       {
+                               // accept
+                       }
+                       else if(oberon_is_string_type(a -> result) && oberon_is_string_type(b -> result))
+                       {
+                               // accept
+                       }
+                       else if(oberon_is_boolean_type(a -> result) && oberon_is_boolean_type(b -> result))
                        {
-                               oberon_error(ctx, "used only with boolean type");
+                               // accept
+                       }
+                       else if(oberon_is_set_type(a -> result) && oberon_is_set_type(b -> result))
+                       {
+                               // accept
+                       }
+                       else if(oberon_is_pointer_type(a -> result) && oberon_is_pointer_type(b -> result))
+                       {
+                               // accept
+                       }
+                       else if(oberon_is_procedure_type(a -> result) && oberon_is_procedure_type(b -> result))
+                       {
+                               // accept
                        }
+                       else
+                       {
+                               oberon_error(ctx, "invalid comparation");
+                       }
+               }
+               else if(token == AND || token == OR)
+               {
+                       if(!oberon_is_boolean_type(a -> result) || !oberon_is_boolean_type(b -> result))
+                       {
+                               oberon_error(ctx, "invalid comparation");
+                       }
+               }
+               else
+               {
+                       oberon_error(ctx, "wat");
                }
 
                oberon_autocast_binary_op(ctx, &a, &b);
@@ -2348,6 +2444,11 @@ oberon_formal_pars(oberon_context_t * ctx, oberon_type_t * signature)
                {
                        oberon_error(ctx, "function result is not type");
                }
+               if(typeobj -> type -> class == OBERON_TYPE_RECORD
+                       || typeobj -> type -> class == OBERON_TYPE_ARRAY)
+               {
+                       oberon_error(ctx, "records or arrays could not be result of function");
+               }
                signature -> base = typeobj -> type;
        }
 }
@@ -3226,14 +3327,30 @@ oberon_statement_seq(oberon_context_t * ctx);
 static void
 oberon_assign(oberon_context_t * ctx, oberon_expr_t * src, oberon_expr_t * dst)
 {
-       if(dst -> read_only)
+       if(src -> is_item
+               && src -> item.mode == MODE_STRING
+               && src -> result -> class == OBERON_TYPE_STRING
+               && dst -> result -> class == OBERON_TYPE_ARRAY
+               && dst -> result -> base -> class == OBERON_TYPE_CHAR
+               && dst -> result -> size > 0)
        {
-               oberon_error(ctx, "read-only destination");
-       }
 
-       oberon_check_dst(ctx, dst);
-       src = oberon_autocast_to(ctx, src, dst -> result);
-       oberon_generate_assign(ctx, src, dst);
+               if(strlen(src -> item.string) < dst -> result -> size)
+               {
+                       src -> next = dst;
+                       oberon_make_copy_call(ctx, 2, src);
+               }
+               else
+               {
+                       oberon_error(ctx, "string too long for destination");
+               }
+       }
+       else
+       {
+               oberon_check_dst(ctx, dst);
+               src = oberon_autocast_to(ctx, src, dst -> result);
+               oberon_generate_assign(ctx, src, dst);
+       }
 }
 
 static oberon_expr_t *
@@ -3532,11 +3649,11 @@ oberon_statement(oberon_context_t * ctx)
                index = oberon_ident_item(ctx, iname);
                oberon_assert_token(ctx, ASSIGN);
                from = oberon_expr(ctx);
-               oberon_assign(ctx, from, index);
                oberon_assert_token(ctx, TO);
                bound = oberon_make_temp_var_item(ctx, index -> result);
                to = oberon_expr(ctx);
-               oberon_assign(ctx, to, bound);
+               oberon_assign(ctx, to, bound); // сначала temp
+               oberon_assign(ctx, from, index); // потом i
                if(ctx -> token == BY)
                {
                        oberon_assert_token(ctx, BY);
@@ -3955,7 +4072,6 @@ oberon_make_new_call(oberon_context_t * ctx, int num_args, oberon_expr_t * list_
                oberon_error(ctx, "too few arguments");
        }
 
-
        oberon_expr_t * dst;
        dst = list_args;
        oberon_check_dst(ctx, dst);
@@ -4031,6 +4147,110 @@ oberon_make_new_call(oberon_context_t * ctx, int num_args, oberon_expr_t * list_
        oberon_assign(ctx, src, dst);
 }
 
+static void
+oberon_make_copy_call(oberon_context_t * ctx, int num_args, oberon_expr_t * list_args)
+{
+       if(num_args < 2)
+       {
+               oberon_error(ctx, "too few arguments");
+       }
+
+       if(num_args > 2)
+       {
+               oberon_error(ctx, "too mach arguments");
+       }
+
+       oberon_expr_t * src;
+       src = list_args;
+       oberon_check_src(ctx, src);
+
+       oberon_expr_t * dst;
+       dst = list_args -> next;
+       oberon_check_dst(ctx, dst);
+
+       if(!oberon_is_string_type(src -> result))
+       {
+               oberon_error(ctx, "source must be string or array of char");
+       }
+
+       if(!oberon_is_string_type(dst -> result))
+       {
+               oberon_error(ctx, "dst must be array of char");
+       }
+
+       oberon_generate_copy(ctx, src, dst);
+}
+
+static void
+oberon_make_assert_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 > 2)
+       {
+               oberon_error(ctx, "too mach arguments");
+       }
+
+       oberon_expr_t * cond;
+       cond = list_args;
+       oberon_check_src(ctx, cond);
+
+       if(cond -> result -> class != OBERON_TYPE_BOOLEAN)
+       {
+               oberon_error(ctx, "expected boolean");
+       }
+
+       if(num_args == 1)
+       {
+               oberon_generate_assert(ctx, cond);
+       }
+       else
+       {
+               oberon_expr_t * num;
+               num = list_args -> next;
+               oberon_check_src(ctx, num);
+
+               if(num -> result -> class != OBERON_TYPE_INTEGER)
+               {
+                       oberon_error(ctx, "expected integer");
+               }
+
+               oberon_check_const(ctx, num);
+
+               oberon_generate_assert_n(ctx, cond, num -> item.integer);
+       }
+}
+
+static void
+oberon_make_halt_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 * num;
+       num = list_args;
+       oberon_check_src(ctx, num);
+
+       if(num -> result -> class != OBERON_TYPE_INTEGER)
+       {
+               oberon_error(ctx, "expected integer");
+       }
+
+       oberon_check_const(ctx, num);
+
+       oberon_generate_halt(ctx, num -> item.integer);
+}
+
 static void
 oberon_new_const(oberon_context_t * ctx, char * name, oberon_expr_t * expr)
 {
@@ -4067,6 +4287,9 @@ oberon_create_context(ModuleImportCallback import_module)
 
        /* Procedures */
        oberon_new_intrinsic(ctx, "NEW", NULL, oberon_make_new_call);
+       oberon_new_intrinsic(ctx, "COPY", NULL, oberon_make_copy_call);
+       oberon_new_intrinsic(ctx, "ASSERT", NULL, oberon_make_assert_call);
+       oberon_new_intrinsic(ctx, "HALT", NULL, oberon_make_halt_call);
 
        return ctx;
 }