diff --git a/src/snc/analysis.c b/src/snc/analysis.c index 1748fa2a83b37778b720baaeaebf67d6b4bf4e2c..01e4e10846eb66d2385cc9a12f2a4ce1083083db 100644 --- a/src/snc/analysis.c +++ b/src/snc/analysis.c @@ -24,6 +24,7 @@ in the file LICENSE that is included with this distribution. static const int impossible = 0; +static void analyse_funcdefs(Expr *prog); static void analyse_definitions(Program *p); static void analyse_option(Options *options, Expr *defn); static void analyse_state_option(StateOptions *options, Expr *defn); @@ -76,6 +77,7 @@ Program *analyse_program(Expr *prog, Options options) report("created symbol table, channel list, and syncq list\n"); #endif + analyse_funcdefs(p->prog); analyse_definitions(p); p->num_ss = connect_states(p->sym_table, prog); connect_variables(p->sym_table, prog); @@ -86,6 +88,52 @@ Program *analyse_program(Expr *prog, Options options) return p; } +static void analyse_funcdefs(Expr *prog) +{ + Expr *f; + + assert(prog->type == D_PROG); + foreach(f, prog->prog_funcdefs) + { + Expr *d = f->funcdef_decl; + Var *var = d->extra.e_decl; + Expr *p; + + assert(f->type == D_FUNCDEF); + if (var->type->tag != T_FUNCTION) + { + error_at_expr(d, "not a function type\n"); + continue; + } + f->funcdef_params = var->type->val.function.param_decls; + assert(f->funcdef_params); /* invariant enforced by syntax */ + p = f->funcdef_params; + if (p->extra.e_decl->type->tag == T_VOID) + { + /* no other params should be there */ + if (p->next) + { + error_at_expr(p->next, "void must be the only parameter\n"); + } + if (p->extra.e_decl->name) + { + error_at_expr(p, "void parameter should not have a name\n"); + } + /* void means empty parameter list */ + f->funcdef_params = var->type->val.function.param_decls = 0; + } + foreach(p, f->funcdef_params) + { + /* check parameter has a name */ + if (!p->extra.e_decl->name) + { + error_at_expr(p, "function parameter must have a name\n"); + } + } + prog->prog_defns = link_expr(prog->prog_defns, d); + } +} + static int analyse_defn(Expr *scope, Expr *parent_scope, void *parg) { Program *p = (Program *)parg; @@ -171,6 +219,8 @@ VarList **pvar_list_from_scope(Expr *scope) return &scope->extra.e_state->var_list; case S_CMPND: return &scope->extra.e_cmpnd; + case D_FUNCDEF: + return &scope->extra.e_funcdef; default: assert(impossible); return NULL; } @@ -191,6 +241,8 @@ Expr *defn_list_from_scope(Expr *scope) return scope->state_defns; case S_CMPND: return scope->cmpnd_defns; + case D_FUNCDEF: + return scope->funcdef_params; default: assert(impossible); return NULL; } @@ -305,6 +357,8 @@ static void analyse_declaration(SymTable st, Expr *scope, Expr *defn) var_list = var_list_from_scope(scope); + assert(vp->name); + if (!sym_table_insert(st, vp->name, var_list, vp)) { Var *vp2 = (Var *)sym_table_lookup(st, vp->name, var_list); diff --git a/src/snc/gen_code.c b/src/snc/gen_code.c index 843f2a10184b09a8170032da4d7a6b92cca314b4..64e5a217a3a5758cd641ff17537d2fdae8312b2d 100644 --- a/src/snc/gen_code.c +++ b/src/snc/gen_code.c @@ -152,7 +152,8 @@ static void gen_user_var(Program *p) /* Convert internal type to `C' type */ foreach (vp, p->prog->extra.e_prog->first) { - if (vp->decl && vp->type->tag != T_NONE && vp->type->tag != T_EVFLAG) + if (vp->decl && vp->type->tag != T_NONE && vp->type->tag != T_EVFLAG && + vp->type->tag != T_FUNCTION) { gen_line_marker(vp->decl); if (!opt_reent) gen_code("static"); @@ -220,6 +221,25 @@ static void gen_user_var(Program *p) } gen_code("};\n"); } + foreach (vp, p->prog->extra.e_prog->first) + { + if (vp->decl && vp->type->tag == T_FUNCTION) + { + Expr *param_decl; + + gen_line_marker(vp->decl); + gen_code("static "); + gen_type(vp->type->val.function.return_type, "", vp->name); + gen_code("(SS_ID " NM_SS ", SEQ_VARS *const " NM_VARS_ARG); + foreach (param_decl, vp->type->val.function.param_decls) + { + Var *vp = param_decl->extra.e_decl; + gen_code(", "); + gen_var_decl(vp); + } + gen_code(");\n"); + } + } gen_code("\n"); } diff --git a/src/snc/gen_ss_code.c b/src/snc/gen_ss_code.c index 75da64cab03c891eb64929f6aa9c039be215f7cb..7538ab419758c8446957528571fa7242c8acb297 100644 --- a/src/snc/gen_ss_code.c +++ b/src/snc/gen_ss_code.c @@ -62,6 +62,7 @@ static void gen_prog_entex_func( static void gen_prog_init_body(Expr *prog); static void gen_prog_entry_body(Expr *prog); static void gen_prog_exit_body(Expr *prog); +static void gen_funcdef(Expr *fp); /* * Expression context. Certain nodes of the syntax tree are @@ -74,7 +75,8 @@ enum expr_context C_COND, /* when() condition */ C_TRANS, /* state transition actions */ C_SS, /* otherwise inside a state set */ - C_GLOBAL /* outside state sets */ + C_FUNC, /* inside a function definition */ + C_GLOBAL /* outside state sets and functions */ }; /* @@ -87,7 +89,7 @@ static Options global_options; /* Generate state set C code from analysed syntax tree */ void gen_ss_code(Expr *prog, Options options) { - Expr *sp, *ssp; + Expr *sp, *ssp, *fp; uint ss_num = 0; /* HACK: intialise global variable as implicit parameter */ @@ -135,6 +137,12 @@ void gen_ss_code(Expr *prog, Options options) /* Generate program exit func */ if (prog->prog_exit) gen_prog_entex_func(prog, "exit", NM_EXIT, gen_prog_exit_body); + + /* Generate function definitions */ + foreach(fp, prog->prog_funcdefs) + { + gen_funcdef(fp); + } } /* Generate a local C variable declaration for each variable declared @@ -304,7 +312,7 @@ static void gen_var_access(Var *vp) { gen_code("%d/*%s*/", vp->chan.evflag->index, vp->name); } - else if (vp->type->tag == T_NONE) + else if (vp->type->tag == T_NONE || vp->type->tag == T_FUNCTION) { gen_code("%s", vp->name); } @@ -407,6 +415,15 @@ static void gen_expr( indent(level); gen_code("{*" NM_PNST " = %d; return;}\n", ep->extra.e_change->extra.e_state->index); break; + case S_RETURN: + if (context != C_FUNC) + { + error_at_expr(ep, "return statement not allowed here\n"); + break; + } + indent(level); + gen_code("return "); gen_expr(context, ep->return_expr, level); gen_code(";\n"); + break; /* Expressions */ case E_VAR: gen_var_access(ep->extra.e_var); @@ -434,6 +451,13 @@ static void gen_expr( } gen_expr(context, ep->func_expr, 0); gen_code("("); + if (ep->func_expr->type == E_VAR && ep->func_expr->extra.e_var->type->tag == T_FUNCTION) + { + /* direct call and not a foreign function => add implicit parameters */ + gen_code(NM_SS ", " NM_VARS_ARG); + if (ep->func_args) + gen_code(", "); + } foreach (cep, ep->func_args) { gen_expr(context, cep, 0); @@ -881,3 +905,23 @@ static void gen_prog_exit_body(Expr *prog) assert(prog->type == D_PROG); gen_entex_body(prog->prog_exit, C_SS); } + +static void gen_funcdef(Expr *fp) +{ + Expr *param_decl; + Var *vp = fp->funcdef_decl->extra.e_decl; + + assert(fp->type == D_FUNCDEF); + gen_line_marker(vp->decl); + gen_code("static "); + gen_type(vp->type->val.function.return_type, "", vp->name); + gen_code("(SS_ID " NM_SS ", SEQ_VARS *const " NM_VARS_ARG); + foreach (param_decl, fp->funcdef_params) + { + vp = param_decl->extra.e_decl; + gen_code(", "); + gen_var_decl(vp); + } + gen_code(")\n"); + gen_block(fp->funcdef_block, C_FUNC, 0); +} diff --git a/src/snc/snl.lem b/src/snc/snl.lem index d244388864a4fc264df291bb8e96e3c576a8d5ad..7e017f307f293308d718d9a617190fcb2278ee9c 100644 --- a/src/snc/snl.lem +++ b/src/snc/snl.lem @@ -92,9 +92,10 @@ program ::= entry(en) state_sets(ss) exit(ex) + functions(fs) c_codes(cc). { - *presult = expr(D_PROG, n, pp, ds, en, ss, ex, cc); + *presult = expr(D_PROG, n, pp, ds, en, ss, ex, fs, cc); } program_param(p) ::= LPAREN string(x) RPAREN. { p = x; } @@ -359,6 +360,8 @@ statements(p) ::= . { p = 0; } statement(p) ::= BREAK(t) SEMICOLON. { p = expr(S_JUMP, t); } statement(p) ::= CONTINUE(t) SEMICOLON. { p = expr(S_JUMP, t); } +statement(p) ::= RETURN(t) opt_expr(x) SEMICOLON. + { p = expr(S_RETURN, t, x); } statement(p) ::= STATE NAME(t) SEMICOLON. { p = expr(S_CHANGE, t); } statement(p) ::= c_code(x). { p = x; } statement(p) ::= block(x). { p = x; } @@ -459,7 +462,7 @@ opt_expr(p) ::= comma_expr(x). { p = x; } opt_expr(p) ::= . { p = 0; } // Function arguments -// Ssyntactically the same as opt_expr but interpreted differently. +// Syntactically the same as opt_expr but interpreted differently. args(p) ::= args(xs) COMMA expr(x). { p = link_expr(xs, x); } args(p) ::= expr(x). { p = x; } args(p) ::= . { p = 0; } @@ -468,6 +471,15 @@ string(p) ::= STRCON(t). { p = expr(E_STRING, t); } member(p) ::= NAME(t). { p = expr(E_MEMBER, t); } +// Functions + +functions(p) ::= . { p = 0; } +functions(p) ::= functions(fs) function(f). { p = link_expr(fs, f); } + +function(p) ::= FUNCTION(l) basetype(t) declarator(d) block(b). { + p = expr(D_FUNCDEF, l, decl_add_base_type(d, t), 0, b); +} + // Literal (C) code c_codes(p) ::= c_codes(xs) c_code(x). { p = link_expr(xs, x); } diff --git a/src/snc/snl.re b/src/snc/snl.re index 9c6898c36dbef53a3d1f9f41663b4de9d986ddcb..267b7d9505f84dca5c490fa756cccabc1329dae0 100644 --- a/src/snc/snl.re +++ b/src/snc/snl.re @@ -244,12 +244,14 @@ snl: "float" { TYPEWORD(FLOAT, "float"); } "for" { KEYWORD(FOR, "for"); } "foreign" { TYPEWORD(FOREIGN, "foreign"); } + "function" { TYPEWORD(FUNCTION, "function"); } "if" { KEYWORD(IF, "if"); } "int" { TYPEWORD(INT, "int"); } "long" { TYPEWORD(LONG, "long"); } "monitor" { KEYWORD(MONITOR, "monitor"); } "option" { KEYWORD(OPTION, "option"); } "program" { KEYWORD(PROGRAM, "program"); } + "return" { KEYWORD(RETURN, "return"); } "short" { TYPEWORD(SHORT, "short"); } "sizeof" { TYPEWORD(SIZEOF, "sizeof"); } "ss" { KEYWORD(SS, "ss"); } diff --git a/src/snc/types.h b/src/snc/types.h index 831151a04003fe17e48de80918371430103078c5..ebedc9e2c303ae246ef0e647531f48d6532577cd 100644 --- a/src/snc/types.h +++ b/src/snc/types.h @@ -123,14 +123,15 @@ struct expression /* generic syntax node */ Var *e_var; /* variable reference */ Var *e_decl; /* variable declaration */ uint e_option; /* option value (1 or 0) */ - VarList *e_prog; /* top-level definitions */ + VarList *e_prog; /* top-level declarations */ StateSet *e_ss; /* state set data */ State *e_state; /* state data */ When *e_when; /* transition data */ Expr *e_change; /* declaration of target state */ - VarList *e_cmpnd; /* block local definitions */ + VarList *e_cmpnd; /* block local declarations */ FuncSym *e_builtin; /* builtin function */ ConstSym *e_const; /* builtin constant */ + VarList *e_funcdef; /* parameter declarations */ } extra; }; @@ -238,12 +239,12 @@ struct program /* Expression types that are scopes. By definition, a scope is an expression that allows variable declarations as (immediate) subexpressions. */ -#define scope_mask ( bit(D_PROG) | bit(D_SS) | bit(D_STATE) | bit(S_CMPND) ) +#define scope_mask ( bit(D_PROG) | bit(D_FUNCDEF) | bit(D_SS) | bit(D_STATE) | bit(S_CMPND) ) /* Whether an expression is a scope */ #define is_scope(e) ((bit((e)->type) & scope_mask) != 0) /* Expression types that may have sub-scopes */ -#define has_sub_scope_mask ( bit(D_ENTEX) | bit(D_PROG) | bit(D_SS)\ +#define has_sub_scope_mask ( bit(D_ENTEX) | bit(D_FUNCDEF) | bit(D_PROG) | bit(D_SS)\ | bit(D_STATE) | bit(D_WHEN) | bit(S_CMPND) | bit(S_FOR)\ | bit(S_IF) | bit(S_STMT) | bit(S_WHILE) ) /* Expression types that are actually expressions i.e. no definitions or statements. @@ -270,9 +271,10 @@ enum expr_type /* description [child expressions...] */ D_ASSIGN, /* assign statement [subscr,pvs] */ D_DECL, /* variable declaration [init] */ D_ENTEX, /* entry or exit statement [block] */ + D_FUNCDEF, /* function definition [decl,params,block] */ D_MONITOR, /* monitor statement [subscr] */ D_OPTION, /* option definition [] */ - D_PROG, /* whole program [param,defns,entry,statesets,exit,ccode] */ + D_PROG, /* whole program [param,defns,entry,statesets,exit,funcdefs,ccode] */ D_SS, /* state set statement [defns,states] */ D_STATE, /* state statement [defns,entry,whens,exit] */ D_SYNC, /* sync statement [subscr,evflag] */ @@ -300,6 +302,7 @@ enum expr_type /* description [child expressions...] */ S_FOR, /* for statement [init,cond,iter,stmt] */ S_IF, /* if statement [cond,then,else] */ S_JUMP, /* break or continue stmt [] */ + S_RETURN, /* return stmt [expr] */ S_STMT, /* simple statement, i.e. 'expr;' [expr] */ S_WHILE, /* while statement [cond,stmt] */ @@ -330,6 +333,9 @@ STATIC_ASSERT(NUM_EXPR_TYPES <= 8*sizeof(TypeMask)); #define for_stmt children[3] #define func_expr children[0] #define func_args children[1] +#define funcdef_decl children[0] +#define funcdef_params children[1] +#define funcdef_block children[2] #define if_cond children[0] #define if_then children[1] #define if_else children[2] @@ -343,7 +349,9 @@ STATIC_ASSERT(NUM_EXPR_TYPES <= 8*sizeof(TypeMask)); #define prog_entry children[2] #define prog_statesets children[3] #define prog_exit children[4] -#define prog_ccode children[5] +#define prog_funcdefs children[5] +#define prog_ccode children[6] +#define return_expr children[0] #define select_left children[0] #define select_right children[1] #define ss_defns children[0] @@ -368,12 +376,6 @@ STATIC_ASSERT(NUM_EXPR_TYPES <= 8*sizeof(TypeMask)); #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 cmpnd_var_list extra.e_cmpnd; - #ifndef expr_type_GLOBAL extern #endif @@ -388,9 +390,10 @@ expr_type_info[] { "D_ASSIGN", 2 }, { "D_DECL", 1 }, { "D_ENTEX", 1 }, + { "D_FUNCDEF", 3 }, { "D_MONITOR", 1 }, { "D_OPTION", 0 }, - { "D_PROG", 6 }, + { "D_PROG", 7 }, { "D_SS", 2 }, { "D_STATE", 4 }, { "D_SYNC", 2 }, @@ -416,6 +419,7 @@ expr_type_info[] { "S_FOR", 4 }, { "S_IF", 3 }, { "S_JUMP", 0 }, + { "S_RETURN", 1 }, { "S_STMT", 1 }, { "S_WHILE", 2 }, { "T_TEXT", 0 }, diff --git a/src/snc/var_types.c b/src/snc/var_types.c index f6b8c837ac2ff1144e34f6e26f6da121d0ef903c..3c2cb6e1754a16d9b2b86b13c679803a6f96b815 100644 --- a/src/snc/var_types.c +++ b/src/snc/var_types.c @@ -60,7 +60,7 @@ Expr *decl_add_base_type(Expr *ds, Type basetype) error_at_expr(d, "cannot declare function returning foreign variable\n"); } if (basetype.tag == T_EVFLAG) { - error_at_expr(d, "cannot declare returning event flag\n"); + error_at_expr(d, "cannot declare function returning event flag\n"); } t->parent->val.function.return_type = t; break; @@ -135,8 +135,6 @@ Expr *decl_postfix_array(Expr *d, char *s) Expr *decl_postfix_function(Expr *decl, Expr *param_decls) { Type *t = new(Type); - Expr *param_decl; - unsigned n = 0; assert(decl->type == D_DECL); /* pre-condition */ @@ -145,18 +143,7 @@ Expr *decl_postfix_function(Expr *decl, Expr *param_decls) #endif t->tag = T_FUNCTION; - foreach (param_decl, param_decls) { - n++; - } - t->val.function.num_params = n; - t->val.function.param_types = newArray(Type*, n); - n = 0; - foreach (param_decl, param_decls) { - assert(param_decl->type == D_DECL); /* pre-condition */ - assert(param_decl->extra.e_decl); /* pre-condition */ - t->val.function.param_types[n] = param_decl->extra.e_decl->type; - n++; - } + t->val.function.param_decls = param_decls; t->parent = decl->extra.e_decl->type; decl->extra.e_decl->type = t; return decl; @@ -280,7 +267,7 @@ unsigned type_assignable(Type *t) static void gen_array_pointer(Type *t, enum type_tag last_tag, const char *prefix, const char *name) { int paren = last_tag == T_ARRAY || last_tag == T_FUNCTION; - int i; + Expr *pd; switch (t->tag) { case T_POINTER: @@ -298,10 +285,11 @@ static void gen_array_pointer(Type *t, enum type_tag last_tag, const char *prefi case T_FUNCTION: gen_array_pointer(t->parent, t->tag, prefix, name); gen_code("("); - for (i = 0; i < t->val.function.num_params; i++) { - if (i > 0) + foreach (pd, t->val.function.param_decls) { + Var *var = pd->extra.e_decl; + gen_type(var->type, "", var->name); + if (pd->next) gen_code (", "); - gen_type(t->val.function.param_types[i], 0, 0); } gen_code(")"); break; @@ -315,6 +303,12 @@ static void gen_array_pointer(Type *t, enum type_tag last_tag, const char *prefi void gen_type(Type *t, const char *prefix, const char *name) { Type *bt = base_type(t); + Type *saved_bt = bt; + + /* DIRTY HACK: overwrite parent pointer so we can use this for the return type of a function */ + if (bt->tag == T_FUNCTION) { + bt = t->parent = bt->parent; + } switch (bt->tag) { case T_EVFLAG: @@ -333,4 +327,6 @@ void gen_type(Type *t, const char *prefix, const char *name) assert(impossible); } gen_array_pointer(bt->parent, T_NONE, prefix, name); + /* DIRTY HACK: restore parent pointer */ + t->parent = saved_bt; } diff --git a/src/snc/var_types.h b/src/snc/var_types.h index 1f2188b0d7b24b87dc111800ff2c1dfa7580c0fa..ba48b05f7662feb6f43bfbfacc14260d68e37812 100644 --- a/src/snc/var_types.h +++ b/src/snc/var_types.h @@ -28,7 +28,7 @@ enum foreign_type_tag { }; struct array_type { - unsigned num_elems; + unsigned num_elems; struct type *elem_type; }; @@ -37,8 +37,7 @@ struct pointer_type { }; struct function_type { - unsigned num_params; - struct type **param_types; /* array of pointers */ + struct expression *param_decls; struct type *return_type; }; diff --git a/test/compiler/Makefile b/test/compiler/Makefile index 99356d88bcc56898be61b6281a1d7864f414e8ba..a09e4d8883ef92515cd2ea092618a5981401a71a 100644 --- a/test/compiler/Makefile +++ b/test/compiler/Makefile @@ -20,6 +20,8 @@ TESTSCRIPTS_HOST += snc_test.t #TESTSCRIPTS_HOST += build64_test.t #TESTSCRIPTS_CROSS += build64_test.t +TESTPROD_HOST += funcdef + PROD_LIBS += seq pv PROD_LIBS += $(EPICS_BASE_IOC_LIBS) diff --git a/test/compiler/funcdef.st b/test/compiler/funcdef.st new file mode 100644 index 0000000000000000000000000000000000000000..97d17b504d35b648c705e2622f476438330b0d84 --- /dev/null +++ b/test/compiler/funcdef.st @@ -0,0 +1,33 @@ +program funcdefTest + +option +r; + +int x; +assign x; + +ss simple { + state simple { + when () { + void *p = f1(); + p = f2(p,0); + pvSetX(1.0); + } exit + } +} + +function void *f1(void) +{ + return 0; +} + +function void *f2(void *x, double **d) +{ + void *(*f)(void) = (void *(*)(void))x; + return f(); +} + +function void pvSetX(int val) +{ + pvPut(x,val); + printf("x=%d, val=%d\n", x, val); +}