println( "Hello, world!" );
SGScript is a dynamic, procedural language, similar in syntax to many other programming languages, including C and JavaScript. It consists of various operations and structures that can be combined to specify operations and data that they use.
function printText()
{
println( "text" );
}
printText();function <name> ( <argument-list> ) { <code> }
functiony = 15.51; x = 3 * y + 10; x * y;
include "math";
function sinc( x )
{
xpi = x * M_PI;
return sin( xpi ) / xpi;
}
sinc3 = sinc( 3 );<name> <subexpression>
( <expression-list> )
include statement loads a library or a code file.
math is one of the few built-in libraries. In this example, it defines the function sin and the constant M_PIx = 1; y = 2; x += y; ++x; --y;
x += 1 and x -= 1, respectively.
x = 5; y = 3; x *= y * ( y + x ); y -= x + x * y; y += 5 * y += y; printvar( y ); println( dumpvar( x ) );
printvar / dumpvar functions.
println is a function that prints the given variables to standard output and moves write cursor to the next line.
( .. ) are evaluated first, then multiplication * / division / / modulo (remainder) % , and then - addition + and subtraction - .x = 5;
global y = 6;
function z()
{
a = 7;
global b = 8;
function c(){}
}global specifies a list of variables that are global and allows to assign their values.
// testing the assignment operator a = 5; /* this ---------- -- should print -- ------------- 5 */ println( a ); ////
// ... and multiline comments / ... /a = null; b = true; c = 123; d = 123.456; e = "text";
null is the 'no value' type. There is only one value for the type.
true/false type.
string type stores an array of bytes, generally meant to store text but can be used for any data.f = function(){ return 4; };
g = println;
h = io_file(); // returns a file object
i = toptr(100);arr = [ 1, 3, 5, 7 ]; println( arr[2] ); // prints 5 println( arr[4] ); // warning: index out of bounds println( arr.size ); // prints 4
[ .. ] and property . operators.
size property returns the length of the array, the number of variables in it.arr = []; arr.push( 5 ); x = arr.pop(); arr.unshift( 4 ); y = arr.shift();
dct = { x = 5 };
dct[ "key" ] = "value";
println( dct ); // {x=5,key=value}
fnmap = map();
fnmap[ print ] = "print";dict type (generated by the dict literal {} or function dict()) store string keys. Documentation: dict [object]
map type (generated by the map function) store literally all kinds of keys. Documentation: map [object]
dict object is expected to be the building block of most data stored because it is most accessible.myObj = { x = 5, y = 7 };
function myObj.moveLeft(){ --this.x; }
myObj.moveLeft(); // method call
function myObj_moveRight(){ ++this.x; }
myObj!myObj_moveRight(); // compatible call<variable-name> . <property-name> for the name.
<variable-name> . <property-name> = function ( ) <code> or assigning any callable to a property.
this can be used inside the function to access the associated object.
<data-source> . <property-name> ( <argument-list> ).
<data-source> . <callable-source> ( <argument-list> ).if( a > 5 )
{
println( "'a' is greater than 5" );
if( a < 10 )
println( "...but less than 10" );
}
else
println( "'a' is not greater than 5" );if statement.
if ( <expression> ) <statement>, optionally followed by else <statement>
{ .. } is a block statement, it can be used anywhere a statement can be used
> ("greater than") is one of 8 comparison operators. The others are:
< - "less than"
>= - "greater or equal"
<= - "less or equal"
== - "equal"
!= - "not equal"
=== - "strict equality" (not only value must be equal, types must also be same)
!== - "strict inequality" (inverse of strict equality)
if and other such statements, they can be used as any other operator.
bool, it has only two values - true and falsewhile( a > 5 )
{
println( a );
--a;
}
for( i = 0; i < 5; ++i )
println( i );while ( <expression> ) <statement>
for ( <expression-list> ; <expression> ; <expression-list> ) <statement>
do <statement> while ( <expression> )
foreach ( <nvalue-ame> : <expression> ) <statement>
foreach ( <key-name> , : <expression> ) <statement>
foreach ( <key-name> , <value-name> : <expression> )foreach( name : [ "one", "two", "three" ] )
println( name ); // prints one, two, three
foreach( key , : _G )
println( key ); // prints names of all global variables
foreach( key, value : myObject )
{
if( string_part( key, 0, 2 ) == "!_" )
println( value ); // print all values for keys beginning with "!_"
}foreach loops give read-only values (they can be written to but source will not be updated).foreach( value : data )
{
if( value === false )
continue;
if( value )
break;
}break or jump to the next iteration with continue.
break will skip to code right after the loop.
continue will skip to right before the end of the loop.
hex = 0x12400; bin = 0b10011010; b_or = hex | bin ^ hex & bin;
&, &= - returns 1 if both sides are 1
|, |= - returns 1 if any side is 1
^, ^= - returns 1 if either side is 1, but not both
~ - inverts bits
<<, <<= - move all bits to the left (more significant positions)
>>, >>= - move all bits to the right (less significant positions)The main difference from other languages is that the integer type by default is 64 bits long. Left/right shift rules are equal to those of the C language. Thus, it is safe to use only shift distances between 0 and 63.
Truth tables for AND/OR/XOR operations
| AND | 0 | 1 | OR | 0 | 1 | XOR | 0 | 1 | ||
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | ||
| 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
This section describes all supported ways to compile SGScript and integrate it into your project.
There are two kinds of downloads - source and binaries. All downloads are available in the download page. Source files are hosted on Github.
Even though master branch is supposed to be the 'stable' branch, it is highly suggested that apidev branch is tried first since it is the most up-to-date branch, it is expected that generally less bugs are there. master branch is more thoroughly tested at the time of release and apidev may contain recent changes that subtly break the build.
cmd on Windows, sh on Linux/Mac) window in SGScript source root directory.
make (mingw32-make on Windows), optionally specifying targets and options.
make <target|option>[, <target|option> ...]
make;
make tools.
make (create a file make.bat in C:\MinGW\bin directory or equivalent, add the directory to PATH if it's not there):@ECHO OFF mingw32-make %*
Targets:
Options:
All IDE-related build data is under the build directory.
build/codeblocks)
build/vc10)build/xcode)
There is a file CMakeLists.txt in the root directory of the project. It only builds the dynamic library at the moment.
The code is made to support various compilers and compile under C and C++ so it is quite safe to just drag & drop it into your project.
There are generally two ways to include SGScript: link the library or include the files directly.
Note: Including the files isn't an option if SGScript modules are to be used.
For simplified inclusion of files, add src/ and ext/ to the include paths.
src/sgscript.h.
src/*) compiled in.
src/sgs_util.h.
src/*) compiled in.
ext/sgsxgmath.h.
ext/sgsxgmath.c) compiled in.This section describes various use cases and how to apply the SGScript C API to achieve the expected results.
SGS_CTX = sgs_CreateEngine(); // SGS_CTX is alias to `sgs_Context* C` sgs_Include( C, "something" ); sgs_ExecFile( C, "myscript.sgs" ); sgs_DestroyEngine( C );
sgs_CreateEngine or sgs_CreateEngineExt.
sgs_DestroyEngine to free all used resources.
sgs_Include tries to find the right file using the search path, and it supports loading of native libraries.
sgs_ExecFile expects a specific file and supports only script files (both compiled and source).sgs_GlobalCall( C, "main", 0, 0 ); // may fail but it's not important in this case
sgs_GlobalCall are:
sgs_PushBool( C, 1 ); sgs_PushInt( C, 42 ); sgs_PushReal( C, 3.14159 ); sgs_PushString( C, "some C string" ); sgs_PushStringBuf( C, "not\0really\0C\0string", 19 ); sgs_PushPtr( C, C ); sgs_PushGlobalByName( C, "main" ); // assuming this does not fail sgs_Call( C, SGS_FSTKTOP, 6, 1 ); // calls function from global 'main' with 6 arguments and expects 1 value in return // again, it's assumed that sgs_Call does not fail // ...but it may, so checking return value is a very good idea
nullif( SGS_CALL_FAILED( sgs_GlobalCall( C, "func", 0, 3 ) ) )
/* handle the error */; // it is important this time since stack state matters
sgs_Int i = sgs_GetInt( C, -3 ); // first return value
const char* string_ptr = NULL;
sgs_SizeVal string_size = 0;
if( sgs_ParseString( C, -2, &string_ptr, &string_length ) )
/* successfully parsed a string, deal with it */;
sgs_Bool b = sgs_GetBool( C, -1 );
sgs_Pop( C, 3 ); // remove all 3 returned values from top of the stacksgs_Pop.sgs_PushBool( C, 1 ); sgs_PushInt( C, 42 ); sgs_PushReal( C, 3.14159 ); sgs_PushArray( C, 3 ); // pushes an array with 3 items, made from 3 topmost stack items sgs_PushString( C, "some C string" ); sgs_PushStringBuf( C, "not\0really\0C\0string", 19 ); sgs_CreateDict( C, NULL, 2 ); // pushes a dictionary with one entry, made from 2 topmost stack items
sgs_CreateArray takes the specified number of topmost stack items, puts them into the array and removes them from the stack
sgs_CreateDict and sgs_CreateMap take the specified number of topmost stack items, map values to keys and remove them from the stack
int sample_func( SGS_CTX )
{
char* str;
float q = 1;
SGSFN( "sample_func" );
if( sgs_LoadArgs( "s|f", &str, &q ) )
return 0;
// ... some more code here ...
sgs_PushBool( C, 1 );
return 1; // < number of return values or a negative number on failure
}int and one sgs_Context* argument
SGSFN is used to set the name of the function, the name will be used in:
sgs_LoadArgs does type-based argument parsing
// method #1
sgs_SetGlobalByName( C, "sample_func", sgs_MakeCFunc( sample_func ) );
// method #2
sgs_RegFuncConst funcs[] = { { "sample_func", sample_func } };
sgs_RegFuncConsts( C, funcs, sizeof(funcs) / sizeof(funcs[0]) );
// method #3
sgs_RegFuncConst funcs2[] = { { "sample_func", sample_func }, SGS_RC_END() };
sgs_RegFuncConsts( C, funcs2, -1 );// push the 'print' function // method 1 sgs_PushGlobalByName( C, "print" ); // method 2 sgs_PushEnv( C ); sgs_PushProperty( C, -1, "print" ); sgs_PopSkip( C, 1, 1 ); // method 3 sgs_PushEnv( C ); sgs_PushString( C, "print" ); sgs_PushIndex( C, sgs_StackItem( C, -2 ), sgs_StackItem( C, -1 ), 1 );
sgs_CreateDict( C, NULL, 0 );
sgs_PushString( C, "test" );
sgs_SetIndex( C, sgs_StackItem( C, -2 ), sgs_StackItem( C, -1 ), sgs_StackItem( C, -1 ), 0 );
// result: {test=test}Prefer index access (isprop=0) to property access since the general convention is that indices provide raw access but property access can be augmented with function calls.
sgs_Variable dict, key;
sgs_CreateDict( C, &dict, 0 );
sgs_InitString( C, &key, "test" );
sgs_PushInt( C, 5 ):
sgs_SetIndex( C, dict, key, sgs_StackItem( C, -1 ), 0 );
// result: {test=5}
sgs_PushVariable( C, dict );
sgs_Release( C, &dict );
sgs_Release( C, &key );sgs_PushInt( C, 3 ); sgs_PushInt( C, 4 ); sgs_PushInt( C, 5 ); sgs_CreateArray( C, NULL, 3 ); sgs_PushNumIndex( C, sgs_StackItem( C, -1 ), 1 ); // pushes [int 4] sgs_PushPath( C, sgs_StackItem( C, -1 ), "i", (sgs_SizeVal) 1 ); // pushes [int 4] too
sgs_ObjInterface object_iface[1] =
{{
"object_name", // type name
NULL, NULL, // destruct, gcmark
NULL, NULL, // getindex, setindex
NULL, NULL, NULL, NULL, // convert, serialize, dump, getnext
NULL, NULL // call, expr
}};sgs_CreateObject( C, NULL, malloc( sizeof( mystruct ) ), object_iface ); sgs_CreateObjectIPA( C, NULL, sizeof( mystruct ), object_iface ); // preferred method
It is very important that all memory operations on in-place allocated blocks do not, at any time, operate beyond the boundaries of those blocks. Be especially wary of putting arrays at the beginning of a structure since accidentally applying negative indices to such arrays could create issues that are extremely hard to debug.
int object_destruct( SGS_CTX, sgs_VarObj* obj )
{
free( obj->data );
return SGS_SUCCESS;
}int and have the same first two arguments (sgs_Context* and sgs_VarObj*).
int object_getindex( SGS_CTX, sgs_VarObj* obj )
{
char* str;
if( sgs_ParseString( C, 0, &str, NULL ) )
{
if( strcmp( str, "data_pointer" ) == 0 ) return sgs_PushPtr( C, obj->data );
if( strcmp( str, "do_something" ) == 0 ) return sgs_PushCFunc( C, object_do_something );
if( strcmp( str, "do_smth_else" ) == 0 ) return sgs_PushCFunc( C, object_do_smth_else );
}
return SGS_ENOTFND; // return that the specified key was not found in object
}
// a slightly cleaner but less hands-on, the macro-based version:
int object_getindex( SGS_ARGS_GETINDEXFUNC )
{
SGS_BEGIN_INDEXFUNC
SGS_CASE( "data_pointer" ) return sgs_PushPtr( C, obj->data );
SGS_CASE( "do_something" ) return sgs_PushCFunc( C, object_do_something );
SGS_CASE( "do_smth_else" ) return sgs_PushCFunc( C, object_do_smth_else );
SGS_END_INDEXFUNC
}data_pointer, it returns pointer to allocated memory.