Howto:Extend Nasal: Difference between revisions

Jump to navigation Jump to search
m
add syntax highlighting tags (more to be added later)
m (→‎Locking a Nasal context (threading): naModLock() and naModUnlock() - taken verbatim from the Nasal source code)
m (add syntax highlighting tags (more to be added later))
Line 32: Line 32:
The following is a copy of the extension function list, taken in 05/2009:
The following is a copy of the extension function list, taken in 05/2009:


<syntaxhighlight lang="cpp">
   // Table of extension functions.  Terminate with zeros.
   // Table of extension functions.  Terminate with zeros.
   static struct { const char* name; naCFunction func; } funcs[] = {
   static struct { const char* name; naCFunction func; } funcs[] = {
Line 55: Line 56:
     { 0, 0 }
     { 0, 0 }
   };
   };
</syntaxhighlight>


So, the basic format is "name" (string), function_pointer - whereas "name" refers to the internal name used by Nasal and its scripts, and "function_pointer" has to use the right function signature and is a pointer to the implementation of the Nasal function in C/C++ code space:
So, the basic format is "name" (string), function_pointer - whereas "name" refers to the internal name used by Nasal and its scripts, and "function_pointer" has to use the right function signature and is a pointer to the implementation of the Nasal function in C/C++ code space:


<syntaxhighlight lang="c">
  // The function signature for an extension function:
  // The function signature for an extension function:
  typedef naRef (*naCFunction)(naContext ctx, naRef me, int argc, naRef* args);
  typedef naRef (*naCFunction)(naContext ctx, naRef me, int argc, naRef* args);
 
</syntaxhighlight>


You will need to add your new extension function to this list of static functions, preferably following the existing naming convention (i.e. "f_" prefix).
You will need to add your new extension function to this list of static functions, preferably following the existing naming convention (i.e. "f_" prefix).
Line 69: Line 72:
These extension functions look like:
These extension functions look like:


<syntaxhighlight lang="c">
   static naRef f_your_function(naContext c, naRef me, int argc, naRef* args)
   static naRef f_your_function(naContext c, naRef me, int argc, naRef* args)
   {
   {
       // ...
       // ...
   }
   }
</syntaxhighlight>


So, this is basically the boilerplate that you'll need to use in order to expose a new function (i.e. you can just copy & paste this into the NasalSys.cxx file and change the function's name accordingly).
So, this is basically the boilerplate that you'll need to use in order to expose a new function (i.e. you can just copy & paste this into the NasalSys.cxx file and change the function's name accordingly).
Line 78: Line 83:
If you don't have anything else to return, you can just return naNil(), so that a function named "f_cool" looks like this:
If you don't have anything else to return, you can just return naNil(), so that a function named "f_cool" looks like this:


<syntaxhighlight lang="c">
   static naRef f_cool (naContext c, naRef me, int argc, naRef* args)
   static naRef f_cool (naContext c, naRef me, int argc, naRef* args)
   {
   {
Line 83: Line 89:
       return naNil();
       return naNil();
   }
   }
</syntaxhighlight>


In order to make this function known to the Nasal interpreter, you'll want to extend the previously mentioned list of Nasal extension functions (in src/Scripting/NasalSys.cxx), to read:
In order to make this function known to the Nasal interpreter, you'll want to extend the previously mentioned list of Nasal extension functions (in src/Scripting/NasalSys.cxx), to read:
Line 94: Line 101:
Once you have made these modifications and rebuilt FlightGear, you can simply invoke your new function, of course it will not yet do anything useful, though:
Once you have made these modifications and rebuilt FlightGear, you can simply invoke your new function, of course it will not yet do anything useful, though:


<syntaxhighlight lang="php">
  #cooltest.nas (to be saved in $FG_ROOT/Nasal)
  #cooltest.nas (to be saved in $FG_ROOT/Nasal)
  cool();
  cool();
</syntaxhighlight>


So, in order to make it slightly more interesting, we are going to change the return statement to return something else, instead of nil:
So, in order to make it slightly more interesting, we are going to change the return statement to return something else, instead of nil:


<syntaxhighlight lang="c">
   static naRef f_cool (naContext c, naRef me, int argc, naRef* args)
   static naRef f_cool (naContext c, naRef me, int argc, naRef* args)
   {
   {
Line 106: Line 116:
       return retval;
       return retval;
   }
   }
<syntaxhighlight>


So, after rebuilding the FlightGear binary, whenever you call the new "cool" API function, it will always return a "cool" string, which you could for example print out using a script like the following:
So, after rebuilding the FlightGear binary, whenever you call the new "cool" API function, it will always return a "cool" string, which you could for example print out using a script like the following:


<syntaxhighlight lang="php">
  #cooltest.nas (to be saved in $FG_ROOT/Nasal)
  #cooltest.nas (to be saved in $FG_ROOT/Nasal)
  var result=cool();
  var result=cool();
  print(result);
  print(result);
</syntaxhighlight>


= Argument Processing =
= Argument Processing =
Consider the callback signature:
Consider the callback signature:
<syntaxhighlight lang="c">
  static naRef f_cool (naContext c, naRef me, int argc, naRef* args)
  static naRef f_cool (naContext c, naRef me, int argc, naRef* args)
<syntaxhighlight>


The arguments to the function are passed in in the args array. The number of arguments is passed via the argc parameter (this is basically consistent with the standard signature of main in C/C++).
The arguments to the function are passed in in the args array. The number of arguments is passed via the argc parameter (this is basically consistent with the standard signature of main in C/C++).
Line 121: Line 136:
So, if you know that you require a certain number of arguments, you can also directly check argc to to match your requirements and show an error message, return nil, or throw an exception using naRuntimeError():
So, if you know that you require a certain number of arguments, you can also directly check argc to to match your requirements and show an error message, return nil, or throw an exception using naRuntimeError():


<syntaxhighlight lang="c">
  // Throw an error from the current call stack.  This function makes a
  // Throw an error from the current call stack.  This function makes a
  // longjmp call to a handler in naCall() and DOES NOT RETURN.  It is
  // longjmp call to a handler in naCall() and DOES NOT RETURN.  It is
Line 128: Line 144:
  // printf().
  // printf().
  void naRuntimeError(naContext c, const char* fmt, ...);
  void naRuntimeError(naContext c, const char* fmt, ...);
 
</syntaxhighlight>


The "me" reference is set if the function was called as a method call on an object (e.g. object.your_function() instead of just your_function(), in which case "me" would be set to the object).
The "me" reference is set if the function was called as a method call on an object (e.g. object.your_function() instead of just your_function(), in which case "me" would be set to the object).
Line 136: Line 152:
Basically, you can check the type of the reference with the following naIs*() functions:
Basically, you can check the type of the reference with the following naIs*() functions:


<syntaxhighlight lang="c">
  int naIsNil(naRef r);
  int naIsNil(naRef r);
  int naIsNum(naRef r);
  int naIsNum(naRef r);
Line 145: Line 162:
  int naIsFunc(naRef r);
  int naIsFunc(naRef r);
  int naIsCCode(naRef r);
  int naIsCCode(naRef r);
 
</syntaxhighlight>
And then get the appropriate value out with things like naNumValue(), naStr_data(), etc...
And then get the appropriate value out with things like naNumValue(), naStr_data(), etc...


So, if we're now to change the interface of our earlier "cool" function in the example, to only return "cool" if we are passed at least one numerical parameter, and otherwise return "uncool", it could look like this:
So, if we're now to change the interface of our earlier "cool" function in the example, to only return "cool" if we are passed at least one numerical parameter, and otherwise return "uncool", it could look like this:


<syntaxhighlight lang="c">
  static naRef f_cool (naContext c, naRef me, int argc, naRef* args)
  static naRef f_cool (naContext c, naRef me, int argc, naRef* args)
  {
  {
Line 162: Line 180:
     return naStr_fromdata(retval, result, strlen(result) );
     return naStr_fromdata(retval, result, strlen(result) );
  }
  }
</syntaxhighlight>


You can test this, by running a simple Nasal script like the following:
You can test this, by running a simple Nasal script like the following:


<syntaxhighlight lang="php">
  # test new cool() function:
  # test new cool() function:
  print( cool("foo") );  
  print( cool("foo") );  
  print( cool(100)  );
  print( cool(100)  );
</syntaxhighlight>


A common way to evaluate parameters passed to Nasal extension functions in C, looks like this:


A common way to evaluate parameters passed to Nasal extension functions in C, looks like this:
<syntaxhighlight lang="c">
  static naRef f_foo (naContext c, naRef me, int argc, naRef* args) {
  static naRef f_foo (naContext c, naRef me, int argc, naRef* args) {
     naRef param1 = argc > 0 ? args[0] : naNil();
     naRef param1 = argc > 0 ? args[0] : naNil();
Line 178: Line 200:
     return naNil;
     return naNil;
  }
  }
</syntaxhighlight>


This will basically look for 3 parameters, and assign them accordingly - if they are not available, they will just be assigned nil using the naNil() calls (the next step would be to check if the parameters have the right types using the naIs* helpers mentioned above).
This will basically look for 3 parameters, and assign them accordingly - if they are not available, they will just be assigned nil using the naNil() calls (the next step would be to check if the parameters have the right types using the naIs* helpers mentioned above).
Line 184: Line 207:
There are also functions to create Nasal data structures (hash tables, vectors, etc...) which you can return to the caller simply by returning the resulting naRef:
There are also functions to create Nasal data structures (hash tables, vectors, etc...) which you can return to the caller simply by returning the resulting naRef:


<syntaxhighlight lang="c">
  naRef naNil();
  naRef naNil();
  naRef naNum(double num);
  naRef naNum(double num);
Line 191: Line 215:
  naRef naNewFunc(naContext c, naRef code);
  naRef naNewFunc(naContext c, naRef code);
  naRef naNewCCode(naContext c, naCFunction fptr);
  naRef naNewCCode(naContext c, naCFunction fptr);
</syntaxhighlight>


== String Manipulation API ==
== String Manipulation API ==

Navigation menu