Artifact Content
Not logged in

Artifact 2bd159f2e71889e8dc4f7ef9fdd80a16eaafdf3c


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