]> ocean-lang.org Git - ocean/blobdiff - csrc/oceani.mdc
oceani: add the option for "const" sections
[ocean] / csrc / oceani.mdc
index b47383ac97b2243e0f18fd078371832c2d1caaf7..58d5d32009f297102359c4feabeed21c7ea4f15f 100644 (file)
@@ -114,6 +114,7 @@ structures can be used.
                struct token_config config;
                char *file_name;
                int parse_error;
+               struct exec *prog;
                ## parse context
        };
 
@@ -179,7 +180,6 @@ structures can be used.
                        },
                };
                int doprint=0, dotrace=0, doexec=1, brackets=0;
-               struct exec **prog;
                int opt;
                while ((opt = getopt_long(argc, argv, options, long_options, NULL))
                       != -1) {
@@ -223,32 +223,31 @@ structures can be used.
                                        break;
                        }
                        if (ss)
-                               prog = parse_oceani(ss->code, &context.config,
-                                                   dotrace ? stderr : NULL);
+                               parse_oceani(ss->code, &context.config,
+                                            dotrace ? stderr : NULL);
                        else {
                                fprintf(stderr, "oceani: cannot find section %s\n",
                                        section);
                                exit(1);
                        }
                } else
-                       prog = parse_oceani(s->code, &context.config,
-                                   dotrace ? stderr : NULL);
-               if (!prog) {
-                       fprintf(stderr, "oceani: fatal parser error.\n");
+                       parse_oceani(s->code, &context.config,
+                                    dotrace ? stderr : NULL);
+               if (!context.prog) {
+                       fprintf(stderr, "oceani: no program found.\n");
                        context.parse_error = 1;
                }
-               if (prog && doprint)
-                       print_exec(*prog, 0, brackets);
-               if (prog && doexec && !context.parse_error) {
-                       if (!analyse_prog(*prog, &context)) {
+               if (context.prog && doprint)
+                       print_exec(context.prog, 0, brackets);
+               if (context.prog && doexec && !context.parse_error) {
+                       if (!analyse_prog(context.prog, &context)) {
                                fprintf(stderr, "oceani: type error in program - not running.\n");
                                exit(1);
                        }
-                       interp_prog(*prog, argv+optind+1);
+                       interp_prog(context.prog, argv+optind+1);
                }
-               if (prog) {
-                       free_exec(*prog);
-                       free(prog);
+               if (context.prog) {
+                       free_exec(context.prog);
                }
                while (s) {
                        struct section *t = s->next;
@@ -375,8 +374,8 @@ context so indicate that parsing failed.
                        }
                        fmt++;
                        switch (*fmt) {
-                       case '%': fputc(*fmt, stderr); break;
-                       default: fputc('?', stderr); break;
+                       case '%': fputc(*fmt, stderr); break;   // NOTEST
+                       default: fputc('?', stderr); break;     // NOTEST
                        case '1':
                                type_print(t1, stderr);
                                break;
@@ -523,7 +522,7 @@ which are often passed around by value.
                else if (type->print_type)
                        type->print_type(type, f);
                else
-                       fputs("*invalid*type*", f);
+                       fputs("*invalid*type*", f);     // NOTEST
        }
 
        static struct value val_prepare(struct type *type)
@@ -567,7 +566,7 @@ which are often passed around by value.
                if (v.type && v.type->print)
                        v.type->print(v);
                else
-                       printf("*Unknown*");
+                       printf("*Unknown*");            // NOTEST
        }
 
        static struct value parse_value(struct type *type, char *arg)
@@ -576,8 +575,8 @@ which are often passed around by value.
 
                if (type && type->parse)
                        return type->parse(type, arg);
-               rv.type = NULL;
-               return rv;
+               rv.type = NULL;                         // NOTEST
+               return rv;                              // NOTEST
        }
 
 ###### forward decls
@@ -685,8 +684,8 @@ to parse each type from a string.
 
                rv.type = type;
                switch(type->vtype) {
-               case Vnone:
-                       break;
+               case Vnone:             // NOTEST
+                       break;          // NOTEST
                case Vnum:
                        mpq_init(rv.num); break;
                case Vstr:
@@ -696,9 +695,9 @@ to parse each type from a string.
                case Vbool:
                        rv.bool = 0;
                        break;
-               case Vlabel:
-                       rv.label = NULL;
-                       break;
+               case Vlabel:                    // NOTEST
+                       rv.label = NULL;        // NOTEST
+                       break;                  // NOTEST
                }
                return rv;
        }
@@ -708,8 +707,8 @@ to parse each type from a string.
                struct value rv;
                rv.type = v.type;
                switch (rv.type->vtype) {
-               case Vnone:
-                       break;
+               case Vnone:             // NOTEST
+                       break;          // NOTEST
                case Vlabel:
                        rv.label = v.label;
                        break;
@@ -733,13 +732,13 @@ to parse each type from a string.
        {
                int cmp;
                if (left.type != right.type)
-                       return left.type - right.type;
+                       return left.type - right.type;  // NOTEST
                switch (left.type->vtype) {
                case Vlabel: cmp = left.label == right.label ? 0 : 1; break;
                case Vnum: cmp = mpq_cmp(left.num, right.num); break;
                case Vstr: cmp = text_cmp(left.str, right.str); break;
                case Vbool: cmp = left.bool - right.bool; break;
-               case Vnone: cmp = 0;
+               case Vnone: cmp = 0;                    // NOTEST
                }
                return cmp;
        }
@@ -747,10 +746,10 @@ to parse each type from a string.
        static void _print_value(struct value v)
        {
                switch (v.type->vtype) {
-               case Vnone:
-                       printf("*no-value*"); break;
-               case Vlabel:
-                       printf("*label-%p*", v.label); break;
+               case Vnone:                             // NOTEST
+                       printf("*no-value*"); break;    // NOTEST
+               case Vlabel:                            // NOTEST
+                       printf("*label-%p*", v.label); break; // NOTEST
                case Vstr:
                        printf("%.*s", v.str.len, v.str.txt); break;
                case Vbool:
@@ -776,10 +775,10 @@ to parse each type from a string.
 
                val.type = type;
                switch(type->vtype) {
-               case Vlabel:
-               case Vnone:
-                       val.type = NULL;
-                       break;
+               case Vlabel:                            // NOTEST
+               case Vnone:                             // NOTEST
+                       val.type = NULL;                // NOTEST
+                       break;                          // NOTEST
                case Vstr:
                        val.str.len = strlen(arg);
                        val.str.txt = malloc(val.str.len);
@@ -1082,6 +1081,8 @@ no longer be primary.
 
                        v = t->previous;
                        free_value(t->val);
+                       if (t->min_depth == 0)
+                               free_exec(t->where_decl);
                        free(t);
                }
        }
@@ -1317,7 +1318,7 @@ subclasses, and to access these we need to be able to `cast` the
        static int __fput_loc(struct exec *loc, FILE *f)
        {
                if (!loc)
-                       return 0;
+                       return 0;               // NOTEST
                if (loc->line >= 0) {
                        fprintf(f, "%d:%d: ", loc->line, loc->column);
                        return 1;
@@ -1330,7 +1331,7 @@ subclasses, and to access these we need to be able to `cast` the
        static void fput_loc(struct exec *loc, FILE *f)
        {
                if (!__fput_loc(loc, f))
-                       fprintf(f, "??:??: ");
+                       fprintf(f, "??:??: ");  // NOTEST
        }
 
 Each different type of `exec` node needs a number of functions
@@ -1401,7 +1402,7 @@ also want to know what sort of bracketing to use.
        static void print_exec(struct exec *e, int indent, int bracket)
        {
                if (!e)
-                       return;
+                       return;         // NOTEST
                switch (e->type) {
                case Xbinode:
                        print_binode(cast(binode, e), indent, bracket); break;
@@ -2024,9 +2025,9 @@ link to find the primary instance.
                                struct binding *b = v->var->name;
                                fprintf(stderr, "%.*s", b->name.len, b->name.txt);
                        } else
-                               fputs("???", stderr);
+                               fputs("???", stderr);   // NOTEST
                } else
-                       fputs("NOTVAR", stderr);
+                       fputs("NOTVAR", stderr);        // NOTEST
                break;
 
 ###### propagate exec cases
@@ -2036,9 +2037,9 @@ link to find the primary instance.
                struct var *var = cast(var, prog);
                struct variable *v = var->var;
                if (!v) {
-                       type_err(c, "%d:BUG: no variable!!", prog, Tnone, 0, Tnone);
-                       *ok = 0;
-                       return Tnone;
+                       type_err(c, "%d:BUG: no variable!!", prog, Tnone, 0, Tnone); // NOTEST
+                       *ok = 0;                                        // NOTEST
+                       return Tnone;                                   // NOTEST
                }
                if (v->merged)
                        v = v->merged;
@@ -2357,7 +2358,7 @@ expression operator.
                case GtrEq:  printf(" >= "); break;
                case Eql:    printf(" == "); break;
                case NEql:   printf(" != "); break;
-               default: abort();
+               default: abort();               // NOTEST
                }
                print_exec(b->right, -1, 0);
                break;
@@ -2405,7 +2406,7 @@ expression operator.
                case GtrEq:     rv.bool = cmp >= 0; break;
                case Eql:       rv.bool = cmp == 0; break;
                case NEql:      rv.bool = cmp != 0; break;
-               default: rv.bool = 0; break;
+               default: rv.bool = 0; break;    // NOTEST
                }
                break;
        }
@@ -2494,8 +2495,8 @@ precedence is handled better I might be able to discard this.
                case Divide: fputs(" / ", stdout); break;
                case Rem:    fputs(" % ", stdout); break;
                case Concat: fputs(" ++ ", stdout); break;
-               default: abort();
-               }
+               default: abort();       // NOTEST
+               }                       // NOTEST
                print_exec(b->right, indent, 0);
                break;
        case Absolute:
@@ -3009,7 +3010,7 @@ it is declared, and error will be raised as the name is created as
                        free_value(*lleft);
                        *lleft = right;
                } else
-                       free_value(right);
+                       free_value(right);      // NOTEST
                right.type = NULL;
                break;
 
@@ -3544,12 +3545,111 @@ defined.
                break;
        }
 
+### Top level structure
+
+All the language elements so far can be used in various places.  Now
+it is time to clarify what those places are.
+
+At the top level of a file there will be a number of declarations.
+Many of the things that can be declared haven't been described yet,
+such as functions, procedures, imports, named types, and probably
+more.
+For now there are two sorts of things that can appear at the top
+level.  They are predefined constants and the main program.  While the
+syntax will allow the main program to appear multiple times, that will
+trigger an error if it is actually attempted.
+
+The various declarations do not return anything.  They store the
+various declarations in the parse context.
+
+###### Parser: grammar
+
+       $void
+       Ocean -> DeclarationList
+
+       DeclarationList -> Declaration
+               | DeclarationList Declaration
+
+       Declaration -> DeclareConstant
+               | DeclareProgram
+               | NEWLINE
+
+       ## top level grammar
+
+### The `const` section
+
+As well as being defined in with the code that uses them, constants
+can be declared at the top level.  These have full-file scope, so they
+are always `InScope`.  The value of a top level constant can be given
+as an expression, and this is evaluated immediately rather than in the
+later interpretation stage.  Once we add functions to the language, we
+will need rules concern which, if any, can be used to define a top
+level constant.
+
+Constants are defined in a sectiont that starts with the reserved word
+`const` and then has a block with a list of assignment statements.
+For syntactic consistency, these must use the double-colon syntax to
+make it clear that they are constants.  Type can also be given: if
+not, the type will be determined during analysis, as with other
+constants.
+
+###### top level grammar
+
+       DeclareConstant -> const Open ConstList Close
+               | const Open Newlines ConstList Close
+               | const Open SimpleConstList }
+               | const Open Newlines SimpleConstList }
+               | const : ConstList
+               | const SimpleConstList
+
+       ConstList -> ComplexConsts
+       ComplexConsts -> ComplexConst ComplexConsts
+               | ComplexConst
+       ComplexConst -> SimpleConstList NEWLINE
+       SimpleConstList -> Const ; SimpleConstList
+               | Const
+               | Const ; SimpleConstList ;
+
+       $*type
+       CType -> Type   ${ $0 = $<1; }$
+               |       ${ $0 = NULL; }$
+       $void
+       Const -> IDENTIFIER :: CType = Expression ${ {
+               int ok;
+               struct variable *v;
+
+               v = var_decl(config2context(config), $1.txt);
+               if (v) {
+                       struct var *var = new_pos(var, $1);
+                       v->where_decl = var;
+                       v->where_set = var;
+                       var->var = v;
+                       v->constant = 1;
+               } else {
+                       v = var_ref(config2context(config), $1.txt);
+                       tok_err(config2context(config), "error: name already declared", &$1);
+                       type_err(config2context(config), "info: this is where '%v' was first declared",
+                                v->where_decl, NULL, 0, NULL);
+               }
+               do {
+                       ok = 1;
+                       propagate_types($5, config2context(config), &ok, $3, 0);
+               } while (ok == 2);
+               if (!ok)
+                       config2context(config)->parse_error = 1;
+               else if (v) {
+                       v->val = interp_exec($5);
+               }
+       } }$
+
+
 ### Finally the whole program.
 
 Somewhat reminiscent of Pascal a (current) Ocean program starts with
 the keyword "program" and a list of variable names which are assigned
 values from command line arguments.  Following this is a `block` which
-is the code to execute.
+is the code to execute.  Unlike Pascal, constants and other
+declarations come *before* the program.
 
 As this is the top level, several things are handled a bit
 differently.
@@ -3560,7 +3660,17 @@ analysis is a bit more interesting at this level.
 ###### Binode types
        Program,
 
-###### Parser: grammar
+###### top level grammar
+
+       DeclareProgram -> Program ${ {
+               struct parse_context *c = config2context(config);
+               if (c->prog)
+                       type_err(c, "Program defined a second time",
+                                $1, NULL, 0, NULL);
+               else
+                       c->prog = $<1;
+       } }$
+
 
        $*binode
        Program -> program OpenScope Varlist Block OptNL ${
@@ -3610,7 +3720,7 @@ analysis is a bit more interesting at this level.
                break;
 
 ###### propagate binode cases
-       case Program: abort();
+       case Program: abort();          // NOTEST
 
 ###### core functions
 
@@ -3620,7 +3730,7 @@ analysis is a bit more interesting at this level.
                int ok = 1;
 
                if (!b)
-                       return 0;
+                       return 0;       // NOTEST
                do {
                        ok = 1;
                        propagate_types(b->right, c, &ok, Tnone, 0);
@@ -3655,7 +3765,7 @@ analysis is a bit more interesting at this level.
                struct value v;
 
                if (!prog)
-                       return;
+                       return;         // NOTEST
                al = cast(binode, p->left);
                while (al) {
                        struct var *v = cast(var, al->left);
@@ -3677,7 +3787,7 @@ analysis is a bit more interesting at this level.
        }
 
 ###### interp binode cases
-       case Program: abort();
+       case Program: abort();  // NOTEST
 
 ## And now to test it out.
 
@@ -3694,8 +3804,18 @@ Fibonacci, and performs a binary search for a number.
 
 ###### test: hello
 
+       const:
+               pi ::= 3.1415926
+               four ::= 2 + 2 ; five ::= 10/2
+       const pie ::= "I like Pie";
+               cake ::= "The cake is"
+                 ++ " a lie"
+
        program A B:
                print "Hello World, what lovely oceans you have!"
+               print "are there", five, "?"
+               print pi, pie, "but", cake
+
                /* When a variable is defined in both branches of an 'if',
                 * and used afterwards, the variables are merged.
                 */