include "string";
/*
extern func println( string str );
template< typename T > array
{
T* m_data;
int m_size;
int m_mem;
operator [] ( int index ) : T
{
if( index < 0 || index >= m_size )
{
WARNING( "array index out of bounds" );
return T();
}
return m_data[ index ];
}
};
func main( array<string> args )
{
for( i = 0; i < args.size; ++i )
println( args[ i ] );
println( "Hello, world! (" $ args.size $ ")" );
}
func _test()
{
main( [ "array", "data", "test" ] );
}
*/
/*
PROBLEMS
> efficient local variable encoding
- all local variables have a scope but name should not be duplicated or resolved, we can use an offset table at most (padding could be arch-dependent)
- { type = "scope", locals = [{ name = "one", type = "string" }, { name = "b", type = "int" }], code = [..] },
- locals are added to stack and accessed by their index
IDEAS
> WARNING arguments:
- string specific - the case-specific error part ("15 outside [0;7]")
- optional string generic - the error group part ("array index out of bounds")
- optional string category - the error category path ("RangeError/OutOfBounds")
*/
global AST =
{
"string*" = { type = "pointer", datatype = "string" },
"array<string>" =
{
type = "object",
classname = "array",
fullname = "array<string>",
members =
[
{ name = "m_data", type = "string*" },
{ name = "m_size", type = "int" },
{ name = "m_mem", type = "int" },
],
static = [],
},
"array<string>:[](int)" =
{
type = "function",
retntype = "string",
params =
[
{ name = "index", type = "int" },
],
locals = [],
code =
[
{ type = "if",
test = { type = "or",
lft = { type = "lt", datatype = "int", lft = { type = "local", off = 0 }, rgt = { type = "data", datatype = "int", data = 0 } },
rgt = { type = "gte", datatype = "int", lft = { type = "local", off = 0 }, rgt = { type = "getmember", name = "m_size" } },
},
locals_ontrue = [],
locals_onfalse = [],
ontrue =
[
{ type = "fcall", func = "WARNING", ctx = null, args = [{ type = "data", datatype = "string", data = "array index out of bounds" }] },
{ type = "return", arg = { type = "data", datatype = "string", data = "" } },
],
onfalse = [],
},
{ type = "return", arg = { type = "getindex", object = { type = "getmember", name = "m_data" }, index = { type = "local", off = 0 } } },
],
},
"array<string>:size.get" = { type = "getter", retntype = "int", source = "m_size" },
"array<string>:clear()" =
{
type = "function",
retntype = "void",
params = [],
locals = [],
code =
[
{ type = "fcall", func = "array<string>:resize(int)", ctx = null, args = [{ type = "data", datatype = "int", data = 0 }] },
],
},
"println(string)" = { type = "external", func = println, retntype = "void", params = [{ name = "str", type = "string" }] },
"main(array<string>)" =
{
type = "function",
retntype = "void",
params =
[
{ name = "args", type = "array<string>" },
],
locals = [],
code =
[
{ type = "for",
init = { type = "setlocal", datatype = "int", off = 1, value = { type = "data", datatype = "int", data = 0 } },
test = { type = "lt", datatype = "int",
lft = { type = "local", off = 1 },
rgt = { type = "getprop", object = { type = "local", off = 0 }, property = { type = "data", datatype = "string", data = "size" } }
},
next = { type = "setlocal", datatype = "int", off = 1, value = { type = "add", datatype = "int",
lft = { type = "local", off = 1 },
rgt = { type = "data", datatype = "int", data = 1 } }
},
locals = [{ name = "i", type = "int" }],
body =
[
{ type = "fcall", func = "println(string)", ctx = null, args = [{ type = "getindex", object = { type = "local", off = 0 }, index = { type = "local", off = 1 } }] },
]
},
{ type = "fcall", func = "println(string)", ctx = null, args =
[
{ type = "concat", args =
[
{ type = "data", datatype = "string", data = "Hello, world! (" },
{ type = "convert", datatype = "string", source = { type = "getprop", object = { type = "local", off = 0 }, property = { type = "data", datatype = "string", data = "size" } } },
{ type = "data", datatype = "string", data = ")" },
]},
]},
],
},
"_test()" =
{
type = "function",
retntype = "void",
params = [],
locals = [],
code =
[
{ type = "fcall", func = "main(array<string>)", ctx = null, args =
[
{ type = "data", datatype = "array<string>", data =
{
m_data =
[
{ type = "string", data = "array" },
{ type = "string", data = "data" },
{ type = "string", data = "test" }
],
m_size = { type = "int", data = 3 },
m_mem = { type = "int", data = 3 },
}},
]},
],
},
};
function resolve_type( typename )
{
tydata = @AST[ typename ];
if( tydata )
return tydata;
if( typename != "int" && typename != "string" )
return ERROR( "no such type: " $ typename );
return { type = typename };
}
function value_is_nonzero( value )
{
if( !value )
return false;
if( value.type == "bool" )
return value.data;
if( value.type == "int" )
return value.data != 0;
if( value.type == "string" )
return value.data.size != 0;
return true;
}
global CALL_STACK = [];
function do__push_locals( locals )
{
outlocals = CALL_STACK.last.locals;
foreach( local : locals )
outlocals.push({ name = local.name, type = local.type, data = null });
}
function do__pop_locals( locals )
{
for( i = 0; i < locals.size; ++i )
CALL_STACK.last.locals.pop();
}
function do_fcall( func, ctx, args )
{
fndata = @AST[ func ];
if( !fndata )
return WARNING( "function was not found: " $ func );
if( fndata.type == "external" )
{
argsdata = [];
foreach( carg : args )
argsdata.push( carg.data );
fndata.func.apply( ctx, argsdata );
}
else
{
CALL_STACK.push
({
name = func,
func = fndata,
ctx = ctx,
locals = [],
retn = false,
});
do__push_locals( fndata.params );
foreach( i, arg : args )
CALL_STACK.last.locals[ i ].data = arg;
do__push_locals( fndata.locals );
foreach( line : fndata.code )
{
do_item( line );
}
return CALL_STACK.pop().retn;
}
}
function vm_is_a( type, what )
{
return type == what;
}
function do_concat( args )
{
out = "";
foreach( arg : args )
{
arg = do_item( arg );
if( !vm_is_a( arg.type, "string" ) )
ERROR( "CONCAT argument " $ arg $ " is not a string" );
out $= arg.data;
}
return { type = "string", data = out };
}
function do_getprop( obj, prop )
{
if( prop.type != "string" )
return WARNING( "property has invalid type: " $ prop );
typeinfo = resolve_type( obj.type );
key = typeinfo.fullname $ ":" $ prop.data $ ".get";
fndata = @AST[ key ];
if( fndata )
{
if( fndata.type == "getter" )
return obj.data[ fndata.source ];
if( fndata.type == "function" )
return do_fcall( key, obj, [] );
}
foreach( name, val : obj.data )
{
if( name == prop.data )
return val;
}
WARNING( "unknown property " $ prop $ " for object " $ obj );
}
function do_getindex( obj, idx )
{
// special case: pointers
if( is_array( obj ) )
{
return obj[ idx.data ];
}
typeinfo = resolve_type( obj.type );
key = typeinfo.fullname $ ":[](" $ idx.type $ ")";
fndata = @AST[ key ];
if( fndata )
{
if( fndata.type == "function" )
return do_fcall( key, obj, [ idx ] );
}
WARNING( "unknown index " $ idx $ " for object " $ obj );
}
function do_convert( item, typeto )
{
if( item.type == typeto )
return item;
if( typeto == "string" )
{
return { type = "string", data = tostring( item.data ) };
}
ERROR( "don't know how to convert from " $ item.type $ " to " $ typeto );
}
function do_add( lft, rgt )
{
lft = do_item( lft );
rgt = do_item( rgt );
if( lft.type == "int" && rgt.type == "int" )
return { type = "int", data = lft.data + rgt.data };
ERROR( "don't know how to do 'add' on this type" );
}
function do_lt( lft, rgt )
{
lft = do_item( lft );
rgt = do_item( rgt );
if( lft.type == "int" && rgt.type == "int" )
return { type = "bool", data = lft.data < rgt.data };
ERROR( "don't know how to do 'lt' on this type" );
}
function do_gte( lft, rgt )
{
lft = do_item( lft );
rgt = do_item( rgt );
if( lft.type == "int" && rgt.type == "int" )
return { type = "bool", data = lft.data >= rgt.data };
ERROR( "don't know how to do 'gte' on this type" );
}
function do_or( lft, rgt )
{
if( value_is_nonzero( do_item( lft ) ) )
return true;
return value_is_nonzero( do_item( rgt ) );
}
function do_item( item )
{
if( !item )
return;
if( CALL_STACK.last.retn )
return;
println( string_repeat( " ", 40 ) $ "doing " $ item.type $ " in " $ CALL_STACK.last.name );
if( item.type == "data" )
return { type = item.datatype, data = item.data };
if( item.type == "local" )
{
local = CALL_STACK.last.locals[ item.off ];
println( string_repeat( " ", 30 ) $ "reading local " $ local.name $ "@" $ item.off $ " = " $ local.data );
return local.data;
}
if( item.type == "setlocal" )
{
local = CALL_STACK.last.locals[ item.off ];
value = do_item( item.value );
println( string_repeat( " ", 30 ) $ "writing local " $ local.name $ "@" $ item.off $ " = " $ value );
return local.data = value;
}
if( item.type == "convert" )
{
return do_convert( do_item( item.source ), item.datatype );
}
if( item.type == "if" )
{
if( value_is_nonzero( do_item( item.test ) ) )
{
do__push_locals( item.locals_ontrue );
foreach( line : item.ontrue )
do_item( line );
do__pop_locals( item.locals_ontrue );
}
else
{
do__push_locals( item.locals_onfalse );
foreach( line : item.onfalse )
do_item( line );
do__pop_locals( item.locals_onfalse );
}
return;
}
if( item.type == "for" )
{
do__push_locals( item.locals );
do_item( item.init );
while( value_is_nonzero( do_item( item.test ) ) )
{
foreach( line : item.body )
do_item( line );
do_item( item.next );
}
do__pop_locals( item.locals );
return;
}
if( item.type == "fcall" )
{
args = [];
foreach( arg : item.args )
args.push( do_item( arg ) );
return do_fcall( item.func, do_item( item.ctx ), args );
}
if( item.type == "return" )
{
CALL_STACK.last.retn = if( @item.arg, do_item( item.arg ), true );
return;
}
if( item.type == "concat" )
return do_concat( item.args );
if( item.type == "getprop" ) return do_getprop( do_item( item.object ), do_item( item.property ) );
if( item.type == "getmember" ) return do_getprop( CALL_STACK.last.ctx, { type = "string", data = item.name } );
if( item.type == "getindex" ) return do_getindex( do_item( item.object ), do_item( item.index ) );
if( item.type == "add" ) return do_add( item.lft, item.rgt );
if( item.type == "lt" ) return do_lt( item.lft, item.rgt );
if( item.type == "gte" ) return do_gte( item.lft, item.rgt );
if( item.type == "or" ) return do_or( item.lft, item.rgt );
ERROR( "cannot do action " $ item.type );
}
do_fcall( "_test()", null, [] );
Comments