snake5 icon

new lang ast proto on sgs

snake5 | PRO | 12/21/14 09:02:18 PM UTC | 0 ⭐ | 893 👁️ | Never ⏰ | []
JavaScript |

11.2 KB

|

None

|

0 👍

/

0 👎

 
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