NullLogic Embedded Scripting Language - Reference


 Menu

Introduction

Functions - core
Functions - file
Functions - io
Functions - math
Functions - string
Functions - time
libext
libssh
libtcp
C API


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.


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:

function new_str(val) {
        this = {
                size  = function () { return sizeof(this.value); };
                lower = function () { return string.tolower(this.value); };
                upper = function () { return string.toupper(this.value); };
                value = val;
        };
        return this;
}
x=new_str("AbC");
print("["+x.size()+"]\n");  // will print 3
x.value="aBcDeF";
print("["+x.size()+"]\n");  // will print 6
print("["+x.lower()+"]\n"); // will print abcdef
print("["+x.upper()+"]\n"); // will print ABCDEF

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.

2 - The Language

Keywords

break continue return function global local var if else for do while exit

Working code looks a little like the following sample.

function demo(arg1) {
        global x=1;
        # you don't actually need local or var, but it makes the code clearer
        local y=2;
        var z=3;

        for (i=0;i<10;i++) {
                if (i<5) {
                        print("less than half ", i, "\n");
                } else if (i==5) {
                        print("half\n");
                } else {
                        print("more than half ", i, "\n");
                }
                if (i==arg1) break;
        }
        return;
}
demo(8);

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: = {}

function subf() { return 69; }
var pi = math.asin(1)*2;
var table = {
	{ a1="x", b = pi, [12]=41, [1]=42, 43, 'blah', subf() },
	{ "x", 'y', x = 41, 42, 43, 'blah', [5]=12, heh=";-)" }
};

setting a var's value to null will completely free the value and effectively make the var non-existant

eval

Description

Evaluates a supplied expression.

iname

Description

Returns the name of a table entry at a given index.

include

Description

Include (and interpret) the code in the specified file.

Syntax

number = include(filename)

ParameterDescription
filename String containing the name of the file to be included.

Return Value

Returns zero if the file has been successfully included. Any non-zero 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!=0) print("error loading ./somefile.ns\n");

ival

Description

Returns a pointer to the value of a table entry at a given index.

print

Description

Print the complete list of arguments to the standard output.

Syntax

number = print(...)

ParameterDescription
... 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()

printvar

Description

Print the provided var in a format appropriate for use in a nesla script. This will recursively print entire tables.

Syntax

printvar(object var)

ParameterDescription
var Object to be printed. 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; printvar(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 printvar.

var x="this \"is a\" test\n";
print(x);

will display

this "is a" test

var x="this \"is a\" test\n";
printvar(x);

will display

"this \"is a\" test\n"

var x={ { key='c' }, { key='a' }, { key='b' } };
printvar(x);

will display

{
        [0] = {
                key = "c"
        },
        [1] = {
                key = "a"
        },
        [2] = {
                key = "b"
        }
}

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)

ParameterDescription
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)

ParameterDescription
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)

ParameterDescription
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");
x=file.read("x.txt");
print(x);
file.unlink("x.txt");

tonumber

Description

Returns a number value equivalent of the source.

Syntax

number = tonumber(object)

ParameterDescription
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])

ParameterDescription
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)

ParameterDescription
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)

ParameterDescription
object Object to be written to standard output.

Return Value

Returns the number of bytes written.

See Also

io.write()

3 - file.*()


(string) 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) 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.


(table) file.stat(string filename);

Stats filename and returns a table with stats.


(number) file.write(string filename[, object]);

Writes the entire object as a string and returns the result code. This function is binary safe.

4 - io.*()

io.flush

Description

Flushes the output buffer.

Syntax

number = io.flush()

Return Value

Returns zero.

io.print

Description

Print the complete list of arguments to the standard output.

Syntax

number = io.print(...)

ParameterDescription
... 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()

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)

ParameterDescription
object Object to be written to standard output.

Return Value

Returns the number of bytes written.

See Also

write()

5 - math.*()

/ * math functions * /

(number) math.abs(number n);

6 - string.*()


(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.

string.tolower

Description

Returns a lower case version of the supplied string.

string.toupper

Description

Returns an upper case version of the supplied string.

7 - time.*()

time.gmtime

Description

Returns a table representation of timestamp. If timestamp is not defined, the function will use the current local system date.

time.localtime

Description

Returns a table representation of timestamp. If timestamp is not defined, the function will use the current local system date.

time.now

Description

Returns the elapsed time in seconds since January 1, 1970.

Syntax

number = time()

Return Value

Returns the time in seconds since the creation of the UNIXverse.

date

date -- Print a date in SQL format

Description

string date ( [number timestamp] )

Returns a string representation of timestamp in the format 'YYYY-MM-DD'. If timestamp is not defined, the function will use the current system date.

Parameters

timestamp

The number of seconds since January 1, 1970 (commonly called a UNIX timestamp).

Return Values

Returns a formatted date string in the format 'YYYY-MM-DD'.

Examples

Example 1. A strlen() example

<?php
$str 
'abcdef';
echo 
strlen($str); // 6

$str ' ab cd ';
echo 
strlen($str); // 7
?>

Array
(
    [0] => f'oo
    [1] => b'ar
    [2] => Array
        (
            [0] => fo'o
            [1] => b'ar
        )
)

See Also

count()
mb_strlen()

time.sqldate

Description

Returns a string representation of timestamp in the format 'YYYY-MM-DD'. If timestamp is not defined, the function will use the current local system date.

Syntax

string = sqldate([number timestamp])

ParameterDescription
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'.

Remarks

No remarks.

Examples

print(sqldate());

The above example will output something similar to: "2009-01-01"

print(sqldate(0)+" "+sqltime(0));

The above example will output something similar to (note the adjustment for local time): "1969-12-31 19:00:00"

See Also

time.sqltime()

time.sqltime

Description

Returns a string representation of timestamp in the format 'HH:MM:SS'. If timestamp is not defined, the function will use the current local system time.

Syntax

string = sqltime([number timestamp])

ParameterDescription
timestamp The number of seconds since January 1, 1970 UTC (commonly called a UNIX timestamp).

Return Value

Returns a formatted time string in the format 'HH:MM:SS'.

Remarks

No remarks.

Examples

print(sqltime());

The above example will output something similar to: "16:30:45"

print(sqldate(0)+" "+sqltime(0));

The above example will output something similar to (note the adjustment for local time): "1969-12-31 19:00:00"

See Also

time.sqldate()

8 - 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.


(table) xml.read(string str);

Read the supplied XML text and return a digested table. This is _not_ a pretty function.

9 - ssh.*()

ssh.open

Description

Open an SSH connection to a given host/port.

Syntax

ssh-conn = ssh.open(hostname[, port])

ParameterDescription
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);

ssh.open

Description

Cloae an SSH connection.

Syntax

number = ssh.close(conn)

ParameterDescription
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.auth

Description

Authenticate an SSH connection.

Syntax

boolean = ssh.auth(sshconn, username, password | { pass, prv, pub })

ParameterDescription
sshconn An active ssh-conn connection.
username String containing the account username to connect as.
password String containing the account password.
pass String containing the passphrase of the private key (used with key-based authentication).
prv String containing the location of the private key (used with key-based authentication).
pub String containing the location of the public key (used with key-based authentication).

Return Value

Returns true if successful. Any failure to authenticate returns false.

Remarks

None

Example

The following example demonstrates how to authenticate via user/pass or a keypair.

global username = "user";
global password = "pass";
global rsa_keys = {
	pass="rsapass",
	prv="~/.ssh/id_rsa",
	pub="~/.ssh/id_rsa.pub"
};

/* you can authenticate with a user/pass pair */
if (ssh.auth(conn, username, password)!=true) {
	print("failed auth");
	exit;
}
/* or you can authenticate with keys */
if (ssh.auth(conn, username, rsa_keys)!=true) {
	print("failed auth");
	exit;
}

10 - tcp.*()


(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.



(sock4) tcp.info(sock4 socket);

Returns a table containing information about the socket.

11 - 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-2009 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 _NESLA_H
#define _NESLA_H 1

#if defined(TINYCC)||defined(__TURBOC__)
struct timeval { long tv_sec; long tv_usec; };
#endif
#if defined(_MSC_VER)
#define WIN32_LEAN_AND_MEAN
/* always include winsock2 before windows */
#include <winsock2.h>
#include <windows.h>
#include <time.h>
#elif !defined(__TURBOC__)
#include <sys/time.h>
#endif
#include <setjmp.h>

#define NESLA_NAME      "nesla"
#define NESLA_VERSION   "0.9.1"

#define MAX_OBJNAMELEN  64
#define MAX_OUTBUFLEN   8192
#define OUTBUFLOWAT	4096

/* nesla 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

/* nesla object status flags */
#define NST_HIDDEN	0x01
#define NST_READONLY	0x02
#define NST_SYSTEM	0x04
#define NST_AUTOSORT	0x08
#define NST_LINK	0x10

#define num_t double
#define uchar unsigned char
#define obj_t struct nes_objrec
#define val_t struct nes_valrec
#define nes_t struct nes_state

/* should be typedef int(*NES_CFUNC)(nes_state *); */
typedef int(*NES_CFUNC)(void *);
#define NES_FUNCTION(name) int name(nes_state *N)

/*
 * define a callback function type so CDATA objects
 * can choose the terms of their own death.
 */
/* should be typedef void(*NES_CFREE)(nes_state *, obj_t *); */
typedef void(*NES_CFREE)(void *, void *);

typedef struct NES_CDATA {
	/* standard header info for CDATA object */
	char      obj_type[16]; /* tell us all about yourself in 15 characters or less */
	NES_CFREE obj_term;     /* now tell us how to kill you */
	/* now begin the stuff that's type-specific */
} NES_CDATA;
typedef struct nes_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 */
	union {
		num_t  num;
		char  *str;
		NES_CFUNC cfunc;
		NES_CDATA *cdata;
		obj_t *table;
	} d;
} nes_valrec;
typedef struct nes_objrec {
	obj_t *prev;
	obj_t *next;
	val_t *val;
	char name[MAX_OBJNAMELEN+1];
} nes_objrec;
typedef struct nes_state {
	uchar *blockptr;
	uchar *blockend;
	uchar *readptr;
	obj_t g;
	obj_t l;
	obj_t r;
	short brk;
	short cnt;
	short ret;
	short err;
	/* intended for external signals to the parser.  for now, non-zero just means to shut down */
	short signal;
	short debug;
	short single;
	short strict;
	short warnings;
	jmp_buf *savjmp;
	struct timeval ttime;
	unsigned short outbuflen;
	char numbuf[128];
	char outbuf[MAX_OUTBUFLEN+1];
	char errbuf[256];
} nes_state;

#ifndef NESLA_NOFUNCTIONS
/* exec */
nes_state *nes_newstate   (void);
nes_state *nes_endstate   (nes_state *N);
obj_t     *nes_exec       (nes_state *N, const char *string);
int        nes_execfile   (nes_state *N, char *file);
/* objects */
void       nes_setvaltype (nes_state *N, obj_t *cobj, unsigned short type);
void       nes_linkval    (nes_state *N, obj_t *cobj1, obj_t *cobj2);
void       nes_unlinkval  (nes_state *N, obj_t *cobj);
void       nes_freetable  (nes_state *N, obj_t *tobj);
obj_t     *nes_getobj     (nes_state *N, obj_t *tobj, char *oname);
obj_t     *nes_getiobj    (nes_state *N, obj_t *tobj, unsigned long oindex);
obj_t     *nes_setobj     (nes_state *N, obj_t *tobj, char *oname, unsigned short otype, NES_CFUNC _fptr, num_t _num, char *_str, long _slen);
void       nes_strcat     (nes_state *N, obj_t *cobj, char *str, long len);
void       nes_strmul     (nes_state *N, obj_t *cobj, unsigned long n);
short      nes_tobool     (nes_state *N, obj_t *cobj);
num_t      nes_tonum      (nes_state *N, obj_t *cobj);
char      *nes_tostr      (nes_state *N, obj_t *cobj);
/* parser */
obj_t     *nes_eval       (nes_state *N, const char *string);
obj_t     *nes_evalf      (nes_state *N, const char *fmt, ...);
#endif

#define    nes_isnull(o)            (o==NULL||o->val==NULL||o->val->type==NT_NULL)
#define    nes_isbool(o)            (o!=NULL&&o->val!=NULL&&o->val->type==NT_BOOLEAN)
#define    nes_isnum(o)             (o!=NULL&&o->val!=NULL&&o->val->type==NT_NUMBER)
#define    nes_isstr(o)             (o!=NULL&&o->val!=NULL&&o->val->type==NT_STRING)
#define    nes_istable(o)           (o!=NULL&&o->val!=NULL&&o->val->type==NT_TABLE)

#define    nes_istrue(o)            nes_tobool(N, o)?1:0

#define    nes_typeof(o)            nes_isnull(o)?NT_NULL:o->val->type

#define    nes_getnum(N,o,n)        nes_tonum(N, nes_getobj(N,o,n))
#define    nes_getstr(N,o,n)        nes_tostr(N, nes_getobj(N,o,n))

#define    nes_setnum(N,t,n,v)      nes_setobj(N, t, n, NT_NUMBER, (NES_CFUNC)NULL, v, NULL, 0)
#define    nes_setstr(N,t,n,s,l)    nes_setobj(N, t, n, NT_STRING, (NES_CFUNC)NULL, 0, s,    l)
#define    nes_settable(N,t,n)      nes_setobj(N, t, n, NT_TABLE,  (NES_CFUNC)NULL, 0, NULL, 0)
#define    nes_setcfunc(N,t,n,p)    nes_setobj(N, t, n, NT_CFUNC,  (NES_CFUNC)p,    0, NULL, 0)
#define    nes_setnfunc(N,t,n,s,l)  nes_setobj(N, t, n, NT_NFUNC,  (NES_CFUNC)NULL, 0, s,    l)
#define    nes_setcdata(N,t,n,s,l)  nes_setobj(N, t, n, NT_CDATA,  (NES_CFUNC)NULL, 0, (void *)s, l)

#define    nes_setinum(N,t,n,v)     nes_setiobj(N, t, n, NT_NUMBER, (NES_CFUNC)NULL, v, NULL, 0)
#define    nes_setistr(N,t,n,s,l)   nes_setiobj(N, t, n, NT_STRING, (NES_CFUNC)NULL, 0, s,    l)
#define    nes_setitable(N,t,n)     nes_setiobj(N, t, n, NT_TABLE,  (NES_CFUNC)NULL, 0, NULL, 0)
#define    nes_seticfunc(N,t,n,p)   nes_setiobj(N, t, n, NT_CFUNC,  (NES_CFUNC)p,    0, NULL, 0)
#define    nes_setinfunc(N,t,n,s,l) nes_setiobj(N, t, n, NT_NFUNC,  (NES_CFUNC)NULL, 0, s,    l)
#define    nes_seticdata(N,t,n,s,l) nes_setiobj(N, t, n, NT_CDATA,  (NES_CFUNC)NULL, 0, (void *)s, l)

#endif /* nesla.h */
 
  Please direct feedback to:
nulllogic@users.sourceforge.net
Copyright © 2009 NullLogic
All rights reserved