Artifact Content
Not logged in

Artifact a63938dc88b0c4246e52c73cc7293a8861fded62


     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  // [todo] move to value.d
    17  
    18  FunValue nativef(Value delegate(immutable LexPosition pos, Layer lay, Value[] args) dg)
    19  {
    20  	return new FunValue(dg);
    21  }
    22  
    23  FunValue native(R,T...)(R delegate (T) dg)
    24  {
    25  	return nativef( delegate Value(immutable LexPosition pos, Layer lay, Value[] args) {
    26  		if( lay != "@v" )
    27  			throw genex!RuntimeException(pos, "only @v layer can call native function");
    28  		if( T.length != args.length )
    29  			throw genex!RuntimeException(pos, "argument number mismatch!");
    30  		T typed_args;
    31  		foreach(i, Ti; T)
    32  		{
    33  			typed_args[i] = cast(Ti) args[i];
    34  			if( typed_args[i] is null )
    35  				throw genex!RuntimeException(pos, sprintf!"type mismatch on the argument %d"(i));
    36  		}
    37  		try {
    38  			return dg(typed_args);
    39  		} catch( RuntimeException e ) {
    40  			throw e.pos is null ? new RuntimeException(pos, e.msg, e.file, e.line) : e;
    41  		}
    42  	});
    43  }
    44  
    45  ///
    46  Table createGlobalContext()
    47  {
    48  	auto ctx = new Table;
    49  	ctx.set("+", "@v", native( (IntValue lhs, IntValue rhs){return new IntValue(lhs.data + rhs.data);} ));
    50  	ctx.set("-", "@v", native( (IntValue lhs, IntValue rhs){return new IntValue(lhs.data - rhs.data);} ));
    51  	ctx.set("*", "@v", native( (IntValue lhs, IntValue rhs){return new IntValue(lhs.data * rhs.data);} ));
    52  	ctx.set("/", "@v", native( (IntValue lhs, IntValue rhs){return new IntValue(lhs.data / rhs.data);} ));
    53  	ctx.set("%", "@v", native( (IntValue lhs, IntValue rhs){return new IntValue(lhs.data % rhs.data);} ));
    54  	ctx.set("<", "@v", native( (IntValue lhs, IntValue rhs){return new IntValue(BigInt(lhs.data < rhs.data ? 1: 0));} ));
    55  	ctx.set(">", "@v", native( (IntValue lhs, IntValue rhs){return new IntValue(BigInt(lhs.data > rhs.data ? 1: 0));} ));
    56  	ctx.set("<=", "@v", native( (IntValue lhs, IntValue rhs){return new IntValue(BigInt(lhs.data <= rhs.data ? 1: 0));} ));
    57  	ctx.set(">=", "@v", native( (IntValue lhs, IntValue rhs){return new IntValue(BigInt(lhs.data >= rhs.data ? 1: 0));} ));
    58  	ctx.set("==", "@v", native( (IntValue lhs, IntValue rhs){return new IntValue(BigInt(lhs.data == rhs.data ? 1: 0));} ));
    59  	ctx.set("!=", "@v", native( (IntValue lhs, IntValue rhs){return new IntValue(BigInt(lhs.data != rhs.data ? 1: 0));} ));
    60  	ctx.set("print", "@v", new FunValue(delegate Value(immutable LexPosition pos, Layer lay, Value[] args){
    61  		foreach(a; args)
    62  			write(a);
    63  		writeln("");
    64  		return new IntValue(BigInt(178));
    65  	}));
    66  	ctx.set("if", "@v", new FunValue(delegate Value(immutable LexPosition pos, Layer lay, Value[] args){
    67  		if( args.length != 3 )
    68  			throw genex!RuntimeException(pos, "if takes three arguments!!");
    69  		if( auto x = cast(IntValue)args[0] )
    70  		if( auto ft = cast(FunValue)args[1] )
    71  		if( auto fe = cast(FunValue)args[2] )
    72  			return (x.data == 0 ? fe : ft).call(pos,lay,[]);
    73  		throw genex!RuntimeException(pos, "type mismatch in if");
    74  	}));
    75  	return ctx;
    76  }
    77  
    78  /// Entry point of this module
    79  
    80  Tuple!(Value,"val",Table,"ctx") evalString(S,T...)(S str, T fn_ln_cn)
    81  {
    82  	return eval( polemy.parse.parseString(str, fn_ln_cn) );
    83  }
    84  
    85  /// Entry point of this module
    86  
    87  Tuple!(Value,"val",Table,"ctx") evalFile(S, T...)(S filename, T ln_cn)
    88  {
    89  	return eval( polemy.parse.parseFile(filename, ln_cn) );
    90  }
    91  
    92  /// Entry point of this module
    93  
    94  Tuple!(Value,"val",Table,"ctx") eval(AST e)
    95  {
    96  	Table ctx = createGlobalContext();
    97  	return typeof(return)(eval(e, ctx, false, "@v"), ctx);
    98  }
    99  
   100  /// Entry point of this module
   101  /// If splitCtx = true, then inner variable declaration do not overwrite ctx.
   102  /// lay is the layer ID for evaluation (standard value semantics uses "@v").
   103  
   104  Value eval(AST e, Table ctx, bool splitCtx, Layer lay)
   105  {
   106  	return e.match(
   107  		(StrLiteral e)
   108  		{
   109  			Value v = new StrValue(e.data);
   110  			if( lay == "@v" )
   111  				return v;
   112  			else // rise
   113  				return (cast(FunValue)ctx.get(lay, "(system)", e.pos)).call(e.pos, "@v", [v]);
   114  		},
   115  		(IntLiteral e)
   116  		{
   117  			Value v = new IntValue(e.data);
   118  			if( lay == "@v" )
   119  				return v;
   120  			else // rise
   121  				return (cast(FunValue)ctx.get(lay, "(system)", e.pos)).call(e.pos, "@v", [v]);
   122  		},
   123  		(VarExpression e)
   124  		{
   125  			try {
   126  				return ctx.get(e.var, lay, e.pos);
   127  			} catch( RuntimeException ) {
   128  				// rise from @v
   129  				return (cast(FunValue)ctx.get(lay, "(system)", e.pos)).call(e.pos, "@v", 
   130  					[ctx.get(e.var, "@v", e.pos)]
   131  				);
   132  			}
   133  		},
   134  		(LayeredExpression e)
   135  		{
   136  			return eval(e.expr, ctx, false, e.lay);
   137  		},
   138  		(LetExpression e)
   139  		{
   140  			// for letrec, we need this, but should avoid overwriting????
   141  			// ctx.set(e.var, "@v", new UndefinedValue, e.pos);
   142  			Value v = eval(e.init, ctx, true, lay);
   143  			if(splitCtx)
   144  				ctx = new Table(ctx, Table.Kind.NotPropagateSet);
   145  			ctx.set(e.var, (e.layer.length ? e.layer : lay), v, e.pos);
   146  			return eval(e.expr, ctx, false, lay);
   147  		},
   148  		(FuncallExpression e)
   149  		{
   150  			Value _f = eval(e.fun, ctx, true, lay);
   151  			if( auto f = cast(FunValue)_f ) {
   152  				Value[] args;
   153  				foreach(a; e.args)
   154  					args ~= eval(a, ctx, true, lay);
   155  				return f.call(e.pos, lay, args);
   156  			}
   157  			throw genex!RuntimeException(e.pos, "Non-funcion is applied");
   158  		},
   159  		(FunLiteral e)
   160  		{
   161  			// funvalue need not be rised
   162  			return new FunValue(delegate Value(immutable LexPosition pos, string lay, Value[] args){
   163  				if( e.params.length != args.length )
   164  					throw genex!RuntimeException(e.pos, sprintf!"Argument Number Mismatch (%d required but %d given)"
   165  						(e.params.length, args.length));
   166  				Table ctxNeo = new Table(ctx, Table.Kind.NotPropagateSet);
   167  				foreach(i,p; e.params)
   168  					ctxNeo.set(p.name, lay, args[i]);
   169  				return eval(e.funbody, ctxNeo, true, lay);
   170  			});
   171  		},
   172  		delegate Value (AST e)
   173  		{
   174  			throw genex!RuntimeException(e.pos, sprintf!"Unknown Kind of Expression %s"(typeid(e)));
   175  		}
   176  	);
   177  }
   178  
   179  unittest
   180  {
   181  	auto r = assert_nothrow( evalString(`var x = 21; x + x*x;`) );
   182  	assert_eq( r.val, new IntValue(BigInt(21+21*21)) );
   183  	assert_eq( r.ctx.get("x","@v"), new IntValue(BigInt(21)) );
   184  	assert_nothrow( r.ctx.get("x","@v") );
   185  	assert_throw!RuntimeException( r.ctx.get("y","@v") );
   186  }
   187  unittest
   188  {
   189  	auto r = assert_nothrow( evalString(`var x = 21; var x = x + x*x;`) );
   190  	assert_eq( r.val, new IntValue(BigInt(21+21*21)) );
   191  	assert_eq( r.ctx.get("x","@v"), new IntValue(BigInt(21+21*21)) );
   192  	assert_nothrow( r.ctx.get("x","@v") );
   193  	assert_throw!RuntimeException( r.ctx.get("y","@v") );
   194  }
   195  unittest
   196  {
   197  	assert_eq( evalString(`let x=1; let y=(let x=2); x`).val, new IntValue(BigInt(1)) ); 
   198  	assert_eq( evalString(`let x=1; let y=(let x=2;fun(){x}); y()`).val, new IntValue(BigInt(2)) ); 
   199  }
   200  unittest
   201  {
   202  	assert_eq( evalString(`@a x=1; @b x=2; @a(x)`).val, new IntValue(BigInt(1)) );
   203  	assert_eq( evalString(`@a x=1; @b x=2; @b(x)`).val, new IntValue(BigInt(2)) );
   204  	assert_eq( evalString(`let x=1; let _ = (@a x=2;2); x`).val, new IntValue(BigInt(1)) );
   205  	assert_throw!Error( evalString(`let x=1; let _ = (@a x=2;2); @a(x)`) );
   206  }
   207  
   208  unittest
   209  {
   210  	assert_eq( evalString(`var fac = fun(x){
   211  		if(x)
   212  			{ x*fac(x-1); }
   213  		else
   214  			{ 1; };
   215  	};
   216  	fac(10);`).val, new IntValue(BigInt(10*9*8*5040)));
   217  	assert_eq( evalString(`var fib = fun(x){
   218  		if(x<2)
   219  			{ 1; }
   220  		else
   221  			{ fib(x-1) + fib(x-2); };
   222  	};
   223  	fib(10);`).val, new IntValue(BigInt(89)));
   224  }
   225  
   226  unittest
   227  {
   228  	assert_throw!Throwable( evalString(`@@s(x){x}; @s "+"=fun(x,y){x-y};@s(1+2)`) );
   229  	assert_eq( evalString(`@@s(x){x}; @s "+"=fun(x,y){x-y};1+2`).val, new IntValue(BigInt(3)) );
   230  	assert_eq( evalString(`@@s(x){x}; @s "+"=fun(x,y){@v(@s(x)-@s(y))};1+2`).val, new IntValue(BigInt(3)) );
   231  	assert_eq( evalString(`@@s(x){x}; @s "+"=fun(x,y){@v(@s(x)-@s(y))};@s(1+2)`).val, new IntValue(BigInt(-1)) );
   232  }
   233  
   234  unittest
   235  {
   236  	assert_eq( evalString(`@@t = fun(x){x+1}; @t(123)`).val, new IntValue(BigInt(124)) );
   237  }