outside the if, the variables in the different branches are distinct
and can be of different types.
-Determining the types of all variables early is important for
-processing command line arguments. These can be assigned to any of
-several types of variable, but we must first know the correct type so
-any required conversion can happen. If a variable is associated with
-a command line argument but no type can be interpreted (e.g. the
-variable is only ever used in a `print` statement), then the type is
-set to 'string'.
-
Undeclared names may only appear in "use" statements and "case" expressions.
These names are given a type of "label" and a unique value.
This allows them to fill the role of a name in an enumerated type, which
### Types
Values come in a wide range of types, with more likely to be added.
-Each type needs to be able to parse and print its own values (for
-convenience at least) as well as to compare two values, at least for
-equality and possibly for order. For now, values might need to be
-duplicated and freed, though eventually such manipulations will be
-better integrated into the language.
+Each type needs to be able to print its own values (for convenience at
+least) as well as to compare two values, at least for equality and
+possibly for order. For now, values might need to be duplicated and
+freed, though eventually such manipulations will be better integrated
+into the language.
Rather than requiring every numeric type to support all numeric
operations (add, multiple, etc), we allow types to be able to present
struct type *next;
int size, align;
void (*init)(struct type *type, struct value *val);
- int (*parse)(struct type *type, char *str, struct value *val);
void (*print)(struct type *type, struct value *val);
void (*print_type)(struct type *type, FILE *f);
int (*cmp_order)(struct type *t1, struct type *t2,
printf("*Unknown*"); // NOTEST
}
- static int parse_value(struct type *type, char *arg,
- struct value *val)
- {
- if (type && type->parse)
- return type->parse(type, arg, val);
- return 0; // NOTEST
- }
-
static struct value *val_alloc(struct type *t, struct value *init)
{
struct value *ret;
static int value_cmp(struct type *tl, struct type *tr,
struct value *left, struct value *right);
static void print_value(struct type *type, struct value *v);
- static int parse_value(struct type *type, char *arg, struct value *val);
###### free context types
return require == have;
}
-When assigning command line arguments to variables, we need to be able
-to parse each type from a string.
-
###### includes
#include <gmp.h>
#include "parse_string.h"
}
}
- static int _parse_value(struct type *type, char *arg, struct value *val)
- {
- struct text tx;
- int neg = 0;
- char tail[3] = "";
-
- switch(type->vtype) {
- case Vlabel: // NOTEST
- case Vnone: // NOTEST
- return 0; // NOTEST
- case Vstr:
- val->str.len = strlen(arg);
- val->str.txt = malloc(val->str.len);
- memcpy(val->str.txt, arg, val->str.len);
- break;
- case Vnum:
- if (*arg == '-') {
- neg = 1;
- arg++;
- }
- tx.txt = arg; tx.len = strlen(tx.txt);
- if (number_parse(val->num, tail, tx) == 0)
- mpq_init(val->num);
- else if (neg)
- mpq_neg(val->num, val->num);
- if (tail[0]) {
- printf("Unsupported suffix: %s\n", arg);
- return 0;
- }
- break;
- case Vbool:
- if (strcasecmp(arg, "true") == 0 ||
- strcmp(arg, "1") == 0)
- val->bool = 1;
- else if (strcasecmp(arg, "false") == 0 ||
- strcmp(arg, "0") == 0)
- val->bool = 0;
- else {
- printf("Bad bool: %s\n", arg);
- return 0;
- }
- break;
- }
- return 1;
- }
-
static void _free_value(struct type *type, struct value *v);
static struct type base_prototype = {
.init = _val_init,
- .parse = _parse_value,
.print = _print_value,
.cmp_order = _value_cmp,
.cmp_eq = _value_cmp,
{
struct lrval ret = _interp_exec(e);
- if (typeret)
+ if (ret.lval)
*typeret = ret.type;
+ else
+ free_value(ret.type, &ret.rval);
return ret.lval;
}
Thus far we have arrays and structs.
-Some complex types need do not exist in a name table, so they are kept
-on a linked list in the context (`anon_typelist`). This allows them to
-be freed when parsing is complete.
-
#### Arrays
Arrays can be declared by giving a size and a type, as `[size]type' so
struct type *member;
} array;
-###### value union fields
- void *array;
-
###### value functions
static void array_init(struct type *type, struct value *val)
.free = array_free,
};
+###### declare terminals
+ $TERM [ ]
+
###### type grammar
- | [ NUMBER ] Type ${
- $0 = calloc(1, sizeof(struct type));
- *($0) = array_prototype;
- $0->array.member = $<4;
- $0->array.vsize = NULL;
- {
+ | [ NUMBER ] Type ${ {
char tail[3];
mpq_t num;
+ struct text noname = { "", 0 };
+
+ $0 = add_type(c, noname, &array_prototype);
+ $0->array.member = $<4;
+ $0->array.vsize = NULL;
if (number_parse(num, tail, $2.txt) == 0)
tok_err(c, "error: unrecognised number", &$2);
else if (tail[0])
&$2);
mpq_clear(num);
}
- $0->next = c->anon_typelist;
- c->anon_typelist = $0;
- }
- }$
+ } }$
| [ IDENTIFIER ] Type ${ {
struct variable *v = var_ref(c, $2.txt);
+ struct text noname = { "", 0 };
if (!v)
tok_err(c, "error: name undeclared", &$2);
else if (!v->constant)
tok_err(c, "error: array size must be a constant", &$2);
- $0 = calloc(1, sizeof(struct type));
- *($0) = array_prototype;
+ $0 = add_type(c, noname, &array_prototype);
$0->array.member = $<4;
$0->array.size = 0;
$0->array.vsize = v;
- $0->next = c->anon_typelist;
- c->anon_typelist = $0;
} }$
-###### parse context
-
- struct type *anon_typelist;
-
-###### free context types
-
- while (context.anon_typelist) {
- struct type *t = context.anon_typelist;
-
- context.anon_typelist = t->next;
- free(t);
- }
-
###### Binode types
Index,
free(e);
break;
+###### declare terminals
+ $TERM struct .
+
###### variable grammar
| Variable . IDENTIFIER ${ {
###### Grammar
+ $TERM True False
+
$*val
Value -> True ${
$0 = new_val(Tbool, $1);
###### Grammar
+ $TERM : ::
+
$*var
VariableDecl -> IDENTIFIER : ${ {
struct variable *v = var_decl(c, $1.txt);
### Expressions: The rest
-The remaining expressions with the highest precedence are arithmetic and
-string concatenation. String concatenation (`++`) has the same
-precedence as multiplication and division, but lower than the uniary.
+The remaining expressions with the highest precedence are arithmetic,
+string concatenation, and string conversion. String concatenation
+(`++`) has the same precedence as multiplication and division, but lower
+than the uniary.
+
+String conversion is a temporary feature until I get a better type
+system. `$` is a prefix operator which expects a string and returns
+a number.
`+` and `-` are both infix and prefix operations (where they are
absolute value and negation). These have different operator names.
Times, Divide, Rem,
Concat,
Absolute, Negate,
+ StringConv,
Bracket,
###### expr precedence
$LEFT + - Eop
$LEFT * / % ++ Top
- $LEFT Uop
+ $LEFT Uop $
$TERM ( )
###### expression grammar
Uop -> + ${ $0.op = Absolute; }$
| - ${ $0.op = Negate; }$
+ | $ ${ $0.op = StringConv; }$
Top -> * ${ $0.op = Times; }$
| / ${ $0.op = Divide; }$
if (bracket) printf(")");
break;
case Absolute:
- if (bracket) printf("(");
- printf("+");
- print_exec(b->right, indent, bracket);
- if (bracket) printf(")");
- break;
case Negate:
+ case StringConv:
if (bracket) printf("(");
- printf("-");
+ switch (b->op) {
+ case Absolute: fputs("+", stdout); break;
+ case Negate: fputs("-", stdout); break;
+ case StringConv: fputs("$", stdout); break;
+ default: abort(); // NOTEST
+ } // NOTEST
print_exec(b->right, indent, bracket);
if (bracket) printf(")");
break;
Tstr, rules, type);
return Tstr;
+ case StringConv:
+ /* op must be string, result is number */
+ propagate_types(b->left, c, ok, Tstr, 0);
+ if (!type_compat(type, Tnum, 0))
+ type_err(c,
+ "error: Can only convert string to number, not %1",
+ prog, type, 0, NULL);
+ return Tnum;
+
case Bracket:
return propagate_types(b->right, c, ok, type, 0);
rvtype = Tstr;
rv.str = text_join(left.str, right.str);
break;
+ case StringConv:
+ right = interp_exec(b->right, &rvtype);
+ rtype = Tstr;
+ rvtype = Tnum;
+
+ struct text tx = right.str;
+ char tail[3];
+ int neg = 0;
+ if (tx.txt[0] == '-') {
+ neg = 1;
+ tx.txt++;
+ tx.len--;
+ }
+ if (number_parse(rv.num, tail, tx) == 0)
+ mpq_init(rv.num);
+ else if (neg)
+ mpq_neg(rv.num, rv.num);
+ if (tail[0])
+ printf("Unsupported suffix: %.*s\n", tx.len, tx.txt);
+
+ break;
###### value functions
###### Binode types
Block,
-###### expr precedence
- $TERM pass
-
###### Grammar
+ $TERM { } ;
+
$*binode
Block -> { IN OptNL Statementlist OUT OptNL } ${ $0 = $<Sl; }$
| { SimpleStatements } ${ $0 = reorder_bilist($<SS); }$
$0->right = $<1;
}$
+ $TERM pass
SimpleStatement -> pass ${ $0 = NULL; }$
| ERROR ${ tok_err(c, "Syntax error in statement", &$1); }$
## SimpleStatement Grammar
Assign,
Declare,
+###### declare terminals
+ $TERM =
+
###### SimpleStatement Grammar
| Variable = Expression ${
$0 = new(binode);
rtype = Tnone;
} else {
free_value(v->type, v->val);
+ free(v->val);
v->val = val_alloc(v->type, NULL);
}
break;
$void
Ocean -> OptNL DeclarationList
+ ## declare terminals
+
OptNL ->
| OptNL NEWLINE
Newlines -> NEWLINE
###### top level grammar
+ $TERM const
+
DeclareConstant -> const { IN OptNL ConstList OUT OptNL } Newlines
| const { SimpleConstList } Newlines
| const IN OptNL ConstList OUT Newlines
c->prog = $<1;
} }$
+ $TERM program
+
$*binode
Program -> program OpenScope Varlist ColonBlock Newlines ${
$0 = new(binode);
v->var->val = vl;
}
free_value(v->var->type, vl);
- if (!parse_value(v->var->type, argv[0], vl))
- exit(1);
+ vl->str.len = strlen(argv[0]);
+ vl->str.txt = malloc(vl->str.len);
+ memcpy(vl->str.txt, argv[0], vl->str.len);
argv++;
}
v = interp_exec(p->right, &vtype);
name:string
alive:Boolean
- program A B:
+ program Astr Bstr:
print "Hello World, what lovely oceans you have!"
print "Are there", five, "?"
print pi, pie, "but", cake
+ A := $Astr; B := $Bstr
+
/* When a variable is defined in both branches of an 'if',
* and used afterwards, the variables are merged.
*/