📄 cslang.txt
字号:
This is because when the interpreter calls an intrinsic function, it passes values to the function by reference and not by value. For example, intrinsic with the declarations: int intrinsic_0 (void); int intrinsic_1 (char *s); void intrinsic_2 (char *s, int *i); void intrinsic_3 (int *i, double *d, double *e); are all valid. However, int invalid_1 (char *s, int len); is not valid since the len parameter is not a pointer. The return value of an intrinsic function must be one of the following types: void, char, short, int, long, double, char *, as well as unsigned versions of the integer types. A function such as int *invalid (void); is not permitted since int* is not a valid return-type for an intrin- sic function. Any other type of value can be passed back to the interpreter by explicitly pushing the object onto the interpreter's stack via the appropriate "push" function. The current implementation limits the number of arguments of an intrinsic function to 7. The "pop" functions can be used to allow the function to take an arbitrary number as seen from an interpreter script. Another restriction is that the intrinsic function should regard all its parameters as pointers to constant objects and make no attempt to modify the value to which they point. For example, void truncate (char *s) { s[0] = 0; } is illegal since the function modifies the string s. 3.3.2. Adding a New Intrinsic There are two basic mechanisms for adding an intrinsic function to the interpreter: SLadd_intrinsic_function and SLadd_intrin_fun_table. Functions may be added to a specified namespace via SLns_add_intrinsic_function and SLns_add_intrin_fun_table functions. As an specific example, consider a function that will cause the program to exit via the exit C library function. It is not possible to make this function an intrinsic because it does not meet the specifications for an intrinsic function that were described earlier. However, one can call exit from a function that is suitable, e.g., void intrin_exit (int *code) { exit (*code); } This function may be made available to the interpreter as an intrinsic via the SLadd_intrinsic_function routine: if (-1 == SLadd_intrinsic_function ("exit", (FVOID_STAR) intrin_exit, SLANG_VOID_TYPE, 1, SLANG_INT_TYPE)) exit (EXIT_FAILURE); This statement basically tells the interpreter that intrin_exit is a function that returns nothing and takes a single argument: a pointer to an integer (SLANG_INT_TYPE). A user can call this function from within the interpreter via message ("Calling the exit function"); exit (0); After printing a message, this will cause the intrin_exit function to execute, which in turn calls exit. The most convenient mechanism for adding new intrinsic functions is to create a table of SLang_Intrin_Fun_Type objects and add the table via the SLadd_intrin_fun_table function. The table will look like: SLang_Intrin_Fun_Type My_Intrinsics [] = { /* table entries */ MAKE_INTRINSIC_N(...), MAKE_INTRINSIC_N(...), . . MAKE_INTRINSIC_N(...), SLANG_END_INTRIN_FUN_TABLE }; Construction of the table entries may be facilitated using a set of MAKE_INTRINSIC macros defined in slang.h. The main macro is called MAKE_INTRINSIC_N and takes 11 arguments: MAKE_INTRINSIC_N(name, funct-ptr, return-type, num-args, arg-1-type, arg-2-type, ... arg-7-type) Here name is the name of the intrinsic function that the interpreter is to give to the function. func-ptr is a pointer to the intrinsic function taking num-args and returning ret-type. The final 7 argu- ments specifiy the argument types. For example, the intrin_exit intrinsic described above may be added to the table using MAKE_INTRINSIC_N("exit", intrin_exit, SLANG_VOID_TYPE, 1, SLANG_INT_TYPE, 0,0,0,0,0,0) While MAKE_INTRINSIC_N is the main macro for constructing table entries, slang.h defines other macros that may prove useful. In particular, an entry for the intrin_exit function may also be created using any of the following forms: MAKE_INTRINSIC_1("exit", intrin_exit, SLANG_VOID_TYPE, SLANG_INT_TYPE) MAKE_INTRINSIC_I("exit", intrin_exit, SLANG_VOID_TYPE) See slang.h for related macros. You are also encouraged to look at, e.g., slang/src/slstd.c for a more extensive examples. The table may be added via the SLadd_intrin_fun_table function, e.g., if (-1 == SLadd_intrin_fun_table (My_Intrinsics, NULL)) { /* an error occurred */ } Please note that there is no need to load a given table more than once, and it is considered to be an error on the part of the application it adds the same table multiple times. For performance reasons, no checking is performed by the library to see if a table has already been added. Earlier it was mentioned that intrinsics may be added to a specified namespace. To this end, one must first get a pointer to the namespace via the SLns_create_namespace function. The following example illustrates how this function is used to add the My_Intrinsics table to a namespace called my: SLang_NameSpace_Type *ns = SLns_create_namespace ("my"); if (ns == NULL) return -1; return SLns_add_intrin_fun_table (ns, My_Intrinsics, "__MY__")); 3.3.3. More Complicated Intrinsics The intrinsic functions described in the previous example were functions that took a fixed number of arguments. In this section we explore more complex intrinsics such as those that take a variable number of arguments. Consider a function that takes two double precision numbers and returns the lesser: double intrin_min (double *a, double *b) { if (*a < *b) return *a; return *b; } This function may be added to a table of intrinsics using MAKE_INTRINSIC_2("vmin", intrin_min, SLANG_DOUBLE_TYPE, SLANG_DOUBLE_TYPE, SLANG_DOUBLE_TYPE) It is useful to extend this function to take an arbitray number of arguments and return the lesser. Consider the following variant: double intrin_min_n (int *num_ptr) { double min_value, x; unsigned int num = (unsigned int) *num_ptr; if (-1 == SLang_pop_double (&min_value, NULL, NULL)) return 0.0; num--; while (num > 0) { num--; if (-1 == SLang_pop_double (&x, NULL, NULL)) return 0.0; if (x < min_value) min_value = x; } return min_value; } Here the number to compare is passed to the function and the actual numbers are removed from the stack via the SLang_pop_double function. A suitable table entry for it is MAKE_INTRINSIC_I("vmin", intrin_min_n, SLANG_DOUBLE_TYPE) This function would be used in an interpreter script via a statement such as variable xmin = vmin (x0, x1, x2, x3, x4, 5); which computes the smallest of 5 values. The problem with this intrinsic function is that the user must explicitly specify how many numbers to compare. It would be more convenient to simply use variable xmin = vmin (x0, x1, x2, x3, x4); An intrinsic function can query the value of the variable SLang_Num_Function_Args to obtain the necessary information: double intrin_min (void) { double min_value, x; unsigned int num = SLang_Num_Function_Args; if (-1 == SLang_pop_double (&min_value, NULL, NULL)) return 0.0; num--; while (num > 0) { num--; if (-1 == SLang_pop_double (&x, NULL, NULL)) return 0.0; if (x < min_value) min_value = x; } return min_value; } This may be declared as an intrinsic using: MAKE_INTRINSIC_0("vmin", intrin_min, SLANG_DOUBLE_TYPE) 3.4. Intrinsic Variables It is possible to access an application's global variables from within the interpreter. The current implementation supports the access of variables of type int, char *, and double. There are two basic methods of making an intrinsic variable available to the interpreter. The most straight forward method is to use the function SLadd_intrinsic_variable: int SLadd_intrinsic_variable (char *name, VOID_STAR addr, unsigned char data_type, int read_only); For example, suppose that I is an integer variable, e.g., int I; One can make it known to the interpreter as I_Variable via a statement such as if (-1 == SLadd_intrinsic_variable ("I_Variable", &I, SLANG_INT_TYPE, 0)) exit (EXIT_FAILURE); Similarly, if S is declared as char *S; then if (-1 == SLadd_intrinsic_variable ("S_Variable", &S, SLANG_STRING_TYPE, 1)) exit (EXIT_FAILURE); makes S available as a read-only variable with the name S_Variable. Note that if a pointer variable is made available to the interpreter, its value is managed by the interpreter and not the application. For this reason, it is recommended that such variables be declared as read-only. It is important to note that if S were declared as an array of characters, e.g., char S[256]; then it would not be possible to make it directly available to the interpreter. However, one could create a pointer to it, i.e., char *S_Ptr = S; and make S_Ptr available as a read-only variable. One should not make the mistake of trying to use the same address for different variables as the following example illustrates: int do_not_try_this (void) { static char *names[3] = {"larry", "curly", "moe"}; unsigned int i; for (i = 0; i < 3; i++) { int value; if (-1 == SLadd_intrinsic_variable (names[i], (VOID_STAR) &value, SLANG_INT_TYPE, 1)) return -1; } return 0; } Not only does this piece of code create intrinsic variables that use the same address, it also uses the address of a local variable that will go out of scope. The most convenient method for adding many intrinsic variables to the interpreter is to create an array of SLang_Intrin_Var_Type objects and then add the array via SLadd_intrin_var_table. For example, the array
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -