// syntax: syn3 import "tcshTypes"; //---------------- How function defines are handled... -------------------- // // Params come in through the params array: // _params = %{ src=_params type%{ stringList } }; // // This declares all the proto's parameters, and initializes them: // (the returned value is a list of "lines" to go in the output code.) // defun synsh_define_init_params(params, parmno) { if $params then { // e.g., "int i" (@%{ synsh_var_decl{ type%{ $params's first's type } var%{ $params's first's name } } })'s code; // e.g., "i = params[$parmno]" (@%{ assign{ left{ synsh_var{ var%{ $params's first's name } } } right{ index{ left{ synsh_var{ var=_params } } right{ synsh_intval{ val%{$parmno's syn_num} } } } } } })'s code; synsh_define_init_params($params's rest, $parmno+1); }; }; defun synsh_define_paramstring(params) if $params then %{ syn_strcat{ syn_val= -param syn_valb{ syn_strcat{ syn_val{ shline_var{ syn_macval%{ $params's first's name } } } syn_valb%{ synsh_define_paramstring($params's rest) } } } } } else { " $argv:q"; }; // // origProto is the unmolested prototype from the original parsin. // proto is the type-evaled version which includes the mangled // name of the function and such. // def is the body of the function. // defun synsh_define2(origProto, proto, def) { // When someone "define"s a function in synsh-tcsh, // we need to do three things: // // One, generate a // macro for the function itself so that when it // is invoked, it does the right thing (which in // this case means calling the script recursively // with appropriate parameters, and then retreiving // the return value). // Two, generate variable declarations so when the // function parameters are accessed, they refer to // the right thing. // Three, generate the actual body of the function. // // The macro for the function should look something like // this (using factorial as our example): // // macro int fact(int x) // { // code:$0 -jump fact_int -param %(x) $argv:q;; // code:if ( $status ) exit $status;; // int(_rv()); // }; // // Below is the code to build and exec this macro, which // is mostly just copy-pasted from a parsed version of // the above example macro: // //println("DEFINE:", $proto's fullname, "using", synsh_define_paramstring($proto's params)); //println("PARAMS:", $proto's params); @%{ synsh_macro{ proto{ $origProto } def{ synsh_exprs{ first{ code{ line{ syn_strcat{ syn_val=$0 -jump syn_valb{ syn_strcat{ syn_val%{ $proto's fullname } syn_valb%{ synsh_define_paramstring($proto's params) } } } } } } } rest{ first{ code{ line=if ( $status ) exit $status } } rest{ first{ // This is a cast of the return val to the appropriate type: synsh_funcall{ func%{ $proto's type's src } params{ first{ synsh_funcall{ func=_rv } } } } } } } } } } }; // This is how the body of a function is put together: code: { line: %strcat($proto's fullname, ":"); // The label for the function. line: "INDENT"; synsh_define_init_params($proto's params, 0); // Note def's evaluation has been postponed until here, since it // may depend on the macro created above by the prototype: (@$def)'s code; // The code of the body. // And a check of the exit value to make sure we're not falling // off the end of a non-void function: // println("RETURN type of", $proto's fullname, "is", $proto's type's src); if $proto's type's src == "void" then { line: "exit 0"; } else { line: %strcat("echo ERROR: no value returned from ", $proto's fullname); line: "exit 1"; }; line: "OUTDENT"; }; }; macro synsh_define synsh_define2('$&proto, synsh_fix_proto('$&proto), '$&def);