Author Topic: Script BASIC Extension Module  (Read 21364 times)

Offline John

  • Forum Support / SB Dev
  • Posts: 3510
    • ScriptBasic Open Source Project
Re: Script BASIC Extension Module
« Reply #15 on: April 29, 2015, 08:58:41 PM »
I got it to work by defining ArgData[8] to be the maximum allowable passed arguments to the embedded script. I still don't have string passing working or returning the results of the embed script thread.

My thoughts are why the besNEWARRAY may be partially working but causing the seg fault on exit is it must need the code I remarked out as gcc bitched that NULL wasn't legal syntax with the structure presented. I also don't think I need the besRELEASE(ArgData); any longer as that DIM'ed structure variable is freed on exit. (I'm assuming based on AIR's code)

Code: C
  1. besFUNCTION(SB_CallSubArgs)
  2.   DIM AS VARIABLE Argument;
  3.   DIM AS SbData ArgData[8];
  4.   DIM AS SbData FunctionResult;
  5.   DIM AS unsigned long sbobj;
  6.   DIM AS int i, slen, fnsn;
  7.   DIM AS char *strval;
  8.  
  9.   Argument = besARGUMENT(1);
  10.   besDEREFERENCE(Argument);
  11.   sbobj = LONGVALUE(Argument);
  12.  
  13.   Argument = besARGUMENT(2);
  14.   besDEREFERENCE(Argument);
  15.   fnsn = LONGVALUE(Argument);
  16.  
  17.   // ArgData = besNEWARRAY(0,besARGNR-3);
  18.   for( i=3 ; i <= (unsigned)besARGNR ; i++ ){
  19.     Argument = besARGUMENT(i);
  20.     besDEREFERENCE(Argument);
  21.     switch( slen=TYPE(Argument) ){
  22.       case VTYPE_LONG:
  23.         ArgData[i-3].type = SBT_LONG;
  24.         ArgData[i-3].size = 0;
  25.         ArgData[i-3].v.l = LONGVALUE(Argument);
  26.         break;
  27.       case VTYPE_DOUBLE:
  28.         ArgData[i-3].type = SBT_DOUBLE;
  29.         ArgData[i-3].size = 0;
  30.         ArgData[i-3].v.d = DOUBLEVALUE(Argument);
  31.         break;
  32.       case VTYPE_STRING:
  33.         memcpy(&strval,STRINGVALUE(Argument),sizeof(strval));
  34.         ArgData[i-3].type = SBT_STRING;
  35.         ArgData[i-3].size = strlen(strval);
  36.         ArgData[i-3].v.s = strval;
  37.         break;
  38.     }
  39.   }
  40.   scriba_CallArgEx(sbobj, fnsn, &FunctionResult, besARGNR-2, ArgData);
  41. /*
  42.   for( i=3 ; i <= (unsigned)besARGNR ; i++ ){
  43.      ArgData[i-3] = NULL;
  44.      }
  45. */
  46.   besRELEASE(ArgData);
  47. //  besRELEASE(FunctionResult);
  48.   besRETURNVALUE = NULL;
  49. besEND
  50.  

Code: ScriptBasic
  1. DECLARE SUB SB_New ALIAS "SB_New" LIB "sbt"
  2. DECLARE SUB SB_Configure ALIAS "SB_Configure" LIB "sbt"
  3. DECLARE SUB SB_Load ALIAS "SB_Load" LIB "sbt"
  4. DECLARE SUB SB_Run ALIAS "SB_Run" LIB "sbt"
  5. DECLARE SUB SB_NoRun ALIAS "SB_NoRun" LIB "sbt"
  6. DECLARE SUB SB_GetInt ALIAS "SB_GetInt" LIB "sbt"
  7. DECLARE SUB SB_GetDbl ALIAS "SB_GetDbl" LIB "sbt"
  8. DECLARE SUB SB_GetStr ALIAS "SB_GetStr" LIB "sbt"
  9. DECLARE SUB SB_SetInt ALIAS "SB_SetInt" LIB "sbt"
  10. DECLARE SUB SB_SetDbl ALIAS "SB_SetDbl" LIB "sbt"
  11. DECLARE SUB SB_SetStr ALIAS "SB_SetStr" LIB "sbt"
  12. DECLARE SUB SB_Address ALIAS "SB_Address" LIB "sbt"
  13. DECLARE SUB SB_CallSub ALIAS "SB_CallSub" LIB "sbt"
  14. DECLARE SUB SB_CallSubArgs ALIAS "SB_CallSubArgs" LIB "sbt"
  15. DECLARE SUB SB_Destroy ALIAS "SB_Destroy" LIB "sbt"
  16.  
  17. sb = SB_New()
  18. SB_Configure sb, "/etc/scriba/basic.conf"
  19. SB_Load sb, "prtvars.sb"
  20. SB_NoRun sb
  21. fnsn = SB_Address(sb, "main::prtvars")
  22. SB_CallSubArgs(sb, fnsn, 123, 1.23)
  23. SB_Destroy sb
  24.  

prtvars.sb
Code: ScriptBasic
  1. SUB prtvars(a, b)
  2.   PRINT a,"\n"
  3.   PRINT FORMAT("%g\n", b)
  4. END SUB
  5.  


jrs@laptop:~/sb/sb22/sbt$ scriba testicall.sb
123
1.23
jrs@laptop:~/sb/sb22/sbt$


@Charles - Now I'm getting the complaint by gcc about structure mismatch when I call besRELEASE(FunctionResult);.
« Last Edit: April 29, 2015, 09:30:45 PM by John »

Offline John

  • Forum Support / SB Dev
  • Posts: 3510
    • ScriptBasic Open Source Project
Re: Script BASIC Extension Module
« Reply #16 on: April 29, 2015, 09:56:20 PM »
I cleaned up the code and SB_CallSubArgs can return a script function return value. It's currently hard coded to a LONG type value response but it works in the below example.

Quote from: Peter Verhas - scriba_CallArgEx Documentation
This is the most sophisticated function of the ones that call a ScriptBasic subroutine. This function is capable handling parameters to scriba subroutines, and returning the modified argument variables and the return value.

interface.c
Code: C
  1. besFUNCTION(SB_CallSubArgs)
  2.   DIM AS VARIABLE Argument;
  3.   DIM AS SbData ArgData[8];
  4.   DIM AS SbData FunctionResult;
  5.   DIM AS unsigned long sbobj;
  6.   DIM AS int i, slen, fnsn;
  7.   DIM AS char *strval;
  8.  
  9.   Argument = besARGUMENT(1);
  10.   besDEREFERENCE(Argument);
  11.   sbobj = LONGVALUE(Argument);
  12.  
  13.   Argument = besARGUMENT(2);
  14.   besDEREFERENCE(Argument);
  15.   fnsn = LONGVALUE(Argument);
  16.  
  17.   for( i=3 ; i <= (unsigned)besARGNR ; i++ ){
  18.     Argument = besARGUMENT(i);
  19.     besDEREFERENCE(Argument);
  20.     switch( slen=TYPE(Argument) ){
  21.       case VTYPE_LONG:
  22.         ArgData[i-3].type = SBT_LONG;
  23.         ArgData[i-3].size = 0;
  24.         ArgData[i-3].v.l = LONGVALUE(Argument);
  25.         break;
  26.       case VTYPE_DOUBLE:
  27.         ArgData[i-3].type = SBT_DOUBLE;
  28.         ArgData[i-3].size = 0;
  29.         ArgData[i-3].v.d = DOUBLEVALUE(Argument);
  30.         break;
  31.       case VTYPE_STRING:
  32.         memcpy(&strval,STRINGVALUE(Argument),sizeof(strval));
  33.         ArgData[i-3].type = SBT_STRING;
  34.         ArgData[i-3].size = strlen(strval);
  35.         ArgData[i-3].v.s = strval;
  36.         break;
  37.     }
  38.   }
  39.   scriba_CallArgEx(sbobj, fnsn, &FunctionResult, besARGNR-2, ArgData);
  40.   besRETURN_LONG(FunctionResult.v.l);
  41. besEND
  42.  

testicall.sb
Code: ScriptBasic
  1. DECLARE SUB SB_New ALIAS "SB_New" LIB "sbt"
  2. DECLARE SUB SB_Configure ALIAS "SB_Configure" LIB "sbt"
  3. DECLARE SUB SB_Load ALIAS "SB_Load" LIB "sbt"
  4. DECLARE SUB SB_Run ALIAS "SB_Run" LIB "sbt"
  5. DECLARE SUB SB_NoRun ALIAS "SB_NoRun" LIB "sbt"
  6. DECLARE SUB SB_GetInt ALIAS "SB_GetInt" LIB "sbt"
  7. DECLARE SUB SB_GetDbl ALIAS "SB_GetDbl" LIB "sbt"
  8. DECLARE SUB SB_GetStr ALIAS "SB_GetStr" LIB "sbt"
  9. DECLARE SUB SB_SetInt ALIAS "SB_SetInt" LIB "sbt"
  10. DECLARE SUB SB_SetDbl ALIAS "SB_SetDbl" LIB "sbt"
  11. DECLARE SUB SB_SetStr ALIAS "SB_SetStr" LIB "sbt"
  12. DECLARE SUB SB_Address ALIAS "SB_Address" LIB "sbt"
  13. DECLARE SUB SB_CallSub ALIAS "SB_CallSub" LIB "sbt"
  14. DECLARE SUB SB_CallSubArgs ALIAS "SB_CallSubArgs" LIB "sbt"
  15. DECLARE SUB SB_Destroy ALIAS "SB_Destroy" LIB "sbt"
  16.  
  17. sb = SB_New()
  18. SB_Configure sb, "/etc/scriba/basic.conf"
  19. SB_Load sb, "prtvars.sb"
  20. SB_NoRun sb
  21. fnsn = SB_Address(sb, "main::prtvars")
  22. script_rtn = SB_CallSubArgs(sb, fnsn, 123, 1.23)
  23. PRINT script_rtn,"\n"
  24. SB_Destroy sb
  25.  

prtvars.sb
Code: ScriptBasic
  1. FUNCTION prtvars(a, b)
  2.   PRINT a,"\n"
  3.   PRINT FORMAT("%g\n", b)
  4.   prtvars = 99
  5. END FUNCTION
  6.  

Output

jrs@laptop:~/sb/sb22/sbt$ time scriba testicall.sb
123
1.23
99

real   0m0.006s
user   0m0.005s
sys   0m0.000s
jrs@laptop:~/sb/sb22/sbt$

« Last Edit: April 29, 2015, 10:06:37 PM by John »

Offline AIR

  • BASIC Developer
  • Posts: 932
  • Coder
Re: Script BASIC Extension Module
« Reply #17 on: April 30, 2015, 04:52:45 PM »
It seems you edited the post that had the code that segfaulted on strings.

Based on what you have left, I think your issue centers around the memcpy of the string.  The way you have it, you're overwriting memory.

Try this trivial example:

Code: C
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <stdlib.h>
  4.  
  5.  
  6. int main(int argc, char **argv) {
  7.         char tst[] = "My Test";
  8.         char *strval;
  9.        
  10.         strval = (char*)calloc(strlen(tst)+1,sizeof(char));
  11.         memcpy(strval,tst,strlen(tst));
  12.        
  13.         printf("%s\n",tst);
  14.         printf("%s\n",strval);
  15.         free(strval);
  16.         return 0;
  17.  
  18. }

You should see "My Test" printed twice.

Now comment out the "calloc" and "free" lines.

You should segfault.

So what I'm thinking is that while you created a char*, you haven't allocated any space for it.

An alternative to using "calloc" and "memcpy" is "asprintf".

It's considered a GNU extension, so you may have to place "#define _GNU_SOURCE" before including the stdio.h header.  You would use it like this:

Code: [Select]
asprintf(&strval,"%s",tst);
I vaguely recall there being a BES macro to allocate a string.  If there is, that would be the way to go for consistency's sake.

A.

Offline John

  • Forum Support / SB Dev
  • Posts: 3510
    • ScriptBasic Open Source Project
Re: Script BASIC Extension Module
« Reply #18 on: April 30, 2015, 05:32:29 PM »
Just as you said.


jrs@laptop:~/AIR/SB$ gcc tststr.c -o tststr
jrs@laptop:~/AIR/SB$ ./tststr
My Test
My Test
jrs@laptop:~/AIR/SB$ echo Now comment out the "calloc" and "free" lines.
Now comment out the calloc and free lines.
jrs@laptop:~/AIR/SB$ gcc tststr.c -o tststr
jrs@laptop:~/AIR/SB$ ./tststr
Segmentation fault (core dumped)
jrs@laptop:~/AIR/SB$


Quote
I vaguely recall there being a BES macro to allocate a string.  If there is, that would be the way to go for consistency's sake.

I just posted this on O2 in reference to that thought.

Quote from: john - O2
Since these embedding routines (scriba_NewSbString, ...) return a pSbData pointer, maybe I should define my ArgData array structure as pSbData rather than SbData like I'm doing now. I'm also thinking to maintain thread safe management of all SB variables, the scriba_NewSbLong, scriba_NewSbDouble, scriba_NewSbString and scriba_DestroySbData routines should be used and not  assign the ArgData[0]v.* structure element directly. (Read-Only)

At this point I'm trying to understand why these functions exist and when they should be used. I have to believe Peter created them to ease the pain of what is under the SB API (macro/define) covers and gluing it all together.

Offline AIR

  • BASIC Developer
  • Posts: 932
  • Coder
Re: Script BASIC Extension Module
« Reply #19 on: April 30, 2015, 05:37:57 PM »
So in your code, you should start by allocating the required space for the variable.

Offline John

  • Forum Support / SB Dev
  • Posts: 3510
    • ScriptBasic Open Source Project
Re: Script BASIC Extension Module
« Reply #20 on: April 30, 2015, 06:36:24 PM »
@AIR - It looks like Charles has made progress with using the SB functions to create the ArgData structure scriba_CallArgEx needs.

O2 SBT Thread reference

It seems reopening the All BASIC forum got noticed.  :)
« Last Edit: April 30, 2015, 08:00:21 PM by John »

Offline John

  • Forum Support / SB Dev
  • Posts: 3510
    • ScriptBasic Open Source Project
Re: Script BASIC Extension Module
« Reply #21 on: April 30, 2015, 11:27:06 PM »
I was able to get it working based on Charles's example and a bit of fiddling in C. I'm unable to return a generic type. (hard coded to LONG) I still think this could be tightened up further by getting the ArgData structure from the wrapper arguments.

interface.c
Code: C
  1. // Script BASIC Extension Module
  2.  
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include <ctype.h>
  7. #include <math.h>
  8. #include <time.h>
  9. #include "../../basext.h"
  10. #include "../../scriba.h"
  11. #include "cbasic.h"
  12.  
  13.  
  14. /****************************
  15.  Extension Module Functions
  16. ****************************/
  17.  
  18. besVERSION_NEGOTIATE
  19.   RETURN_FUNCTION((int)INTERFACE_VERSION);
  20. besEND
  21.  
  22. besSUB_START
  23.   DIM AS long PTR p;
  24.   besMODULEPOINTER = besALLOC(sizeof(long));
  25.   IF (besMODULEPOINTER EQ NULL) THEN_DO RETURN_FUNCTION(0);
  26.   p = (long PTR)besMODULEPOINTER;
  27.   RETURN_FUNCTION(0);
  28. besEND
  29.  
  30. besSUB_FINISH
  31.   DIM AS long PTR p;
  32.   p = (long PTR)besMODULEPOINTER;
  33.   IF (p EQ NULL) THEN_DO RETURN_FUNCTION(0);
  34.   RETURN_FUNCTION(0);
  35. besEND
  36.  
  37.  
  38. /**********************
  39.  Script BASIC Instance
  40. **********************/
  41.  
  42. besFUNCTION(SB_New)
  43.   DIM AS pSbProgram pProgram;
  44.   pProgram = scriba_new(malloc,free);
  45.   besRETURN_LONG(pProgram);
  46. besEND
  47.  
  48. besFUNCTION(SB_Configure)
  49.   DIM AS int sbobj;
  50.   DIM AS char PTR cfgfilename;
  51.   DIM AS int rtnval = -1;
  52.   besARGUMENTS("iz")
  53.     AT sbobj, AT cfgfilename
  54.   besARGEND
  55.   rtnval = scriba_LoadConfiguration(sbobj, cfgfilename);
  56.   besRETURN_LONG(rtnval);
  57. besEND
  58.  
  59. besFUNCTION(SB_Load)
  60.   DIM AS int sbobj;
  61.   DIM AS char PTR sbfilename;
  62.   DIM AS int rtnval = -1;
  63.   besARGUMENTS("iz")
  64.     AT sbobj, AT sbfilename
  65.   besARGEND
  66.   rtnval = scriba_SetFileName(sbobj, sbfilename);
  67.   scriba_LoadSourceProgram(sbobj);
  68.   besRETURN_LONG(rtnval);
  69. besEND
  70.  
  71. besFUNCTION(SB_LoadStr)
  72.   DIM AS int sbobj;
  73.   DIM AS char PTR sbpgm;
  74.   DIM AS int rtnval = -1;
  75.   besARGUMENTS("iz")
  76.     AT sbobj, AT sbpgm
  77.   besARGEND
  78.   scriba_SetFileName(sbobj, "fake");
  79.   rtnval = scriba_LoadProgramString(sbobj, sbpgm, strlen(sbpgm));
  80.   besRETURN_LONG(rtnval);
  81. besEND
  82.  
  83. besFUNCTION(SB_Run)
  84.   DIM AS int sbobj, rtnval = -1;
  85.   DIM AS char PTR sbcmdline;
  86.   besARGUMENTS("iz")
  87.     AT sbobj, AT sbcmdline
  88.   besARGEND
  89.   IF (besARGNR < 2) THEN_DO sbcmdline = "";
  90.   rtnval = scriba_Run(sbobj, sbcmdline);
  91.   besRETURN_LONG(rtnval);
  92. besEND
  93.  
  94. besFUNCTION(SB_NoRun)
  95.   DIM AS int sbobj, rtnval = -1;
  96.   besARGUMENTS("i")
  97.     AT sbobj
  98.   besARGEND
  99.   rtnval = scriba_NoRun(sbobj);
  100.   besRETURN_LONG(rtnval);
  101. besEND
  102.  
  103. besFUNCTION(SB_Destroy)
  104.   DIM AS int sbobj;
  105.   besARGUMENTS("i")
  106.     AT sbobj
  107.   besARGEND
  108.   scriba_destroy(sbobj);
  109.   RETURN_FUNCTION(0);
  110. besEND
  111.  
  112. besFUNCTION(SB_CallSub)
  113.   DIM AS int sbobj, funcsernum;
  114.   DIM AS char PTR funcname;
  115.   besARGUMENTS("iz")
  116.     AT sbobj, AT funcname
  117.   besARGEND
  118.   funcsernum = scriba_LookupFunctionByName(sbobj, funcname);
  119.   besRETURN_LONG(scriba_Call(sbobj, funcsernum));
  120. besEND
  121.  
  122. besFUNCTION(SB_Address)
  123.   DIM AS int sbobj, funcsernum;
  124.   DIM AS char PTR funcname;
  125.   besARGUMENTS("iz")
  126.     AT sbobj, AT funcname
  127.   besARGEND
  128.   funcsernum = scriba_LookupFunctionByName(sbobj, funcname);
  129.   besRETURN_LONG(funcsernum);
  130. besEND
  131.  
  132. besFUNCTION(SB_CallSubArgs)
  133.   DIM AS VARIABLE Argument;
  134.   DIM AS SbData ArgData[8];
  135.   DIM AS SbData FunctionResult;
  136.   DIM AS unsigned long sbobj;
  137.   DIM AS int i, slen, fnsn;
  138.  
  139.   Argument = besARGUMENT(1);
  140.   besDEREFERENCE(Argument);
  141.   sbobj = LONGVALUE(Argument);
  142.  
  143.   Argument = besARGUMENT(2);
  144.   besDEREFERENCE(Argument);
  145.   fnsn = LONGVALUE(Argument);
  146.  
  147.   for( i=3 ; i <= (unsigned)besARGNR ; i++ ){
  148.     Argument = besARGUMENT(i);
  149.     besDEREFERENCE(Argument);
  150.     switch( slen=TYPE(Argument) ){
  151.       case VTYPE_LONG:
  152.         ArgData[i-3] = *scriba_NewSbLong(sbobj, LONGVALUE(Argument));
  153.         break;
  154.       case VTYPE_DOUBLE:
  155.         ArgData[i-3] = *scriba_NewSbDouble(sbobj, DOUBLEVALUE(Argument));
  156.         break;
  157.       case VTYPE_STRING:
  158.         ArgData[i-3] = *scriba_NewSbString(sbobj, STRINGVALUE(Argument));
  159.         break;
  160.     }
  161.   }
  162.   scriba_CallArgEx(sbobj, fnsn, &FunctionResult, besARGNR-2, &ArgData);
  163.   besRETURN_LONG(FunctionResult.v.l);
  164. besEND
  165.  
  166. besFUNCTION(SB_GetInt)
  167.   DIM AS pSbData varobj;
  168.   DIM AS int sbobj, vsn;
  169.   DIM AS char PTR varname;
  170.   besARGUMENTS("iz")
  171.     AT sbobj, AT varname
  172.   besARGEND
  173.   vsn = scriba_LookupVariableByName(sbobj, varname);
  174.   scriba_GetVariable(sbobj, vsn, &varobj);
  175.   besRETURN_LONG(varobj[0].v.l);
  176. besEND
  177.  
  178. besFUNCTION(SB_GetDbl)
  179.   DIM AS pSbData varobj;
  180.   DIM AS int sbobj, vsn;
  181.   DIM AS char PTR varname;
  182.   besARGUMENTS("iz")
  183.     AT sbobj, AT varname
  184.   besARGEND
  185.   vsn = scriba_LookupVariableByName(sbobj, varname);
  186.   scriba_GetVariable(sbobj, vsn, &varobj);
  187.   besRETURN_DOUBLE(varobj[0].v.d);
  188. besEND
  189.  
  190. besFUNCTION(SB_GetStr)
  191.   DIM AS pSbData varobj;
  192.   DIM AS int sbobj, vsn;
  193.   DIM AS char PTR varname;
  194.   besARGUMENTS("iz")
  195.     AT sbobj, AT varname
  196.   besARGEND
  197.   vsn = scriba_LookupVariableByName(sbobj, varname);
  198.   scriba_GetVariable(sbobj, vsn, &varobj);
  199.   besRETURN_STRING(varobj[0].v.s);
  200. besEND
  201.  
  202. besFUNCTION(SB_SetInt)
  203.   DIM AS VARIABLE Argument;
  204.   DIM AS pSbData varobj;
  205.   DIM AS int sbobj, vsn, usrval, i;
  206.   DIM AS char PTR varname;
  207.   IF (besARGNR < 3) THEN_DO RETURN_FUNCTION(EX_ERROR_TOO_FEW_ARGUMENTS);
  208.   DEF_FOR (i = 1 TO i <= 3 STEP INCR i)
  209.   BEGIN_FOR
  210.     Argument = besARGUMENT(i);
  211.     besDEREFERENCE(Argument);
  212.     IF (i EQ 1) THEN_DO sbobj = LONGVALUE(Argument);
  213.     IF (i EQ 2) THEN_DO varname = STRINGVALUE(Argument);
  214.     IF (i EQ 3) THEN_DO usrval = LONGVALUE(Argument);
  215.   NEXT
  216.   vsn = scriba_LookupVariableByName(sbobj, varname);
  217.   besRETURN_LONG(scriba_SetVariable(sbobj, vsn, SBT_LONG, usrval, 0, "", 0));
  218. besEND
  219.  
  220. besFUNCTION(SB_SetDbl)
  221.   DIM AS VARIABLE Argument;
  222.   DIM AS pSbData varobj;
  223.   DIM AS int sbobj, vsn, i;
  224.   DIM AS char PTR varname;
  225.   DIM AS double usrval;
  226.   IF (besARGNR < 3) THEN_DO RETURN_FUNCTION(EX_ERROR_TOO_FEW_ARGUMENTS);
  227.   DEF_FOR (i = 1 TO i <= 3 STEP INCR i)
  228.   BEGIN_FOR
  229.     Argument = besARGUMENT(i);
  230.     besDEREFERENCE(Argument);
  231.     IF (i EQ 1) THEN_DO sbobj = LONGVALUE(Argument);
  232.     IF (i EQ 2) THEN_DO varname = STRINGVALUE(Argument);
  233.     IF (i EQ 3) THEN_DO usrval = DOUBLEVALUE(Argument);
  234.   NEXT
  235.   vsn = scriba_LookupVariableByName(sbobj, varname);
  236.   besRETURN_LONG(scriba_SetVariable(sbobj, vsn,  SBT_DOUBLE, 0, usrval, "", 0));
  237. besEND
  238.  
  239. besFUNCTION(SB_SetStr)
  240.   DIM AS VARIABLE Argument;
  241.   DIM AS pSbData varobj;
  242.   DIM AS int sbobj, vsn, i;
  243.   DIM AS char PTR varname;
  244.   DIM AS char PTR usrval;
  245.   IF (besARGNR < 3) THEN_DO RETURN_FUNCTION(EX_ERROR_TOO_FEW_ARGUMENTS);
  246.   DEF_FOR (i = 1 TO i <= 3 STEP INCR i)
  247.   BEGIN_FOR
  248.     Argument = besARGUMENT(i);
  249.     besDEREFERENCE(Argument);
  250.     IF (i EQ 1) THEN_DO sbobj = LONGVALUE(Argument);
  251.     IF (i EQ 2) THEN_DO varname = STRINGVALUE(Argument);
  252.     IF (i EQ 3) THEN_DO usrval = STRINGVALUE(Argument);
  253.   NEXT
  254.   vsn = scriba_LookupVariableByName(sbobj, varname);
  255.   besRETURN_LONG(scriba_SetVariable(sbobj, vsn,  SBT_STRING, 0, 0, usrval, strlen(usrval)));
  256. besEND
  257.  
  258.  
  259. besFUNCTION(SB_ResetVars)
  260.   DIM AS int sbobj;
  261.   besARGUMENTS("i")
  262.     AT sbobj
  263.   besARGEND
  264.   scriba_ResetVariables(sbobj);
  265.   besRETURNVALUE = NULL;
  266. besEND
  267.  

testicall.sb
Code: ScriptBasic
  1. DECLARE SUB SB_New ALIAS "SB_New" LIB "sbt"
  2. DECLARE SUB SB_Configure ALIAS "SB_Configure" LIB "sbt"
  3. DECLARE SUB SB_Load ALIAS "SB_Load" LIB "sbt"
  4. DECLARE SUB SB_Run ALIAS "SB_Run" LIB "sbt"
  5. DECLARE SUB SB_NoRun ALIAS "SB_NoRun" LIB "sbt"
  6. DECLARE SUB SB_GetInt ALIAS "SB_GetInt" LIB "sbt"
  7. DECLARE SUB SB_GetDbl ALIAS "SB_GetDbl" LIB "sbt"
  8. DECLARE SUB SB_GetStr ALIAS "SB_GetStr" LIB "sbt"
  9. DECLARE SUB SB_SetInt ALIAS "SB_SetInt" LIB "sbt"
  10. DECLARE SUB SB_SetDbl ALIAS "SB_SetDbl" LIB "sbt"
  11. DECLARE SUB SB_SetStr ALIAS "SB_SetStr" LIB "sbt"
  12. DECLARE SUB SB_Address ALIAS "SB_Address" LIB "sbt"
  13. DECLARE SUB SB_CallSub ALIAS "SB_CallSub" LIB "sbt"
  14. DECLARE SUB SB_CallSubArgs ALIAS "SB_CallSubArgs" LIB "sbt"
  15. DECLARE SUB SB_Destroy ALIAS "SB_Destroy" LIB "sbt"
  16.  
  17. sb = SB_New()
  18. SB_Configure sb, "/etc/scriba/basic.conf"
  19. SB_Load sb, "prtvars.sb"
  20. SB_NoRun sb
  21. fnsn = SB_Address(sb, "main::prtvars")
  22. script_rtn = SB_CallSubArgs(sb, fnsn, 123, 1.23, "One,Two,Three")
  23. PRINT script_rtn,"\n"
  24. SB_Destroy sb
  25.  

prtvars.sb
Code: ScriptBasic
  1. FUNCTION prtvars(a, b, c)
  2.   PRINT a,"\n"
  3.   PRINT FORMAT("%g\n", b)
  4.   PRINT c,"\n"
  5.   prtvars = 99
  6. END FUNCTION
  7.  

Output

jrs@laptop:~/sb/sb22/sbt$ time scriba testicall.sb
123
1.23
One,Two,Three
99

real   0m0.007s
user   0m0.007s
sys   0m0.000s
jrs@laptop:~/sb/sb22/sbt$

« Last Edit: May 01, 2015, 12:15:43 AM by John »

Offline John

  • Forum Support / SB Dev
  • Posts: 3510
    • ScriptBasic Open Source Project
Re: Script BASIC Extension Module
« Reply #22 on: May 01, 2015, 01:03:03 PM »
Success!

interface.c
Code: C
  1. // Script BASIC Extension Module
  2.  
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include <ctype.h>
  7. #include <math.h>
  8. #include <time.h>
  9. #include "../../basext.h"
  10. #include "../../scriba.h"
  11. #include "cbasic.h"
  12.  
  13.  
  14. /****************************
  15.  Extension Module Functions
  16. ****************************/
  17.  
  18. besVERSION_NEGOTIATE
  19.   RETURN_FUNCTION((int)INTERFACE_VERSION);
  20. besEND
  21.  
  22. besSUB_START
  23.   DIM AS long PTR p;
  24.   besMODULEPOINTER = besALLOC(sizeof(long));
  25.   IF (besMODULEPOINTER EQ NULL) THEN_DO RETURN_FUNCTION(0);
  26.   p = (long PTR)besMODULEPOINTER;
  27.   RETURN_FUNCTION(0);
  28. besEND
  29.  
  30. besSUB_FINISH
  31.   DIM AS long PTR p;
  32.   p = (long PTR)besMODULEPOINTER;
  33.   IF (p EQ NULL) THEN_DO RETURN_FUNCTION(0);
  34.   RETURN_FUNCTION(0);
  35. besEND
  36.  
  37.  
  38. /**********************
  39.  Script BASIC Instance
  40. **********************/
  41.  
  42. besFUNCTION(SB_New)
  43.   DIM AS pSbProgram sbobj;
  44.   sbobj = scriba_new(malloc,free);
  45.   besRETURN_LONG(sbobj);
  46. besEND
  47.  
  48. besFUNCTION(SB_Configure)
  49.   DIM AS unsigned long sbobj;
  50.   DIM AS char PTR cfgfilename;
  51.   DIM AS int rtnval = -1;
  52.   besARGUMENTS("iz")
  53.     AT sbobj, AT cfgfilename
  54.   besARGEND
  55.   rtnval = scriba_LoadConfiguration(sbobj, cfgfilename);
  56.   besRETURN_LONG(rtnval);
  57. besEND
  58.  
  59. besFUNCTION(SB_Load)
  60.   DIM AS unsigned long sbobj;
  61.   DIM AS char PTR sbfilename;
  62.   DIM AS int rtnval = -1;
  63.   besARGUMENTS("iz")
  64.     AT sbobj, AT sbfilename
  65.   besARGEND
  66.   rtnval = scriba_SetFileName(sbobj, sbfilename);
  67.   scriba_LoadSourceProgram(sbobj);
  68.   besRETURN_LONG(rtnval);
  69. besEND
  70.  
  71. besFUNCTION(SB_LoadStr)
  72.   DIM AS unsigned long sbobj;
  73.   DIM AS char PTR sbpgm;
  74.   DIM AS int rtnval = -1;
  75.   besARGUMENTS("iz")
  76.     AT sbobj, AT sbpgm
  77.   besARGEND
  78.   scriba_SetFileName(sbobj, "fake");
  79.   rtnval = scriba_LoadProgramString(sbobj, sbpgm, strlen(sbpgm));
  80.   besRETURN_LONG(rtnval);
  81. besEND
  82.  
  83. besFUNCTION(SB_Run)
  84.   DIM AS unsigned long sbobj;
  85.   DIM AS int rtnval;
  86.   DIM AS char PTR sbcmdline;
  87.   besARGUMENTS("iz")
  88.     AT sbobj, AT sbcmdline
  89.   besARGEND
  90.   IF (besARGNR < 2) THEN_DO sbcmdline = "";
  91.   rtnval = scriba_Run(sbobj, sbcmdline);
  92.   besRETURN_LONG(rtnval);
  93. besEND
  94.  
  95. besFUNCTION(SB_NoRun)
  96.   DIM AS unsigned long sbobj;
  97.   DIM AS int rtnval;
  98.   besARGUMENTS("i")
  99.     AT sbobj
  100.   besARGEND
  101.   rtnval = scriba_NoRun(sbobj);
  102.   besRETURN_LONG(rtnval);
  103. besEND
  104.  
  105. besFUNCTION(SB_Destroy)
  106.   DIM AS unsigned long sbobj;
  107.   besARGUMENTS("i")
  108.     AT sbobj
  109.   besARGEND
  110.   scriba_destroy(sbobj);
  111.   RETURN_FUNCTION(0);
  112. besEND
  113.  
  114. besFUNCTION(SB_CallSub)
  115.   DIM AS unsigned long sbobj;
  116.   DIM AS int funcsernum;
  117.   DIM AS char PTR funcname;
  118.   besARGUMENTS("iz")
  119.     AT sbobj, AT funcname
  120.   besARGEND
  121.   funcsernum = scriba_LookupFunctionByName(sbobj, funcname);
  122.   besRETURN_LONG(scriba_Call(sbobj, funcsernum));
  123. besEND
  124.  
  125. besFUNCTION(SB_Address)
  126.   DIM AS unsigned long sbobj;
  127.   DIM AS int funcsernum;
  128.   DIM AS char PTR funcname;
  129.   besARGUMENTS("iz")
  130.     AT sbobj, AT funcname
  131.   besARGEND
  132.   funcsernum = scriba_LookupFunctionByName(sbobj, funcname);
  133.   besRETURN_LONG(funcsernum);
  134. besEND
  135.  
  136. besFUNCTION(SB_CallSubArgs)
  137.   DIM AS VARIABLE Argument;
  138.   DIM AS SbData ArgData[8];
  139.   DIM AS SbData FunctionResult;
  140.   DIM AS unsigned long sbobj;
  141.   DIM AS int i, sbtype, fnsn;
  142.  
  143.   Argument = besARGUMENT(1);
  144.   besDEREFERENCE(Argument);
  145.   sbobj = LONGVALUE(Argument);
  146.  
  147.   Argument = besARGUMENT(2);
  148.   besDEREFERENCE(Argument);
  149.   fnsn = LONGVALUE(Argument);
  150.  
  151.   for( i=3 ; i <= (unsigned)besARGNR ; i++ ){
  152.     Argument = besARGUMENT(i);
  153.     besDEREFERENCE(Argument);
  154.     switch(sbtype = TYPE(Argument)){
  155.       case VTYPE_LONG:
  156.         ArgData[i-3] = *scriba_NewSbLong(sbobj, LONGVALUE(Argument));
  157.         break;
  158.       case VTYPE_DOUBLE:
  159.         ArgData[i-3] = *scriba_NewSbDouble(sbobj, DOUBLEVALUE(Argument));
  160.         break;
  161.       case VTYPE_STRING:
  162.         ArgData[i-3] = *scriba_NewSbString(sbobj, STRINGVALUE(Argument));
  163.         break;
  164.     }
  165.   }
  166.  
  167.   scriba_CallArgEx(sbobj, fnsn, &FunctionResult, besARGNR-2, &ArgData);
  168.  
  169.   switch (FunctionResult.type) {
  170.     case SBT_LONG   :
  171.       besRETURN_LONG(FunctionResult.v.l);
  172.       break ;
  173.     case SBT_DOUBLE :
  174.       besRETURN_DOUBLE(FunctionResult.v.d);
  175.       break ;
  176.     case SBT_STRING :
  177.       besRETURN_STRING(FunctionResult.v.s);
  178.       break ;
  179.   }
  180. besEND
  181.  
  182. besFUNCTION(SB_GetInt)
  183.   DIM AS pSbData varobj;
  184.   DIM AS unsigned long sbobj;
  185.   DIM AS int vsn;
  186.   DIM AS char PTR varname;
  187.   besARGUMENTS("iz")
  188.     AT sbobj, AT varname
  189.   besARGEND
  190.   vsn = scriba_LookupVariableByName(sbobj, varname);
  191.   scriba_GetVariable(sbobj, vsn, &varobj);
  192.   besRETURN_LONG(varobj[0].v.l);
  193. besEND
  194.  
  195. besFUNCTION(SB_GetDbl)
  196.   DIM AS pSbData varobj;
  197.   DIM AS unsigned long sbobj;
  198.   DIM AS int vsn;
  199.   DIM AS char PTR varname;
  200.   besARGUMENTS("iz")
  201.     AT sbobj, AT varname
  202.   besARGEND
  203.   vsn = scriba_LookupVariableByName(sbobj, varname);
  204.   scriba_GetVariable(sbobj, vsn, &varobj);
  205.   besRETURN_DOUBLE(varobj[0].v.d);
  206. besEND
  207.  
  208. besFUNCTION(SB_GetStr)
  209.   DIM AS pSbData varobj;
  210.   DIM AS unsigned long sbobj;
  211.   DIM AS int vsn;
  212.   DIM AS char PTR varname;
  213.   besARGUMENTS("iz")
  214.     AT sbobj, AT varname
  215.   besARGEND
  216.   vsn = scriba_LookupVariableByName(sbobj, varname);
  217.   scriba_GetVariable(sbobj, vsn, &varobj);
  218.   besRETURN_STRING(varobj[0].v.s);
  219. besEND
  220.  
  221. besFUNCTION(SB_SetInt)
  222.   DIM AS VARIABLE Argument;
  223.   DIM AS pSbData varobj;
  224.   DIM AS unsigned long sbobj;
  225.   DIM AS int vsn, usrval, i;
  226.   DIM AS char PTR varname;
  227.   IF (besARGNR < 3) THEN_DO RETURN_FUNCTION(EX_ERROR_TOO_FEW_ARGUMENTS);
  228.   DEF_FOR (i = 1 TO i <= 3 STEP INCR i)
  229.   BEGIN_FOR
  230.     Argument = besARGUMENT(i);
  231.     besDEREFERENCE(Argument);
  232.     IF (i EQ 1) THEN_DO sbobj = LONGVALUE(Argument);
  233.     IF (i EQ 2) THEN_DO varname = STRINGVALUE(Argument);
  234.     IF (i EQ 3) THEN_DO usrval = LONGVALUE(Argument);
  235.   NEXT
  236.   vsn = scriba_LookupVariableByName(sbobj, varname);
  237.   besRETURN_LONG(scriba_SetVariable(sbobj, vsn, SBT_LONG, usrval, 0, "", 0));
  238. besEND
  239.  
  240. besFUNCTION(SB_SetDbl)
  241.   DIM AS VARIABLE Argument;
  242.   DIM AS pSbData varobj;
  243.   DIM AS unsigned long sbobj;
  244.   DIM AS int vsn, i;
  245.   DIM AS char PTR varname;
  246.   DIM AS double usrval;
  247.   IF (besARGNR < 3) THEN_DO RETURN_FUNCTION(EX_ERROR_TOO_FEW_ARGUMENTS);
  248.   DEF_FOR (i = 1 TO i <= 3 STEP INCR i)
  249.   BEGIN_FOR
  250.     Argument = besARGUMENT(i);
  251.     besDEREFERENCE(Argument);
  252.     IF (i EQ 1) THEN_DO sbobj = LONGVALUE(Argument);
  253.     IF (i EQ 2) THEN_DO varname = STRINGVALUE(Argument);
  254.     IF (i EQ 3) THEN_DO usrval = DOUBLEVALUE(Argument);
  255.   NEXT
  256.   vsn = scriba_LookupVariableByName(sbobj, varname);
  257.   besRETURN_LONG(scriba_SetVariable(sbobj, vsn,  SBT_DOUBLE, 0, usrval, "", 0));
  258. besEND
  259.  
  260. besFUNCTION(SB_SetStr)
  261.   DIM AS VARIABLE Argument;
  262.   DIM AS pSbData varobj;
  263.   DIM AS unsigned long sbobj;
  264.   DIM AS int vsn, i;
  265.   DIM AS char PTR varname;
  266.   DIM AS char PTR usrval;
  267.   IF (besARGNR < 3) THEN_DO RETURN_FUNCTION(EX_ERROR_TOO_FEW_ARGUMENTS);
  268.   DEF_FOR (i = 1 TO i <= 3 STEP INCR i)
  269.   BEGIN_FOR
  270.     Argument = besARGUMENT(i);
  271.     besDEREFERENCE(Argument);
  272.     IF (i EQ 1) THEN_DO sbobj = LONGVALUE(Argument);
  273.     IF (i EQ 2) THEN_DO varname = STRINGVALUE(Argument);
  274.     IF (i EQ 3) THEN_DO usrval = STRINGVALUE(Argument);
  275.   NEXT
  276.   vsn = scriba_LookupVariableByName(sbobj, varname);
  277.   besRETURN_LONG(scriba_SetVariable(sbobj, vsn,  SBT_STRING, 0, 0, usrval, strlen(usrval)));
  278. besEND
  279.  
  280.  
  281. besFUNCTION(SB_ResetVars)
  282.   DIM AS unsigned long sbobj;
  283.   DIM AS int;
  284.   besARGUMENTS("i")
  285.     AT sbobj
  286.   besARGEND
  287.   scriba_ResetVariables(sbobj);
  288.   besRETURNVALUE = NULL;
  289. besEND
  290.  

testicall.sb
Code: ScriptBasic
  1. DECLARE SUB SB_New ALIAS "SB_New" LIB "sbt"
  2. DECLARE SUB SB_Configure ALIAS "SB_Configure" LIB "sbt"
  3. DECLARE SUB SB_Load ALIAS "SB_Load" LIB "sbt"
  4. DECLARE SUB SB_Run ALIAS "SB_Run" LIB "sbt"
  5. DECLARE SUB SB_NoRun ALIAS "SB_NoRun" LIB "sbt"
  6. DECLARE SUB SB_GetInt ALIAS "SB_GetInt" LIB "sbt"
  7. DECLARE SUB SB_GetDbl ALIAS "SB_GetDbl" LIB "sbt"
  8. DECLARE SUB SB_GetStr ALIAS "SB_GetStr" LIB "sbt"
  9. DECLARE SUB SB_SetInt ALIAS "SB_SetInt" LIB "sbt"
  10. DECLARE SUB SB_SetDbl ALIAS "SB_SetDbl" LIB "sbt"
  11. DECLARE SUB SB_SetStr ALIAS "SB_SetStr" LIB "sbt"
  12. DECLARE SUB SB_Address ALIAS "SB_Address" LIB "sbt"
  13. DECLARE SUB SB_CallSub ALIAS "SB_CallSub" LIB "sbt"
  14. DECLARE SUB SB_CallSubArgs ALIAS "SB_CallSubArgs" LIB "sbt"
  15. DECLARE SUB SB_Destroy ALIAS "SB_Destroy" LIB "sbt"
  16.  
  17. sb = SB_New()
  18. SB_Configure sb, "/etc/scriba/basic.conf"
  19. SB_Load sb, "prtvars.sb"
  20. SB_NoRun sb
  21. fnsn = SB_Address(sb, "main::prtvars")
  22. PRINT SB_CallSubArgs(sb, fnsn, 123, 1.23, "One,Two,Three"),"\n"
  23. SB_Destroy sb
  24.  

prtvars.sb
Code: ScriptBasic
  1. FUNCTION prtvars(a, b, c)
  2.   PRINT a,"\n"
  3.   PRINT FORMAT("%g\n", b)
  4.   PRINT c,"\n"
  5.   prtvars = "Too Cool!"
  6. END FUNCTION
  7.  

Output

jrs@laptop:~/sb/sb22/sbt$ time scriba testicall.sb
123
1.23
One,Two,Three
Too Cool!

real   0m0.007s
user   0m0.008s
sys   0m0.000s
jrs@laptop:~/sb/sb22/sbt$


Thanks Charles and AIR for your assistance in getting this working!
« Last Edit: May 01, 2015, 06:41:53 PM by John »

Offline John

  • Forum Support / SB Dev
  • Posts: 3510
    • ScriptBasic Open Source Project
Re: Script BASIC Extension Module
« Reply #23 on: May 01, 2015, 11:35:26 PM »
In this version I added undef support and condensed all the SB_Get(type) to SB_GetVar. Getting functions and subs no longer needs the SB_Address function and you can just use the function name. (main::prtvars) for example. I might try to condense the SB_Set(type) function as well.

interface.c
Code: C
  1. // Script BASIC Extension Module
  2.  
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include <ctype.h>
  7. #include <math.h>
  8. #include <time.h>
  9. #include "../../basext.h"
  10. #include "../../scriba.h"
  11. #include "cbasic.h"
  12.  
  13.  
  14. /****************************
  15.  Extension Module Functions
  16. ****************************/
  17.  
  18. besVERSION_NEGOTIATE
  19.   RETURN_FUNCTION((int)INTERFACE_VERSION);
  20. besEND
  21.  
  22. besSUB_START
  23.   DIM AS long PTR p;
  24.   besMODULEPOINTER = besALLOC(sizeof(long));
  25.   IF (besMODULEPOINTER EQ NULL) THEN_DO RETURN_FUNCTION(0);
  26.   p = (long PTR)besMODULEPOINTER;
  27.   RETURN_FUNCTION(0);
  28. besEND
  29.  
  30. besSUB_FINISH
  31.   DIM AS long PTR p;
  32.   p = (long PTR)besMODULEPOINTER;
  33.   IF (p EQ NULL) THEN_DO RETURN_FUNCTION(0);
  34.   RETURN_FUNCTION(0);
  35. besEND
  36.  
  37.  
  38. /**********************
  39.  Script BASIC Instance
  40. **********************/
  41.  
  42. besFUNCTION(SB_New)
  43.   DIM AS pSbProgram sbobj;
  44.   sbobj = scriba_new(malloc,free);
  45.   besRETURN_LONG(sbobj);
  46. besEND
  47.  
  48. besFUNCTION(SB_Configure)
  49.   DIM AS unsigned long sbobj;
  50.   DIM AS char PTR cfgfilename;
  51.   DIM AS int rtnval = -1;
  52.   besARGUMENTS("iz")
  53.     AT sbobj, AT cfgfilename
  54.   besARGEND
  55.   rtnval = scriba_LoadConfiguration(sbobj, cfgfilename);
  56.   besRETURN_LONG(rtnval);
  57. besEND
  58.  
  59. besFUNCTION(SB_Load)
  60.   DIM AS unsigned long sbobj;
  61.   DIM AS char PTR sbfilename;
  62.   DIM AS int rtnval = -1;
  63.   besARGUMENTS("iz")
  64.     AT sbobj, AT sbfilename
  65.   besARGEND
  66.   rtnval = scriba_SetFileName(sbobj, sbfilename);
  67.   scriba_LoadSourceProgram(sbobj);
  68.   besRETURN_LONG(rtnval);
  69. besEND
  70.  
  71. besFUNCTION(SB_LoadStr)
  72.   DIM AS unsigned long sbobj;
  73.   DIM AS char PTR sbpgm;
  74.   DIM AS int rtnval = -1;
  75.   besARGUMENTS("is")
  76.     AT sbobj, AT sbpgm
  77.   besARGEND
  78.   scriba_SetFileName(sbobj, "fake");
  79.   rtnval = scriba_LoadProgramString(sbobj, sbpgm, strlen(sbpgm));
  80.   besRETURN_LONG(rtnval);
  81. besEND
  82.  
  83. besFUNCTION(SB_Run)
  84.   DIM AS unsigned long sbobj;
  85.   DIM AS int rtnval;
  86.   DIM AS char PTR sbcmdline;
  87.   besARGUMENTS("iz")
  88.     AT sbobj, AT sbcmdline
  89.   besARGEND
  90.   IF (besARGNR < 2) THEN_DO sbcmdline = "";
  91.   rtnval = scriba_Run(sbobj, sbcmdline);
  92.   besRETURN_LONG(rtnval);
  93. besEND
  94.  
  95. besFUNCTION(SB_NoRun)
  96.   DIM AS unsigned long sbobj;
  97.   DIM AS int rtnval;
  98.   besARGUMENTS("i")
  99.     AT sbobj
  100.   besARGEND
  101.   rtnval = scriba_NoRun(sbobj);
  102.   besRETURN_LONG(rtnval);
  103. besEND
  104.  
  105. besFUNCTION(SB_Destroy)
  106.   DIM AS unsigned long sbobj;
  107.   besARGUMENTS("i")
  108.     AT sbobj
  109.   besARGEND
  110.   scriba_destroy(sbobj);
  111.   RETURN_FUNCTION(0);
  112. besEND
  113.  
  114. besFUNCTION(SB_CallSub)
  115.   DIM AS unsigned long sbobj;
  116.   DIM AS int funcsernum;
  117.   DIM AS char PTR funcname;
  118.   besARGUMENTS("iz")
  119.     AT sbobj, AT funcname
  120.   besARGEND
  121.   funcsernum = scriba_LookupFunctionByName(sbobj, funcname);
  122.   besRETURN_LONG(scriba_Call(sbobj, funcsernum));
  123. besEND
  124.  
  125. besFUNCTION(SB_Address)
  126.   DIM AS unsigned long sbobj;
  127.   DIM AS int funcsernum;
  128.   DIM AS char PTR funcname;
  129.   besARGUMENTS("iz")
  130.     AT sbobj, AT funcname
  131.   besARGEND
  132.   funcsernum = scriba_LookupFunctionByName(sbobj, funcname);
  133.   besRETURN_LONG(funcsernum);
  134. besEND
  135.  
  136. besFUNCTION(SB_CallSubArgs)
  137.   DIM AS VARIABLE Argument;
  138.   DIM AS SbData ArgData[8];
  139.   DIM AS SbData FunctionResult;
  140.   DIM AS unsigned long sbobj;
  141.   DIM AS char PTR funcname;
  142.   DIM AS int i, sbtype, fnsn;
  143.  
  144.   Argument = besARGUMENT(1);
  145.   besDEREFERENCE(Argument);
  146.   sbobj = LONGVALUE(Argument);
  147.  
  148.   Argument = besARGUMENT(2);
  149.   besDEREFERENCE(Argument);
  150.   funcname = STRINGVALUE(Argument);
  151.  
  152.   for( i=3 ; i <= (unsigned)besARGNR ; i++ ){
  153.     Argument = besARGUMENT(i);
  154.     besDEREFERENCE(Argument);
  155.     switch(sbtype = TYPE(Argument)){
  156.       case VTYPE_LONG:
  157.         ArgData[i-3] = *scriba_NewSbLong(sbobj, LONGVALUE(Argument));
  158.         break;
  159.       case VTYPE_DOUBLE:
  160.         ArgData[i-3] = *scriba_NewSbDouble(sbobj, DOUBLEVALUE(Argument));
  161.         break;
  162.       case VTYPE_STRING:
  163.         ArgData[i-3] = *scriba_NewSbString(sbobj, STRINGVALUE(Argument));
  164.         break;
  165.       default:
  166.         ArgData[i-3] = *scriba_NewSbUndef(sbobj);
  167.     }
  168.   }
  169.  
  170.   fnsn = scriba_LookupFunctionByName(sbobj, funcname);
  171.   scriba_CallArgEx(sbobj, fnsn, &FunctionResult, besARGNR-2, &ArgData);
  172.  
  173.   switch (FunctionResult.type) {
  174.     case SBT_LONG   :
  175.       besRETURN_LONG(FunctionResult.v.l);
  176.       break ;
  177.     case SBT_DOUBLE :
  178.       besRETURN_DOUBLE(FunctionResult.v.d);
  179.       break ;
  180.     case SBT_STRING :
  181.       besRETURN_STRING(FunctionResult.v.s);
  182.       break ;
  183.     case SBT_UNDEF  :
  184.       besRETURNVALUE = NULL;;
  185.       break ;
  186.   }
  187. besEND
  188.  
  189. besFUNCTION(SB_GetVar)
  190.   DIM AS pSbData varobj;
  191.   DIM AS unsigned long sbobj;
  192.   DIM AS int vsn;
  193.   DIM AS char PTR varname;
  194.   besARGUMENTS("iz")
  195.     AT sbobj, AT varname
  196.   besARGEND
  197.   vsn = scriba_LookupVariableByName(sbobj, varname);
  198.   scriba_GetVariable(sbobj, vsn, &varobj);
  199.   switch (scriba_GetVariableType(sbobj, vsn)) {
  200.     case SBT_LONG   :
  201.       besRETURN_LONG(varobj[0].v.l);
  202.       break ;
  203.     case SBT_DOUBLE :
  204.       besRETURN_DOUBLE(varobj[0].v.d);
  205.       break ;
  206.     case SBT_STRING :
  207.       besRETURN_STRING(varobj[0].v.s);
  208.       break ;
  209.     case SBT_UNDEF  :
  210.       besRETURNVALUE = NULL;;
  211.       break ;
  212.   }
  213. besEND
  214.  
  215. besFUNCTION(SB_SetInt)
  216.   DIM AS VARIABLE Argument;
  217.   DIM AS pSbData varobj;
  218.   DIM AS unsigned long sbobj;
  219.   DIM AS int vsn, usrval, i;
  220.   DIM AS char PTR varname;
  221.   IF (besARGNR < 3) THEN_DO RETURN_FUNCTION(EX_ERROR_TOO_FEW_ARGUMENTS);
  222.   DEF_FOR (i = 1 TO i <= 3 STEP INCR i)
  223.   BEGIN_FOR
  224.     Argument = besARGUMENT(i);
  225.     besDEREFERENCE(Argument);
  226.     IF (i EQ 1) THEN_DO sbobj = LONGVALUE(Argument);
  227.     IF (i EQ 2) THEN_DO varname = STRINGVALUE(Argument);
  228.     IF (i EQ 3) THEN_DO usrval = LONGVALUE(Argument);
  229.   NEXT
  230.   vsn = scriba_LookupVariableByName(sbobj, varname);
  231.   besRETURN_LONG(scriba_SetVariable(sbobj, vsn, SBT_LONG, usrval, 0, "", 0));
  232. besEND
  233.  
  234. besFUNCTION(SB_SetDbl)
  235.   DIM AS VARIABLE Argument;
  236.   DIM AS pSbData varobj;
  237.   DIM AS unsigned long sbobj;
  238.   DIM AS int vsn, i;
  239.   DIM AS char PTR varname;
  240.   DIM AS double usrval;
  241.   IF (besARGNR < 3) THEN_DO RETURN_FUNCTION(EX_ERROR_TOO_FEW_ARGUMENTS);
  242.   DEF_FOR (i = 1 TO i <= 3 STEP INCR i)
  243.   BEGIN_FOR
  244.     Argument = besARGUMENT(i);
  245.     besDEREFERENCE(Argument);
  246.     IF (i EQ 1) THEN_DO sbobj = LONGVALUE(Argument);
  247.     IF (i EQ 2) THEN_DO varname = STRINGVALUE(Argument);
  248.     IF (i EQ 3) THEN_DO usrval = DOUBLEVALUE(Argument);
  249.   NEXT
  250.   vsn = scriba_LookupVariableByName(sbobj, varname);
  251.   besRETURN_LONG(scriba_SetVariable(sbobj, vsn,  SBT_DOUBLE, 0, usrval, "", 0));
  252. besEND
  253.  
  254. besFUNCTION(SB_SetStr)
  255.   DIM AS VARIABLE Argument;
  256.   DIM AS pSbData varobj;
  257.   DIM AS unsigned long sbobj;
  258.   DIM AS int vsn, i;
  259.   DIM AS char PTR varname;
  260.   DIM AS char PTR usrval;
  261.   IF (besARGNR < 3) THEN_DO RETURN_FUNCTION(EX_ERROR_TOO_FEW_ARGUMENTS);
  262.   DEF_FOR (i = 1 TO i <= 3 STEP INCR i)
  263.   BEGIN_FOR
  264.     Argument = besARGUMENT(i);
  265.     besDEREFERENCE(Argument);
  266.     IF (i EQ 1) THEN_DO sbobj = LONGVALUE(Argument);
  267.     IF (i EQ 2) THEN_DO varname = STRINGVALUE(Argument);
  268.     IF (i EQ 3) THEN_DO usrval = STRINGVALUE(Argument);
  269.   NEXT
  270.   vsn = scriba_LookupVariableByName(sbobj, varname);
  271.   besRETURN_LONG(scriba_SetVariable(sbobj, vsn,  SBT_STRING, 0, 0, usrval, strlen(usrval)));
  272. besEND
  273.  
  274. // Causes seg fault when used ???
  275. besFUNCTION(SB_ResetVars)
  276.   DIM AS unsigned long sbobj;
  277.   DIM AS int;
  278.   besARGUMENTS("i")
  279.     AT sbobj
  280.   besARGEND
  281.   scriba_ResetVariables(sbobj);
  282.   besRETURNVALUE = NULL;
  283. besEND
  284.  

testicall.sb
Code: ScriptBasic
  1. DECLARE SUB SB_New ALIAS "SB_New" LIB "sbt"
  2. DECLARE SUB SB_Configure ALIAS "SB_Configure" LIB "sbt"
  3. DECLARE SUB SB_Load ALIAS "SB_Load" LIB "sbt"
  4. DECLARE SUB SB_LoadStr ALIAS "SB_LoadStr" LIB "sbt"
  5. DECLARE SUB SB_Run ALIAS "SB_Run" LIB "sbt"
  6. DECLARE SUB SB_NoRun ALIAS "SB_NoRun" LIB "sbt"
  7. DECLARE SUB SB_GetVar ALIAS "SB_GetVar" LIB "sbt"
  8. DECLARE SUB SB_SetInt ALIAS "SB_SetInt" LIB "sbt"
  9. DECLARE SUB SB_SetDbl ALIAS "SB_SetDbl" LIB "sbt"
  10. DECLARE SUB SB_SetStr ALIAS "SB_SetStr" LIB "sbt"
  11. DECLARE SUB SB_Address ALIAS "SB_Address" LIB "sbt"
  12. DECLARE SUB SB_CallSub ALIAS "SB_CallSub" LIB "sbt"
  13. DECLARE SUB SB_CallSubArgs ALIAS "SB_CallSubArgs" LIB "sbt"
  14. DECLARE SUB SB_Destroy ALIAS "SB_Destroy" LIB "sbt"
  15.  
  16. sb_code = """
  17. FUNCTION prtvars(a, b, c)
  18.  a_gbl = a
  19.  PRINT a,"\\n"
  20.  PRINT FORMAT("%g", b),"\\n"
  21.  PRINT c,"\\n"
  22.  prtvars = "Script Return Variable"
  23. END FUNCTION
  24. """
  25.  
  26. sb = SB_New()
  27. SB_Configure sb, "/etc/scriba/basic.conf"
  28. SB_Loadstr sb, sb_code
  29. SB_NoRun sb
  30. PRINT STRING(30,"*"),"\n"
  31. PRINT SB_GetVar(sb, "main::a_gbl"),"\n"
  32. PRINT STRING(30,"="),"\n"
  33. PRINT SB_CallSubArgs(sb, "main::prtvars", 123, 1.23, "One,Two,Three"),"\n"
  34. PRINT STRING(30,"="),"\n"
  35. PRINT SB_GetVar(sb, "main::a_gbl"),"\n"
  36. PRINT STRING(30,"*"),"\n"
  37. SB_Destroy sb
  38.  

Output

jrs@laptop:~/sb/sb22/sbt$ scriba testicall.sb
******************************
undef
==============================
123
1.23
One,Two,Three
Script Return Variable
==============================
123
******************************
jrs@laptop:~/sb/sb22/sbt$

« Last Edit: May 02, 2015, 12:19:18 AM by John »

Offline John

  • Forum Support / SB Dev
  • Posts: 3510
    • ScriptBasic Open Source Project
Re: Script BASIC Extension Module
« Reply #24 on: May 02, 2015, 12:58:53 AM »
I thought I would mention that SB_ResetVars() should only be called just before SB_Run/SB_NoRun. If you try to access variable after doing a reset and not follow up with a run/norun, it will seg fault. SB embedded keeps it's global variable static during the life of the execution object. This means you can load multiple scripts and run/norun them keeping the global variables intact. The reset is to provide the default scriba like functionality running from the command line.


Offline John

  • Forum Support / SB Dev
  • Posts: 3510
    • ScriptBasic Open Source Project
Re: Script BASIC Extension Module
« Reply #25 on: May 02, 2015, 04:47:35 PM »
I decided that having separate SB_Set(type) functions offered more definitive control over variable types rather than SB determining their type. I finished the rest of the C BASIC masking of C and made the SBT DECLAREs its own include file.

SBT interface.c
Code: C
  1. /*  SBT (Script BASIC Tutorial) - Extension Module */
  2.  
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include <ctype.h>
  7. #include <math.h>
  8. #include <time.h>
  9. #include "../../basext.h"
  10. #include "../../scriba.h"
  11. #include "cbasic.h"
  12.  
  13.  
  14. /****************************
  15.  Extension Module Functions
  16. ****************************/
  17.  
  18. besVERSION_NEGOTIATE
  19.   RETURN_FUNCTION((int)INTERFACE_VERSION);
  20. besEND
  21.  
  22. besSUB_START
  23.   DIM AS long PTR p;
  24.   besMODULEPOINTER = besALLOC(sizeof(long));
  25.   IF (besMODULEPOINTER EQ NULL) THEN_DO RETURN_FUNCTION(0);
  26.   p = (long PTR)besMODULEPOINTER;
  27.   RETURN_FUNCTION(0);
  28. besEND
  29.  
  30. besSUB_FINISH
  31.   DIM AS long PTR p;
  32.   p = (long PTR)besMODULEPOINTER;
  33.   IF (p EQ NULL) THEN_DO RETURN_FUNCTION(0);
  34.   RETURN_FUNCTION(0);
  35. besEND
  36.  
  37.  
  38. /**********************
  39.  Script BASIC Instance
  40. **********************/
  41.  
  42. besFUNCTION(SB_New)
  43.   DIM AS pSbProgram sbobj;
  44.   sbobj = scriba_new(malloc,free);
  45.   besRETURN_LONG(sbobj);
  46. besEND
  47.  
  48. besFUNCTION(SB_Configure)
  49.   DIM AS unsigned long sbobj;
  50.   DIM AS char PTR cfgfilename;
  51.   DIM AS int rtnval = -1;
  52.   besARGUMENTS("iz")
  53.     AT sbobj, AT cfgfilename
  54.   besARGEND
  55.   rtnval = scriba_LoadConfiguration(sbobj, cfgfilename);
  56.   besRETURN_LONG(rtnval);
  57. besEND
  58.  
  59. besFUNCTION(SB_Load)
  60.   DIM AS unsigned long sbobj;
  61.   DIM AS char PTR sbfilename;
  62.   DIM AS int rtnval = -1;
  63.   besARGUMENTS("iz")
  64.     AT sbobj, AT sbfilename
  65.   besARGEND
  66.   rtnval = scriba_SetFileName(sbobj, sbfilename);
  67.   scriba_LoadSourceProgram(sbobj);
  68.   besRETURN_LONG(rtnval);
  69. besEND
  70.  
  71. besFUNCTION(SB_LoadStr)
  72.   DIM AS unsigned long sbobj;
  73.   DIM AS char PTR sbpgm;
  74.   DIM AS int rtnval = -1;
  75.   besARGUMENTS("is")
  76.     AT sbobj, AT sbpgm
  77.   besARGEND
  78.   scriba_SetFileName(sbobj, "fake");
  79.   rtnval = scriba_LoadProgramString(sbobj, sbpgm, strlen(sbpgm));
  80.   besRETURN_LONG(rtnval);
  81. besEND
  82.  
  83. besFUNCTION(SB_Run)
  84.   DIM AS unsigned long sbobj;
  85.   DIM AS int rtnval;
  86.   DIM AS char PTR sbcmdline;
  87.   besARGUMENTS("iz")
  88.     AT sbobj, AT sbcmdline
  89.   besARGEND
  90.   IF (besARGNR < 2) THEN_DO sbcmdline = "";
  91.   rtnval = scriba_Run(sbobj, sbcmdline);
  92.   besRETURN_LONG(rtnval);
  93. besEND
  94.  
  95. besFUNCTION(SB_NoRun)
  96.   DIM AS unsigned long sbobj;
  97.   DIM AS int rtnval;
  98.   besARGUMENTS("i")
  99.     AT sbobj
  100.   besARGEND
  101.   rtnval = scriba_NoRun(sbobj);
  102.   besRETURN_LONG(rtnval);
  103. besEND
  104.  
  105. besFUNCTION(SB_Destroy)
  106.   DIM AS unsigned long sbobj;
  107.   besARGUMENTS("i")
  108.     AT sbobj
  109.   besARGEND
  110.   scriba_destroy(sbobj);
  111.   RETURN_FUNCTION(0);
  112. besEND
  113.  
  114. besFUNCTION(SB_CallSub)
  115.   DIM AS unsigned long sbobj;
  116.   DIM AS int funcsernum;
  117.   DIM AS char PTR funcname;
  118.   besARGUMENTS("iz")
  119.     AT sbobj, AT funcname
  120.   besARGEND
  121.   funcsernum = scriba_LookupFunctionByName(sbobj, funcname);
  122.   besRETURN_LONG(scriba_Call(sbobj, funcsernum));
  123. besEND
  124.  
  125. besFUNCTION(SB_CallSubArgs)
  126.   DIM AS VARIABLE Argument;
  127.   DIM AS SbData ArgData[8];
  128.   DIM AS SbData FunctionResult;
  129.   DIM AS unsigned long sbobj;
  130.   DIM AS char PTR funcname;
  131.   DIM AS int i, sbtype, fnsn;
  132.  
  133.   Argument = besARGUMENT(1);
  134.   besDEREFERENCE(Argument);
  135.   sbobj = LONGVALUE(Argument);
  136.  
  137.   Argument = besARGUMENT(2);
  138.   besDEREFERENCE(Argument);
  139.   funcname = STRINGVALUE(Argument);
  140.  
  141.   DEF_FOR (i = 3 TO i <= besARGNR STEP INCR i)
  142.   BEGIN_FOR
  143.     Argument = besARGUMENT(i);
  144.     besDEREFERENCE(Argument);
  145.     SELECT_CASE (sbtype = TYPE(Argument))
  146.     BEGIN_SELECT
  147.       CASE VTYPE_LONG:
  148.         ArgData[i-3] = PTR scriba_NewSbLong(sbobj, LONGVALUE(Argument));
  149.         END_CASE
  150.       CASE VTYPE_DOUBLE:
  151.         ArgData[i-3] = PTR scriba_NewSbDouble(sbobj, DOUBLEVALUE(Argument));
  152.         END_CASE
  153.       CASE VTYPE_STRING:
  154.         ArgData[i-3] = PTR scriba_NewSbString(sbobj, STRINGVALUE(Argument));
  155.         END_CASE
  156.       CASE_ELSE
  157.         ArgData[i-3] = PTR scriba_NewSbUndef(sbobj);
  158.         END_CASE
  159.     END_SELECT
  160.   NEXT
  161.  
  162.   fnsn = scriba_LookupFunctionByName(sbobj, funcname);
  163.   scriba_CallArgEx(sbobj, fnsn, AT FunctionResult, besARGNR - 2, AT ArgData);
  164.  
  165.   SELECT_CASE (FunctionResult.type)
  166.   BEGIN_SELECT
  167.     CASE SBT_LONG:
  168.       besRETURN_LONG(FunctionResult.v.l);
  169.       END_CASE
  170.     CASE SBT_DOUBLE:
  171.       besRETURN_DOUBLE(FunctionResult.v.d);
  172.       END_CASE
  173.     CASE SBT_STRING:
  174.       besRETURN_STRING(FunctionResult.v.s);
  175.       END_CASE
  176.     CASE SBT_UNDEF:
  177.       besRETURNVALUE = NULL;
  178.       END_CASE
  179.   END_SELECT
  180. besEND
  181.  
  182. besFUNCTION(SB_GetVar)
  183.   DIM AS pSbData varobj;
  184.   DIM AS unsigned long sbobj;
  185.   DIM AS int vsn;
  186.   DIM AS char PTR varname;
  187.   besARGUMENTS("iz")
  188.     AT sbobj, AT varname
  189.   besARGEND
  190.   vsn = scriba_LookupVariableByName(sbobj, varname);
  191.   scriba_GetVariable(sbobj, vsn, AT varobj);
  192.   SELECT_CASE (scriba_GetVariableType(sbobj, vsn))
  193.   BEGIN_SELECT
  194.     CASE SBT_LONG   :
  195.       besRETURN_LONG(varobj[0].v.l);
  196.       END_CASE
  197.     CASE SBT_DOUBLE :
  198.       besRETURN_DOUBLE(varobj[0].v.d);
  199.       END_CASE
  200.     CASE SBT_STRING :
  201.       besRETURN_STRING(varobj[0].v.s);
  202.       END_CASE
  203.     CASE SBT_UNDEF  :
  204.       besRETURNVALUE = NULL;;
  205.       END_CASE
  206.   END_SELECT
  207. besEND
  208.  
  209. besFUNCTION(SB_SetUndef)
  210.   DIM AS pSbData varobj;
  211.   DIM AS unsigned long sbobj;
  212.   DIM AS int vsn;
  213.   DIM AS char PTR varname;
  214.   besARGUMENTS("iz")
  215.     AT sbobj, AT varname
  216.   besARGEND
  217.   vsn = scriba_LookupVariableByName(sbobj, varname);
  218.   besRETURN_LONG(scriba_SetVariable(sbobj, vsn, SBT_UNDEF, NULL, 0, "", 0));
  219. besEND
  220.  
  221. besFUNCTION(SB_SetInt)
  222.   DIM AS VARIABLE Argument;
  223.   DIM AS pSbData varobj;
  224.   DIM AS unsigned long sbobj;
  225.   DIM AS int vsn, usrval, i;
  226.   DIM AS char PTR varname;
  227.   IF (besARGNR < 3) THEN_DO RETURN_FUNCTION(EX_ERROR_TOO_FEW_ARGUMENTS);
  228.   DEF_FOR (i = 1 TO i <= 3 STEP INCR i)
  229.   BEGIN_FOR
  230.     Argument = besARGUMENT(i);
  231.     besDEREFERENCE(Argument);
  232.     IF (i EQ 1) THEN_DO sbobj = LONGVALUE(Argument);
  233.     IF (i EQ 2) THEN_DO varname = STRINGVALUE(Argument);
  234.     IF (i EQ 3) THEN_DO usrval = LONGVALUE(Argument);
  235.   NEXT
  236.   vsn = scriba_LookupVariableByName(sbobj, varname);
  237.   besRETURN_LONG(scriba_SetVariable(sbobj, vsn, SBT_LONG, usrval, 0, "", 0));
  238. besEND
  239.  
  240. besFUNCTION(SB_SetDbl)
  241.   DIM AS VARIABLE Argument;
  242.   DIM AS pSbData varobj;
  243.   DIM AS unsigned long sbobj;
  244.   DIM AS int vsn, i;
  245.   DIM AS char PTR varname;
  246.   DIM AS double usrval;
  247.   IF (besARGNR < 3) THEN_DO RETURN_FUNCTION(EX_ERROR_TOO_FEW_ARGUMENTS);
  248.   DEF_FOR (i = 1 TO i <= 3 STEP INCR i)
  249.   BEGIN_FOR
  250.     Argument = besARGUMENT(i);
  251.     besDEREFERENCE(Argument);
  252.     IF (i EQ 1) THEN_DO sbobj = LONGVALUE(Argument);
  253.     IF (i EQ 2) THEN_DO varname = STRINGVALUE(Argument);
  254.     IF (i EQ 3) THEN_DO usrval = DOUBLEVALUE(Argument);
  255.   NEXT
  256.   vsn = scriba_LookupVariableByName(sbobj, varname);
  257.   besRETURN_LONG(scriba_SetVariable(sbobj, vsn,  SBT_DOUBLE, 0, usrval, "", 0));
  258. besEND
  259.  
  260. besFUNCTION(SB_SetStr)
  261.   DIM AS VARIABLE Argument;
  262.   DIM AS pSbData varobj;
  263.   DIM AS unsigned long sbobj;
  264.   DIM AS int vsn, i;
  265.   DIM AS char PTR varname;
  266.   DIM AS char PTR usrval;
  267.   IF (besARGNR < 3) THEN_DO RETURN_FUNCTION(EX_ERROR_TOO_FEW_ARGUMENTS);
  268.   DEF_FOR (i = 1 TO i <= 3 STEP INCR i)
  269.   BEGIN_FOR
  270.     Argument = besARGUMENT(i);
  271.     besDEREFERENCE(Argument);
  272.     IF (i EQ 1) THEN_DO sbobj = LONGVALUE(Argument);
  273.     IF (i EQ 2) THEN_DO varname = STRINGVALUE(Argument);
  274.     IF (i EQ 3) THEN_DO usrval = STRINGVALUE(Argument);
  275.   NEXT
  276.   vsn = scriba_LookupVariableByName(sbobj, varname);
  277.   besRETURN_LONG(scriba_SetVariable(sbobj, vsn,  SBT_STRING, 0, 0, usrval, strlen(usrval)));
  278. besEND
  279.  
  280. besFUNCTION(SB_ResetVars)
  281.   DIM AS unsigned long sbobj;
  282.   DIM AS int;
  283.   besARGUMENTS("i")
  284.     AT sbobj
  285.   besARGEND
  286.   scriba_ResetVariables(sbobj);
  287.   besRETURNVALUE = NULL;
  288. besEND
  289.  

sbt.inc
Code: ScriptBasic
  1. ' SBT (Script BASIC Tutorial) - Include File
  2.  
  3. DECLARE SUB SB_New ALIAS "SB_New" LIB "sbt"
  4. DECLARE SUB SB_Configure ALIAS "SB_Configure" LIB "sbt"
  5. DECLARE SUB SB_Load ALIAS "SB_Load" LIB "sbt"
  6. DECLARE SUB SB_LoadStr ALIAS "SB_LoadStr" LIB "sbt"
  7. DECLARE SUB SB_Run ALIAS "SB_Run" LIB "sbt"
  8. DECLARE SUB SB_NoRun ALIAS "SB_NoRun" LIB "sbt"
  9. DECLARE SUB SB_GetVar ALIAS "SB_GetVar" LIB "sbt"
  10. DECLARE SUB SB_SetUndef ALIAS "SB_SetUndef" LIB "sbt"
  11. DECLARE SUB SB_SetInt ALIAS "SB_SetInt" LIB "sbt"
  12. DECLARE SUB SB_SetDbl ALIAS "SB_SetDbl" LIB "sbt"
  13. DECLARE SUB SB_SetStr ALIAS "SB_SetStr" LIB "sbt"
  14. DECLARE SUB SB_ResetVars ALIAS "SB_ResetVars" LIB "sbt"
  15. DECLARE SUB SB_CallSub ALIAS "SB_CallSub" LIB "sbt"
  16. DECLARE SUB SB_CallSubArgs ALIAS "SB_CallSubArgs" LIB "sbt"
  17. DECLARE SUB SB_Destroy ALIAS "SB_Destroy" LIB "sbt"
  18.  

sbtdemo.sb
Code: ScriptBasic
  1. ' SBT (Script BASIC Tutorial) - Example Script
  2.  
  3. IMPORT sbt.inc
  4.  
  5. sb_code = """
  6. FUNCTION prtvars(a, b, c)
  7.  PRINT a,"\\n"
  8.  PRINT FORMAT("%g\\n", b)
  9.  PRINT c,"\\n"
  10.  prtvars = "Function Return"
  11. END FUNCTION
  12.  
  13. a = 0
  14. b = 0
  15. c = ""
  16. """
  17.  
  18. sb = SB_New()
  19. SB_Configure sb, "/etc/scriba/basic.conf"
  20. SB_LoadStr sb, sb_code
  21. SB_NoRun sb
  22. funcrtn = SB_CallSubArgs(sb,"main::prtvars", 123, 1.23, "One, Two, Three")
  23. PRINT funcrtn,"\n"
  24. SB_Run sb, ""
  25. SB_SetInt sb, "main::a", 321
  26. SB_SetDbl sb, "main::b", 32.1
  27. SB_SetStr sb, "main::c", "Three,Two,One"
  28. SB_CallSubArgs sb, "main::prtvars", _
  29.           SB_GetVar(sb, "main::a"), _
  30.           SB_GetVar(sb, "main::b"), _
  31.           SB_GetVar(sb, "main::c")      
  32. SB_Destroy sb
  33.  

Output

jrs@laptop:~/sb/sb22/sbt$ time scriba sbtdemo.sb
123
1.23
One, Two, Three
Function Return
321
32.1
Three,Two,One

real   0m0.007s
user   0m0.007s
sys   0m0.000s
jrs@laptop:~/sb/sb22/sbt$

 
« Last Edit: May 05, 2015, 06:07:29 PM by John »

Offline John

  • Forum Support / SB Dev
  • Posts: 3510
    • ScriptBasic Open Source Project
Re: Script BASIC Extension Module
« Reply #26 on: May 02, 2015, 10:33:02 PM »
I hope everyone has grasped how to embed the Script BASIC interpreter in a project along with accessing embedded variables and functions. (Good Job!)

Next on the list as promised is using an internal preprocessor to debug the embedded script. Dave Zimmer (VB/COM contributor) hacked Peter's sdbg socket based internal preprocessor shared object so it communicates with calls to the preprocessor.

The XP example is cool as it's debugging an embedded application (MY-BASIC) while the Editor/IDE itself is embedding Script BASIC.  8)

dbg.h
Code: C
  1. /*
  2. dbg.h
  3. */
  4. #ifndef __DBG_H__
  5. #define __DBG_H__ 1
  6. #ifdef  __cplusplus
  7. extern "C" {
  8. #endif
  9.  
  10. typedef struct _UserFunction_t {
  11.   long cLocalVariables;
  12.   char *pszFunctionName;
  13.   char **ppszLocalVariables;
  14.   long NodeId;
  15.   } UserFunction_t, *pUserFunction_t;
  16.  
  17.  
  18. typedef struct _DebugNode_t {
  19.   char *pszFileName;
  20.   long lLineNumber;  
  21.   long lNodeId;      
  22.   long lSourceLine;  
  23.                      
  24.   } DebugNode_t, *pDebugNode_t;
  25.  
  26.  
  27. typedef struct _SourceLine_t {
  28.   char *line;
  29.   long lLineNumber;
  30.   char *szFileName;
  31.   int BreakPoint;
  32.   } SourceLine_t, *pSourceLine_t;
  33.  
  34.  
  35. typedef struct _DebugCallStack_t {
  36.   long Node;
  37.   pUserFunction_t pUF;
  38.   pFixSizeMemoryObject LocalVariables;
  39.   struct _DebugCallStack_t *up,*down;
  40.   } DebugCallStack_t, *pDebugCallStack_t;
  41.  
  42.  
  43.  
  44.  
  45.  
  46.  
  47. typedef struct _DebuggerObject {
  48.   pPrepext pEXT;
  49.   pExecuteObject pEo;
  50.   long cGlobalVariables;
  51.   char **ppszGlobalVariables;
  52.   long cUserFunctions;
  53.   pUserFunction_t pUserFunctions;
  54.   long cFileNames;
  55.   char **ppszFileNames;
  56.   long cNodes;
  57.   pDebugNode_t Nodes;
  58.   long cSourceLines;
  59.   pSourceLine_t SourceLines;
  60.   pDebugCallStack_t DbgStack;
  61.   pDebugCallStack_t StackTop;
  62.   pDebugCallStack_t StackListPointer;
  63.   long CallStackDepth;
  64.   long Run2CallStack;
  65.   long Run2Line;
  66.   int bLocalStart;
  67.   long FunctionNode;
  68.   long lPrevPC,lPC;
  69.   } DebuggerObject, *pDebuggerObject;
  70. /*FUNDEF*/
  71.  
  72. int SPrintVariable(pDebuggerObject pDO,
  73.                    VARIABLE v,
  74.                    char *pszBuffer,
  75.                    unsigned long *cbBuffer);
  76. /*FEDNUF*/
  77. /*FUNDEF*/
  78.  
  79. int SPrintVarByName(pDebuggerObject pDO,
  80.                     pExecuteObject pEo,
  81.                     char *pszName,
  82.                     char *pszBuffer,
  83.                     unsigned long *cbBuffer);
  84. /*FEDNUF*/
  85. /*FUNDEF*/
  86.  
  87. long GetSourceLineNumber(pDebuggerObject pDO,
  88.                          long PC);
  89. /*FEDNUF*/
  90. /*FUNDEF*/
  91.  
  92. long GetCurrentDebugLine(pDebuggerObject pDO);
  93. /*FEDNUF*/
  94. #ifdef __cplusplus
  95. }
  96. #endif
  97. #endif
  98.  

interface.c
Code: C
  1. /*
  2. FILE:   interface.c
  3. HEADER: dbg.h
  4.  
  5. --GNU LGPL
  6. This library is free software; you can redistribute it and/or
  7. modify it under the terms of the GNU Lesser General Public
  8. License as published by the Free Software Foundation; either
  9. version 2.1 of the License, or (at your option) any later version.
  10.  
  11. This library is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14. Lesser General Public License for more details.
  15.  
  16. You should have received a copy of the GNU Lesser General Public
  17. License along with this library; if not, write to the Free Software
  18. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  19.  
  20. This program implements a simple debugger "preprocessor" for ScriptBasic.
  21.  
  22. NTLIBS: user32.lib
  23. UXLIBS:
  24. DWLIBS:
  25. MCLIBS:
  26.  
  27. */
  28. #include <stdio.h>
  29. #include <stdlib.h>
  30. #include <string.h>
  31. #include <ctype.h>
  32.  
  33. #include "conftree.h"
  34. #include "report.h"
  35. #include "reader.h"
  36. #include "basext.h"
  37. #include "prepext.h"
  38.  
  39. #include "dbg.h"
  40. #include "dbg_comm.h"
  41.  
  42. /*
  43. TO_HEADER:
  44. // Debug information on user defined functions.
  45. typedef struct _UserFunction_t {
  46.   long cLocalVariables;
  47.   char *pszFunctionName;
  48.   char **ppszLocalVariables;
  49.   long NodeId; // node id where the function starts
  50.   } UserFunction_t, *pUserFunction_t;
  51.  
  52. // Debug information for each byte-code node.
  53. typedef struct _DebugNode_t {
  54.   char *pszFileName; // the file name where the source for the node is
  55.   long lLineNumber;  // the line number in the file where the node is
  56.   long lNodeId;      // the id of the node
  57.   long lSourceLine;  // the source line number as it is in the memory with included lines counted from 1
  58.                      // this field is zero and is set when the line is first searched to avoid further searches
  59.   } DebugNode_t, *pDebugNode_t;
  60.  
  61. // struct for a source line to hold in memory while debugging
  62. typedef struct _SourceLine_t {
  63.   char *line;
  64.   long lLineNumber;
  65.   char *szFileName;
  66.   int BreakPoint;
  67.   } SourceLine_t, *pSourceLine_t;
  68.  
  69. // to maintain a call stack to make it available for the user to see local variables and PC and so on
  70. typedef struct _DebugCallStack_t {
  71.   long Node;//where the execution came here to the function (where the function call is)
  72.   pUserFunction_t pUF;
  73.   pFixSizeMemoryObject LocalVariables;
  74.   struct _DebugCallStack_t *up,*down;
  75.   } DebugCallStack_t, *pDebugCallStack_t;
  76.  
  77. // this is the thread local variable struct.
  78.  
  79. //Note that altough this is a debugger it is possible to have multiple threads to be debugged
  80. //using several debug "windows".
  81.  
  82. typedef struct _DebuggerObject {
  83.   pPrepext pEXT;
  84.   pExecuteObject pEo;
  85.   long cGlobalVariables;
  86.   char **ppszGlobalVariables;
  87.   long cUserFunctions;
  88.   pUserFunction_t pUserFunctions;
  89.   long cFileNames;
  90.   char **ppszFileNames;
  91.   long cNodes;
  92.   pDebugNode_t Nodes;
  93.   long cSourceLines;
  94.   pSourceLine_t SourceLines;
  95.   pDebugCallStack_t DbgStack;
  96.   pDebugCallStack_t StackTop;
  97.   pDebugCallStack_t StackListPointer;
  98.   long CallStackDepth;
  99.   long Run2CallStack;
  100.   long Run2Line;
  101.   int bLocalStart;
  102.   long FunctionNode;
  103.   long lPrevPC,lPC;
  104.   } DebuggerObject, *pDebuggerObject;
  105. */
  106.  
  107. /* Push the item on the debugger stack when entering the function
  108.    starting at the node Node
  109. */
  110. static void PushStackItem(pDebuggerObject pDO,
  111.                           long Node
  112.   ){
  113.   pDebugCallStack_t p;
  114.   long i;
  115.  
  116.   p = pDO->pEXT->pST->Alloc(sizeof(DebugCallStack_t),pDO->pEXT->pMemorySegment);
  117.   if( p == NULL )return;
  118.   if( pDO->StackTop == NULL )pDO->StackTop = p;
  119.   p->up = pDO->DbgStack;
  120.   p->down = NULL;
  121.   p->Node = pDO->lPC;
  122.   if( pDO->DbgStack )pDO->DbgStack->down = p;
  123.   pDO->DbgStack = p;
  124.   p->pUF = NULL;
  125.   for( i = 0 ; i < pDO->cUserFunctions ; i++ )
  126.     if( pDO->pUserFunctions[i].NodeId == Node ){
  127.       p->pUF = pDO->pUserFunctions+i;
  128.       break;
  129.       }
  130.   p->LocalVariables = NULL;
  131.   pDO->CallStackDepth++;
  132.   return;
  133.   }
  134.  
  135. /* return from a function and pop off the item from the stack */
  136. static void PopStackItem(pDebuggerObject pDO
  137.   ){
  138.   pDebugCallStack_t p;
  139.  
  140.   if( pDO->DbgStack == NULL || pDO->CallStackDepth == 0 )return;
  141.   p = pDO->DbgStack;
  142.   pDO->DbgStack = pDO->DbgStack->up;
  143.   if( pDO->DbgStack )pDO->DbgStack->down = NULL;
  144.   pDO->pEXT->pST->Free(p,pDO->pEXT->pMemorySegment);
  145.   pDO->CallStackDepth--;
  146.   if( pDO->CallStackDepth == 0 )pDO->StackTop = NULL;
  147.   return;
  148.   }
  149.  
  150. static char hexi(unsigned int x ){
  151.   if( x < 10 )return x+'0';
  152.   return x+'A'-10;
  153.   }
  154.  
  155. /*POD
  156. =section SPrintVariable
  157. =H Print the value of a variable into a string
  158.  
  159. This function should be used to get the textual representation of a
  160. ScriptBasic T<VARIABLE>.
  161.  
  162. /*FUNCTION*/
  163. int SPrintVariable(pDebuggerObject pDO,
  164.                    VARIABLE v,
  165.                    char *pszBuffer,
  166.                    unsigned long *cbBuffer
  167.   ){
  168. /*noverbatim
  169.  
  170. =itemize
  171. =item T<pDO> is the debugger object
  172. =item T<v> is the variable to print
  173. =item T<pszBuffer> is pointer to the buffer that has to have at least
  174. =item T<cbBuffer> number of bytes available
  175. =noitemize
  176.  
  177. The function returns zero on success.
  178.  
  179. The function returns 1 if the buffer is not large enough. In this case the
  180. number returned in T<*cbBuffer> will be the size of the buffer needed. It may
  181. happen in case the buffer is extremely short that even the returned size is not
  182. enough. Choosing a buffer length of 80 bytes or so ensures that either the
  183. result fits into the buffer or the returned number is large enough to hold
  184. the result.
  185.  
  186. Note that the number can be extremely large in case the variable is a string. In
  187. this case all the characters are copied into the result and non-printable characters
  188. are converted to hex.
  189.  
  190. The buffer should be large enough to hold the "->->->->->...->" string representing the
  191. references and the number or string.
  192. CUT*/
  193.   long refcount;
  194.   unsigned char *s,*r;
  195.   char buf[80];
  196.   unsigned long slen,i;
  197.   unsigned long _cbBuffer = *cbBuffer;
  198.  
  199.   if( v == NULL || TYPE(v) == VTYPE_UNDEF ){
  200.     if( _cbBuffer < 6 )return 1;
  201.     strcpy(pszBuffer,"undef");
  202.     return 0;
  203.     }
  204.  
  205. #define APPEND(X) slen = strlen(X);\
  206.                   if( _cbBuffer < slen+1 ){\
  207.                     *cbBuffer += 40;\
  208.                     return 1;\
  209.                     }\
  210.                   strcpy(s,X);\
  211.                   s += slen;\
  212.                   _cbBuffer -= slen;
  213.  
  214.   *pszBuffer = (char)0;
  215.   s = pszBuffer;
  216.   if( TYPE(v) == VTYPE_REF ){
  217.     refcount = 0;
  218.     while( TYPE(v) == VTYPE_REF ){
  219.       v = *(v->Value.aValue);
  220.       if( refcount < 5 ){
  221.         APPEND("->")
  222.         }
  223.       refcount++;
  224.       if( refcount == 1000 ){
  225.         APPEND("... infinit")
  226.         return 0;
  227.         }
  228.       }
  229.     if( refcount > 5 ){
  230.       APPEND(" ... ->")
  231.       }
  232.     }
  233.  
  234.   if( TYPE(v) == VTYPE_UNDEF ){
  235.     APPEND("undef")
  236.     return 0;
  237.     }
  238.  
  239.   if( TYPE(v) == VTYPE_LONG ){
  240.     sprintf(buf,"%d",v->Value.lValue);
  241.     slen = strlen(buf);
  242.     if( _cbBuffer < slen+1 ){
  243.       *cbBuffer += slen - _cbBuffer;
  244.       return 1;
  245.       }
  246.     strcpy(s,buf);
  247.     return 0;
  248.     }
  249.  
  250.   if( TYPE(v) == VTYPE_DOUBLE ){
  251.     sprintf(buf,"%lf",v->Value.dValue);
  252.     slen = strlen(buf);
  253.     if( _cbBuffer < slen+1 ){
  254.       *cbBuffer += slen - _cbBuffer;
  255.       return 1;
  256.       }
  257.     strcpy(s,buf);
  258.     return 0;
  259.     }
  260.  
  261.   if( TYPE(v) == VTYPE_ARRAY ){
  262.     sprintf(buf,"ARRAY@#%08X",LONGVALUE(v));
  263.     slen = strlen(buf);
  264.     if( _cbBuffer < slen+1 ){
  265.       *cbBuffer += slen - _cbBuffer;
  266.       return 1;
  267.       }
  268.     strcpy(s,buf);
  269.     return 0;
  270.     }
  271.  
  272.   if( TYPE(v) == VTYPE_STRING ){
  273.     /* calculate the printed size */
  274.     r = v->Value.pValue;
  275.     slen = 2; /* starting and ending " */
  276.     i = 0;
  277.     while( i < STRLEN(v) ){
  278.       if( *r < 0x20 || *r > 0x7F ){
  279.         slen += 4 ; /* \xXX */
  280.         i++;
  281.         r++;
  282.         continue;
  283.         }
  284.       if( *r == '"' ){
  285.         slen += 2 ; /* \" */
  286.         i++;
  287.         r++;
  288.         continue;
  289.         }
  290.       slen ++;
  291.       i++;
  292.       r++;
  293.       continue;
  294.       }
  295.  
  296.     if( _cbBuffer < slen+1 ){
  297.       *cbBuffer += slen - _cbBuffer;
  298.       return 1;
  299.       }
  300.  
  301.     r = v->Value.pValue;
  302.     *s ++ = '"';
  303.     i = 0;
  304.     while( i < STRLEN(v) ){
  305.       if( *r < 0x20 || *r > 0x7F ){
  306.         *s ++ = '\\';
  307.         *s ++ = 'x';
  308.         *s ++ = hexi( (*r) / 16 );
  309.         *s ++ = hexi( (*r) & 0xF);
  310.         i++;
  311.         r++;
  312.         continue;
  313.         }
  314.       if( *r == '"' ){
  315.         *s ++ = '\\';
  316.         *s ++ = '"';
  317.         i++;
  318.         r++;
  319.         continue;
  320.         }
  321.       *s ++ = *r;
  322.       i++;
  323.       r++;
  324.       continue;
  325.       }
  326.     *s ++ = '"';
  327.     *s = (char)0;
  328.     return 0;
  329.     }
  330.   return 1;
  331.   }
  332.  
  333. /*POD
  334. =section SPrintVarByName
  335. =H Print the value of a variable into a string
  336.  
  337. This fucntion prints a variable string representation into a buffer.
  338. The name of the variable is given in the variable T<pszName>.
  339.  
  340. The fucntion first searches the variable and then calls the function
  341. R<SPrintVariable> to print the value.
  342.  
  343. The fucntion first tries to locate the variable as local variable.
  344. For this not the normal debug stack pointer is used, but rather the
  345. T<StackListPointer>. This allows the client to print local
  346. variables levels higher than the bottom of the stack.
  347.  
  348. If the function succeeds finding the variable it returns the return value of the
  349. function R<SPrintVariable>. If the variable is not found it returns 2.
  350.  
  351. /*FUNCTION*/
  352. int SPrintVarByName(pDebuggerObject pDO,
  353.                     pExecuteObject pEo,
  354.                     char *pszName,
  355.                     char *pszBuffer,
  356.                     unsigned long *cbBuffer
  357.   ){
  358. /*noverbatim
  359. CUT*/
  360.   pUserFunction_t pUF;
  361.   long i;
  362.   char *s;
  363.  
  364.   s = pszName;
  365.   while( *s ){
  366.     if( isupper(*s) )*s = tolower(*s);
  367.     s++;
  368.     }
  369.  
  370.   if( pDO->StackListPointer && pDO->StackListPointer->pUF ){
  371.     pUF = pDO->StackListPointer->pUF;
  372.     for( i=0 ; i < pUF->cLocalVariables ; i++ ){
  373.       if( !strcmp(pUF->ppszLocalVariables[i],pszName) )
  374.         return SPrintVariable(pDO,ARRAYVALUE(pDO->StackListPointer->LocalVariables,i+1),pszBuffer,cbBuffer);
  375.       }
  376.     }
  377.   for( i=0 ; i < pDO->cGlobalVariables ; i++ ){
  378.      if( !strcmp(pDO->ppszGlobalVariables[i],pszName) ){
  379.        if( pEo->GlobalVariables )
  380.          return SPrintVariable(pDO,ARRAYVALUE(pEo->GlobalVariables,i+1),pszBuffer,cbBuffer);
  381.        }
  382.      }
  383.  
  384.   if( pDO->StackListPointer && pDO->StackListPointer->pUF ){
  385.     pUF = pDO->StackListPointer->pUF;
  386.     for( i=0 ; i < pUF->cLocalVariables ; i++ ){
  387.       if( !strncmp(pUF->ppszLocalVariables[i],"main::",6) && !strcmp(pUF->ppszLocalVariables[i]+6,pszName) )
  388.         return SPrintVariable(pDO,ARRAYVALUE(pDO->StackListPointer->LocalVariables,i+1),pszBuffer,cbBuffer);
  389.       }
  390.     }
  391.   for( i=0 ; i < pDO->cGlobalVariables ; i++ ){
  392.      if( !strncmp(pDO->ppszGlobalVariables[i],"main::",6) && !strcmp(pDO->ppszGlobalVariables[i]+6,pszName) ){
  393.        if( pEo->GlobalVariables )
  394.          return SPrintVariable(pDO,ARRAYVALUE(pEo->GlobalVariables,i+1),pszBuffer,cbBuffer);
  395.        }
  396.      }
  397.   return 2;
  398.   }
  399.  
  400. /*POD
  401. =section GetSourceLineNumber
  402. =H Get the source line number for a given node
  403.  
  404. /*FUNCTION*/
  405. long GetSourceLineNumber(pDebuggerObject pDO,
  406.                          long PC
  407.   ){
  408. /*noverbatim
  409. CUT*/
  410.   long i,j;
  411.   long lLineNumber;
  412.   char *pszFileName;
  413.  
  414.   if( PC < 1 || PC > pDO->cNodes )return 0;
  415.  
  416.   if( pDO->Nodes[PC-1].lSourceLine )return pDO->Nodes[PC-1].lSourceLine-1;
  417.  
  418.   /* fill in the whole array */
  419.   for( j=0 ; j < pDO->cNodes ; j++ ){
  420.     lLineNumber = pDO->Nodes[j].lLineNumber;
  421.     pszFileName = pDO->Nodes[j].pszFileName;
  422.  
  423.     for( i=0 ; i < pDO->cSourceLines ; i ++ )
  424.       if( pDO->SourceLines[i].lLineNumber == lLineNumber &&
  425.           pDO->SourceLines[i].szFileName                 &&
  426.           pszFileName                                    &&
  427.           !strcmp(pDO->SourceLines[i].szFileName,pszFileName) )break;
  428.     pDO->Nodes[j].lSourceLine = i+1;
  429.     }
  430.  
  431.   return pDO->Nodes[PC-1].lSourceLine-1;
  432.   }
  433.  
  434. /*FUNCTION*/
  435. long GetCurrentDebugLine(pDebuggerObject pDO
  436.   ){
  437.  
  438.   if( pDO->StackListPointer == NULL && pDO->StackTop )
  439.      return GetSourceLineNumber(pDO,pDO->StackTop->Node);
  440.  
  441.   if( pDO->StackListPointer == NULL || pDO->StackListPointer->down == NULL )
  442.     return GetSourceLineNumber(pDO,pDO->pEo->ProgramCounter);
  443.  
  444.   return GetSourceLineNumber(pDO,pDO->StackListPointer->down->Node);
  445.   }
  446.  
  447. int MyExecBefore(pExecuteObject pEo){
  448.   long i,j,lThisLine;
  449.   pPrepext pEXT;
  450.   pDebuggerObject pDO;
  451.   char lbuf[80];
  452.   char cmd;
  453.  
  454.   pEXT = pEo->pHookers->hook_pointer;
  455.   pDO  = pEXT->pPointer;
  456.   pDO->pEo = pEo;
  457.  
  458.   pDO->lPrevPC = pDO->lPC;
  459.   pDO->lPC = pEo->ProgramCounter;
  460.   if( pDO->DbgStack )pDO->DbgStack->LocalVariables = pEo->LocalVariables;
  461.  
  462.   lThisLine = GetSourceLineNumber(pDO,pEo->ProgramCounter);
  463.  
  464.   if( pDO->SourceLines[lThisLine].BreakPoint == 0 ){
  465.     /* if we are executing some step over function */
  466.     if( pDO->Run2CallStack != -1 && pDO->Run2CallStack < pDO->CallStackDepth )return 0;
  467.     if( pDO->Run2Line && pDO->Nodes[pDO->lPC-1].lSourceLine != pDO->Run2Line )return 0;
  468.     }
  469.  
  470.   comm_WeAreAt(pDO,lThisLine);
  471.  
  472.   pDO->StackListPointer = pDO->DbgStack;
  473.   while(1){
  474.     cmd = comm_GetCommand(pDO,lbuf,80);
  475.  
  476.  
  477.     switch( cmd ){
  478.  
  479.       case 'D':/* step the stack list pointer to the bottom */
  480.          pDO->StackListPointer = pDO->DbgStack;
  481.          comm_Message(pDO,"done");
  482.          continue;
  483.       case 'u':/* step the stack list pointer up */
  484.         if( pDO->StackListPointer ){
  485.           pDO->StackListPointer = pDO->StackListPointer->up;
  486.           comm_Message(pDO,"done");
  487.           }else comm_Message(pDO,"No way up more");
  488.         continue;
  489.       case 'd':/* step the stack list pointer down */
  490.         if( pDO->StackListPointer && pDO->StackListPointer->down )
  491.           pDO->StackListPointer = pDO->StackListPointer->down;
  492.         else
  493.           pDO->StackListPointer = pDO->StackTop;
  494.         if( pDO->StackListPointer )
  495.           comm_Message(pDO,"done");
  496.         else
  497.           comm_Message(pDO,"No way down more");
  498.         continue;
  499.       case 'b': /* set break point at a line */
  500.         if( ! *lbuf )/* set it at the current line */
  501.           i = GetCurrentDebugLine(pDO)+1;
  502.         else
  503.           GetRange(lbuf,&i,&j);
  504.         if( i < 1 || i > pDO->cSourceLines ){
  505.           comm_Message(pDO,"invalid line number");
  506.           continue;
  507.           }
  508.         pDO->SourceLines[i-1].BreakPoint = 1;
  509.         comm_Message(pDO,"done");
  510.         continue;
  511.       case 'B':/* remove breakpoint from line(s) */
  512.         if( ! *lbuf )/* remove all */
  513.           i = 1, j = pDO->cSourceLines;
  514.         else
  515.           GetRange(lbuf,&i,&j);
  516.         if( i < 1 || i >= pDO->cSourceLines ){
  517.           comm_Message(pDO,"invalid line number");
  518.           continue;
  519.           }
  520.         if( j == 0 )j = i;
  521.         if( j > pDO->cSourceLines )j = pDO->cSourceLines;
  522.         while( i <= j ){
  523.           pDO->SourceLines[i-1].BreakPoint = 0;
  524.           i++;
  525.           }
  526.         comm_Message(pDO,"done");
  527.         continue;
  528.       case 'q':/* quit the program execution */
  529.         comm_Message(pDO,"Ok... you have said that... quitting...");
  530.         pEo->pszModuleError = "Debugger Operator Forced Exit.";
  531.         return COMMAND_ERROR_PREPROCESSOR_ABORT;
  532.  
  533.       case 's':/*step a single line and step into functions */
  534.         pDO->Run2CallStack = pDO->CallStackDepth+1;
  535.         pDO->Run2Line = 0;
  536.         return 0; /* step one step forward */
  537.       case 'o':/* run program until it gets out of the current function */
  538.         pDO->Run2CallStack = pDO->CallStackDepth ? pDO->CallStackDepth - 1 : 0 ;
  539.         pDO->Run2Line = 0;
  540.         return 0; /* step one step forward */
  541.       case 'S':
  542.         pDO->Run2CallStack = pDO->CallStackDepth;
  543.         pDO->Run2Line = 0;
  544.         return 0; /* step one step forward but remain on the same level */
  545.       case 'r':
  546.          pDO->Run2CallStack = -1;/* any level deep */
  547.          if( ! *lbuf ){
  548.            pDO->Run2Line = -1;/* a nonzero value that can not be a valid line number */
  549.            return 0;
  550.            }
  551.          GetRange(lbuf,&i,&j);
  552.          pDO->Run2Line = i;
  553.          return 0;
  554.       case 'R':
  555.          pDO->Run2CallStack = pDO->CallStackDepth; /* on the current level */
  556.          if( ! *lbuf ){
  557.            pDO->Run2Line = -1;/* a nonzero value that can not be a valid line number */
  558.            return 0;
  559.            }
  560.          GetRange(lbuf,&i,&j);
  561.          pDO->Run2Line = i;
  562.          return 0;
  563.       }
  564.     }
  565.   return 0;
  566.   }
  567. int MyExecAfter(pExecuteObject pEo){
  568.   pPrepext pEXT;
  569.   pDebuggerObject pDO;
  570.  
  571.   pEXT = pEo->pHookers->hook_pointer;
  572.   pDO  = pEXT->pPointer;
  573.   pDO->pEo = pEo;
  574.  
  575.   return 0;
  576.   }
  577. int MyExecCall(pExecuteObject pEo){
  578.   pPrepext pEXT;
  579.   pDebuggerObject pDO;
  580.  
  581.   pEXT = pEo->pHookers->hook_pointer;
  582.   pDO  = pEXT->pPointer;
  583.   pDO->pEo = pEo;
  584.  
  585.   PushStackItem(pDO,pEo->ProgramCounter);
  586.  
  587.   return 0;
  588.   }
  589. int MyExecReturn(pExecuteObject pEo){
  590.   pPrepext pEXT;
  591.   pDebuggerObject pDO;
  592.  
  593.   pEXT = pEo->pHookers->hook_pointer;
  594.   pDO  = pEXT->pPointer;
  595.   pDO->pEo = pEo;
  596.  
  597.   PopStackItem(pDO);
  598.  
  599.   return 0;
  600.   }
  601.  
  602. static pDebuggerObject new_DebuggerObject(pPrepext pEXT){
  603.   pDebuggerObject pDO;
  604.  
  605.   pDO = pEXT->pST->Alloc(sizeof(DebuggerObject),pEXT->pMemorySegment);
  606.   if( pDO == NULL )return NULL;
  607.  
  608.   pDO->pEXT = pEXT;
  609.   pDO->cGlobalVariables = 0;
  610.   pDO->ppszGlobalVariables = NULL;
  611.  
  612.   pDO->cUserFunctions = 0;
  613.   pDO->pUserFunctions = NULL;
  614.  
  615.   pDO->cFileNames = 0;
  616.   pDO->ppszFileNames = NULL;
  617.  
  618.   pDO->cNodes = 0;
  619.   pDO->Nodes = NULL;
  620.  
  621.   pDO->cSourceLines = 0;
  622.   pDO->SourceLines = NULL;
  623.  
  624.   pDO->Run2CallStack = 0;
  625.   pDO->Run2Line = 0;
  626.   return pDO;
  627.   }
  628.  
  629. /*
  630. This function allocates space for the file name in the
  631. preprocessor memory segment.
  632.  
  633. In case the name was already used then returns the pointer to
  634. the already allocated file name.
  635. */
  636. static char *AllocFileName(pPrepext pEXT,
  637.                            char *pszFileName
  638.   ){
  639.   long i;
  640.   pDebuggerObject pDO = pEXT->pPointer;
  641.   char **p;
  642.  
  643.   if( pszFileName == NULL )return NULL;
  644.   for( i=0 ;  i < pDO->cFileNames ; i++ )
  645.     if( !strcmp(pDO->ppszFileNames[i],pszFileName) )return pDO->ppszFileNames[i];
  646.   pDO->cFileNames++;
  647.   p = pEXT->pST->Alloc( sizeof(char *)*pDO->cFileNames,pEXT->pMemorySegment);
  648.   if( p == NULL )return NULL;
  649.   if( pDO->ppszFileNames ){
  650.     memcpy(p,pDO->ppszFileNames,sizeof(char *)*pDO->cFileNames);
  651.     pEXT->pST->Free(pDO->ppszFileNames,pEXT->pMemorySegment);
  652.     }
  653.   pDO->ppszFileNames = p;
  654.   pDO->ppszFileNames[pDO->cFileNames-1] = pEXT->pST->Alloc( strlen(pszFileName)+1,pEXT->pMemorySegment);
  655.   if( pDO->ppszFileNames[pDO->cFileNames-1] == NULL )return NULL;
  656.   strcpy(pDO->ppszFileNames[pDO->cFileNames-1],pszFileName);
  657.   return pDO->ppszFileNames[pDO->cFileNames-1];
  658.   }
  659.  
  660. static pUserFunction_t AllocUserFunction(pPrepext pEXT,
  661.                                          char *pszUserFunction
  662.   ){
  663.   pDebuggerObject pDO = pEXT->pPointer;
  664.   pUserFunction_t p;
  665.  
  666.   pDO->cUserFunctions++;
  667.   p = pEXT->pST->Alloc( sizeof(UserFunction_t)*pDO->cUserFunctions,pEXT->pMemorySegment);
  668.   if( p == NULL )return NULL;
  669.   if( pDO->pUserFunctions ){
  670.     memcpy(p,pDO->pUserFunctions,sizeof(UserFunction_t)*pDO->cUserFunctions);
  671.     pEXT->pST->Free(pDO->pUserFunctions,pEXT->pMemorySegment);
  672.     }
  673.   pDO->pUserFunctions = p;
  674.   pDO->pUserFunctions[pDO->cUserFunctions-1].pszFunctionName = pEXT->pST->Alloc( strlen(pszUserFunction)+1,pEXT->pMemorySegment);
  675.   if( pDO->pUserFunctions[pDO->cUserFunctions-1].pszFunctionName == NULL )return NULL;
  676.   strcpy(pDO->pUserFunctions[pDO->cUserFunctions-1].pszFunctionName,pszUserFunction);
  677.   pDO->pUserFunctions[pDO->cUserFunctions-1].ppszLocalVariables = NULL;
  678.   pDO->pUserFunctions[pDO->cUserFunctions-1].cLocalVariables = 0;
  679.   return &(pDO->pUserFunctions[pDO->cUserFunctions-1]);
  680.   }
  681.  
  682. void CBF_ListLocalVars(char *pszName,
  683.                        void *pSymbol,
  684.                        void **pv){
  685.   pSymbolVAR pVAR = pSymbol;
  686.   pUserFunction_t pUF= pv[0];
  687.   pPrepext pEXT = pv[1];
  688.  
  689.   pUF->ppszLocalVariables[pVAR->Serial-1] = pEXT->pST->Alloc(strlen(pszName)+1,pEXT->pMemorySegment);
  690.   if( pUF->ppszLocalVariables[pVAR->Serial-1] == NULL )return;
  691.   strcpy(pUF->ppszLocalVariables[pVAR->Serial-1],pszName);
  692.   }
  693.  
  694. void CBF_ListGlobalVars(char *pszName,
  695.                        void *pSymbol,
  696.                        void *pv){
  697.   pSymbolVAR pVAR = pSymbol;
  698.   pDebuggerObject pDO = pv;
  699.  
  700.   pDO->ppszGlobalVariables[pVAR->Serial-1] = pDO->pEXT->pST->Alloc(strlen(pszName)+1,pDO->pEXT->pMemorySegment);
  701.   if( pDO->ppszGlobalVariables[pVAR->Serial-1] == NULL )return;
  702.   strcpy(pDO->ppszGlobalVariables[pVAR->Serial-1],pszName);
  703.   }
  704.  
  705. int  /*DLL_EXPORT*/  dbg_preproc(pPrepext pEXT,
  706.                        long *pCmd,
  707.                        void *p
  708.   ){
  709.  
  710.   switch( *pCmd ){
  711.  
  712.          //reader has done all reading tasks, processed and linked the include files
  713.     case PreprocessorReadDone3:{
  714.       pReadObject pRo = p;
  715.       pDebuggerObject pDO = pEXT->pPointer;
  716.       pSourceLine Result;
  717.       long i;
  718.  
  719.       Result = pRo->Result;
  720.  
  721.       i = 0;
  722.       while( Result ){
  723.         i++;
  724.         Result = Result->next;
  725.         }
  726.       pDO->cSourceLines = i;
  727.       pDO->SourceLines = pEXT->pST->Alloc(sizeof(SourceLine_t)*i,pEXT->pMemorySegment);
  728.       *pCmd = PreprocessorUnload;
  729.       if( pDO->SourceLines == NULL )return 1;
  730.       Result = pRo->Result;
  731.       i = 0;
  732.       while( Result ){
  733.         pDO->SourceLines[i].line = pEXT->pST->Alloc(strlen(Result->line)+1,pEXT->pMemorySegment);
  734.         if( pDO->SourceLines[i].line == NULL )return 1;
  735.         strcpy(pDO->SourceLines[i].line,Result->line);
  736.         pDO->SourceLines[i].szFileName = AllocFileName(pEXT,Result->szFileName);
  737.         pDO->SourceLines[i].lLineNumber = Result->lLineNumber;
  738.         pDO->SourceLines[i].BreakPoint = 0;
  739.         i++;
  740.         Result = Result->next;
  741.         }
  742.       *pCmd = PreprocessorContinue;
  743.       return 0;
  744.       }
  745.  
  746.     //this is the very first call to the function within the actual interpreter thread.
  747.     //entry point is used when the preprocessor is loaded
  748.     case PreprocessorLoad:{
  749.       pDebuggerObject pDO;
  750.  
  751.       if( pEXT->lVersion != IP_INTERFACE_VERSION ){
  752.         *pCmd = PreprocessorUnload;
  753.         return 0;
  754.         }
  755.  
  756.       pDO = new_DebuggerObject(pEXT);
  757.       *pCmd = PreprocessorUnload;
  758.       if( pDO == NULL )return 1;
  759.  
  760.       pEXT->pPointer = pDO;
  761.       *pCmd = PreprocessorContinue;
  762.       return 0;
  763.       }
  764.  
  765.   //called from scriba_DoSyntaxAnalysis, syntax analyzer has finished
  766.   //peXobject->pCommandList points to the list of nodes.
  767.   case PreprocessorExFinish:{
  768.       peXobject pEx = p;
  769.       pDebuggerObject pDO = pEXT->pPointer;
  770.       peNODE_l Result = pEx->pCommandList;
  771.       long i;
  772.  
  773.       pDO->cNodes = pEx->NodeCounter;
  774.       pDO->Nodes = pEXT->pST->Alloc(sizeof(DebugNode_t)*pDO->cNodes,pEXT->pMemorySegment);
  775.       if( pDO->Nodes == NULL ){
  776.         *pCmd = PreprocessorUnload;
  777.         return 1;
  778.         }
  779.       for( i=0 ; i < pDO->cNodes ; i++ ){
  780.         pDO->Nodes[i].pszFileName = NULL;
  781.         pDO->Nodes[i].lLineNumber = 0;
  782.         pDO->Nodes[i].lSourceLine = 0;
  783.         }
  784.       while( Result ){
  785.         pDO->Nodes[Result->NodeId-1].pszFileName = AllocFileName(pEXT,Result->szFileName);
  786.         pDO->Nodes[Result->NodeId-1].lLineNumber = Result->lLineNumber;
  787.         Result = Result->rest;
  788.         }
  789.       pDO->cGlobalVariables = pEx->cGlobalVariables;
  790.       pDO->ppszGlobalVariables = pEXT->pST->Alloc( sizeof(char *)*pDO->cGlobalVariables,pEXT->pMemorySegment);
  791.       if( pDO->ppszGlobalVariables == NULL ){
  792.         *pCmd = PreprocessorUnload;
  793.         return 1;
  794.         }
  795.       pEXT->pST->TraverseSymbolTable(pEx->GlobalVariables,CBF_ListGlobalVars,pDO);
  796.       *pCmd = PreprocessorContinue;
  797.       return 0;
  798.       }
  799.  
  800.   //when the syntax analyzer starts a new local scope. This is when a function or a sub starts
  801.   case PreprocessorExStartLocal:{
  802.       peXobject pEx = p;
  803.       pDebuggerObject pDO = pEXT->pPointer;
  804.  
  805.       pDO->bLocalStart = 1;
  806.       *pCmd = PreprocessorContinue;
  807.       return 0;
  808.       }
  809.  
  810.   //when the syntax analyzer creates a new node for a basic program line.
  811.   case PreprocessorExLineNode:{
  812.       peXobject pEx = p;
  813.       pDebuggerObject pDO = pEXT->pPointer;
  814.  
  815.       if( pDO->bLocalStart ){
  816.         pDO->bLocalStart = 0;
  817.         pDO->FunctionNode = pEx->NodeCounter;
  818.         }
  819.       *pCmd = PreprocessorContinue;
  820.       return 0;
  821.       }
  822.  
  823.   //syntax analyzer exists a local scope. This is when a function or a sub ends.
  824.   case PreprocessorExEndLocal:{
  825.       peXobject pEx = p;
  826.       pUserFunction_t pUF;
  827.       pDebuggerObject pDO = pEXT->pPointer;
  828.       void *pv[2];
  829.  
  830.       *pCmd = PreprocessorContinue;
  831.       if( pEx->ThisFunction == NULL )return 0;/* may happen if syntax error in the BASIC program */
  832.       pUF = AllocUserFunction(pEXT,pEx->ThisFunction->FunctionName);
  833.       pUF->cLocalVariables = pEx->cLocalVariables;
  834.       if( pUF->cLocalVariables )
  835.         pUF->ppszLocalVariables = pEXT->pST->Alloc( sizeof(char *)*pUF->cLocalVariables,pEXT->pMemorySegment);
  836.       else
  837.         pUF->ppszLocalVariables = NULL;
  838.       pUF->NodeId = pDO->FunctionNode;
  839.       *pCmd = PreprocessorUnload;
  840.       if( pUF->cLocalVariables && pUF->ppszLocalVariables == NULL )return 1;
  841.       pv[0] = pUF;
  842.       pv[1] = pEXT;
  843.       pEXT->pST->TraverseSymbolTable(pEx->LocalVariables,(void *)CBF_ListLocalVars,pv);
  844.       *pCmd = PreprocessorContinue;
  845.       return 0;
  846.       }
  847.  
  848.   //entry point from scriba_Run, before the execution of the basic program starts
  849.   case PreprocessorExeStart:
  850.     { pExecuteObject pEo = p;
  851.       pDebuggerObject pDO = pEXT->pPointer;
  852.       pEo->pHookers->hook_pointer = pEXT;
  853.       pDO->CallStackDepth = 0;
  854.       pDO->DbgStack = NULL;
  855.       pDO->StackTop = NULL;
  856.       pEo->pHookers->HOOK_ExecBefore = MyExecBefore; /* called before executing a command       */
  857.       pEo->pHookers->HOOK_ExecAfter = MyExecAfter;   /* called after executing a command        */
  858.       pEo->pHookers->HOOK_ExecCall = MyExecCall;     /* executed when calling a function        */
  859.       pEo->pHookers->HOOK_ExecReturn = MyExecReturn; /* executed when returning from a function */
  860.       GetSourceLineNumber(pDO,1);/* to calculate all the node numbers for each lines (or the other way around?) */
  861.       comm_Init(pDO);
  862.       *pCmd = PreprocessorContinue;
  863.       return 0;
  864.       }
  865.  
  866.     default: /* in any cases that are not handled by the preprocessor just go on */
  867.       *pCmd = PreprocessorContinue;
  868.       return 0;
  869.     }
  870.  
  871.   }
  872.  

« Last Edit: May 03, 2015, 03:36:22 PM by John »

Offline John

  • Forum Support / SB Dev
  • Posts: 3510
    • ScriptBasic Open Source Project
Re: Script BASIC Extension Module - Thread Support
« Reply #27 on: May 14, 2015, 12:09:19 AM »
I was able to get embedding a SB script as a thread working. The Script BASIC MT extension module will take care of inter-thread and host communications. It's possible to restart the threaded script rather than exiting and I will add it later.

ttmain.sb (host SB script)
Code: ScriptBasic
  1. IMPORT sbt.inc
  2.  
  3. SB_ThreadStart("tt1.sb")
  4. SB_ThreadStart("tt2.sb")
  5. SB_ThreadStart("tt3.sb")
  6.  
  7. PRINT "SB Host\n"
  8. LINE INPUT wait
  9.  

tt1-3.sb (test thread scripts - all the same except thread ID)
Code: ScriptBasic
  1. ' Test Thread
  2.  
  3. DECLARE SUB SB_ThreadEnd ALIAS "SB_ThreadEnd" LIB "sbt"
  4.  
  5. FOR x = 1 TO 10
  6.   PRINT "Thread 1: ",x,"\n"
  7. NEXT
  8.  
  9. SB_ThreadEnd
  10.  

Output

jrs@laptop:~/sb/sb22/sbt$ scriba ttmain.sb
SB Host
Thread 2: 1
Thread 2: 2
Thread 2: 3
Thread 2: 4
Thread 2: 5
Thread 2: 6
Thread 2: 7
Thread 2: 8
Thread 2: 9
Thread 2: 10
Thread 3: 1
Thread 3: 2
Thread 3: 3
Thread 3: 4
Thread 3: 5
Thread 3: 6
Thread 3: 7
Thread 3: 8
Thread 3: 9
Thread 3: 10
Thread 1: 1
Thread 1: 2
Thread 1: 3
Thread 1: 4
Thread 1: 5
Thread 1: 6
Thread 1: 7
Thread 1: 8
Thread 1: 9
Thread 1: 10

jrs@laptop:~/sb/sb22/sbt$
« Last Edit: May 14, 2015, 12:49:08 AM by John »

Offline John

  • Forum Support / SB Dev
  • Posts: 3510
    • ScriptBasic Open Source Project
Re: Script BASIC Extension Module
« Reply #28 on: May 14, 2015, 01:18:28 PM »
I'm adding two optional arguments to SB_ThreadStart() to allow passing a command line argument and a configuration file. As it is now, whatever extension modules you IMPORT in the host script is usable in the threads with a simple DECLARE of the function. The thread currently is using the SB internal defaults which doesn't include path info to the modules & include directory.

Done!

Code: ScriptBasic
  1. IMPORT sbt.inc
  2.  
  3. SB_ThreadStart "tt1.sb", "JRS", "/etc/scriba/basic.conf"
  4.  
  5. PRINT "SB Host\n"
  6. LINE INPUT wait
  7.  

Code: ScriptBasic
  1. ' Test Thread
  2.  
  3. IMPORT sbt.inc
  4.  
  5. cmd = COMMAND()
  6. PRINT cmd,"\n"
  7.  
  8. FOR x = 1 TO 10
  9.   PRINT "Thread 1: ",x,"\n"
  10. NEXT
  11.  
  12. SB_ThreadEnd
  13.  


jjrs@laptop:~/sb/sb22/sbt$ scriba ttmain.sb
SB Host
JRS
Thread 1: 1
Thread 1: 2
Thread 1: 3
Thread 1: 4
Thread 1: 5
Thread 1: 6
Thread 1: 7
Thread 1: 8
Thread 1: 9
Thread 1: 10

jrs@laptop:~/sb/sb22/sbt$

« Last Edit: May 14, 2015, 03:20:01 PM by John »

Offline John

  • Forum Support / SB Dev
  • Posts: 3510
    • ScriptBasic Open Source Project
Re: Script BASIC Extension Module - MT
« Reply #29 on: May 14, 2015, 04:48:03 PM »
Here is an example of using the MT extension module to communicate between threads and the host script.

ttmain.sb
Code: ScriptBasic
  1. IMPORT mt.bas
  2. IMPORT sbt.inc
  3.  
  4. SB_ThreadStart("tt1.sb", "JRS","/etc/scriba/basic.conf")
  5. PRINT "SB Host\n"
  6. LINE INPUT wait
  7. PRINT mt::GetVariable("thread_status"),"\n"
  8.  

tt1.sb
Code: ScriptBasic
  1. ' Test Thread
  2.  
  3. IMPORT mt.bas
  4. IMPORT sbt.inc
  5.  
  6. cmd = COMMAND()
  7. PRINT cmd,"\n"
  8.  
  9. FOR x = 1 TO 10
  10.   PRINT "Thread 1: ",x,"\n"
  11. NEXT
  12.  
  13. mt::SetVariable "thread_status","Completed"
  14.  
  15. SB_ThreadEnd
  16.  

Output

jrs@laptop:~/sb/sb22/sbt$ scriba ttmain.sb
SB Host
JRS
Thread 1: 1
Thread 1: 2
Thread 1: 3
Thread 1: 4
Thread 1: 5
Thread 1: 6
Thread 1: 7
Thread 1: 8
Thread 1: 9
Thread 1: 10

Completed
jrs@laptop:~/sb/sb22/sbt$
« Last Edit: May 14, 2015, 06:45:59 PM by John »