Scripter2 Help Page

So you're wanting to give Scripter2 a test run.. Well, you've came to the right place. The Scripter2 demo application is at http://scripter2.lastyearswishes.com

There are a few limitations to keep in mind for the demo application

  1. Scripts can be no longer than 1000 bytes long
  2. Scripts are sandboxed completely(so far). return is the only way to get data out of your scripts right now
  3. Scripts can take no longer than 5 seconds to execute
  4. Script results will be trimmed if they are larger than 2000 bytes
  5. Floating point operations are unimplemented so far
  6. && and || logical and/or is not supported yet (nor is truely logical !)

With that in mind, continue...

We'll start of course with a hello world application:

return "Hello World!";

Now I'll try to cover a few different topics:

Variable Declarations

Although variables can be created without prior declaration, it is not how I recommend doing things.

There are two different variable scopes available. Local and Global.

var l; //local variable
global var g; //global variables

Global variables can be accessed from any scope, including inside functions(without passing it in as an argument). Local variables are local to their scope. Scopes are "created" via {} or by function calls. For example:

var bar="baz";
{
  var foo="biz";
}
bar=foo; //bar is now `undefined` 
var f=function(){
  var fizz="123";
  return;
};
f();
bar=fizz; //bar is now `undefined`

Tables

Tables are the cure-all to all data structures you'll use in Scripter2. Tables can implement lists, hashes, dictionaries, arrays, classes, structures, and anything else you can think of.

var table=[]; //an empty table
table=[1,2,3] //a 0 indexed array containing 1, 2, 3
assert(table[0]==1);
assert(table[1]==2);
table=["foo" => "bar"];
assert(table["foo"]=="bar");
assert(table.foo=="bar");
//for string keys, short cutting similar to javascript is allowed

Tables can be added on to without any special operators as well.

var table=[];
table.foo="bar";
table[10]="foobar";

There are also several built in functions for transversing and getting information about tables.

var table=[1, 2, 3];
var keys=keylist(table); //A table is returned like [0 => 0, 1 => 1, 2 => 2]
var count=keycount(table); //returns 3

There is also a foreach operation as well:

var table=["foo" = > "bar", "biz" => "baz"];
foreach(value in table){
  //value will be "bar" and then "baz"
}
foreach(key => value in table){
  //key will be "foo" and then "biz"
  //value will be "bar" and then "baz"
}

Private Table Keys

Any key put in a table that is prefixed with _ will be not included in the keylist nor keycount, and therefore will not used in foreach either.

This is to allow for "private" variables. There are some more plans(such as operator overloading) up my sleeve, but so far these are not implemented.

Note private variables can still be modified from anywhere though:

var table=["_foo" => "bar"];
assert(keycount(table)==0);
assert(table._foo=="bar");
table._foo="biz";
assert(table._foo=="biz");

These private variables will be much more useful as the facilities for duck-typing are fleshed out further.

Built in keywords

We already discussed the foreach keyword. There is also if and while which behaves as you would expect:

var i=0;
while(i<10){
  i++;
}
if(i!=10){
  return "all is not well";
}else{
  return "all is well";
}

There are also some built in functions, such as keycount, keylist, and isdefined. There are also some built in value keywords. These are null, nil, true, false, and undefined

Is Defined

The isdefined builtin function will test if a variable is equal to undefined. Due to operator and casting issues, it's not possible to do this with an if statement.

isdefined can return some false positives, depending on how you look at it. For instance, if you have a variable like this: var v=undefined; then isdefined(v) though being "declared" will return true. I believe that this is proper behavior and I'm don't really see much of a better way to do it.

isdefined can also be used on tables. For instance,

var table=[];
assert(!isdefined(table.foo));

Type casting

Type casting is a bit odd right now(hopefully cleaned up as we get closer to 1.0), I'll just warn you.

There is implicit type casting done on nearly every operation. The type to the right of the operation is cast to the type left of the operation.

Some examples:

var s="0123";
var i=5;
assert(s+i=="01235");
assert(i+s==128);
s="000123";
i=123;
assert(i==s);
assert(s!=i);

There is also support for explicit type casting using the as operator.

Some examples:

var s="0123";
var i=5;
assert((i as string)+"123" == "5123");
assert((s as integer)+5 == 128);

You can also test for a type using the is operator.

assert([] is table);
assert 123 is integer);
assert("foo" is string);
assert(function(){} is function);

Functions

Function are treated basically the same as variables and are first class. Lambda support is not yet included however.

Declaring a function is quite easy and looks exactly like Javascript:

var f=function(x){
    return x+1;
}
assert(f(1)==2);

Functions can also be used in tables to produce things that look oddly similar to classes

var foo=[];
foo.func=function(){
  return "bar";
}

Functions, though they are first class and can be passed around with ease, have next to no operations that can be performed on them. They can not be cast to any other type and do not support any operators such as ==

Operator List

Here is a full list of operators:

x++; //postfix increment
x--; //postfix decrement
++x; //prefix increment
--x; //prefix decrement
x*y; //binary multiplication
x/y; //binary division
x%y; //binary modulo
x+y; //binary addition
x-y; //binary subtraction
-x; //unary minus
+x; //unary plus
!x; //logical not
~x; //bitwise not
x&y; //bitwise and
x|y; //bitwise or
x^y; //bitwise xor
x<<y; //shift left
x>>y; //shift right
x=y; //assignment
x==y; //equals
x!=y; //not equal
x<y;
x>y;
x<=y;
x>=y;
x as Y; //typecast
x is y; //type comparison
x[y]; //subscript
x.y; //subscript shortcut

Strings

There are two different quote characters allowed.

'foo';
"foo"; //both the same

There is nothing special between them and is just a matter of personal preference.

There are also some escape codes:

  1. \n newline
  2. \r linefeed
  3. \ \
  4. \' '
  5. \" "

Conclusion

Hopefully that explains enough for right now. If you find anything that causes, say a NullReferenceException or other error that isn't a Engine Error, I'd really appreciate a comment down below with the code that caused it.

Tags: scripter2
Posted: 6/29/2012 3:05:55 AM

New Platforms For Scripter2

Well, I went ahead and downloaded Mono For Android and the Windows Phone/XBox360 .Net SDK... and I have good news.

Scripter2 works right out of the box with Mono For Android. And it works with very minimal modifications when compiled for the XBox.(just had to reimplement ApplicationException and change a minor method call to use an array)

So, if you're looking for a scripting engine to run on those devices where regular .Net dynamic languages don't work, I have your (I think) only option.

I will probably be doing more testing with XBox and Mono For Android in the near future. My only worries are memory usage. I know performance won't be the best, but I already know some places to start improving performance, and I'll be profiling it a lot more in the future.

Posted: 6/27/2012 8:25:49 AM

Scripter2 Call For Beta Testers

This is a call for some beta testers for my completely managed scripting engine.

While I'm not quite ready for beta testing yet, nearly all of the functionality I plan on implementing for 1.0 is there and (hopefully) the major bugs have been weeded out.

NEW There is now a demonstration website.

Scripter2 is this:

  • A scripting engine written in .Net that does not use Reflection at all and is completely interpreted
  • Implements a nice and concise language akin to Lua, PHP, and Javascript.. with some nice features for making "tables" pretend to be classes, and to make duck-typing easy
  • Is simple to extend
  • Completely sandboxed, by design.

So for reference, here is what a simple script looks like:

var table=["foo" => "bar","baz" => 10];
return table.foo+(table.baz as string); //returns "bar10"

There is no lambda support planned for 1.0, but there is anonymous functions. And of course, order of operations and all of that works unlike some scripting engines I've seen. There shouldn't be any huge syntax work-arounds either. My scripting engine is implemented properly and should behave like a regular language.

How easy is it to use and extend?

Well, here is a quick example:

public void RunScript(string script)
{
    Engine=new ScriptingEngine(script);
    //Now add our print function
    var args=new VariableList();
    args.Add(new VariableContainer("message"));
    Engine.Global.Add(new VariableContainer("print", new FunctionVariable(new ExternalScriptFunction(PrintFunction), args)));
    Engine.Execute();
}

BaseVariable PrintFunction(VariableList args, ScriptingEngine s){
    string msg=args.Get<string>("message");
    System.Console.WriteLine(msg);
}

And here is an example Hello World script for it:

print("Hello World!");
return;

Quite easy if I do say so myself. I also am in the process of writing some documentation to go along with it of course to better explain how it all works.

So far, it runs on

  • Mono 2.10
  • ,Net 3.5
  • Mono For Android
  • XBox 360/XNA (For Indie Games) (with very minor changes)
  • Windows 7 Phones

For the last two, XBox and Windows 7, very minimal testing has been done. I don't own a Windows 7 phone, and have never messed with developing for the XBox 360. Also, because it supports all of these platforms, it should be ready to go for a "write-once-run-everywhere" type system, such as Unity.

What do beta testers get:

  • A time-expiring assembly
  • Those that give me some feed back may get a copy of the source code if it will assist in beta testing and/or bug fixing
  • Some documentation

After beta testing is complete and it is released, all beta testers that sent some form of feedback will get a commercial license for the engine which includes source code.

If this sounds like something you'd like to test out, drop me a line at earlz -at- this domain name(lastyearswishes.com). You can also drop me a line if you want more information about it or a planned release date.

Posted: 6/26/2012 9:03:47 AM

Next Up: Scripter2

Hello, I thought I'd give a little update as to what I've been doing with my time the past week or two.

I've been building a lightweight scripting engine in fully managed(no reflection) C# code. Now, I've done this once before. At one of my previous employer's, FosTech, I built a scripting engine. Sadly, I never was able to negotiate any source code rights with my boss so I could extend on to it further.

So... when you can't get the code, the only choice is to rewrite it. Fortunately, it's probably a good thing I didn't get the source code for that project. I was still very new to C# when I wrote it, and it was my first attempt at a scripting engine. The code as I remember was horrible, and it barely cobbled along with some superficial syntax restrictions due to my lack of experience with coding parsers.

Luckily, now, I'm writing something similar. I don't have a name for the language or engine yet. The codename I guess is scripter2 as that's what I called it when I needed a project directory.

So, what is Scripter2 and why does it poop rainbows and marshmallows? Basically, it's a very cool scripting engine that is written in C#. It is an interpreter. It uses no dynamic code or reflection of any sort. This means it will run in even the most restricted of .Net environments. It's designed to resemble Javascript and Lua. And it's also designed so that interfacing with C# code is as simple as possible. It's also in a sandbox by design, and all methods out of the sandbox must be very explicit.

So, what does this language look like?

Here's a sampler of what's supported so far:

//a comment
var f=function(){
  return "foo";
}
assert(f()=="foo");
var t=[]; //a "table". Very similar to what's in Lua
t["foo"]="bar"; //this will work
t.foo="bar"; //as will this and reference "foo"
assert(t.foo=="bar");

var num=("123" as int + "10" as int) as string + "foo";
assert(num=="133foo");
assert(!isdefined(xyz)); //isdefined() is a built in function that test if a variable is declared
assert(!isdefined(t.bar)); //works on tables too

t.func=function(x){
  return x+10;
}
assert(t.func(10)==20);

More cool stuff is some syntactic sugar for duck-typing and for making "private" variables in tables... and hopefully (eventually) lambdas.

So far, it's actually quite usable for some basic scripting. It's turing-complete, follows order of operations, and easily extended. However, I know that quite a few things still have throw new NotImplementedException() and there are surely some lurking bugs as my regression testing is not nearly as thorough as it should be right now.

Eventually, when the engine gets more complete (probably to where it has everything but lambdas and operator overloading) I'll put up a public demo application so you can test it out.

My plans for it once it's to a point of "completion" are to try to sell it. I'm unsure on if it's going to be dual-licensed GPL/commercial or if it's just going to be straight commercial licenses. But, if you think this thing sounds interesting and are looking for a sandboxed and managed scripting language for .Net, send me an email at earlz -at- this domain name(lastyearswishes.com). I'd love some alpha testers and beta testers! And of course you'd get free commercial licenses as well.

Posted: 6/25/2012 9:09:11 AM