Artifact Content
Not logged in

Artifact cea762cacb86424f3fe949fbebf4b4d43121324d


     1  /**
     2   * Authors: k.inaba
     3   * License: NYSL 0.9982 http://www.kmonos.net/nysl/
     4   *
     5   * Evaluator for Polemy programming language.
     6   */
     7  module polemy.eval;
     8  import polemy._common;
     9  import polemy.lex : LexPosition;
    10  import polemy.ast;
    11  import polemy.parse;
    12  import polemy.runtime;
    13  import std.typecons;
    14  
    15  Context createGlobalContext()
    16  {
    17  	auto ctx = new Context;
    18  	ctx.add("+", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){
    19  		if( args.length != 2 )
    20  			throw new PolemyRuntimeException("+ takes two arguments!! ["~to!string(pos)~"]");
    21  		if( auto x = cast(IntValue)args[0] )
    22  			if( auto y = cast(IntValue)args[1] )
    23  				return new IntValue(x.data+y.data);
    24  		throw new PolemyRuntimeException("cannot add non-integers ["~to!string(pos)~"]");
    25  	}));
    26  	ctx.add("-", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){
    27  		if( args.length != 2 )
    28  			throw new PolemyRuntimeException("- takes two arguments!! ["~to!string(pos)~"]");
    29  		if( auto x = cast(IntValue)args[0] )
    30  			if( auto y = cast(IntValue)args[1] )
    31  				return new IntValue(x.data-y.data);
    32  		throw new PolemyRuntimeException("cannot add non-integers ["~to!string(pos)~"]");
    33  	}));
    34  	ctx.add("*", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){
    35  		if( args.length != 2 )
    36  			throw new PolemyRuntimeException("* takes two arguments!! ["~to!string(pos)~"]");
    37  		if( auto x = cast(IntValue)args[0] )
    38  			if( auto y = cast(IntValue)args[1] )
    39  				return new IntValue(x.data*y.data);
    40  		throw new PolemyRuntimeException("cannot add non-integers ["~to!string(pos)~"]");
    41  	}));
    42  	ctx.add("/", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){
    43  		if( args.length != 2 )
    44  			throw new PolemyRuntimeException("/ takes two arguments!! ["~to!string(pos)~"]");
    45  		if( auto x = cast(IntValue)args[0] )
    46  			if( auto y = cast(IntValue)args[1] )
    47  				return new IntValue(x.data/y.data);
    48  		throw new PolemyRuntimeException("cannot add non-integers ["~to!string(pos)~"]");
    49  	}));
    50  	return ctx;
    51  }
    52  
    53  Tuple!(Value,"val",Context,"ctx") evalString(T...)(T params)
    54  {
    55  	return eval( parserFromString(params).parseProgram() );
    56  }
    57  
    58  Tuple!(Value,"val",Context,"ctx") evalFile(T...)(T params)
    59  {
    60  	return eval( parserFromFile(params).parseProgram() );
    61  }
    62  
    63  Tuple!(Value,"val",Context,"ctx") eval(Program prog)
    64  {
    65  	Context ctx = createGlobalContext();
    66  	return typeof(return)(eval(prog, ctx), ctx);
    67  }
    68  
    69  Value eval(Program prog, Context ctx)
    70  {
    71  	Value v = new UndefinedValue;
    72  	foreach(s; prog)
    73  		v = eval(s, ctx);
    74  	return v;
    75  }
    76  
    77  Value eval(Statement _s, Context ctx)
    78  {
    79  	if( auto s = cast(DeclStatement)_s )
    80  	{
    81  		auto v = eval(s.expr, ctx);
    82  		ctx.add(s.var, v);
    83  		return v;
    84  	}
    85  	else
    86  	if( auto s = cast(ExprStatement)_s )
    87  	{
    88  		return eval(s.expr, ctx);
    89  	}
    90  	throw new PolemyRuntimeException(sprintf!"Unknown Kind of Statement %s at [%s]"(typeid(_s), _s.pos));
    91  }
    92  
    93  Value eval(Expression _e, Context ctx)
    94  {
    95  	if( auto e = cast(StrLiteralExpression)_e )
    96  	{
    97  		return new StrValue(e.data);
    98  	}
    99  	else
   100  	if( auto e = cast(IntLiteralExpression)_e )
   101  	{
   102  		return new IntValue(e.data);
   103  	}
   104  	else
   105  	if( auto e = cast(VarExpression)_e )
   106  	{
   107  		return ctx[e.var];
   108  	}
   109  	else
   110  	if( auto e = cast(AssignExpression)_e )
   111  	{
   112  		if( auto ev = cast(VarExpression)e.lhs )
   113  		{
   114  			Value r = eval(e.rhs, ctx);
   115  			ctx[ev.var] = r;
   116  			return r;
   117  		}
   118  		throw new PolemyRuntimeException(sprintf!"Lhs of assignment must be a variable: %s"(e.pos));
   119  	}
   120  	else
   121  	if( auto e = cast(FuncallExpression)_e )
   122  	{
   123  		Value _f = eval(e.fun, ctx);
   124  		if( auto f = cast(FunValue)_f ) {
   125  			Value[] args;
   126  			foreach(a; e.args)
   127  				args ~= eval(a, ctx);
   128  			return f.call(e.pos, args);
   129  		} else
   130  			throw new PolemyRuntimeException(sprintf!"Non-funcion is applied at [%s]"(e.pos));
   131  	}
   132  	else
   133  	if( auto e = cast(FunLiteralExpression)_e )
   134  	{
   135  		return new FunValue(delegate Value(immutable LexPosition pos, Value[] args){
   136  			if( e.params.length != args.length )
   137  				throw new PolemyRuntimeException(sprintf!"Argument Number Mismatch (%d required but %d given) at [%s]"
   138  					(e.params.length, args.length, e.pos));
   139  			Context ctxNeo = new Context(ctx);
   140  			foreach(i,p; e.params)
   141  				ctxNeo.add(p, args[i]);
   142  			return eval(e.funbody, ctxNeo);
   143  		});
   144  	}
   145  	throw new PolemyRuntimeException(sprintf!"Unknown Kind of Expression %s at [%s]"(typeid(_e), _e.pos));
   146  }
   147  
   148  import std.stdio;
   149  unittest
   150  {
   151  	auto r = evalString(`var x = 21; x = x + x*x;`);
   152  	assert( r.val == new IntValue(BigInt(21+21*21)) );
   153  	assert( r.ctx["x"] == new IntValue(BigInt(21+21*21)) );
   154  	assert( !collectException(r.ctx["x"]) );
   155  	assert( collectException(r.ctx["y"]) );
   156  }
   157  unittest
   158  {
   159  	assert( collectException(evalString(`var x = 21; x = x + x*y;`)) );
   160  	assert( collectException(evalString(`x=1;`)) );
   161  }
   162  unittest
   163  {
   164  	auto r = evalString(`var x = fun(a){1+a;}(2);`);
   165  	assert( r.ctx["x"] == new IntValue(BigInt(3)) );
   166  	assert( r.val == new IntValue(BigInt(3)) );
   167  }
   168  unittest
   169  {
   170  	auto r = evalString(`var x = 1; var f = fun(){x=x+1;}; f(); f(); f();`);
   171  	assert( r.ctx["x"] == new IntValue(BigInt(4)) );
   172  	assert( r.val == new IntValue(BigInt(4)) );
   173  }