From e9cd900501eab60e4ad5751517de5de7277836f7 Mon Sep 17 00:00:00 2001
From: "benjamin.franksen" <benjamin.franksen@helmholtz-berlin.de>
Date: Mon, 11 Jun 2012 09:48:22 +0000
Subject: [PATCH] snc: added foreign types and type casts

---
 src/snc/analysis.c    |  32 ++++-----
 src/snc/analysis.h    |   4 +-
 src/snc/expr.h        |   5 +-
 src/snc/gen_code.c    |   2 +-
 src/snc/gen_ss_code.c |  20 ++++--
 src/snc/gen_tables.c  |  46 ++++++------
 src/snc/snl.lem       |  92 ++++++++++++++++++------
 src/snc/snl.re        |   4 ++
 src/snc/types.h       |  58 ++++++++-------
 src/snc/var_types.c   | 159 +++++++++++++++++++++++++++---------------
 src/snc/var_types.h   |  86 ++++++++++++++++++++---
 11 files changed, 346 insertions(+), 162 deletions(-)

diff --git a/src/snc/analysis.c b/src/snc/analysis.c
index f999d393..399787c7 100644
--- a/src/snc/analysis.c
+++ b/src/snc/analysis.c
@@ -295,11 +295,12 @@ static void analyse_declaration(SymTable st, Expr *scope, Expr *defn)
 	report("declaration: %s\n", vp->name);
 #endif
 
-	if (scope->type != D_PROG && vp->type->tag <= V_EVFLAG)
+	if (scope->type != D_PROG && 
+		(vp->type->tag == T_NONE || vp->type->tag == T_EVFLAG))
 	{
 		error_at_expr(defn,
 			"%s can only be declared at the top-level\n",
-			vp->type->tag == V_NONE ? "foreign variables"
+			vp->type->tag == T_NONE ? "foreign variables"
 			: "event flags");
 	}
 
@@ -462,7 +463,7 @@ static void assign_subscript(
 	report("assign %s[%s] to '%s';\n", vp->name, subscr->value, pv_name);
 #endif
 
-	if (vp->type->tag != V_ARRAY)	/* establish L3 */
+	if (vp->type->tag != T_ARRAY)	/* establish L3 */
 	{
 		error_at_expr(defn, "variable '%s' is not an array\n", vp->name);
 		return;
@@ -502,7 +503,7 @@ static void assign_multi(
 	report("assign %s to {", vp->name);
 #endif
 
-	if (vp->type->tag != V_ARRAY)	/* establish L3 */
+	if (vp->type->tag != T_ARRAY)	/* establish L3 */
 	{
 		error_at_expr(defn, "variable '%s' is not an array\n", vp->name);
 		return;
@@ -581,7 +582,7 @@ static void monitor_elem(Expr *defn, Var *vp, Expr *subscr)
 	report("monitor %s[%s];\n", vp->name, subscr->value);
 #endif
 
-	if (vp->type->tag != V_ARRAY)	/* establish L3 */
+	if (vp->type->tag != T_ARRAY)	/* establish L3 */
 	{
 		error_at_expr(defn, "variable '%s' is not an array\n", vp->name);
 		return;
@@ -720,7 +721,7 @@ static void sync_elem(Expr *defn, Var *vp, Expr *subscr, Var *evp)
 	report("sync %s[%d] to %s;\n", vp->name, subscr->value, evp->name);
 #endif
 
-	if (vp->type->tag != V_ARRAY)	/* establish L3 */
+	if (vp->type->tag != T_ARRAY)	/* establish L3 */
 	{
 		error_at_expr(defn, "variable '%s' is not an array\n", vp->name);
 		return;
@@ -795,7 +796,7 @@ static void analyse_sync(SymTable st, Expr *scope, Expr *defn)
 		error_at_expr(defn, "event flag '%s' not declared\n", ef_name);
 		return;
 	}
-	if (evp->type->tag != V_EVFLAG)
+	if (evp->type->tag != T_EVFLAG)
 	{
 		error_at_expr(defn, "variable '%s' is not a event flag\n", ef_name);
 		return;
@@ -861,7 +862,7 @@ static void syncq_elem(Expr *defn, Var *vp, Expr *subscr, SyncQ *qp)
 
 	assert(vp->syncq != M_SINGLE);			/* call */
 
-	if (vp->type->tag != V_ARRAY)	/* establish L3 */
+	if (vp->type->tag != T_ARRAY)	/* establish L3 */
 	{
 		error_at_expr(defn, "variable '%s' is not an array\n", vp->name);
 		return;
@@ -950,7 +951,7 @@ static void analyse_syncq(SymTable st, SyncQList *syncq_list, Expr *scope, Expr
 			error_at_expr(defn, "event flag '%s' not declared\n", ef_name);
 			return;
 		}
-		if (evp->type->tag != V_EVFLAG)
+		if (evp->type->tag != T_EVFLAG)
 		{
 			error_at_expr(defn, "variable '%s' is not a event flag\n", ef_name);
 			return;
@@ -1070,7 +1071,7 @@ Var *find_var(SymTable st, char *name, Expr *scope)
 
 /* Connect a variable in an expression (E_VAR) to the Var structure.
    If there is no such structure, e.g. because the variable has not been
-   declared, then allocate one, assign type V_NONE, and assign the
+   declared, then allocate one, assign type T_NONE, and assign the
    top-level scope for the variable. */
 static int connect_variable(Expr *ep, Expr *scope, void *parg)
 {
@@ -1113,7 +1114,7 @@ static int connect_variable(Expr *ep, Expr *scope, void *parg)
 		vp = new(Var);
 		vp->name = ep->value;
                 vp->type = new(Type);
-		vp->type->tag = V_NONE;	/* undeclared type */
+		vp->type->tag = T_NONE;	/* undeclared type */
 		vp->init = 0;
 		/* add this variable to the top-level scope, NOT the current scope */
 		while (var_list->parent_scope) {
@@ -1132,8 +1133,7 @@ static void connect_variables(SymTable st, Expr *scope)
 #ifdef DEBUG
 	report("**begin** connect_variables\n");
 #endif
-	traverse_expr_tree(scope, 1u<<E_VAR, ~has_sub_expr_mask,
-		0, connect_variable, &st);
+	traverse_expr_tree(scope, 1u<<E_VAR, 0, 0, connect_variable, &st);
 #ifdef DEBUG
 	report("**end** connect_variables\n");
 #endif
@@ -1141,8 +1141,8 @@ static void connect_variables(SymTable st, Expr *scope)
 
 void traverse_expr_tree(
 	Expr		*ep,		/* start expression */
-	uint		call_mask,	/* when to call iteratee */
-	uint		stop_mask,	/* when to stop descending */
+	TypeMask	call_mask,	/* when to call iteratee */
+	TypeMask	stop_mask,	/* when to stop descending */
 	Expr		*scope,		/* current scope, 0 at top-level */
 	expr_iter	*iteratee,	/* function to call */
 	void		*parg		/* argument to pass to function */
@@ -1429,7 +1429,7 @@ static uint assign_ef_bits(Expr *scope)
 	/* Assign event flag numbers (starting at 1) */
 	foreach (vp, var_list->first)
 	{
-		if (vp->type->tag == V_EVFLAG)
+		if (vp->type->tag == T_EVFLAG)
 		{
 			vp->chan.evflag->index = ++num_event_flags;
 		}
diff --git a/src/snc/analysis.h b/src/snc/analysis.h
index 2b981afc..a3718c84 100644
--- a/src/snc/analysis.h
+++ b/src/snc/analysis.h
@@ -31,8 +31,8 @@ typedef int expr_iter(Expr *ep, Expr *scope, void *parg);
 
 void traverse_expr_tree(
 	Expr		*ep,		/* start expression */
-	uint		call_mask,	/* when to call iteratee */
-	uint		stop_mask,	/* when to stop descending */
+	TypeMask	call_mask,	/* when to call iteratee */
+	TypeMask	stop_mask,	/* when to stop descending */
 	Expr		*scope,		/* current scope, 0 at top-level */
 	expr_iter	*iteratee,	/* function to call */
 	void		*parg		/* argument to pass to function */
diff --git a/src/snc/expr.h b/src/snc/expr.h
index c615c0f2..605c812e 100644
--- a/src/snc/expr.h
+++ b/src/snc/expr.h
@@ -15,6 +15,7 @@ in the file LICENSE that is included with this distribution.
 
 #include "types.h"
 
+/* defined in expr.c */
 Expr *expr(
 	uint	type,		/* E_BINOP, E_ASGNOP, etc */
 	Token	tok,		/* "==", "+=", var name, constant, etc. */
@@ -37,8 +38,10 @@ uint strtoui(
 	uint *pnumber		/* location for result if successful */
 );
 
-Expr *decl_add_base_type(Expr *ds, uint tag);
+/* defined in var_types.c */
+Expr *decl_add_base_type(Expr *ds, Type t);
 Expr *decl_add_init(Expr *d, Expr *init);
+Expr *abstract_decl_create(void);
 Expr *decl_create(Token name);
 Expr *decl_postfix_array(Expr *d, char *s);
 Expr *decl_prefix_pointer(Expr *d);
diff --git a/src/snc/gen_code.c b/src/snc/gen_code.c
index d2a273d2..a1b43207 100644
--- a/src/snc/gen_code.c
+++ b/src/snc/gen_code.c
@@ -36,7 +36,7 @@ static int assert_var_declared(Expr *ep, Expr *scope, void *parg)
 	assert(ep->type == E_VAR);
 	assert(ep->extra.e_var != 0);
 	assert(ep->extra.e_var->decl != 0 ||
-		ep->extra.e_var->type->tag == V_NONE);
+		ep->extra.e_var->type->tag == T_NONE);
 	return TRUE;		/* there are no children anyway */
 }
 
diff --git a/src/snc/gen_ss_code.c b/src/snc/gen_ss_code.c
index 070059c9..c13da734 100644
--- a/src/snc/gen_ss_code.c
+++ b/src/snc/gen_ss_code.c
@@ -150,7 +150,7 @@ static void gen_local_var_decls(Expr *scope, int level)
 	/* Convert internal type to `C' type */
 	foreach (vp, var_list->first)
 	{
-		if (vp->decl && vp->type->tag != V_NONE)
+		if (vp->decl && vp->type->tag != T_NONE)
 		{
 			gen_line_marker(vp->decl);
 			indent(level);
@@ -177,7 +177,7 @@ static void gen_type_default(Type *type)
 	case V_STRING:
 		printf("\"\"");
 		break;
-	case V_ARRAY:
+	case T_ARRAY:
 		printf("{");
 		for (n=0; n<type->val.array.num_elems; n++)
 		{
@@ -368,13 +368,13 @@ static void gen_var_access(Var *vp)
 	report("var_access: %s, scope=(%s,%s)\n",
 		vp->name, expr_type_name(vp->scope), vp->scope->value);
 #endif
-	assert((1u<<vp->scope->type) & scope_mask);
+	assert(is_scope(vp->scope));
 
-	if (vp->type->tag == V_EVFLAG)
+	if (vp->type->tag == T_EVFLAG)
 	{
 		printf("%d/*%s*/", vp->chan.evflag->index, vp->name);
 	}
-	else if (vp->type->tag == V_NONE)
+	else if (vp->type->tag == T_NONE)
 	{
 		printf("%s", vp->name);
 	}
@@ -544,6 +544,14 @@ static void gen_expr(
 		gen_expr(context, ep->paren_expr, 0);
 		printf(")");
 		break;
+	case E_CAST:
+		printf("(");
+		/* gen_type_expr(ep->cast_type); */
+		assert(ep->cast_type->type == D_DECL);
+		gen_var_decl(ep->cast_type->extra.e_decl);
+		printf(")");
+		gen_expr(context, ep->cast_operand, 0);
+		break;
 	case E_PRE:
 		printf("%s", ep->value);
 		gen_expr(context, ep->pre_operand, 0);
@@ -642,7 +650,7 @@ static void gen_ef_arg(
 	}
 	vp = ap->extra.e_var;
 	assert(vp->type);
-	if (vp->type->tag != V_EVFLAG)
+	if (vp->type->tag != T_EVFLAG)
 	{
 		error_at_expr(ap,
 		  "argument to built-in function %s must be an event flag\n", func_name);
diff --git a/src/snc/gen_tables.c b/src/snc/gen_tables.c
index 7f8f63de..e53e2c91 100644
--- a/src/snc/gen_tables.c
+++ b/src/snc/gen_tables.c
@@ -104,26 +104,30 @@ static void gen_channel(Chan *cp, uint num_event_flags, int opt_reent)
 	Var		*vp = cp->var;
 	char		elem_str[20] = "";
 	uint		ef_num;
-	enum type_tag	type = type_base_type(vp->type);
+	Type		*basetype = base_type(vp->type);
 
-	if (type == V_LONG || type == V_ULONG)
+	if (basetype->tag == T_PRIM)
 	{
-		printf(
+		enum prim_type_tag type = basetype->val.prim;
+		if (type == V_LONG || type == V_ULONG)
+		{
+			printf(
 "#if LONG_MAX > 0x7fffffffL\n"
-		);
-		gen_line_marker(vp->decl);
-		printf(
+			);
+			gen_line_marker(vp->decl);
+			printf(
 "#error variable '"
-		);
-		gen_var_decl(vp);
-		printf("'"
+			);
+			gen_var_decl(vp);
+			printf("'"
 " cannot be assigned to a PV (on the chosen target system)\\\n"
 " because Channel Access does not support integral types longer than 4 bytes.\\\n"
 " You can use '%s' instead, or the fixed size type '%s'.\n"
 "#endif\n",
-		type == V_LONG ? "int" : "unsigned int",
-		type == V_LONG ? "int32_t" : "uint32_t"
-		);
+				type == V_LONG ? "int" : "unsigned int",
+				type == V_LONG ? "int32_t" : "uint32_t"
+			);
+		}
 	}
 
 	if (vp->assign == M_MULTI)
@@ -155,7 +159,8 @@ static void gen_channel(Chan *cp, uint num_event_flags, int opt_reent)
 	/* variable name with optional elem num */
 	printf("\"%s%s\", ", vp->name, elem_str);
 	/* variable type */
-	printf("\"%s\", ", type_name(type_base_type(vp->type)));
+	assert(base_type(vp->type)->tag == T_PRIM);
+	printf("\"%s\", ", prim_type_name[base_type(vp->type)->val.prim]);
 	/* count, for requests */
 	printf("%d, ", cp->count);
 	/* event number */
@@ -380,12 +385,7 @@ static int iter_event_mask_scalar(Expr *ep, Expr *scope, void *parg)
 	vp = ep->extra.e_var;
 	assert(vp != 0);
 
-	/* this subroutine handles only the scalar variables and event flags */
-	if (vp->type->tag < V_EVFLAG || vp->type->tag >= V_POINTER)
-		return FALSE;		/* no children anyway */
-
-	/* event flag? */
-	if (vp->type->tag == V_EVFLAG)
+	if (vp->type->tag == T_EVFLAG)
 	{
 #ifdef DEBUG
 		report("  iter_event_mask_scalar: evflag: %s, ef_num=%d\n",
@@ -394,6 +394,10 @@ static int iter_event_mask_scalar(Expr *ep, Expr *scope, void *parg)
 		bitSet(event_words, vp->chan.evflag->index);
 		return FALSE;		/* no children anyway */
 	}
+	if (vp->type->tag != T_PRIM)
+		return FALSE;		/* no children anyway */
+
+	assert(vp->type->tag == T_PRIM);
 
 	/* if not associated with channel, return */
 	if (vp->assign == M_NONE)
@@ -441,9 +445,11 @@ static int iter_event_mask_array(Expr *ep, Expr *scope, void *parg)
 	assert(vp != 0);
 
 	/* this subroutine handles only the array variables */
-	if (vp->type->tag != V_ARRAY)
+	if (vp->type->tag != T_ARRAY)
 		return TRUE;
 
+	assert(vp->type->tag == T_ARRAY);
+
 	if (vp->assign == M_NONE)
 	{
 		return FALSE;
diff --git a/src/snc/snl.lem b/src/snc/snl.lem
index 3142f7db..190f12df 100644
--- a/src/snc/snl.lem
+++ b/src/snc/snl.lem
@@ -80,6 +80,7 @@ in the file LICENSE that is included with this distribution.
 %left	LSHIFT RSHIFT.
 %left	ADD SUB.
 %left	ASTERISK SLASH MOD.
+%right	CAST.
 %right	NOT INCR DECR PRE.
 %left	LBRACKET RBRACKET POINTER PERIOD POST.
 // LPAREN RPAREN not listed as we do not support indirect calls. */
@@ -176,8 +177,10 @@ subscript(p) ::= LBRACKET INTCON(n) RBRACKET.	{ p = n; }
 
 // Declarations
 
-declaration(p) ::= type(t) init_declarators(ds) SEMICOLON.
+declaration(p) ::= basetype(t) init_declarators(ds) SEMICOLON.
 						{ p = decl_add_base_type(ds, t); }
+declaration(p) ::= FOREIGN init_declarators(ds) SEMICOLON.
+						{ p = decl_add_base_type(ds, mk_no_type()); }
 
 init_declarators(p) ::= init_declarator(x).	{ p = x; }
 init_declarators(p) ::= init_declarators(xs) COMMA init_declarator(x).
@@ -198,6 +201,8 @@ direct_declarator(p) ::= direct_declarator(x) subscript(s).
 
 // Initializer
 // Note: comma operator not allowed in 'expr'.
+init_expr(p) ::= LPAREN(tc) type_expr(c) RPAREN LBRACE(tx) init_exprs(x) RBRACE.
+						{ p = expr(E_CAST, tc, c, expr(E_INIT, tx, x)); }
 init_expr(p) ::= LBRACE(t) init_exprs(x) RBRACE.{ p = expr(E_INIT, t, x); }
 init_expr(p) ::= expr(x).			{ p = x; }
 
@@ -205,28 +210,66 @@ init_exprs(p) ::= init_exprs(xs) COMMA init_expr(x).	{ p = link_expr(xs, x); }
 init_exprs(p) ::= init_expr(x).				{ p = x; }
 init_exprs(p) ::= .					{ p = 0; }
 
-%type type {int}
-type(p) ::= CHAR.				{ p = V_CHAR;	}
-type(p) ::= SHORT.				{ p = V_SHORT;	}
-type(p) ::= INT.				{ p = V_INT;	}
-type(p) ::= LONG.				{ p = V_LONG;	}
-type(p) ::= UNSIGNED CHAR.			{ p = V_UCHAR;	}
-type(p) ::= UNSIGNED SHORT.			{ p = V_USHORT;	}
-type(p) ::= UNSIGNED INT.			{ p = V_UINT;	}
-type(p) ::= UNSIGNED LONG.			{ p = V_ULONG;	}
-
-type(p) ::= INT8T.				{ p = V_INT8T;	}
-type(p) ::= UINT8T.				{ p = V_UINT8T;	}
-type(p) ::= INT16T.				{ p = V_INT16T;	}
-type(p) ::= UINT16T.				{ p = V_UINT16T;}
-type(p) ::= INT32T.				{ p = V_INT32T;	}
-type(p) ::= UINT32T.				{ p = V_UINT32T;}
-
-type(p) ::= FLOAT.				{ p = V_FLOAT;	}
-type(p) ::= DOUBLE.				{ p = V_DOUBLE;	}
-type(p) ::= STRING.				{ p = V_STRING;	}
-type(p) ::= EVFLAG.				{ p = V_EVFLAG;	}
-type(p) ::= FOREIGN.				{ p = V_NONE;	}
+// Type expressions
+
+// C standard calls this specifier-qualifier-list
+%type basetype {Type}
+basetype(p) ::= CHAR.				{ p = mk_prim_type(V_CHAR);	}
+basetype(p) ::= SHORT.				{ p = mk_prim_type(V_SHORT);	}
+basetype(p) ::= INT.				{ p = mk_prim_type(V_INT);	}
+basetype(p) ::= LONG.				{ p = mk_prim_type(V_LONG);	}
+basetype(p) ::= UNSIGNED CHAR.			{ p = mk_prim_type(V_UCHAR);	}
+basetype(p) ::= UNSIGNED SHORT.			{ p = mk_prim_type(V_USHORT);	}
+basetype(p) ::= UNSIGNED INT.			{ p = mk_prim_type(V_UINT);	}
+basetype(p) ::= UNSIGNED LONG.			{ p = mk_prim_type(V_ULONG);	}
+
+basetype(p) ::= INT8T.				{ p = mk_prim_type(V_INT8T);	}
+basetype(p) ::= UINT8T.				{ p = mk_prim_type(V_UINT8T);	}
+basetype(p) ::= INT16T.				{ p = mk_prim_type(V_INT16T);	}
+basetype(p) ::= UINT16T.			{ p = mk_prim_type(V_UINT16T);	}
+basetype(p) ::= INT32T.				{ p = mk_prim_type(V_INT32T);	}
+basetype(p) ::= UINT32T.			{ p = mk_prim_type(V_UINT32T);	}
+
+basetype(p) ::= FLOAT.				{ p = mk_prim_type(V_FLOAT);	}
+basetype(p) ::= DOUBLE.				{ p = mk_prim_type(V_DOUBLE);	}
+basetype(p) ::= STRING.				{ p = mk_prim_type(V_STRING);	}
+basetype(p) ::= EVFLAG.				{ p = mk_ef_type();		}
+
+basetype(p) ::= ENUM NAME(x).			{ p = mk_foreign_type(F_ENUM, x.str);	}
+basetype(p) ::= STRUCT NAME(x).			{ p = mk_foreign_type(F_STRUCT, x.str);	}
+basetype(p) ::= UNION NAME(x).			{ p = mk_foreign_type(F_UNION, x.str);	}
+basetype(p) ::= TYPENAME NAME(x).		{ p = mk_foreign_type(F_TYPENAME, x.str); }
+
+type_expr(p) ::= basetype(t) opt_abstract_declarator(d). {
+	p = decl_add_base_type(d, t);
+}
+
+opt_abstract_declarator(p) ::= . {
+	p = abstract_decl_create();
+}
+opt_abstract_declarator(p) ::= abstract_declarator(x). {
+	p = x;
+}
+
+abstract_declarator(p) ::= ASTERISK. {
+	p = decl_prefix_pointer(abstract_decl_create());
+}
+abstract_declarator(p) ::= ASTERISK direct_abstract_declarator(x). {
+	p = decl_prefix_pointer(x);
+}
+abstract_declarator(p) ::= direct_abstract_declarator(x). {
+	p = x;
+}
+
+direct_abstract_declarator(p) ::= LPAREN abstract_declarator(x) RPAREN. {
+	p = x;
+}
+direct_abstract_declarator(p) ::= direct_abstract_declarator(x) subscript(s). {
+	p = decl_postfix_array(x, s.str);
+}
+direct_abstract_declarator(p) ::= subscript(s). {
+	p = decl_postfix_array(abstract_decl_create(), s.str);
+}
 
 // Option spec
 
@@ -372,6 +415,9 @@ expr(p) ::= TILDE(t)	expr(x). [PRE]		{ p = expr(E_PRE, t, x); }
 expr(p) ::= INCR(t)	expr(x). [PRE]		{ p = expr(E_PRE, t, x); }
 expr(p) ::= DECR(t)	expr(x). [PRE]		{ p = expr(E_PRE, t, x); }
 
+// Type Cast
+expr(p) ::= LPAREN(t) type_expr(c) RPAREN expr(x). [CAST] { p = expr(E_CAST, t, c, x); }
+
 // Binary Operators, left-to-right
 expr(p) ::= expr(x) SUB(t)	expr(y).	{ p = expr(E_BINOP, t, x, y); }
 expr(p) ::= expr(x) ADD(t)	expr(y).	{ p = expr(E_BINOP, t, x, y); }
diff --git a/src/snc/snl.re b/src/snc/snl.re
index 7105ef8d..cb2a7868 100644
--- a/src/snc/snl.re
+++ b/src/snc/snl.re
@@ -237,6 +237,7 @@ snl:
 	"double"	{ TYPEWORD(DOUBLE,	"double"); }
 	"else"		{ KEYWORD(ELSE,		"else"); }
 	"entry"		{ KEYWORD(ENTRY,	"entry"); }
+	"enum"		{ TYPEWORD(ENUM,	"enum"); }
 	"evflag"	{ TYPEWORD(EVFLAG,	"evflag"); }
 	"exit"		{ KEYWORD(EXIT,		"exit"); }
 	"float"		{ TYPEWORD(FLOAT,	"float"); }
@@ -252,10 +253,13 @@ snl:
 	"ss"		{ KEYWORD(SS,		"ss"); }
 	"state"		{ KEYWORD(STATE,	"state"); }
 	"string"	{ KEYWORD(STRING,	"string"); }
+	"struct"	{ TYPEWORD(STRUCT,	"struct"); }
 	"syncQ"		{ KEYWORD(SYNCQ,	"syncQ"); }
 	"syncq"		{ KEYWORD(SYNCQ,	"syncq"); }
 	"sync"		{ KEYWORD(SYNC,		"sync"); }
 	"to"		{ KEYWORD(TO,		"to"); }
+	"typename"	{ TYPEWORD(TYPENAME,	"typename"); }
+	"union"		{ TYPEWORD(UNION,	"union"); }
 	"unsigned"	{ TYPEWORD(UNSIGNED,	"unsigned"); }
 	"when"		{ KEYWORD(WHEN,		"when"); }
 	"while"		{ KEYWORD(WHILE,	"while"); }
diff --git a/src/snc/types.h b/src/snc/types.h
index 7d5accb7..2078c272 100644
--- a/src/snc/types.h
+++ b/src/snc/types.h
@@ -41,6 +41,7 @@ typedef struct sync_queue_list	SyncQList;
 typedef struct var_list		VarList;
 typedef struct expr_pair	ExprPair;
 
+typedef unsigned int		TypeMask;
 typedef unsigned int		uint;
 
 struct sym_table
@@ -111,14 +112,14 @@ struct expression			/* generic syntax node */
 	Expr		*next;		/* list node: next expression */
 	Expr		*last;		/* list node: last expression */
 	Expr		**children;	/* array of children [left,right,...] */
-	uint		type;		/* expression type (E_XXX) */
+	TypeMask	type;		/* expression type (E_XXX) */
 	char		*value;		/* operator or value string */
 	int		line_num;	/* originating line number */
 	const char	*src_file;	/* originating source file */
 	union				/* extra data, depends on type */
 	{
-		Var	*e_var;		/* variable definiton */
-		Var	*e_decl;	/* variable definiton */
+		Var	*e_var;		/* variable reference */
+		Var	*e_decl;	/* variable declaration */
 		uint	e_delay;	/* delay id */
 		uint	e_option;	/* option value (1 or 0) */
 		VarList	*e_prog;	/* top-level definitions */
@@ -165,7 +166,7 @@ L1c:	syncq	== M_MULTI	=> assign == M_MULTI
 L2a:	assign	== M_NONE	=> monitor == M_NONE
 L2b:	assign	== M_NONE	=> sync == M_NONE
 L2c:	assign	== M_NONE	=> syncq == M_NONE
-L3:	assign	== M_MULTI	=> type->tag == V_ARRAY
+L3:	assign	== M_MULTI	=> type->tag == T_ARRAY
 */
 
 struct channel				/* channel assignment info */
@@ -235,31 +236,25 @@ struct program
 /* Generic iteration on lists */
 #define foreach(e,l)		for (e = l; e != 0; e = e->next)
 
+/* Commonly used sets of expression types */
+
 /* Expression types that are scopes. By definition, a scope is an expression
    that allows variable declarations as (immediate) subexpressions. */
-#define scope_mask		( (1u<<D_PROG)   | (1u<<D_SS)      | (1u<<D_STATE)\
-				| (1u<<D_ENTEX)  | (1u<<D_WHEN)    | (1u<<S_CMPND) )
-
+#define scope_mask		( (1u<<D_PROG)   | (1u<<D_SS)     | (1u<<D_STATE)\
+				| (1u<<D_ENTEX)  | (1u<<D_WHEN)   | (1u<<S_CMPND) )
+/* Whether an expression is a scope */
 #define is_scope(e)		(((1u<<((e)->type)) & scope_mask) != 0)
 
-/* Expressions types that may have sub-scopes */
-#define has_sub_scope_mask	( (1u<<D_ENTEX)  | (1u<<D_PROG)    | (1u<<D_SS)\
-				| (1u<<D_STATE)  | (1u<<D_WHEN)    | (1u<<S_CMPND)  | (1u<<S_FOR)\
-				| (1u<<S_IF)     | (1u<<S_STMT)    | (1u<<S_WHILE) )
-/* Expressions types that may have sub-expressions */
-#define has_sub_expr_mask	( (1u<<D_DECL)   | (1u<<D_ENTEX)   | (1u<<D_PROG)\
-				| (1u<<D_SS)     | (1u<<D_STATE)   | (1u<<D_SYNC)   | (1u<<D_SYNCQ)\
-				| (1u<<D_WHEN)   | (1u<<E_BINOP)   | (1u<<E_DELAY)\
-				| (1u<<E_FUNC)   | (1u<<E_INIT)    | (1u<<E_PAREN)  | (1u<<E_POST)\
-				| (1u<<E_PRE)    | (1u<<E_SUBSCR)  | (1u<<E_TERNOP) | (1u<<E_VAR)\
-				| (1u<<S_CHANGE) | (1u<<S_CMPND)   | (1u<<S_FOR)    | (1u<<S_IF)\
-				| (1u<<S_STMT)   | (1u<<S_WHILE) )
+/* Expression types that may have sub-scopes */
+#define has_sub_scope_mask	( (1u<<D_ENTEX)  | (1u<<D_PROG)   | (1u<<D_SS)\
+				| (1u<<D_STATE)  | (1u<<D_WHEN)   | (1u<<S_CMPND) | (1u<<S_FOR)\
+				| (1u<<S_IF)     | (1u<<S_STMT)   | (1u<<S_WHILE) )
 /* Expression types that are actually expressions i.e. no definitions or statements.
    These are the ones that start with E_. */
-#define	expr_mask		( (1u<<E_BINOP)  | (1u<<E_CONST)   | (1u<<E_DELAY)\
+#define	expr_mask		( (1u<<E_BINOP)  | (1u<<E_CAST)   | (1u<<E_CONST) | (1u<<E_DELAY)\
 				| (1u<<E_FUNC)   | (1u<<E_INIT)\
-				| (1u<<E_PAREN)  | (1u<<E_POST)    | (1u<<E_PRE)    | (1u<<E_STRING)\
-				| (1u<<E_SUBSCR) | (1u<<E_TERNOP)  | (1u<<E_VAR)    | (1u<<T_TEXT) )
+				| (1u<<E_PAREN)  | (1u<<E_POST)   | (1u<<E_PRE)   | (1u<<E_STRING)\
+				| (1u<<E_SUBSCR) | (1u<<E_TERNOP) | (1u<<E_VAR)   | (1u<<T_TEXT) )
 
 #define expr_type_name(e)	expr_type_info[(e)->type].name
 
@@ -287,6 +282,7 @@ enum expr_type			/* description [child expressions...] */
 	D_WHEN,			/* when statement [cond,defns,stmts] */
 
 	E_BINOP,		/* binary operator [left,right] */
+	E_CAST,			/* type cast [operand] */
 	E_CONST,		/* numeric (inkl. character) constant [] */
 	E_DELAY,		/* delay function call [args] */
 	E_FUNC,			/* function call [args] */
@@ -318,6 +314,8 @@ enum expr_type			/* description [child expressions...] */
 #define assign_pvs	children[1]
 #define binop_left	children[0]
 #define binop_right	children[1]
+#define cast_type	children[0]
+#define cast_operand	children[1]
 #define cmpnd_defns	children[0]
 #define cmpnd_stmts	children[1]
 #define decl_init	children[0]
@@ -366,6 +364,14 @@ enum expr_type			/* description [child expressions...] */
 #define while_cond	children[0]
 #define while_stmt	children[1]
 
+/* Accessors for var_lists (only for scopes) */
+#define prog_var_list	extra.e_prog
+#define ss_var_list	extra.e_ss->var_list
+#define state_var_list	extra.e_state->var_list
+#define when_var_list	extra.e_when->var_list
+#define entex_var_list	extra.e_entex;
+#define cmpnd_var_list	extra.e_cmpnd;
+
 #ifndef expr_type_GLOBAL
 extern
 #endif
@@ -389,6 +395,7 @@ expr_type_info[]
 	{ "D_SYNCQ",	3 },
 	{ "D_WHEN",	3 },
 	{ "E_BINOP",	2 },
+	{ "E_CAST",	2 },
 	{ "E_CONST",	0 },
 	{ "E_DELAY",	1 },
 	{ "E_FUNC",	1 },
@@ -407,10 +414,9 @@ expr_type_info[]
 	{ "S_JUMP",	0 },
 	{ "S_STMT",	1 },
 	{ "S_WHILE",	2 },
-	{ "T_TEXT",	0 }
-};
-#else
-;
+	{ "T_TEXT",	0 },
+}
 #endif
+;
 
 #endif	/*INCLtypesh*/
diff --git a/src/snc/var_types.c b/src/snc/var_types.c
index 9df808b2..f0d7a6ed 100644
--- a/src/snc/var_types.c
+++ b/src/snc/var_types.c
@@ -8,43 +8,21 @@ in the file LICENSE that is included with this distribution.
 #include <assert.h>
 #include <stdlib.h>
 #include <stdio.h>
+#include <string.h>
 
 #include "main.h"
+#define var_types_GLOBAL
 #include "expr.h"
+#undef var_types_GLOBAL
 
 /* #define DEBUG */
 
-const char *type_name(unsigned tag)
-{
-    switch (tag) {
-        case V_NONE:    return "foreign";
-        case V_EVFLAG:  return "evflag";
-        case V_CHAR:    return "char";
-        case V_UCHAR:   return "unsigned char";
-        case V_SHORT:   return "short";
-        case V_USHORT:  return "unsigned short";
-        case V_INT:     return "int";
-        case V_UINT:    return "unsigned int";
-        case V_LONG:    return "long";
-        case V_ULONG:   return "unsigned long";
-        case V_INT8T:   return "epicsInt8";
-        case V_UINT8T:  return "epicsUInt8";
-        case V_INT16T:  return "epicsInt16";
-        case V_UINT16T: return "epicsUInt16";
-        case V_INT32T:  return "epicsInt32";
-        case V_UINT32T: return "epicsUInt32";
-        case V_FLOAT:   return "float";
-        case V_DOUBLE:  return "double";
-        case V_STRING:  return "string";
-        case V_ENUM:    return "enumeration";
-        default:        return "";
-    }
-}
+static const int impossible = FALSE;
 
-Expr *decl_add_base_type(Expr *ds, unsigned tag)
+/* Add a common base type to all types in a multi-variable declaration */
+Expr *decl_add_base_type(Expr *ds, Type basetype)
 {
     Expr *d;
-    static const int impossible = FALSE;
 
     foreach(d, ds) {
         Var *var;
@@ -53,37 +31,39 @@ Expr *decl_add_base_type(Expr *ds, unsigned tag)
         assert(d->type == D_DECL);      /* pre-condition */
         var = d->extra.e_decl;
         assert(var);
-        t->tag = tag;
+        /* structure copy */
+        *t = basetype;
         t->parent = var->type;
         /* now roll back the stack of type expressions */
         while(t->parent) {
             switch (t->parent->tag) {
-            case V_ARRAY:
-                if (tag == V_NONE) {
+            case T_ARRAY:
+                if (basetype.tag == T_NONE) {
                     error_at_expr(d, "cannot declare array of foreign variables\n");
                 }
-                if (tag == V_EVFLAG) {
+                if (basetype.tag == T_EVFLAG) {
                     error_at_expr(d, "cannot declare array of event flags\n");
                 }
                 t->parent->val.array.elem_type = t;
                 break;
-            case V_POINTER:
-                if (tag == V_NONE) {
+            case T_POINTER:
+                if (basetype.tag == T_NONE) {
                     error_at_expr(d, "cannot declare pointer to foreign variable\n");
                 }
-                if (tag == V_EVFLAG) {
+                if (basetype.tag == T_EVFLAG) {
                     error_at_expr(d, "cannot declare pointer to event flag\n");
                 }
                 t->parent->val.pointer.value_type = t;
                 break;
-            default: assert(impossible);
+            default:
+                assert(impossible);
             }
             t = t->parent;
         }
         assert(!t->parent);
         t->parent = bt;
         var->type = t;
-        if (tag == V_EVFLAG)
+        if (basetype.tag == T_EVFLAG)
             var->chan.evflag = new(EvFlag);
     }
     return ds;
@@ -100,21 +80,27 @@ Expr *decl_add_init(Expr *d, Expr *init)
     return d;
 }
 
-Expr *decl_create(Token name)
+Expr *decl_create(Token id)
 {
-    Expr *d = expr(D_DECL, name, 0);
+    Expr *d = expr(D_DECL, id, 0);
     Var *var = new(Var);
 
 #ifdef DEBUG
-    report("decl_create: name(%s)\n", name.str);
+    report("decl_create: name(%s)\n", id.str);
 #endif
     assert(d->type == D_DECL);          /* expr() post-condition */
-    var->name = name.str;
+    var->name = id.str;
     d->extra.e_decl = var;
     var->decl = d;
     return d;
 }
 
+Expr *abstract_decl_create(void)
+{
+    Token t = {0,0,0};
+    return decl_create(t);
+}
+
 Expr *decl_postfix_array(Expr *d, char *s)
 {
     Type *t = new(Type);
@@ -130,7 +116,7 @@ Expr *decl_postfix_array(Expr *d, char *s)
     report("decl_postfix_array %u\n", num_elems);
 #endif
 
-    t->tag = V_ARRAY;
+    t->tag = T_ARRAY;
     t->val.array.num_elems = num_elems;
     t->parent = d->extra.e_decl->type;
     d->extra.e_decl->type = t;
@@ -145,16 +131,60 @@ Expr *decl_prefix_pointer(Expr *d)
     report("decl_prefix_pointer\n");
 #endif
     assert(d->type == D_DECL);          /* pre-condition */
-    t->tag = V_POINTER;
+    t->tag = T_POINTER;
     t->parent = d->extra.e_decl->type;
     d->extra.e_decl->type = t;
     return d;
 }
 
+
+Type mk_prim_type(enum prim_type_tag tag)
+{
+    Type t;
+
+    memset(&t, 0, sizeof(Type));
+
+    t.tag = T_PRIM;
+    t.val.prim = tag;
+    return t;
+}
+
+Type mk_foreign_type(enum foreign_type_tag tag, char *name)
+{
+    Type t;
+
+    memset(&t, 0, sizeof(Type));
+
+    t.tag = T_FOREIGN;
+    t.val.foreign.tag = tag;
+    t.val.foreign.name = name;
+    return t;
+}
+
+Type mk_ef_type()
+{
+    Type t;
+
+    memset(&t, 0, sizeof(Type));
+
+    t.tag = T_EVFLAG;
+    return t;
+}
+
+Type mk_no_type()
+{
+    Type t;
+
+    memset(&t, 0, sizeof(Type));
+
+    t.tag = T_NONE;
+    return t;
+}
+
 unsigned type_array_length1(Type *t)
 {
     switch (t->tag) {
-    case V_ARRAY:
+    case T_ARRAY:
         return t->val.array.num_elems;
     default:
         return 1;
@@ -164,7 +194,7 @@ unsigned type_array_length1(Type *t)
 unsigned type_array_length2(Type *t)
 {
     switch (t->tag) {
-    case V_ARRAY:
+    case T_ARRAY:
         return type_array_length1(t->val.array.elem_type);
     default:
         return 1;
@@ -176,12 +206,15 @@ static unsigned type_assignable_array(Type *t, int depth)
     if (depth > 2)
         return FALSE;
     switch (t->tag) {
-    case V_NONE:
-    case V_EVFLAG:
-    case V_POINTER:
+    case T_NONE:
+    case T_FOREIGN:
+    case T_POINTER:
+    case T_EVFLAG:
         return FALSE;
-    case V_ARRAY:
+    case T_ARRAY:
         return type_assignable_array(t->val.array.elem_type, depth + 1);
+    case T_PRIM:
+    /* make the compiler happy: */
     default:
         return TRUE;
     }
@@ -192,11 +225,11 @@ unsigned type_assignable(Type *t)
     return type_assignable_array(t, 0);
 }
 
-static void gen_array_pointer(Type *t, unsigned last_tag, char *name)
+static void gen_array_pointer(Type *t, enum type_tag last_tag, char *name)
 {
-    int paren = last_tag == V_ARRAY;
+    int paren = last_tag == T_ARRAY;
     switch (t->tag) {
-    case V_POINTER:
+    case T_POINTER:
         if (paren)
             printf("(");
         printf("*");
@@ -204,12 +237,14 @@ static void gen_array_pointer(Type *t, unsigned last_tag, char *name)
         if (paren)
             printf(")");
         break;
-    case V_ARRAY:
+    case T_ARRAY:
         gen_array_pointer(t->parent, t->tag, name);
         printf("[%d]", t->val.array.num_elems);
         break;
     default:
-        printf("%s", name);
+        if (name)
+            printf("%s", name);
+        break;
     }
 }
 
@@ -217,6 +252,18 @@ void gen_type(Type *t, char *name)
 {
     Type *bt = base_type(t);
 
-    printf("%s ", type_name(bt->tag));
-    gen_array_pointer(bt->parent, V_NONE, name);
+    switch (bt->tag) {
+    case T_EVFLAG:
+        printf("evflag ");
+        break;
+    case T_PRIM:
+        printf("%s ", prim_type_name[bt->val.prim]);
+        break;
+    case T_FOREIGN:
+        printf("%s%s ", foreign_type_prefix[bt->val.foreign.tag], bt->val.foreign.name);
+        break;
+    default:
+        assert(impossible);
+    }
+    gen_array_pointer(bt->parent, T_NONE, name);
 }
diff --git a/src/snc/var_types.h b/src/snc/var_types.h
index 023ec80e..5ea2ed98 100644
--- a/src/snc/var_types.h
+++ b/src/snc/var_types.h
@@ -8,8 +8,15 @@ in the file LICENSE that is included with this distribution.
 #define INCLvar_typesh
 
 enum type_tag {
-    V_NONE,
-    V_EVFLAG,
+    T_NONE,     /* undeclared (or declared as foreign) variable */
+    T_EVFLAG,   /* event flags */
+    T_PRIM,     /* primitive types: numbers, char, string */
+    T_FOREIGN,  /* foreign types (declared in C code) */
+    T_POINTER,
+    T_ARRAY,
+};
+
+enum prim_type_tag {
     V_CHAR,
     V_UCHAR,
     V_SHORT,
@@ -27,9 +34,13 @@ enum type_tag {
     V_FLOAT,
     V_DOUBLE,
     V_STRING,
-    V_ENUM,
-    V_POINTER,
-    V_ARRAY,
+};
+
+enum foreign_type_tag {
+    F_ENUM,
+    F_STRUCT,
+    F_UNION,
+    F_TYPENAME,
 };
 
 struct array_type {
@@ -41,9 +52,9 @@ struct pointer_type {
     struct type *value_type;
 };
 
-struct enum_type {
-    unsigned    num_names;
-    char        **names;
+struct foreign_type {
+    enum foreign_type_tag tag;
+    char *name;
 };
 
 typedef struct type Type;
@@ -51,19 +62,72 @@ typedef struct type Type;
 struct type {
     enum type_tag tag;
     union {
+        enum prim_type_tag  prim;
+        struct foreign_type foreign;
         struct pointer_type pointer;
         struct array_type   array;
-        struct enum_type    enumeration;
     } val;
     struct type *parent;
 };
 
-const char *type_name (unsigned tag);
-#define type_base_type(t) (t->parent->tag)
+/* base type for any combination of pointers and arrays */
 #define base_type(t) (t->parent)
+
+/* array length in 1st and 2nd dimension */
 unsigned type_array_length1(Type *t);
 unsigned type_array_length2(Type *t);
+
+/* whether type can be assign'ed to a PV */
 unsigned type_assignable(Type *t);
+
+/* generate code for a type, name is an optional variable name  */
 void gen_type(Type *t, char *name);
 
+/* creating types */
+Type mk_prim_type(enum prim_type_tag tag);
+Type mk_foreign_type(enum foreign_type_tag tag, char *name);
+Type mk_ef_type();
+Type mk_no_type();
+
+#ifndef var_types_GLOBAL
+extern
+#endif
+const char *prim_type_name[]
+#ifdef var_types_GLOBAL
+= {
+    "char",
+    "unsigned char",
+    "short",
+    "unsigned short",
+    "int",
+    "unsigned int",
+    "long",
+    "unsigned long",
+    "epicsInt8",
+    "epicsUInt8",
+    "epicsInt16",
+    "epicsUInt16",
+    "epicsInt32",
+    "epicsUInt32",
+    "float",
+    "double",
+    "string",
+}
+#endif
+;
+
+#ifndef var_types_GLOBAL
+extern
+#endif
+const char *foreign_type_prefix[]
+#ifdef var_types_GLOBAL
+= {
+    "enum ",
+    "struct ",
+    "union ",
+    "",
+}
+#endif
+;
+
 #endif /*INCLvar_typesh */
-- 
GitLab