Artifact Content
Not logged in

Artifact b119954d6ce4330154827e1b9fdcd566cea8851e


     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.failure;
    10  import polemy.ast;
    11  import polemy.parse;
    12  import polemy.value;
    13  import polemy.layer;
    14  
    15  class Evaluator
    16  {
    17  public:
    18  	this() { theContext = new Table; }
    19  
    20  	Value evalAST(AST e)
    21  	{
    22  		return eval(e, ValueLayer, theContext, OverwriteCtx);
    23  	}
    24  
    25  	Value evalString(S,T...)(S str, T fn_ln_cn)
    26  	{
    27  		return evalAST(parseString(str,fn_ln_cn));
    28  	}
    29  
    30  	Value evalFile(S,T...)(S filename, T ln_cn)
    31  	{
    32  		return evalAST(parseFile(filename,ln_cn));
    33  	}
    34  
    35  	Table globalContext()
    36  	{
    37  		return theContext;
    38  	}
    39  
    40  private:
    41  	Table theContext;
    42  
    43  private:
    44  	enum : bool { CascadeCtx=false, OverwriteCtx=true };
    45  	
    46  	Value eval( AST e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx )
    47  	{
    48  		// dynamic-overload-resolution-pattern: modify here
    49  		enum funName = "eval";
    50  		alias TypeTuple!(e,lay,ctx,overwriteCtx) params;
    51  
    52  		// dynamic-overload-resolution-pattern: dispatch
    53  		alias typeof(__traits(getOverloads, this, funName)) ovTypes;
    54  		alias staticMap!(firstParam, ovTypes)              fstTypes;
    55  		alias DerivedToFront!(fstTypes)             fstTypes_sorted;
    56  		foreach(i, T; fstTypes_sorted)
    57  			static if( is(T == typeof(params[0])) ) {} else if( auto _x = cast(T)params[0] )
    58  				return __traits(getOverloads, this, funName)[i](_x, params[1..$]);
    59  
    60  		// dynamic-overload-resolution-pattern: default behavior
    61  		assert(false, text("eval() for ",typeid(e)," [",e.pos,"] is not defined"));
    62  	}
    63  
    64  private:
    65  	Value eval( Str e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx )
    66  	{
    67  		Value v = new StrValue(e.data);
    68  		if( lay==RawMacroLayer || lay==MacroLayer )
    69  		{
    70  			auto ast = new Table;
    71  			ast.set("pos",  ValueLayer, fromPos(e.pos));
    72  			ast.set("is",   ValueLayer, new StrValue("str"));
    73  			ast.set("data", ValueLayer, v);
    74  			return ast;
    75  		}
    76  		if( lay==ValueLayer )
    77  			return v;
    78  		return lift(v, lay, ctx, e.pos);
    79  	}
    80  
    81  	Value eval( Int e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx )
    82  	{
    83  		Value v = new IntValue(e.data);
    84  		if( lay==RawMacroLayer || lay==MacroLayer )
    85  		{
    86  			auto ast = new Table;
    87  			ast.set("pos",  ValueLayer, fromPos(e.pos));
    88  			ast.set("is",   ValueLayer, new StrValue("int"));
    89  			ast.set("data", ValueLayer, v);
    90  			return ast;
    91  		}
    92  		if( lay==ValueLayer )
    93  			return v;
    94  		return lift(v, lay, ctx, e.pos);
    95  	}
    96  
    97  	Value eval( Var e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx )
    98  	{
    99  		if( lay==RawMacroLayer || lay==MacroLayer )
   100  		{
   101  			if( ctx.has(e.name,MacroLayer) )
   102  				return ctx.get(e.name, MacroLayer, e.pos);
   103  			auto ast = new Table;
   104  			ast.set("pos",  ValueLayer, fromPos(e.pos));
   105  			ast.set("is",   ValueLayer, new StrValue("var"));
   106  			ast.set("name", ValueLayer, new StrValue(e.name));
   107  			return ast;
   108  		}
   109  		if( lay==ValueLayer || ctx.has(e.name, lay) )
   110  			return ctx.get(e.name, lay, e.pos);
   111  		return lift(ctx.get(e.name, ValueLayer, e.pos), lay, ctx, e.pos);
   112  	}
   113  
   114  	Value eval( App e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx )
   115  	{
   116  		Value f = eval( e.fun, lay, ctx );
   117  		if( lay==RawMacroLayer || lay==MacroLayer )
   118  		{
   119  			if( auto ff = cast(FunValue)f )
   120  				return invokeFunction(ff, e.args, MacroLayer, ctx, e.pos);
   121  			Table ast = new Table;
   122  			ast.set("pos",  ValueLayer, fromPos(e.pos));
   123  			ast.set("is",   ValueLayer, new StrValue("app"));
   124  			ast.set("fun",  ValueLayer, f);
   125  			Table args = new Table;
   126  			foreach_reverse(a; e.args)
   127  				args = makeCons(eval(a, lay, ctx), args);
   128  			ast.set("args", ValueLayer, args);
   129  			return ast;
   130  		}
   131  		else
   132  		{
   133  			return invokeFunction(f, e.args, lay, ctx, e.pos);
   134  		}
   135  	}
   136  	
   137  	Value eval( Fun e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx )
   138  	{
   139  		if( lay==RawMacroLayer || lay==MacroLayer )
   140  		{
   141  			Table t = new Table;
   142  			t.set("pos",     ValueLayer, fromPos(e.pos));
   143  			t.set("is",      ValueLayer, new StrValue("fun"));
   144  			t.set("funbody", ValueLayer, eval(e.funbody,lay,ctx));
   145  			Table params = new Table;
   146  			foreach_reverse(p; e.params)
   147  			{
   148  				Table lays = new Table;
   149  				foreach_reverse(l; p.layers)
   150  					lays = makeCons(new StrValue(l), lays);
   151  				Table kv = new Table;
   152  				kv.set("name", ValueLayer, new StrValue(p.name));
   153  				kv.set("layers", ValueLayer, lays);
   154  				Table cons = new Table;
   155  				params = makeCons(kv, params);
   156  			}
   157  			t.set("params", ValueLayer, params);
   158  			return t;
   159  		}
   160  		else
   161  		{
   162  			return createNewFunction(e, ctx);
   163  		}
   164  	}
   165  	
   166  	Value eval( Lay e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx )
   167  	{
   168  		if( lay == RawMacroLayer )
   169  		{
   170  			Value r = eval(e.expr, lay, ctx);
   171  			auto ast = new Table; // todo: pos
   172  			ast.set("pos",   ValueLayer, fromPos(e.pos));
   173  			ast.set("is",    ValueLayer, new StrValue("lay"));
   174  			ast.set("layer", ValueLayer, new StrValue(e.layer));
   175  			ast.set("expr",  ValueLayer, r);
   176  			return ast;
   177  		}
   178  		else
   179  			return eval(e.expr, e.layer, ctx);
   180  	}
   181  	
   182  	Value eval( Let e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx )
   183  	{
   184  		// todo @macro let
   185  		if( lay==RawMacroLayer || lay==MacroLayer )
   186  		{
   187  			auto ast = new Table; // todo: pos
   188  			ast.set("pos",   ValueLayer, fromPos(e.pos));
   189  			ast.set("is",    ValueLayer, new StrValue("let"));
   190  			ast.set("name",  ValueLayer, new StrValue(e.name));
   191  			ast.set("layer", ValueLayer, new StrValue(e.layer));
   192  			ast.set("init",  ValueLayer, eval(e.init, lay, ctx));
   193  			ast.set("expr",  ValueLayer, eval(e.expr, lay, ctx));
   194  			return ast;
   195  		}
   196  		else
   197  		{
   198  			if( !overwriteCtx )
   199  				ctx = new Table(ctx, Table.Kind.NotPropagateSet);
   200  			Value ri = eval(e.init, lay, ctx);
   201  			string theLayer = e.layer.empty ? (lay==RawMacroLayer ? MacroLayer : lay) : e.layer;
   202  			ctx.set(e.name, theLayer, ri);
   203  			return eval(e.expr, lay, ctx, OverwriteCtx);
   204  		}
   205  	}
   206  
   207  private:
   208  	Value invokeFunction(Value _f, AST[] args, Layer lay, Table ctx, LexPosition pos=null)
   209  	{
   210  		if(auto f = cast(FunValue)_f)
   211  		{
   212  			Table newCtx = new Table(f.definitionContext(), Table.Kind.NotPropagateSet);
   213  			foreach(i,p; f.params())
   214  				if( p.layers.empty )
   215  					newCtx.set(p.name, (lay==RawMacroLayer ? MacroLayer : lay), eval(args[i], lay, ctx));
   216  				else
   217  					foreach(argLay; p.layers)
   218  						newCtx.set(p.name, argLay, eval(args[i], argLay, ctx));
   219  			return f.invoke(pos, lay, newCtx);
   220  		}
   221  		throw genex!RuntimeException(pos, text("tried to call non-function: ",_f));
   222  	}
   223  
   224  	Value lift(Value v, Layer lay, Table ctx, LexPosition pos=null)
   225  	{
   226  		// functions are automatically lifterd
   227  		if( cast(FunValue) v )
   228  			return v;
   229  
   230  		// similar to invoke Function, but with only one argument bound to ValueLayer
   231  		if(auto f = cast(FunValue)ctx.get(lay, SystemLayer, pos))
   232  		{
   233  			Table newCtx = new Table(f.definitionContext(), Table.Kind.NotPropagateSet);
   234  			auto ps = f.params();
   235  			if( ps.length != 1 )
   236  				throw genex!RuntimeException(pos, "lift function must take exactly one argument at "~ValueLayer~" layer");
   237  			if( ps[0].layers.length==0 || ps[0].layers.length==1 && ps[0].layers[0]==ValueLayer )
   238  			{
   239  				newCtx.set(ps[0].name, ValueLayer, v);
   240  				return f.invoke(pos, ValueLayer, newCtx);
   241  			}
   242  			else
   243  				throw genex!RuntimeException(pos, "lift function must take exactly one argument at "~ValueLayer~" layer");
   244  		}
   245  		throw genex!RuntimeException(pos, "tried to call non-function");
   246  	}
   247  
   248  	Value createNewFunction(Fun e, Table ctx)
   249  	{
   250  		class UserDefinedFunValue : FunValue
   251  		{
   252  			Fun ast;
   253  			Table      defCtx;
   254  			override const(Parameter[]) params() { return ast.params; }
   255  			override Table definitionContext() { return defCtx; }
   256  
   257  			this(Fun ast, Table defCtx) { this.ast=ast; this.defCtx=defCtx; }
   258  			override string toString() const { return sprintf!"(function:%x:%x)"(cast(void*)ast, cast(void*)defCtx); }
   259  			override bool opEquals(Object rhs_) const /// member-by-member equality
   260  			{
   261  				if( auto rhs = cast(typeof(this))rhs_ )
   262  					return this.ast==rhs.ast && this.defCtx==rhs.defCtx;
   263  				assert(false, sprintf!"Cannot compare %s with %s"(typeid(this), typeid(rhs_)));
   264  			}
   265  			override hash_t toHash() const /// member-by-member hash
   266  			{
   267  				return typeid(this.ast).getHash(&this.ast) + typeid(this.defCtx).getHash(&this.defCtx);
   268  			}
   269  			override int opCmp(Object rhs_) /// member-by-member compare
   270  			{
   271  				if( auto rhs = cast(typeof(this))rhs_ )
   272  				{
   273  					if(auto i = this.ast.opCmp(rhs.ast))
   274  						return i;
   275  					return this.defCtx.opCmp(rhs.defCtx);
   276  				}
   277  				assert(false, sprintf!"Cannot compare %s with %s"(typeid(this), typeid(rhs_)));
   278  			}
   279  
   280  			override Value invoke(LexPosition pos, Layer lay, Table ctx)
   281  			{
   282  				if( lay == MacroLayer )
   283  					return eval(ast.funbody, lay, ctx);
   284  				auto macroed = tableToAST(ValueLayer, eval(e.funbody, RawMacroLayer, ctx));
   285  				return eval(macroed, lay, ctx);
   286  			}
   287  		}
   288  		return new UserDefinedFunValue(e,ctx);
   289  	}
   290  
   291  public:
   292  	/// TODO: move up
   293  	/// TDOO: to other layers?
   294  	void addPrimitive(R,T...)(string name, Layer lay, R delegate (T) dg)
   295  	{
   296  		class NativeFunValue : FunValue
   297  		{
   298  			Parameter[] params_data;
   299  			override string toString() { return sprintf!"(native:%x)"(dg.funcptr); }
   300  			override const(Parameter[]) params() { return params_data; }
   301  			override Table definitionContext() { return new Table; } // todo: cache
   302  			this(){
   303  				foreach(i, Ti; T)
   304  					params_data ~= new Parameter(text(i), []);
   305  			}
   306  			override Value invoke(LexPosition pos, Layer lay, Table ctx)
   307  			{
   308  				if( lay != ValueLayer )
   309  					throw genex!RuntimeException(pos, "only "~ValueLayer~" layer can call native function");
   310  				T typed_args;
   311  				foreach(i, Ti; T) {
   312  					typed_args[i] = cast(Ti) ctx.get(text(i), ValueLayer);
   313  					if( typed_args[i] is null )
   314  						throw genex!RuntimeException(pos, sprintf!"type mismatch on the argument %d"(i+1));
   315  				}
   316  				try {
   317  					return dg(typed_args);
   318  				} catch( RuntimeException e ) {
   319  					throw e.pos is null ? new RuntimeException(pos, e.msg, e.file, e.line) : e;
   320  				}
   321  			}
   322  		}
   323  		theContext.set(name, lay, new NativeFunValue);
   324  	}
   325  }
   326  
   327  version(unittest) import polemy.runtime;
   328  unittest
   329  {
   330  	auto e = new Evaluator;
   331  	enrollRuntimeLibrary(e);
   332  	auto r = assert_nothrow( e.evalString(`var x = 21; x + x*x;`) );
   333  	assert_eq( r, new IntValue(BigInt(21+21*21)) );
   334  	assert_eq( e.globalContext.get("x",ValueLayer), new IntValue(BigInt(21)) );
   335  	assert_nothrow( e.globalContext.get("x",ValueLayer) );
   336  	assert_throw!RuntimeException( e.globalContext.get("y",ValueLayer) );
   337  }
   338  unittest
   339  {
   340  	auto e = new Evaluator;
   341  	enrollRuntimeLibrary(e);
   342  	auto r = assert_nothrow( e.evalString(`var x = 21; var x = x + x*x;`) );
   343  	assert_eq( r, new IntValue(BigInt(21+21*21)) );
   344  	assert_eq( e.globalContext.get("x",ValueLayer), new IntValue(BigInt(21+21*21)) );
   345  	assert_nothrow( e.globalContext.get("x",ValueLayer) );
   346  	assert_throw!RuntimeException( e.globalContext.get("y",ValueLayer) );
   347  }
   348  unittest
   349  {
   350  	auto e = new Evaluator;
   351  	enrollRuntimeLibrary(e);
   352  	assert_eq( e.evalString(`let x=1; let y=(let x=2); x`), new IntValue(BigInt(1)) ); 
   353  	assert_eq( e.evalString(`let x=1; let y=(let x=2;fun(){x}); y()`), new IntValue(BigInt(2)) ); 
   354  }
   355  
   356  unittest
   357  {
   358  	auto e = new Evaluator;
   359  	enrollRuntimeLibrary(e);
   360  	assert_eq( e.evalString(`@a x=1; @b x=2; @a(x)`), new IntValue(BigInt(1)) );
   361  	assert_eq( e.evalString(`@a x=1; @b x=2; @b(x)`), new IntValue(BigInt(2)) );
   362  	assert_eq( e.evalString(`let x=1; let _ = (@a x=2;2); x`), new IntValue(BigInt(1)) );
   363  	e = new Evaluator;
   364  	assert_throw!Throwable( e.evalString(`let x=1; let _ = (@a x=2;2); @a(x)`) );
   365  }
   366  
   367  unittest
   368  {
   369  	auto e = new Evaluator;
   370  	enrollRuntimeLibrary(e);
   371  	assert_eq( e.evalString(`
   372  		@@s(x){x};
   373  		@s "+" = fun(x, y) {@value(
   374  			@s(x) - @s(y)
   375  		)};
   376  		@s(1 + 2)
   377  	`), new IntValue(BigInt(-1)) );
   378  }
   379  
   380  unittest
   381  {
   382  	auto e = new Evaluator;
   383  	enrollRuntimeLibrary(e);
   384  	assert_eq( e.evalString(`
   385  @@3(x){x};
   386  def incr(x) { x+1 };
   387  @ 3 incr(x) {@value( if(@ 3(x)+1< 3){@ 3(x)+1}else{0} )};
   388  def fb(n @value @3) { @3(n) };
   389  fb(incr(incr(incr(0))))
   390  	`), new IntValue(BigInt(0)) );
   391  }
   392  
   393  unittest
   394  {
   395  	auto e = new Evaluator;
   396  	enrollRuntimeLibrary(e);
   397  	assert_nothrow( e.evalString(`
   398  @macro twice(x) { x; x };
   399  def main() { twice(1) };
   400  main()
   401  	`) );
   402  }
   403  /*
   404  unittest
   405  {
   406  	assert_eq( evalString(`var fac = fun(x){
   407  		if(x)
   408  			{ x*fac(x-1); }
   409  		else
   410  			{ 1; };
   411  	};
   412  	fac(10);`).val, new IntValue(BigInt(10*9*8*5040)));
   413  	assert_eq( evalString(`var fib = fun(x){
   414  		if(x<2)
   415  			{ 1; }
   416  		else
   417  			{ fib(x-1) + fib(x-2); };
   418  	};
   419  	fib(5);`).val, new IntValue(BigInt(8)));
   420  }
   421  unittest
   422  {
   423  	assert_eq( evalString(`@@t = fun(x){x+1}; @t(123)`).val, new IntValue(BigInt(124)) );
   424  	// there was a bug that declaration in the first line of function definition
   425  	// cannot be recursive
   426  	assert_nothrow( evalString(`def foo() {
   427    def bar(y) { if(y<1) {0} else {bar(0)} };
   428    bar(1)
   429  }; foo()`) );
   430  }
   431  */