]> ocean-lang.org Git - ocean/commitdiff
oceani - assorted cleanups.
authorNeilBrown <neil@brown.name>
Mon, 22 Apr 2019 12:01:06 +0000 (22:01 +1000)
committerNeilBrown <neil@brown.name>
Wed, 1 May 2019 08:07:28 +0000 (18:07 +1000)
Reading through the whole document found a few typos,
and few things in the wrong place, some areas where
a better explanation would help etc.

This patch combines several such improvements.

Signed-off-by: NeilBrown <neil@brown.name>
csrc/oceani.mdc

index c4f7e2998c04d7ee475b365b16c1e0d2bcf30793..68027950c81415db606264b3b7fe56ef6cb45aa0 100644 (file)
@@ -1,8 +1,8 @@
 # Ocean Interpreter - Stoney Creek version
 
-Ocean is intended to be an compiled language, so this interpreter is
+Ocean is intended to be a compiled language, so this interpreter is
 not targeted at being the final product.  It is, rather, an intermediate
-stage, and fills that role in two distinct ways.
+stage and fills that role in two distinct ways.
 
 Firstly, it exists as a platform to experiment with the early language
 design.  An interpreter is easy to write and easy to get working, so
@@ -73,10 +73,10 @@ out the program from the parsed internal structure.  This is useful
 for validating the parsing.
 So the main requirements of the interpreter are:
 
-- Parse the program, possibly with tracing
-- Analyse the parsed program to ensure consistency
-- print the program
-- execute the program
+- Parse the program, possibly with tracing,
+- Analyse the parsed program to ensure consistency,
+- Print the program,
+- Execute the program.
 
 This is all performed by a single C program extracted with
 `parsergen`.
@@ -88,6 +88,9 @@ alternate section can be requested so that a file (such as this one)
 can contain multiple programs This is effected with the `--section`
 option.
 
+This code must be compiled with `-fplan9-extensions` so that anonymous
+structures can be used.
+
 ###### File: oceani.mk
 
        myCFLAGS := -Wall -g -fplan9-extensions
@@ -208,6 +211,9 @@ option.
                                argv[optind]);
                        exit(1);
                }
+
+               ## context initialization
+
                if (section) {
                        struct section *ss;
                        for (ss = s; ss; ss = ss->next) {
@@ -257,19 +263,19 @@ option.
 
 ### Analysis
 
-These four requirements of parse, analyse, print, interpret apply to
+The four requirements of parse, analyse, print, interpret apply to
 each language element individually so that is how most of the code
 will be structured.
 
 Three of the four are fairly self explanatory.  The one that requires
 a little explanation is the analysis step.
 
-The current language design does not require (or even allow) the types
-of variables to be declared, but they must still have a single type.
-Different operations impose different requirements on the variables,
-for example addition requires both arguments to be numeric, and
-assignment requires the variable on the left to have the same type as
-the expression on the right.
+The current language design does not require the types of variables to
+be declared, but they must still have a single type.  Different
+operations impose different requirements on the variables, for example
+addition requires both arguments to be numeric, and assignment
+requires the variable on the left to have the same type as the
+expression on the right.
 
 Analysis involves propagating these type requirements around and
 consequently setting the type of each variable.  If any requirements
@@ -344,6 +350,12 @@ stored.  As will be explained later, there are sometimes extra rules for
 type matching and they might affect error messages, we need to pass those
 in too.
 
+As well as type errors, we sometimes need to report problems with
+tokens, which might be unexpected or might name a type that has not
+been defined.  For these we have `tok_err()` which reports an error
+with a given token.  Each of the error functions sets the flag in the
+context so indicate that parsing failed.
+
 ###### forward decls
 
        static void fput_loc(struct exec *loc, FILE *f);
@@ -387,7 +399,8 @@ in too.
 
        static void tok_err(struct parse_context *c, char *fmt, struct token *t)
        {
-               fprintf(stderr, "%s:%d:%d: %s\n", c->file_name, t->line, t->col, fmt);
+               fprintf(stderr, "%s:%d:%d: %s: %.*s\n", c->file_name, t->line, t->col, fmt,
+                       t->txt.len, t->txt.txt);
                c->parse_error = 1;
        }
 
@@ -418,10 +431,18 @@ as one of a few standard types: integer, float, and fraction.  The
 existance of these conversion functions enable types to determine if
 they are compatible with other types.
 
-Named type are stored in a simple linked list.
+Named type are stored in a simple linked list.  Objects of each type are "values"
+which are often passed around by value.
 
 ###### ast
 
+       struct value {
+               struct type *type;
+               union {
+                       ## value union fields
+               };
+       };
+
        struct type {
                struct text name;
                struct type *next;
@@ -441,10 +462,6 @@ Named type are stored in a simple linked list.
                };
        };
 
-       struct typep {
-               struct type *t;
-       };
-
 ###### parse context
 
        struct type *typelist;
@@ -481,9 +498,48 @@ Named type are stored in a simple linked list.
                 */
        }
 
-       static void context_init(struct parse_context *c)
+       static struct value val_init(struct type *type)
        {
-               ## context initialization
+               struct value rv;
+
+               if (type)
+                       return type->init(type);
+               rv.type = type;
+               return rv;
+       }
+
+       static struct value dup_value(struct value v)
+       {
+               if (v.type)
+                       return v.type->dup(v);
+               return v;
+       }
+
+       static int value_cmp(struct value left, struct value right)
+       {
+               if (left.type && left.type->cmp_order)
+                       return left.type->cmp_order(left, right);
+               if (left.type && left.type->cmp_eq)
+                       return left.type->cmp_eq(left, right);
+               return -1;
+       }
+
+       static void print_value(struct value v)
+       {
+               if (v.type && v.type->print)
+                       v.type->print(v);
+               else
+                       printf("*Unknown*");
+       }
+
+       static struct value parse_value(struct type *type, char *arg)
+       {
+               struct value rv;
+
+               if (type && type->parse)
+                       return type->parse(type, arg);
+               rv.type = NULL;
+               return rv;
        }
 
 ###### free context types
@@ -495,13 +551,13 @@ Named type are stored in a simple linked list.
                free(t);
        }
 
-### Values
+### Base Types
 
-Values can be numbers, which we represent as multi-precision
-fractions, strings, Booleans and labels.  When analysing the program
-we also need to allow for places where no value is meaningful (type
-`Tnone`) and where we don't know what type to expect yet (type is
-`NULL`).
+Values of the base types can be numbers, which we represent as
+multi-precision fractions, strings, Booleans and labels.  When
+analysing the program we also need to allow for places where no value
+is meaningful (type `Tnone`) and where we don't know what type to
+expect yet (type is `NULL`).
 
 Values are never shared, they are always copied when used, and freed
 when no longer needed.
@@ -529,24 +585,11 @@ to parse each type from a string.
 ###### type union fields
        enum vtype {Vnone, Vstr, Vnum, Vbool, Vlabel} vtype;
 
-###### ast
-       struct value {
-               struct type *type;
-               union {
-                       struct text str;
-                       mpq_t num;
-                       int bool;
-                       void *label;
-               };
-       };
-
-       enum val_rules {Rnolabel = 1<<0, Rboolok = 1<<1};
-
-###### format cases
-       case 'r':
-               if (rules & Rnolabel)
-                       fputs(" (labels not permitted)", stderr);
-               break;
+###### value union fields
+       struct text str;
+       mpq_t num;
+       int bool;
+       void *label;
 
 ###### ast functions
        static void _free_value(struct value v)
@@ -604,16 +647,6 @@ to parse each type from a string.
                return rv;
        }
 
-       static struct value val_init(struct type *type)
-       {
-               struct value rv;
-
-               if (type)
-                       return type->init(type);
-               rv.type = type;
-               return rv;
-       }
-
        static struct value _dup_value(struct value v)
        {
                struct value rv;
@@ -640,13 +673,6 @@ to parse each type from a string.
                return rv;
        }
 
-       static struct value dup_value(struct value v)
-       {
-               if (v.type)
-                       return v.type->dup(v);
-               return v;
-       }
-
        static int _value_cmp(struct value left, struct value right)
        {
                int cmp;
@@ -662,25 +688,6 @@ to parse each type from a string.
                return cmp;
        }
 
-       static int value_cmp(struct value left, struct value right)
-       {
-               if (left.type && left.type->cmp_order)
-                       return left.type->cmp_order(left, right);
-               if (left.type && left.type->cmp_eq)
-                       return left.type->cmp_eq(left, right);
-               return -1;
-       }
-
-       static struct text text_join(struct text a, struct text b)
-       {
-               struct text rv;
-               rv.len = a.len + b.len;
-               rv.txt = malloc(rv.len);
-               memcpy(rv.txt, a.txt, a.len);
-               memcpy(rv.txt+a.len, b.txt, b.len);
-               return rv;
-       }
-
        static void _print_value(struct value v)
        {
                switch (v.type->vtype) {
@@ -704,14 +711,6 @@ to parse each type from a string.
                }
        }
 
-       static void print_value(struct value v)
-       {
-               if (v.type && v.type->print)
-                       v.type->print(v);
-               else
-                       printf("*Unknown*");
-       }
-
        static struct value _parse_value(struct type *type, char *arg)
        {
                struct value val;
@@ -761,16 +760,6 @@ to parse each type from a string.
                return val;
        }
 
-       static struct value parse_value(struct type *type, char *arg)
-       {
-               struct value rv;
-
-               if (type && type->parse)
-                       return type->parse(type, arg);
-               rv.type = NULL;
-               return rv;
-       }
-
        static void _free_value(struct value v);
 
        static struct type base_prototype = {
@@ -796,16 +785,13 @@ to parse each type from a string.
                return t;
        }
 
-###### forward decls
-       static struct type *add_base_type(struct parse_context *c, char *n, enum vtype vt);
-
 ###### context initialization
 
-       Tbool  = add_base_type(c, "Boolean", Vbool);
-       Tstr   = add_base_type(c, "string", Vstr);
-       Tnum   = add_base_type(c, "number", Vnum);
-       Tnone  = add_base_type(c, "none", Vnone);
-       Tlabel = add_base_type(c, "label", Vlabel);
+       Tbool  = add_base_type(&context, "Boolean", Vbool);
+       Tstr   = add_base_type(&context, "string", Vstr);
+       Tnum   = add_base_type(&context, "number", Vnum);
+       Tnone  = add_base_type(&context, "none", Vnone);
+       Tlabel = add_base_type(&context, "label", Vlabel);
 
 ### Variables
 
@@ -877,7 +863,7 @@ it is constant
 
 Scopes in parallel branches can be partially merged.  More
 specifically, if a given name is declared in both branches of an
-if/else then it's scope is a candidate for merging.  Similarly if
+if/else then its scope is a candidate for merging.  Similarly if
 every branch of an exhaustive switch (e.g. has an "else" clause)
 declares a given name, then the scopes from the branches are
 candidates for merging.
@@ -1381,6 +1367,16 @@ by reference. It is set to `0` when an error is found, and `2` when
 any change is made.  If it remains unchanged at `1`, then no more
 propagation is needed.
 
+###### ast
+
+       enum val_rules {Rnolabel = 1<<0, Rboolok = 1<<1};
+
+###### format cases
+       case 'r':
+               if (rules & Rnolabel)
+                       fputs(" (labels not permitted)", stderr);
+               break;
+
 ###### core functions
 
        static struct type *propagate_types(struct exec *prog, struct parse_context *c, int *ok,
@@ -1484,7 +1480,7 @@ an executable.
                        if (number_parse($0->val.num, tail, $1.txt) == 0)
                                mpq_init($0->val.num);
                                if (tail[0])
-                                       tok_err(config2context(config), "error: unsupported number suffix.",
+                                       tok_err(config2context(config), "error: unsupported number suffix",
                                                &$1);
                        }
                        }$
@@ -1495,7 +1491,7 @@ an executable.
                        char tail[3];
                        string_parse(&$1, '\\', &$0->val.str, tail);
                        if (tail[0])
-                               tok_err(config2context(config), "error: unsupported string suffix.",
+                               tok_err(config2context(config), "error: unsupported string suffix",
                                        &$1);
                        }
                        }$
@@ -1506,7 +1502,7 @@ an executable.
                        char tail[3];
                        string_parse(&$1, '\\', &$0->val.str, tail);
                        if (tail[0])
-                               tok_err(config2context(config), "error: unsupported string suffix.",
+                               tok_err(config2context(config), "error: unsupported string suffix",
                                        &$1);
                        }
                        }$
@@ -1768,7 +1764,7 @@ link to find the primary instance.
 
 Our first user of the `binode` will be expressions, and particularly
 Boolean expressions.  As I haven't implemented precedence in the
-parser generator yet, we need different names from each precedence
+parser generator yet, we need different names for each precedence
 level used by expressions.  The outer most or lowest level precedence
 are Boolean `or` `and`, and `not` which form an `Expression` out of `BTerm`s
 and `BFact`s.
@@ -1931,7 +1927,7 @@ expression operator.
        case GtrEq:
        case Eql:
        case NEql:
-               /* Both must match but not labels, result is Tbool */
+               /* Both must match but not be labels, result is Tbool */
                t = propagate_types(b->left, c, ok, NULL, Rnolabel);
                if (t)
                        propagate_types(b->right, c, ok, t, 0);
@@ -1983,7 +1979,7 @@ are included.
 absolute value and negation).  These have different operator names.
 
 We also have a 'Bracket' operator which records where parentheses were
-found.  This make it easy to reproduce these when printing.  Once
+found.  This makes it easy to reproduce these when printing.  Once
 precedence is handled better I might be able to discard this.
 
 ###### Binode types
@@ -2144,6 +2140,20 @@ precedence is handled better I might be able to discard this.
                rv.str = text_join(left.str, right.str);
                break;
 
+
+###### value functions
+
+       static struct text text_join(struct text a, struct text b)
+       {
+               struct text rv;
+               rv.len = a.len + b.len;
+               rv.txt = malloc(rv.len);
+               memcpy(rv.txt, a.txt, a.len);
+               memcpy(rv.txt+a.len, b.txt, b.len);
+               return rv;
+       }
+
+
 ### Blocks, Statements, and Statement lists.
 
 Now that we have expressions out of the way we need to turn to
@@ -2194,7 +2204,8 @@ and a list.  So we need a function to re-order a list.
 
 The only stand-alone statement we introduce at this stage is `pass`
 which does nothing and is represented as a `NULL` pointer in a `Block`
-list.
+list.  Other stand-alone statements will follow once the infrastructure
+is in-place.
 
 ###### Binode types
        Block,
@@ -2286,7 +2297,7 @@ list.
 ###### propagate binode cases
        case Block:
        {
-               /* If any statement returns something other then Tnone
+               /* If any statement returns something other than Tnone
                 * or Tbool then all such must return same type.
                 * As each statement may be Tnone or something else,
                 * we must always pass NULL (unknown) down, otherwise an incorrect
@@ -2438,8 +2449,11 @@ it is declared, and error will be raised as the name is created as
                        $0->op = Assign;
                        $0->left = $<1;
                        $0->right = $<3;
-                       if (v->var && !v->var->constant) {
-                               /* FIXME error? */
+                       if (v->var && v->var->constant) {
+                               type_err(config2context(config), "Cannot assign to a constant: %v",
+                                        $0->left, NULL, 0, NULL);
+                               type_err(config2context(config), "name was defined as a constant here",
+                                        v->var->where_decl, NULL, 0, NULL);
                        }
                } }$
        | VariableDecl Expression ${
@@ -2930,12 +2944,12 @@ defined.
                // thenpart must return Tnone if there is a dopart,
                // otherwise it is like elsepart.
                // condpart must:
-               //    be bool if there is not casepart
+               //    be bool if there is no casepart
                //    match casepart->values if there is a switchpart
                //    either be bool or match casepart->value if there
                //             is a whilepart
-               // elsepart, casepart->action must match there return type
-               // expected of this statement.
+               // elsepart and casepart->action must match the return type
+               //   expected of this statement.
                struct cond_statement *cs = cast(cond_statement, prog);
                struct casepart *cp;
 
@@ -3051,7 +3065,7 @@ analysis is a bit more interesting at this level.
 ###### Parser: grammar
 
        $*binode
-       Program -> InitProgram OpenScope Varlist Block OptNL ${
+       Program -> program OpenScope Varlist Block OptNL ${
                $0 = new(binode);
                $0->op = Program;
                $0->left = reorder_bilist($<3);
@@ -3061,11 +3075,7 @@ analysis is a bit more interesting at this level.
                }$
                | ERROR ${
                        tok_err(config2context(config),
-                               "error: unhandled parse error.", &$1);
-               }$
-
-       InitProgram -> program ${
-               context_init(config2context(config));
+                               "error: unhandled parse error", &$1);
                }$
 
        Varlist -> Varlist ArgDecl ${