Artifact Content
Not logged in

Artifact bcefce47daacc4a5e6725cb32d499b73e6f1ac11


     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.value;
    13  import std.typecons;
    14  import std.stdio;
    15  
    16  Context createGlobalContext()
    17  {
    18  	auto ctx = new Context;
    19  	ctx.add("+", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){
    20  		if( args.length != 2 )
    21  			throw new PolemyRuntimeException("+ takes two arguments!! ["~to!string(pos)~"]");
    22  		if( auto x = cast(IntValue)args[0] )
    23  			if( auto y = cast(IntValue)args[1] )
    24  				return new IntValue(x.data+y.data);
    25  		throw new PolemyRuntimeException("cannot add non-integers ["~to!string(pos)~"]");
    26  	}));
    27  	ctx.add("-", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){
    28  		if( args.length != 2 )
    29  			throw new PolemyRuntimeException("- takes two arguments!! ["~to!string(pos)~"]");
    30  		if( auto x = cast(IntValue)args[0] )
    31  			if( auto y = cast(IntValue)args[1] )
    32  				return new IntValue(x.data-y.data);
    33  		throw new PolemyRuntimeException("cannot add non-integers ["~to!string(pos)~"]");
    34  	}));
    35  	ctx.add("*", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){
    36  		if( args.length != 2 )
    37  			throw new PolemyRuntimeException("* takes two arguments!! ["~to!string(pos)~"]");
    38  		if( auto x = cast(IntValue)args[0] )
    39  			if( auto y = cast(IntValue)args[1] )
    40  				return new IntValue(x.data*y.data);
    41  		throw new PolemyRuntimeException("cannot add non-integers ["~to!string(pos)~"]");
    42  	}));
    43  	ctx.add("/", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){
    44  		if( args.length != 2 )
    45  			throw new PolemyRuntimeException("/ takes two arguments!! ["~to!string(pos)~"]");
    46  		if( auto x = cast(IntValue)args[0] )
    47  			if( auto y = cast(IntValue)args[1] )
    48  				return new IntValue(x.data/y.data);
    49  		throw new PolemyRuntimeException("cannot add non-integers ["~to!string(pos)~"]");
    50  	}));
    51  	ctx.add("<", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){
    52  		if( args.length != 2 )
    53  			throw new PolemyRuntimeException("< takes two arguments!! ["~to!string(pos)~"]");
    54  		if( auto x = cast(IntValue)args[0] )
    55  			if( auto y = cast(IntValue)args[1] )
    56  				return new IntValue(BigInt(to!int(x.data < y.data)));
    57  		throw new PolemyRuntimeException("cannot add non-integers ["~to!string(pos)~"]");
    58  	}));
    59  	ctx.add(">", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){
    60  		if( args.length != 2 )
    61  			throw new PolemyRuntimeException("> takes two arguments!! ["~to!string(pos)~"]");
    62  		if( auto x = cast(IntValue)args[0] )
    63  			if( auto y = cast(IntValue)args[1] )
    64  				return new IntValue(BigInt(to!int(x.data>y.data)));
    65  		throw new PolemyRuntimeException("cannot add non-integers ["~to!string(pos)~"]");
    66  	}));
    67  	ctx.add("print", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){
    68  		foreach(a; args)
    69  			write(a);
    70  		writeln("");
    71  		return new UndefinedValue;
    72  	}));
    73  	ctx.add("if", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){
    74  		if( args.length != 3 )
    75  			throw new PolemyRuntimeException("if takes three arguments!! ["~to!string(pos)~"]");
    76  		if( auto x = cast(IntValue)args[0] )
    77  		if( auto ft = cast(FunValue)args[1] )
    78  		if( auto fe = cast(FunValue)args[2] )
    79  			return (x.data == 0 ? fe : ft).call(pos,[]);
    80  		throw new PolemyRuntimeException("type mismatch in if ["~to!string(pos)~"]");
    81  	}));
    82  	return ctx;
    83  }
    84  
    85  Tuple!(Value,"val",Context,"ctx") evalString(T...)(T params)
    86  {
    87  	return eval( parserFromString(params).parseProgram() );
    88  }
    89  
    90  Tuple!(Value,"val",Context,"ctx") evalFile(T...)(T params)
    91  {
    92  	return eval( parserFromFile(params).parseProgram() );
    93  }
    94  
    95  Tuple!(Value,"val",Context,"ctx") eval(Program prog)
    96  {
    97  	Context ctx = createGlobalContext();
    98  	return typeof(return)(eval(prog, ctx), ctx);
    99  }
   100  
   101  Value eval(Program prog, Context ctx)
   102  {
   103  	Value v = new UndefinedValue;
   104  	foreach(s; prog)
   105  		v = eval(s, ctx);
   106  	return v;
   107  }
   108  
   109  Value eval(Statement _s, Context ctx)
   110  {
   111  	if( auto s = cast(DeclStatement)_s )
   112  	{
   113  		auto v = eval(s.expr, ctx);
   114  		ctx.add(s.var, v);
   115  		return v;
   116  	}
   117  	else
   118  	if( auto s = cast(ExprStatement)_s )
   119  	{
   120  		return eval(s.expr, ctx);
   121  	}
   122  	throw new PolemyRuntimeException(sprintf!"Unknown Kind of Statement %s at [%s]"(typeid(_s), _s.pos));
   123  }
   124  
   125  Value eval(Expression _e, Context ctx)
   126  {
   127  	if( auto e = cast(StrLiteralExpression)_e )
   128  	{
   129  		return new StrValue(e.data);
   130  	}
   131  	else
   132  	if( auto e = cast(IntLiteralExpression)_e )
   133  	{
   134  		return new IntValue(e.data);
   135  	}
   136  	else
   137  	if( auto e = cast(VarExpression)_e )
   138  	{
   139  		return ctx[e.var];
   140  	}
   141  	else
   142  	if( auto e = cast(AssignExpression)_e )
   143  	{
   144  		if( auto ev = cast(VarExpression)e.lhs )
   145  		{
   146  			Value r = eval(e.rhs, ctx);
   147  			ctx[ev.var] = r;
   148  			return r;
   149  		}
   150  		throw new PolemyRuntimeException(sprintf!"Lhs of assignment must be a variable: %s"(e.pos));
   151  	}
   152  	else
   153  	if( auto e = cast(FuncallExpression)_e )
   154  	{
   155  		Value _f = eval(e.fun, ctx);
   156  		if( auto f = cast(FunValue)_f ) {
   157  			Value[] args;
   158  			foreach(a; e.args)
   159  				args ~= eval(a, ctx);
   160  			return f.call(e.pos, args);
   161  		} else
   162  			throw new PolemyRuntimeException(sprintf!"Non-funcion is applied at [%s]"(e.pos));
   163  	}
   164  	else
   165  	if( auto e = cast(FunLiteralExpression)_e )
   166  	{
   167  		return new FunValue(delegate Value(immutable LexPosition pos, Value[] args){
   168  			if( e.params.length != args.length )
   169  				throw new PolemyRuntimeException(sprintf!"Argument Number Mismatch (%d required but %d given) at [%s]"
   170  					(e.params.length, args.length, e.pos));
   171  			Context ctxNeo = new Context(ctx);
   172  			foreach(i,p; e.params)
   173  				ctxNeo.add(p, args[i]);
   174  			return eval(e.funbody, ctxNeo);
   175  		});
   176  	}
   177  	throw new PolemyRuntimeException(sprintf!"Unknown Kind of Expression %s at [%s]"(typeid(_e), _e.pos));
   178  }
   179  
   180  import std.stdio;
   181  unittest
   182  {
   183  	auto r = evalString(`var x = 21; x = x + x*x;`);
   184  	assert( r.val == new IntValue(BigInt(21+21*21)) );
   185  	assert( r.ctx["x"] == new IntValue(BigInt(21+21*21)) );
   186  	assert( !collectException(r.ctx["x"]) );
   187  	assert( collectException(r.ctx["y"]) );
   188  }
   189  unittest
   190  {
   191  	assert( collectException(evalString(`var x = 21; x = x + x*y;`)) );
   192  	assert( collectException(evalString(`x=1;`)) );
   193  }
   194  unittest
   195  {
   196  	auto r = evalString(`var x = fun(a){1+a;}(2);`);
   197  	assert( r.ctx["x"] == new IntValue(BigInt(3)) );
   198  	assert( r.val == new IntValue(BigInt(3)) );
   199  }
   200  unittest
   201  {
   202  	auto r = evalString(`var x = 1; var f = fun(){x=x+1;}; f(); f(); f();`);
   203  	assert( r.ctx["x"] == new IntValue(BigInt(4)) );
   204  	assert( r.val == new IntValue(BigInt(4)) );
   205  }
   206  unittest
   207  {
   208  	evalString(`print("Hello, world!");`);
   209  	evalString(`print(fun(){});`);
   210  }
   211  unittest
   212  {
   213  	evalString(`var fac = fun(x){
   214  		1;
   215  	};
   216  	print(fac(3));`);
   217  	evalString(`var fac = fun(x){
   218  		if(x)
   219  			{ x*fac(x-1); }
   220  		else
   221  			{ 1; };
   222  	};
   223  	print(fac(10));`);
   224  	evalString(`var fib = fun(x){
   225  		if(x<2)
   226  			{ 1; }
   227  		else
   228  			{ fib(x-1) + fib(x-2); };
   229  	};
   230  	print(fib(10));`);
   231  }