1 - Introduction
I suck at writing documentation, so this will be short and bitter. For working samples, check out scripts/tests/*.ns
Nesla's earliest lines of code were derived from all the configuration file parsers I kept writing over and over, and was never happy with. With the hassle of writing new functions to deal with different files, and adding callback functions to deal with subsections, and _then_ taking the parsed data and making that data accessible to other loaded functions and modules, not to mention the whole memory management part, and the need to keep track of not only the name and type of variable, but also the size... Well, I guess I'm just lucky I have a sense of humour.
So here was a goal: A flexible config parser, a simple and universal data storage model, a short and simple command set, a zero effort memory management system that didn't suck, and a C api that wouldn't be painful or difficult to use in other projects. Whether or not it became a fully functional scripting language of its own was entirely incidental. What I ended up with is the Nesla core library; the scripting language for C programmers.
Syntactically, Nesla probably looks more like javascript than it does any other language. Use of the word 'object' may be less than 100% accurate, there are no properties, methods, events, or classes in the javascript sense, but the C syntax rules are nearly identical. Nesla is not an emulation of javascript, but both language designs do agree that C syntax is good and dollar signs are ugly.
Keywords
break continue return function global local var if else for do while exit
Working code looks a little like the following sample.
break (accepts an optional unbracketed number arg to define the number of levels to break)
still need to add switch and goto..
Operators
= + ++ += - -- -= * *= / /= == != <= >= < > % & | ^ && || !
Strings can also be handled using the slightly shorter list of ops: = + += == != <= >= < >
The statement var x="a"+"b"; will set x to "ab".
Tables have considerably less ops: = {}
setting a var's value to null will completely free the value and effectively make the var non-existant
Objects And Classes
OOPS So here's the deal on 'Object Oriented Programming'. I don't get it. I'm not saying it's bad or that people who do it are deviants. I'm just not sure what the working definition of OO is these days. Nesla's storage system is entirely object-based, but that doesn't make the language object oriented. I added a 'this' variable so functions could infer the context in which they were called. Does this make Nesla an OO language? Consider the following code:
Is that technically an example of OO? True, most OO code has nicer looking constructors and classes and stuff I don't get, but this is working Nesla code.
The Basics
At the most basic level, objects are tables. The only thing that makes an object an object, and not _just_ a table, is 'this'. An object can be created as simply as doing this:
'this' is a variable that links to the parent table and exists to provide a context for methods in the object. You should never create an object in the fashion above because it makes code harder to read. Otherwise, it's a perfectly valid way to create objects. If you want to extend this object with properties and methods, you can do so like this:
And of course, you can call the method in the object like this:
Again, while valid, you should never create objects as shown above.
Constructors
An example of one preferred method would look like this.The function alpha is what is known as a constructor. A constructor is a function that is used to help create an instance of a particular object type. Notice the keyword 'new'. This is what tells the interpreter to create and use an object as the context for the function call, and to return that object as the function's result. This method of creating an object is very similar to how Javascript instantiates objects. Notably, both have no class.
Classes
The use of classes in building objects is optional, but it can be useful.
Do you see the difference? Me neither. Actually there is one major difference here, but it is not obvious. A class is not a function. In fact, it is a table, and this difference is _very_ important for one reason; tables can be linked. This means that an object (a table) can now be linked to a class (a table) without having to recreate it. This can be much faster when creating new objects, and can use significantly less memory when creating a lot of them. Now in this case, the function pc and the variable c have a single instance shared by both objects x and y.
Note the addition of the alpha function to the class. This is a constructor. Any function with the same name as the class (or the name '_constructor') will be called when an object is created. This provides the same level of control over new objects as that of using a function instead of a class.
Inheritance
Using table linking with classes and objects has one other benefit; inheritance. In this example, a new object is created using the beta class, and it also 'inherits' the alpha class, including all functions and variables. It also 'overrides' the value of c without changing the original value in the alpha class.
The inherit function is not limited to use in classes, and can be used on any tables.
2 - The Language
serialize
Description
Print the provided var in a format appropriate for use in a nesla script. This will recursively print entire tables.
Syntax
serialize(object var)
Parameter Description var Object to be exported. Can be any object type, including table.
Return Value
Returns zero.
Remarks
Any argument that is not a string will be formatted automatically. Boolean values will be 'true' or 'false' and numbers will have up to 6 digits past the decimal. Most string functions may be binary safe, but this function _will_ terminate its output at the first \0 character.
The example "var x={}; x.y=x; serialize(x);" is valid code. It also recurses infinitely. Be careful, or you may be cursing infinitely yourself.
Examples
The following examples demonstrate the difference between print and serialize.
var x="this \"is a\" test\n";
print(x);
will display
this "is a" test
var x="this \"is a\" test\n";
print(serialize(x));
will display
"this \"is a\" test\n"
var x = { [0] = { key = "a" }, [1] = { key = "b" } };
print(serialize(x));
will display
{ [0] = { key = "a" }, [1] = { key = "b" } }
include
Description
Include (and interpret) the code in the specified file.
Syntax
boolean = include(filename)
Parameter Description filename String containing the name of the file to be included.
Return Value
Returns true if the file has been successfully included. Any false return code indicates an error.
Remarks
Return codes indicating failure are almost always caused by a nonexistent or unreadable script file, not errors in the script itself.
Example
The following example demonstrates how to load a script and verify its success.
rval=include("./somefile.ns");
if (rval!=true) print("error loading ./somefile.ns\n");
Description
Print the complete list of arguments to the standard output.
Syntax
number = print(...)
Parameter Description ... List of objects to be printed.
Return Value
Returns the number of characters printed.
Remarks
Any argument that is not a string will be formatted automatically. Boolean values will be 'true' or 'false' and numbers will have up to 6 digits past the decimal. Most string functions may be binary safe, but this function _will_ terminate its output at the first \0 character.
Examples
The following example demonstrates how to print multiple objects.
print("Hello", "world.\n");
The following example gives the same output as above, but note that using + to concatenate the strings means that print only receives one argument.
print("Hello"+"world.\n");
See Also
io.print()
runtime
Description
Returns the elapsed time the parser has been running.
Syntax
number = runtime()
Return Value
Returns the time in seconds (with microsecond precision) the parser has been running.
sizeof
Description
Returns the size of the object.
Syntax
number = sizeof(object)
Parameter Description object Object to be sized. The return value's meaning will vary depending on this object's type.
Return Value
Returns the size of the object. For strings, this is the string length; for tables, the number of non-null objects in the table. Null values are zero size.
sleep
Description
Sleep for a specified number of seconds.
Syntax
sleep(number seconds)
Parameter Description seconds Number of seconds the script should pause execution.
Return Value
Returns zero.
system
Description
Performs an operating system call and returns the resulting errorcode.
Syntax
number = system(string command)
Parameter Description command Operating system command to be executed.
Return Value
Returns the errorcode after execution of command.
Remarks
This function does not include or easily allow any sophisticated methods such as piping, signal handling, or interactivity.
Examples
The following example demonstrates a way to list a directory and then read the results.
system("ls"+" -l"+" > x.txt");
print(file.readall("x.txt"));
file.unlink("x.txt");
tonumber
Description
Returns a number value equivalent of the source.
Syntax
number = tonumber(object)
Parameter Description object Object to be converted to a numeric value.
Return Value
Returns a numeric approximation of the object parameter. Objects such as tables and null will return zero since no other numeric value can be meaningfully representative.
tostring
Description
Returns a string value equivalent of the source.
Syntax
string = tostring(object[, number])
Parameter Description object Object to be converted to a numeric value. number Precision to use for a numeric object.
Return Value
Returns a string value equivalent of the source. If both arguments are numbers, the second allows the setting of a decimal precision.
typeof
Description
Returns the name of the type of object.
Syntax
string = typeof(object)
Parameter Description object Object to be identified by type.
Return Value
Returns the name of the object type. Possible types include boolean, number, string, table, function and null.
write
Description
Writes the entire object as a string and returns the result code. This function is binary safe.
Syntax
number = write(object)
Parameter Description object Object to be written to standard output.
Return Value
Returns the number of bytes written.
See Also
io.write()
3 - file.*()
(string) lib.file.append(string filename[, object]);
Writes the entire object to the end of an existing file as a string and returns the result code.
This function is binary safe.
(string) lib.file.read(string filename);
Reads the entire file into a string and returns that string.
This function is binary safe, but use of some string functions to manipulate this string may mangle the data.
(string) lib.file.rename(string filename, string newfilename);
Renames a file.
(table) lib.file.stat(string filename);
Stats filename and returns a table with stats.
(number) lib.file.unlink(string filename);
Unlink filename and return zero if successful.
(number) lib.file.write(string filename[, object]);
Writes the entire object as a string and returns the result code.
This function is binary safe.
4 - io.*()
lib.io.gets
Description
Get a string from the console.
Syntax
string = lib.io.gets()
Return Value
Returns the string entered.
lib.io.flush
Description
Flushes the output buffer.
Syntax
number = io.flush()
Return Value
Returns zero.
lib.io.print
Description
Print the complete list of arguments to the standard output.
Syntax
number = io.print(...)
Parameter Description ... List of objects to be printed.
Return Value
Returns the number of characters printed.
Remarks
Any argument that is not a string will be formatted automatically. Boolean values will be 'true' or 'false' and numbers will have up to 6 digits past the decimal. Most string functions may be binary safe, but this function _will_ terminate its output at the first \0 character.
Examples
The following example demonstrates how to print multiple objects.
io.print("Hello", "world.\n");
The following example gives the same output as above, but note that using + to concatenate the strings means that print only receives one argument.
io.print("Hello"+"world.\n");
See Also
print()
lib.io.write
Description
Writes the entire object as a string and returns the result code. This function is binary safe.
Syntax
number = io.write(object)
Parameter Description object Object to be written to standard output.
Return Value
Returns the number of bytes written.
See Also
write()
5 - math.*()
6 - string.*()
A series of functions exist in the string namespace for working with strings. Most of these functions can also be accessed as methods of the base type itself. For example, 's.sub(0, 2)' is the equivalent of 'string.sub(s, 0, 2)' (assuming 's' is a string). A list of these methods is shown here.
Methods
Method Name | Description |
---|---|
gettype() | Returns a string describing the base type of a string, which is always "string". |
istr(string x) | Returns a substring starting at the point of the first case-insensitive match of x in the string, or null if no match is found. |
length() aka len() |
Returns the length of the string. |
replace(string x) | Returns a new string with occurences of x in this string replaced. |
split(string x, string y) | Returns a table containing substrings of x, separated by y. |
str(string x) | Returns a substring starting at the point of the first match of x in the string, or null if no match is found. |
substring(number x, [number y]) aka sub(number x, [number y]) |
Return a substring starting at index x, and continuing for y characters. If x is negative, the substring starting point is the end of the string minux x. |
tolower() | Returns a lower-case copy of the string. |
tostring() aka tostr() |
Returns a string representation of the object. If the object is already a string, an unmodified copy of the original is returned. |
toupper() | Returns an upper-case copy of the string. |
pages = {
{ name="string.atoi", tag="atoi" };
{ name="string.cat", tag="cat" };
{ name="string.cmp", tag="cmp" };
{ name="string.icmp", tag="icmp" };
// { name="string.istr", tag="istr" };
{ name="string.itoa", tag="itoa" };
{ name="string.join", tag="join" };
// { name="string.len", tag="len" };
{ name="string.ncmp", tag="ncmp" };
{ name="string.nicmp", tag="nicmp" };
// { name="string.replace", tag="replace" };
// { name="string.split", tag="split" };
// { name="string.str", tag="str" };
// { name="string.sub", tag="sub" };
// { name="string.tolower", tag="tolower" };
// { name="string.toupper", tag="toupper" };
};
(string) string.cat(string str1, string str2);
string.cat(x, y) will not change x or y. to set x to the result, use "x=string.cat(x, y)"
actually, all string functions should work like this (by not modifying the original arguments.
(number) string.cmp(string str1, string str2);
Case sensitive string comparison.
(number) string.icmp(string str1, string str2);
Case insensitive string comparison.
(string) string.istr(string stack, string needle);
Case insensitive substring search.
(table) string.join(table str, string sep);
This will merge the table str into a single string separated by sep.
(number) string.len(string str);
Length of the string (binary safe).
(number) string.ncmp(string str1, string str2, number len);
Case sensitive substring comparison up to len characters.
(number) string.nicmp(string str1, string str2, number len);
Case insensitive substring comparison up to len characters.
(table) string.split(string str, string sep);
This will separate the string into substrings and put those substrings in the returned table.
(string) string.str(string stack, string needle);
Case sensitive substring search.
(string) string.sub(string stack, number offset[, number maxlen]);
Returns a substring ranging from offset to maxlen.
7 - lib.table.*()
8 - lib.time.*()
All time functions assume the use of UTC times as input, and with the sole
exception of time.localtime(), all functions return time in UTC if no input
is given, and do not convert time between zones. When getting a time structure,
if you do not want to convert between timezones, will likely want to use the gmtime
function, even when are working with local times, to avoid repeated conversions.
For example,
time.gmtime(time.mktime(time.gmtime()))
will return the same value in the first call to gmtime as the second. In
the case of
time.localtime(time.mktime(time.localtime()))
, the time zone difference is added multiple times, which is very likely not
the desired effect. To convert the time zone only once, this would be safe:
time.gmtime(time.mktime(time.localtime()))
time.now() /* returns a number (seconds since January 1, 1970 UTC) */
time.gettimeofday() /* returns a table with the current time in seconds and microseconds */
/* { tv_sec = 1534689210, tv_usec = 747662 }
*/
time.gmtime([number|string]) /* returns a table describing the current date and time (UTC), */
/* or describing the number provided without converting the time zone. */
time.localtime([number|string]) /* returns a table describing the current date and time , or describing */
/* the number provided, in both cases converting the time to your */
/* system's configured local time. */
time.mktime(table|string) /* returns a number representing the seconds since January 1, 1970 UTC as described in the parameter */
/* obsoletes time.tmtounix(table) and time.sqltounix(string) */
time.sqltime([table|number]) /* returns a string in SQL format of the time provided by the numeric or tm table parameter */
time.asctime([table|number]) /* identical to sqltime but may in the future return dates in other string formats */
//sample tm-based (calendar time) table - Sat Aug 18 21:29:03 EDT 2018
time.localtime() returns {
tm_sec = 3, /* The number of seconds after the minute, from 0-59. */
tm_min = 29, /* The number of minutes after the hour, from 0-59. */
tm_hour = 21, /* The number of hours past midnight, from 0-23. */
tm_mday = 18, /* The day of the month, from 1-31. */
tm_mon = 8, /* The month of the year, from 1-12. */
tm_year = 2018, /* The year. */
tm_wday = 6, /* The number of days after Sunday, from 0-6. */
tm_yday = 229, /* The number of days since January 1, from 0-365. */
tm_isdst = 1 /* A flag indicating daylight savings time is in effect for this date. */
}
lib.time.asctime
Description
Returns a string representation of timestamp in the format 'YYYY-MM-DD HH:MM:SS'. If timestamp is not defined, the function will use the current time.
Syntax
string = time.asctime([number timestamp])
Parameter Description timestamp The number of seconds since January 1, 1970 UTC (commonly called a UNIX timestamp).
Return Value
Returns a formatted date string in the format 'YYYY-MM-DD HH:MM:SS'.
Remarks
No remarks.
Examples
print(time.asctime());
The above example will output something similar to: "2015-01-01 16:30:45"
See Also
time.sqltime()
lib.time.gettimeofday
Description
Returns a table with the elapsed time in seconds and microseconds since January 1, 1970.
Syntax
table = time.gettimeofday()
Return Value
Returns a table with the time in seconds and microseconds since the creation of the UNIXverse.
{
tv_sec = 1534689210,
tv_usec = 747662
}
lib.time.gmtime
Description
Returns a table representation of timestamp. If timestamp
is not defined, the function will use the current local system date.
{
tm_sec = 3, /* The number of seconds after the minute, from 0-59. */
tm_min = 29, /* The number of minutes after the hour, from 0-59. */
tm_hour = 21, /* The number of hours past midnight, from 0-23. */
tm_mday = 18, /* The day of the month, from 1-31. */
tm_mon = 8, /* The month of the year, from 1-12. */
tm_year = 2018, /* The year. */
tm_wday = 6, /* The number of days after Sunday, from 0-6. */
tm_yday = 229, /* The number of days since January 1, from 0-365. */
tm_isdst = 0 /* A flag indicating daylight savings time is in effect for this date. */
}
lib.time.localtime
Description
Returns a table representation of timestamp. If timestamp
is not defined, the function will use the current local system date.
{
tm_sec = 3, /* The number of seconds after the minute, from 0-59. */
tm_min = 29, /* The number of minutes after the hour, from 0-59. */
tm_hour = 21, /* The number of hours past midnight, from 0-23. */
tm_mday = 18, /* The day of the month, from 1-31. */
tm_mon = 8, /* The month of the year, from 1-12. */
tm_year = 2018, /* The year. */
tm_wday = 6, /* The number of days after Sunday, from 0-6. */
tm_yday = 229, /* The number of days since January 1, from 0-365. */
tm_isdst = 1 /* A flag indicating daylight savings time is in effect for this date. */
}
lib.time.mktime
Description
Returns the time in seconds (represented by the parameter) since January 1, 1970.
Syntax
number = time.mktime(table|string)
Return Value
Returns the time in seconds represented by the table or sql-formatted string provided as a parameter.
lib.time.now
Description
Returns the elapsed time in seconds since January 1, 1970 UTC.
Syntax
number = time.now()
Return Value
Returns the time in seconds since the creation of the UNIXverse.
lib.time.sqltime
Description
Returns a string representation of timestamp in the format 'YYYY-MM-DD HH:MM:SS'. If timestamp is not defined, the function will use the current time.
Syntax
string = time.sqltime([number timestamp])
Parameter Description timestamp The number of seconds since January 1, 1970 UTC (commonly called a UNIX timestamp).
Return Value
Returns a formatted date string in the format 'YYYY-MM-DD HH:MM:SS'.
Remarks
sqltime is functionally identical to asctime, but future versions of asctime might return dates in different string formats.
Examples
print(time.sqltime());
The above example will output something similar to: "2015-01-01 16:30:45"
See Also
time.asctime()
9 - lib.data.*()
(table) lib.data.xml.read(string str);
Read the supplied XML text and return a digested table. This is _not_ a pretty function.
10 - The Extension Libraries
neslaext functions
(string) base64.decode(string encoded);
Return a decoded value from a base64-encoded string.
(string) base64.encode(string decoded);
Return an encoded value from a raw string.
(table) dirlist(string dirname);
Returns a table containing files and attributes in the supplied dirname.
(string) rot13(string str);
Returns a rot13 *cough* encoded string from str.
Double-rot13 encryption is so stealthy, you won't even know it's been encrypted.
11 - lib.net.*()
(table) http.get(number ssl, string host, number port, string uri);
Connect to an HTTP server and retrieve the requested uri. Returns a table with { head, body } elements.
lib.load("net")
hconn = new lib.net.http.client("http://nulllogic.ca/");
if (typeof(x=hconn.send())=='table') {
print("\t", x.status, "\n");
}
ssh.open
Description
Cloae an SSH connection.
Syntax
number = ssh.close(conn)
Parameter Description conn Server connection object to be closed.
Return Value
Always returns zero.
Remarks
None
Example
The following example demonstrates how to close a host connection.
ssh.close(conn);
ssh.open
Description
Open an SSH connection to a given host/port.
Syntax
ssh-conn = ssh.open(hostname[, port])
Parameter Description hostname String containing the name of the server to connect to. port Number containing the server port to connect to (default is 22).
Return Value
Returns an ssh-conn connection object if successful. Any number returned likely indicates an error.
Remarks
None
Example
The following example demonstrates how to connect to a host and verify success.
if (typeof(conn=ssh.open("192.168.0.1", 22))!='ssh-conn') {
print("failed connection");
exit;
}
/* checking the host's RSA key fingerprint is optional, but a good idea. */
key="ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff";
if ((k=ssh.hostkey(conn))!=key) {
print("expected = "+key+"\nrecieved = "+k+"\n");
print("Host key verification failed.\n");
exit;
}
print("RSA key fingerprint is "+k+".\n");
if (ssh.auth(conn, "username", "password")!=true) {
print("failed auth");
exit;
}
/*
* do something useful here
*/
ssh.close(conn);
net.tcp.socket
Description
Returns an object that can be used as either a client or a listening socket.
Methods
Name Description bind Binds a socket to the specified local host and port. connect Connects a socket to the specified remote host and port. accept Creates a new socket for incoming requests to a bound socket. close Closes a socket and destroys the socket object. gets Reads a line of text from an open socket. read Reads incoming data from an open socket. write Writes data to an open socket. info Returns information about an open socket.
Remarks
None.
Examples
#!/usr/bin/nsp function test_http() { print("trying http://localhost:80/ - "); sock=new net.tcp.socket(); sock.connect("localhost", 80, false); if (typeof(sock.socket)!='sock4') { print("can't connect to http server\n"); return; } o=sock.write("GET / HTTP/1.0\r\n\r\n"); i=sock.gets(); printf("[%s]\n", i); do { i=sock.gets(); } while (i!=""); do { i=sock.read(); } while (i!=""); sock.close(); } test_http();
(sock4) tcp.info(sock4 socket);
Returns a table containing information about the socket.
12 - The C API
I'm lazy, so here's the long ugly version. This is everything your C program will ever need to know about Nesla. Actually, it needs to know a lot less, but hey.
/*
NESLA NullLogic Embedded Scripting Language
Copyright (C) 2007-2022 Dan Cahill
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _NSP_H
#define _NSP_H 1
#ifdef __cplusplus
extern "C" {
#endif
#define NSP_NAME "nesla"
#define NSP_VERSION "0.9.4"
#if defined(TINYCC)||defined(__TURBOC__)
struct timeval { long tv_sec; long tv_usec; };
struct timezone { int tz_minuteswest; int tz_dsttime; };
#endif
#if defined(_MSC_VER)
struct timezone { int tz_minuteswest; int tz_dsttime; };
#pragma warning(disable:4996)
#define WIN32_LEAN_AND_MEAN
//#define _USE_32BIT_TIME_T
/* always include winsock2 before windows */
#include
#include
#include
#if defined(_WIN32) && defined(_DEBUG)
#include
#endif
#elif !defined(__TURBOC__)
#include
#include
#endif
#include
/* need to add size sanity here */
typedef signed long long int int64;
typedef unsigned long long int uint64;
#if defined(__TURBOC__)
typedef signed long int int32;
typedef unsigned long int uint32;
#elif defined(_LP64)
typedef signed int int32;
typedef unsigned int uint32;
#else
typedef signed int int32;
typedef unsigned int uint32;
#endif
typedef signed char int8;
typedef unsigned char uint8;
#define MAX_OBJNAMELEN 64
#define MAX_OUTBUFSIZE 4096
#define OUTBUFLOWAT 2048
/* object types */
#define NT_NULL 0
#define NT_BOOLEAN 1
#define NT_NUMBER 2
#define NT_STRING 3
#define NT_NFUNC 4
#define NT_CFUNC 5
#define NT_TABLE 6
#define NT_CDATA 7
/* object status flags */
#define NST_HIDDEN 0x01
#define NST_READONLY 0x02
#define NST_SYSTEM 0x04
#define NST_AUTOSORT 0x08
#define NST_LINK 0x10
#define NST_STACKVAL 0x20
#define num_t double
#define uchar unsigned char
#define obj_t struct nsp_objrec
#define tab_t struct nsp_tablerec
#define val_t struct nsp_valrec
#define nsp_t struct nsp_state
/* should be typedef int(*NSP_CFUNC)(nsp_state *); */
typedef int(*NSP_CFUNC)(void *);
#define NSP_FUNCTION(name) int name(nsp_state *N)
#define NSP_CLASS(name) int name(nsp_state *N)
#define NSP_CLASSMETHOD(name) int name(nsp_state *N)
/*
* define a callback function type so CDATA objects
* can choose the terms of their own death.
*/
/* should be typedef void(*NSP_CFREE)(nsp_state *, obj_t *); */
typedef void(*NSP_CFREE)(void *, void *);
typedef struct NSP_CDATA {
/* standard header info for CDATA object */
char obj_type[16]; /* tell us all about yourself in 15 characters or less */
NSP_CFREE obj_term; /* now tell us how to kill you */
/* now begin the stuff that's type-specific */
} NSP_CDATA;
typedef struct nsp_tablerec {
obj_t *f;
obj_t *i;
obj_t *l;
} nsp_tablerec;
typedef struct nsp_valrec {
unsigned short type; /* val type */
unsigned short attr; /* status flags (hidden, readonly, system, autosort, etc...) */
unsigned short refs; /* number of references to this node */
unsigned long size; /* storage size of string, nfunc or cdata */
obj_t *ztable; /* 'z' table for hierarchical lookups */
union {
num_t num;
char *str;
NSP_CFUNC cfunc;
NSP_CDATA *cdata;
tab_t table;
} d;
} nsp_valrec;
typedef struct nsp_objrec {
obj_t *prev;
obj_t *next;
val_t *val;
uint32 hash;
signed long nval;
char name[MAX_OBJNAMELEN + 1];
} nsp_objrec;
typedef struct nsp_execcontext {
obj_t l; // local variables
obj_t t; // 'this' object
uchar *blockptr;
uchar *blockend;
uchar *readptr;
uchar yielded;
char *funcname;
char *filename;
char *tracefn;
long int linenum;
} nsp_execcontext;
typedef struct nsp_state {
jmp_buf *savjmp;
nsp_execcontext *context; // local execution context
obj_t g; // global variables
obj_t r; // return variable
short brk;
short cnt;
short ret;
short err;
short signal; /* intended for external signals to the parser. for now, non-zero just means to shut down */
short debug;
short single;
short yielded;
short strict;
short warnings;
short maxwarnings;
char warnformat;
struct timeval ttime;
char numbuf[128];
char *outbuffer;
unsigned short outbuflen;
unsigned short outbufmax;
char errbuf[256];
/* debug info */
long int allocs;
long int allocmem;
long int frees;
long int freemem;
long int peakmem;
long int counter1;
} nsp_state;
#ifndef NSP_NOFUNCTIONS
/* exec */
nsp_state *nsp_newstate(void);
void nsp_freestate(nsp_state *N);
nsp_state *nsp_endstate(nsp_state *N);
obj_t *nsp_exec(nsp_state *N, const char *string);
int nsp_execfile(nsp_state *N, char *file);
/* objects */
void nsp_setvaltype(nsp_state *N, obj_t *cobj, unsigned short type);
void nsp_linkval(nsp_state *N, obj_t *cobj1, obj_t *cobj2);
void nsp_unlinkval(nsp_state *N, obj_t *cobj);
void nsp_freetable(nsp_state *N, obj_t *tobj);
obj_t *nsp_getobj_ex(nsp_state *N, obj_t *tobj, char *oname, unsigned short followz, unsigned short *foundz);
obj_t *nsp_getobj(nsp_state *N, obj_t *tobj, char *oname);
obj_t *nsp_getiobj(nsp_state *N, obj_t *tobj, unsigned long oindex);
obj_t *nsp_setobj(nsp_state *N, obj_t *tobj, char *oname, unsigned short otype, NSP_CFUNC _fptr, num_t _num, char *_str, size_t _slen);
obj_t *nsp_appendobj(nsp_state *N, obj_t *tobj, char *name);
void nsp_strcat(nsp_state *N, obj_t *cobj, char *str, unsigned long len);
void nsp_strmul(nsp_state *N, obj_t *cobj, unsigned long n);
short nsp_tobool(nsp_state *N, obj_t *cobj);
num_t nsp_tonum(nsp_state *N, obj_t *cobj);
char *nsp_tostr(nsp_state *N, obj_t *cobj);
char *nsp_zlink(nsp_state *N, obj_t *cobj1, obj_t *cobj2);
/* parser */
obj_t *nsp_eval(nsp_state *N, const char *string);
obj_t *nsp_evalf(nsp_state *N, const char *fmt, ...);
#endif
//#define nsp_isnull(o) (o==NULL||o->val==NULL||o->val->type==NT_NULL)
#define nsp_isnull(o) (o==NULL||o->val==NULL||o->val->type==NT_NULL)
#define nsp_isbool(o) (o!=NULL&&o->val!=NULL&&o->val->type==NT_BOOLEAN)
#define nsp_isnum(o) (o!=NULL&&o->val!=NULL&&o->val->type==NT_NUMBER)
#define nsp_isstr(o) (o!=NULL&&o->val!=NULL&&o->val->type==NT_STRING)
#define nsp_istable(o) (o!=NULL&&o->val!=NULL&&o->val->type==NT_TABLE)
#define nsp_istrue(o) (nsp_tobool(N, o)?1:0)
#define nsp_typeof(o) (nsp_isnull(o)?NT_NULL:o->val->type)
#define nsp_getnum(N,o,n) nsp_tonum(N, nsp_getobj(N,o,n))
#define nsp_getstr(N,o,n) nsp_tostr(N, nsp_getobj(N,o,n))
#define nsp_setnull(N,t,n) nsp_setobj(N, t, n, NT_NULL, (NSP_CFUNC)NULL, 0, NULL, 0)
#define nsp_setnum(N,t,n,v) nsp_setobj(N, t, n, NT_NUMBER, (NSP_CFUNC)NULL, v, NULL, 0)
#define nsp_setbool(N,t,n,v) nsp_setobj(N, t, n, NT_BOOLEAN, (NSP_CFUNC)NULL, v?1:0, NULL, 0)
#define nsp_setstr(N,t,n,s,l) nsp_setobj(N, t, n, NT_STRING, (NSP_CFUNC)NULL, 0, s, l)
#define nsp_settable(N,t,n) nsp_setobj(N, t, n, NT_TABLE, (NSP_CFUNC)NULL, 0, NULL, 0)
#define nsp_setcfunc(N,t,n,p) nsp_setobj(N, t, n, NT_CFUNC, (NSP_CFUNC)p, 0, NULL, 0)
#define nsp_setnfunc(N,t,n,s,l) nsp_setobj(N, t, n, NT_NFUNC, (NSP_CFUNC)NULL, 0, s, l)
#define nsp_setcdata(N,t,n,s,l) nsp_setobj(N, t, n, NT_CDATA, (NSP_CFUNC)NULL, 0, (void *)s, l)
#define nsp_setinum(N,t,n,v) nsp_setiobj(N, t, n, NT_NUMBER, (NSP_CFUNC)NULL, v, NULL, 0)
#define nsp_setistr(N,t,n,s,l) nsp_setiobj(N, t, n, NT_STRING, (NSP_CFUNC)NULL, 0, s, l)
#define nsp_setitable(N,t,n) nsp_setiobj(N, t, n, NT_TABLE, (NSP_CFUNC)NULL, 0, NULL, 0)
#define nsp_seticfunc(N,t,n,p) nsp_setiobj(N, t, n, NT_CFUNC, (NSP_CFUNC)p, 0, NULL, 0)
#define nsp_setinfunc(N,t,n,s,l) nsp_setiobj(N, t, n, NT_NFUNC, (NSP_CFUNC)NULL, 0, s, l)
#define nsp_seticdata(N,t,n,s,l) nsp_setiobj(N, t, n, NT_CDATA, (NSP_CFUNC)NULL, 0, (void *)s, l)
#ifdef __cplusplus
}
#endif
#ifdef __cplusplus
class NesObject {
public:
NesObject()
{
/* memset(O, 0, sizeof(O)); */
}
~NesObject()
{
/* nsp_unlink(O); */
}
const char *errbuf()
{
/* return this->N->errbuf; */
}
private:
obj_t O;
};
class NSPState {
private:
nsp_state *N;
public:
NSPState()
{
this->N=nsp_newstate();
}
~NSPState()
{
nsp_endstate(this->N);
}
obj_t *exec(const char *string)
{
return nsp_exec(this->N, string);
}
int execfile(char *file)
{
return nsp_execfile(this->N, file);
}
obj_t *eval(const char *string)
{
return nsp_eval(this->N, string);
}
/*
obj_t *evalf(const char *fmt, ...)
{
return nsp_evalf(this->N, fmt, ...);
}
*/
obj_t *getG()
{
return &this->N->g;
}
obj_t *getT()
{
return &this->N->context->t;
}
obj_t *getL()
{
return &this->N->context->l;
}
nsp_t *getN()
{
return this->N;
}
void setdebug(int x)
{
this->N->debug=x;
}
int err()
{
return this->N->err;
}
int warnings()
{
return this->N->warnings;
}
const char *errbuf()
{
return this->N->errbuf;
}
};
#endif
#endif /* _NSP_H */