Artifact Content
Not logged in

Artifact 9460508c714f0a1b06b1ec6fe740029c6cbecc1a


     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  import polemy.value;
    15  import polemy.valueconv;
    16  import std.signals;
    17  
    18  /// Objects for maitaining global environment and evaluation of expression on it
    19  class Evaluator
    20  {
    21  public:
    22  	/// Evaluate the AST
    23  	Value evalAST(AST e)
    24  		{ return getLayerEval(ValueLayer).macroAndEval(e, theContext, LayerEval.OverwriteCtx); }
    25  
    26  	/// Evaluate the string
    27  	Value evalString(S,T...)(S str, T fn_ln_cn)
    28  		{ return evalAST(parseString(str,fn_ln_cn)); }
    29  
    30  	/// Evaluate the file
    31  	Value evalFile(S,T...)(S filename, T ln_cn)
    32  		{ return evalAST(parseFile(filename,ln_cn)); }
    33  
    34  	/// Get the global context
    35  	Table globalContext()
    36  		{ return theContext; }
    37  
    38  	/// Initialize evaluator with empty context
    39  	this()
    40  	{
    41  		theContext = new Table;
    42  		theLayers[ValueLayer]    = new ValueLayerEval;
    43  		theLayers[MacroLayer]    = new MacroLayerEval;
    44  		theLayers[RawMacroLayer] = new RawMacroLayerEval;
    45  	}
    46  
    47  private:
    48  	Table            theContext;
    49  	LayerEval[Layer] theLayers;
    50  
    51  	/// Get the layer evaluator from layer ID
    52  	LayerEval getLayerEval(Layer lay)
    53  	{
    54  		if(auto p = lay in theLayers)
    55  			return *p;
    56  		return theLayers[lay] = new UserDefinedLayerEval(lay);
    57  	}
    58  
    59  	/// Interface of layers
    60  	abstract class LayerEval
    61  	{
    62  		enum : bool { CascadeCtx=false, OverwriteCtx=true };
    63  
    64  		/// Concrete layers should implement these
    65  		protected Layer layerID();
    66  		protected Value eval_( Die e, Table ctx, bool ctxMod );///
    67  		protected Value eval_( Str e, Table ctx, bool ctxMod );///
    68  		protected Value eval_( Int e, Table ctx, bool ctxMod );///
    69  		protected Value eval_( Var e, Table ctx, bool ctxMod );///
    70  		protected Value eval_( Lay e, Table ctx, bool ctxMod );///
    71  		protected Value eval_( Let e, Table ctx, bool ctxMod );///
    72  		protected Value eval_( App e, Table ctx, bool ctxMod );///
    73  		protected Value eval_( Fun e, Table ctx, bool ctxMod );///
    74  
    75  		/// Should override this also, if the layer may return null for optimization
    76  		Value evalToNonNull( AST e, Table ctx, bool ctxMod = CascadeCtx )
    77  			{ return eval(e,ctx,ctxMod); }
    78  
    79  		/// Do macro expansion first, and then eval. Should override this also if it is NOT macro layer.
    80  		Value macroAndEval( AST e, Table ctx, bool ctxMod = CascadeCtx )
    81  			{ return evalToNonNull(e,ctx,ctxMod); }
    82  
    83  		/// dynamic-overload-resolution
    84  		Value eval( AST e, Table ctx, bool ctxMod = CascadeCtx )
    85  		{
    86  			enum funName = "eval_";                 // modify here for customization
    87  			alias TypeTuple!(e,ctx,ctxMod) params;  // modify here for customization
    88  
    89  			alias typeof(__traits(getOverloads, this, funName)) ovTypes;
    90  			alias staticMap!(firstParam, ovTypes)              fstTypes;
    91  			alias DerivedToFront!(fstTypes)             fstTypes_sorted;
    92  			foreach(i, T; fstTypes_sorted)
    93  				static if( is(T == typeof(params[0])) ) {} else if( auto _x = cast(T)params[0] )
    94  					return __traits(getOverloads, this, funName)[i](_x, params[1..$]);
    95  
    96  			// modify here to customize the default behavior
    97  			assert(false, text("eval() for ",typeid(e)," [",e.pos,"] is not defined"));
    98  		}
    99  
   100  		/// Function calling convention.
   101  		/// Run all arugment AST in the appropriate layer and invoke the function.
   102  		Value invokeFunction(Value f_, AST[] args, Table ctx, LexPosition pos, string callstackmsg)
   103  		{
   104  			FunValue f = cast(FunValue)f_;
   105  			if( f is null )
   106  				throw genex!RuntimeException(pos, text("tried to call non-function: ",f));
   107  			if( f.params().length != args.length )
   108  				throw genex!RuntimeException(pos,
   109  					sprintf!("%d arguments required but %d passed")(f.params().length, args.length));
   110  
   111  			Table newCtx = new Table(f.definitionContext(), Table.Kind.NotPropagateSet);
   112  			foreach(i,p; f.params())
   113  				if( p.layers.empty )
   114  					newCtx.set(p.name, layerID(), this.evalToNonNull(args[i], ctx));
   115  				else
   116  					foreach(argLay; p.layers)
   117  					{
   118  						Layer ll = argLay;
   119  						if( isMacroLayer(argLay) && typeid(this)!=typeid(MacroLayerEval) )
   120  							ll = RawMacroLayer; // explicit @macro invokes (rawmacro)
   121  						newCtx.set(p.name, argLay, getLayerEval(ll).evalToNonNull(args[i], ctx));
   122  					}
   123  			scope _ = new PushCallStack(pos, callstackmsg);
   124  			return f.invoke(layerID(), newCtx, pos);
   125  		}
   126  
   127  		/// Lift the value v to the current layer
   128  		Value lift(Value v, Table ctx, LexPosition pos)
   129  		{
   130  			// functions are automatically lifted by itself
   131  			if( cast(FunValue) v )
   132  				return v;
   133  
   134  			Layer lay = layerID();
   135  
   136  			// load the lift function
   137  			if( !ctx.has(lay, LiftLayer) )
   138  				throw genex!RuntimeException(pos, "lift function for "~lay~" is not registered" );
   139  
   140  			Value    f_ = ctx.get(lay, LiftLayer, pos);
   141  			FunValue f  = cast(FunValue) f_;
   142  			if( f is null )
   143  				throw genex!RuntimeException(pos,
   144  					text("non-function ", f_, " is registered as the lift function for ", lay));
   145  			if( f.params().length != 1 || f.params()[0].layers.length>=1 )
   146  				throw genex!RuntimeException(pos,
   147  					text("lift function must take exactly 1 neutral parameter:",lay));
   148  
   149  			// invokeFunction
   150  			Table newCtx = new Table(f.definitionContext(), Table.Kind.NotPropagateSet);
   151  			newCtx.set(f.params()[0].name, ValueLayer, v);
   152  			scope _ = new PushCallStack(pos, "lift to "~lay);
   153  			return f.invoke(ValueLayer, newCtx, pos);
   154  		}
   155  	}
   156  
   157  	/// Evaluator for standard @value semantics
   158  	class ValueLayerEval : LayerEval
   159  	{
   160  		override Layer layerID()
   161  		{
   162  			return ValueLayer;
   163  		}
   164  		override Value eval_( Die e, Table ctx, bool ctxMod )
   165  		{
   166  			throw genex!RuntimeException(e.pos, "undefined case");
   167  		}
   168  		override Value eval_( Str e, Table ctx, bool ctxMod )
   169  		{
   170  			return new StrValue(e.data);
   171  		}
   172  		override Value eval_( Int e, Table ctx, bool ctxMod )
   173  		{
   174  			return new IntValue(e.data);
   175  		}
   176  		override Value eval_( Var e, Table ctx, bool ctxMod )
   177  		{
   178  			return ctx.get(e.name, layerID(), e.pos);
   179  		}
   180  		override Value eval_( Lay e, Table ctx, bool ctxMod )
   181  		{
   182  			return getLayerEval(e.layer).evalToNonNull(e.expr, ctx);
   183  		}
   184  		override Value eval_( Let e, Table ctx, bool ctxMod )
   185  		{
   186  			if( !ctxMod )
   187  				ctx = new Table(ctx, Table.Kind.NotPropagateSet);
   188  			Value ri = this.eval(e.init, ctx);
   189  			ctx.set(e.name, e.layer.empty ? layerID(): e.layer, ri);
   190  			return this.eval(e.expr, ctx, OverwriteCtx);
   191  		}
   192  		override Value eval_( App e, Table ctx, bool ctxMod )
   193  		{
   194  			Value f = this.eval( e.fun, ctx, CascadeCtx );
   195  			return this.invokeFunction(f, e.args, ctx, e.pos, getNameIfPossible(e.fun));
   196  		}
   197  		override Value eval_( Fun e, Table ctx, bool ctxMod )
   198  		{
   199  			return createNewFunction(e, ctx);
   200  		}
   201  		override Value macroAndEval( AST e, Table ctx, bool ctxMod )
   202  		{
   203  			// incremental execution of let-expressions
   204  			if(auto le = cast(Let)e)
   205  			{
   206  				AST ai = runMacro(le.init, ctx);
   207  
   208  				if( !ctxMod )
   209  					ctx = new Table(ctx, Table.Kind.NotPropagateSet);
   210  
   211  				Value vi = this.eval(ai, ctx);
   212  				ctx.set(le.name, le.layer.empty ? layerID() : le.layer, vi);
   213  				return this.macroAndEval(le.expr, ctx, OverwriteCtx);
   214  			}
   215  			else
   216  				return this.eval(runMacro(e,ctx,ctxMod), ctx, ctxMod);
   217  		}
   218  	}
   219  
   220  	/// Evaluator for user-defined layer
   221  	class UserDefinedLayerEval : ValueLayerEval
   222  	{
   223  		Layer theID;
   224  		mixin SimpleConstructor;
   225  
   226  		override Layer layerID()
   227  		{
   228  			return theID;
   229  		}
   230  		override Value eval_( Die e, Table ctx, bool ctxMod )
   231  		{
   232  			return new BottomValue;
   233  		}
   234  		override Value eval_( Str e, Table ctx, bool ctxMod )
   235  		{
   236  			return this.lift(super.eval_(e,ctx,ctxMod), ctx, e.pos);
   237  		}
   238  		override Value eval_( Int e, Table ctx, bool ctxMod )
   239  		{
   240  			return this.lift(super.eval_(e,ctx,ctxMod), ctx, e.pos);
   241  		}
   242  		override Value eval_( Var e, Table ctx, bool ctxMod )
   243  		{
   244  			if( ctx.has(e.name, layerID()) )
   245  				return ctx.get(e.name, layerID());
   246  			return this.lift(ctx.get(e.name, ValueLayer, e.pos), ctx, e.pos);
   247  		}
   248  	}
   249  
   250  	// Macro layer. For optimization, if AST didn't change it returns null
   251  	class MacroLayerEval : LayerEval
   252  	{
   253  		override Layer layerID()
   254  		{
   255  			return MacroLayer;
   256  		}
   257  		override Value evalToNonNull( AST e, Table ctx, bool ctxMod = CascadeCtx )
   258  		{
   259  			Value v = this.eval(e, ctx, ctxMod);
   260  			return v is null ? ast2table(e) : v;
   261  		}
   262  		override Value eval_( Die e, Table ctx, bool ctxMod )
   263  		{
   264  			return null;
   265  		}
   266  		override Value eval_( Str e, Table ctx, bool ctxMod )
   267  		{
   268  			return null;
   269  		}
   270  		override Value eval_( Int e, Table ctx, bool ctxMod )
   271  		{
   272  			return null;
   273  		}
   274  		override Value eval_( Var e, Table ctx, bool ctxMod )
   275  		{
   276  			if( ctx.has(e.name, layerID()) )
   277  				return ctx.get(e.name, layerID(), e.pos);
   278  			return null;
   279  		}
   280  		override Value eval_( Lay e, Table ctx, bool ctxMod )
   281  		{
   282  			return getLayerEval(e.layer).eval(e.expr,ctx);
   283  		}
   284  		override Value eval_( Let e, Table ctx, bool ctxMod )
   285  		{
   286  			if( !ctxMod )
   287  				ctx = new Table(ctx, Table.Kind.NotPropagateSet);
   288  
   289  			Value ai = this.eval(e.init, ctx);
   290  			ctx.set(e.name, NoopLayer, null);
   291  			Value ae = this.eval(e.expr, ctx, OverwriteCtx);
   292  
   293  			if( ai is null && ae is null ) return null;
   294  			if( ai is null ) ai = ast2table(e.init);
   295  			if( ae is null ) ae = ast2table(e.expr);
   296  
   297  			return ast2table(e, delegate Value (AST _){
   298  				if(_ is e.init) { return ai; }
   299  				if(_ is e.expr) { return ae; }
   300  				assert(false);
   301  			});
   302  		}
   303  		override Value eval_( App e, Table ctx, bool ctxMod )
   304  		{
   305  			Value f = this.eval( e.fun, ctx );
   306  			if(auto ff = cast(FunValue)f)
   307  				return this.invokeFunction(ff, e.args, ctx, e.pos, getNameIfPossible(e.fun));
   308  			else
   309  			{
   310  				bool allNull = (f is null);
   311  				Value[] vas;
   312  				foreach(a; e.args)
   313  				{
   314  					Value va = this.eval(a, ctx, CascadeCtx);
   315  					if(va !is null) allNull = false;
   316  					vas ~= va;
   317  				}
   318  				if( allNull )
   319  					return null;
   320  				return ast2table(e, delegate Value (AST _){
   321  					if(_ is e.fun) return (f is null ? ast2table(e.fun) : f);
   322  					foreach(i,a; e.args) if(_ is a) return (vas[i] is null ? ast2table(a) : vas[i]);
   323  					assert(false);
   324  				});
   325  			}
   326  		}
   327  		override Value eval_( Fun e, Table ctx, bool ctxMod )
   328  		{
   329  			ctx = new Table(ctx, Table.Kind.NotPropagateSet);
   330  			foreach(p; e.params)
   331  				ctx.set(p.name, NoopLayer, null);
   332  			Value af = this.eval(e.funbody, ctx);
   333  			if( af is null )
   334  				return null;
   335  			return ast2table(e, (AST _){if(_ is e.funbody)return af; assert(false);});
   336  		}
   337  	}
   338  
   339  	/// (rawmacro) layer. almost same as @macro, but the Layer expression becomes AST, too.
   340  	class RawMacroLayerEval : MacroLayerEval
   341  	{
   342  		override Value eval_( Lay e, Table ctx, bool ctxMod )
   343  		{
   344  			Value ae = this.eval(e.expr, ctx);
   345  			return ae is null ? null
   346  			       : ast2table(e, delegate Value (AST _){if(_ is e.expr)return ae; assert(false);});
   347  		}
   348  	}
   349  
   350  private: // short utils
   351  
   352  	string getNameIfPossible(AST e)
   353  	{
   354  		if(auto v = cast(Var)e)
   355  			return v.name;
   356  		return "";
   357  	}
   358  
   359  	AST runMacro(AST e, Table ctx, bool ctxMod=LayerEval.CascadeCtx)
   360  	{
   361  		Value v = getLayerEval(RawMacroLayer).eval(e, ctx, ctxMod);
   362  		return (v is null ? e : polemy2d!(AST)(v, e.pos));
   363  	}
   364  
   365  private:
   366  	Value createNewFunction(Fun e, Table ctx)
   367  	{
   368  		class UserDefinedFunValue : FunValue
   369  		{
   370  			Fun   ast;
   371  			Table defCtx;
   372  			override const(Parameter[]) params() { return ast.params; }
   373  			override Table definitionContext()   { return defCtx; }
   374  
   375  			this(Fun ast, Table defCtx) { this.ast=ast; this.defCtx=defCtx; }
   376  			override string toString() const
   377  				{ return sprintf!"(function:%x:%x)"(cast(void*)ast, cast(void*)defCtx); }
   378  			override int opCmp(Object rhs) {
   379  				if(auto r = cast(UserDefinedFunValue)rhs) {
   380  					if(auto c = typeid(void*).compare(cast(void*)ast, cast(void*)r.ast))
   381  						return c;
   382  					if(auto c = typeid(void*).compare(cast(void*)defCtx, cast(void*)r.defCtx))
   383  						return c;
   384  					return 0;// [TODO] avoid using pointer value...
   385  				}
   386  				if(auto r = cast(Value)rhs) return typeid(this).opCmp(typeid(r));
   387  				throw genex!RuntimeException("comparison with value and something other");
   388  			}
   389  			override hash_t toHash() {
   390  				return (cast(hash_t)cast(void*)ast) + (cast(hash_t)cast(void*)defCtx);
   391  			}
   392  
   393  			AST[void*] mandeCache;
   394  			static class MemokeyType
   395  			{
   396  				void* a; Layer b; Table c;
   397  				mixin SimpleClass;
   398  			}
   399  			static Tuple!(Value,int)[MemokeyType] memo;
   400  
   401  			override Value invoke(Layer lay, Table ctx, LexPosition pos)
   402  			{
   403  				auto evlay = getLayerEval(lay);
   404  				auto nonMemoizedRun = (){ return evlay.macroAndEval(ast.funbody, ctx); };
   405  
   406  				if( !isUserDefinedLayer(lay) )
   407  					return nonMemoizedRun();
   408  
   409  				// automatic memoized co-recursive execution
   410  				MemokeyType memokey = new MemokeyType(cast(void*)ast, lay, ctx);
   411  				if(auto p = memokey in memo)
   412  				{
   413  					(*p)[1] ++;
   414  					return (*p)[0];
   415  				}
   416  				else
   417  					memo[memokey] = tuple(evlay.lift(new BottomValue, ctx, pos), 0);
   418  
   419  				Value r = nonMemoizedRun();
   420  
   421  				int touched = memo[memokey][1];
   422  				memo[memokey] = tuple(r, 12345678);
   423  				return r;
   424  			}
   425  		}
   426  		return new UserDefinedFunValue(e,ctx);
   427  	}
   428  
   429  public:
   430  	/// Add primitive function to the global context
   431  	void addPrimitive(R,T...)(string name, Layer defLay, R delegate (T) dg)
   432  	{
   433  		class NativeFunValue : FunValue
   434  		{
   435  			override const(Parameter[]) params() { return params_data; }
   436  			override Table definitionContext()   { return theContext; }
   437  
   438  			override string toString() { return sprintf!"(native:%x)"(dg.funcptr); }
   439  			override int opCmp(Object rhs) {
   440  				if(auto r = cast(NativeFunValue)rhs) return typeid(typeof(dg)).compare(&dg,&r.dg);
   441  				if(auto r = cast(Value)rhs)          return typeid(this).opCmp(typeid(r));
   442  				throw genex!RuntimeException("comparison with value and something other");
   443  			}
   444  			override hash_t toHash() const {
   445  				return typeid(dg).getHash(&dg);
   446  			}
   447  
   448  			R delegate(T) dg;
   449  			Parameter[] params_data;
   450  
   451  			this(R delegate(T) dg)
   452  			{
   453  				this.dg = dg;
   454  				foreach(i, Ti; T)
   455  					params_data ~= new Parameter(text(i), []);
   456  			}
   457  
   458  			override Value invoke(Layer lay, Table ctx, LexPosition pos)
   459  			{
   460  				if( lay != defLay )
   461  					throw genex!RuntimeException(pos,
   462  						text("only ", defLay, " layer can call native function: ", name));
   463  				T typed_args;
   464  				foreach(i, Ti; T) {
   465  					typed_args[i] = cast(Ti) ctx.get(text(i), ValueLayer, pos);
   466  					if( typed_args[i] is null )
   467  						throw genex!RuntimeException(pos,
   468  							sprintf!"type mismatch on the argument %d of native function: %s"(i+1,name));
   469  				}
   470  				try {
   471  					return dg(typed_args);
   472  				} catch( RuntimeException e ) {
   473  					throw e.pos is null ? new RuntimeException(pos, e.msg, e.file, e.line) : e;
   474  				}
   475  			}
   476  		}
   477  		theContext.set(name, defLay, new NativeFunValue(dg));
   478  	}
   479  }
   480  
   481  version(unittest)
   482  	import polemy.runtime;
   483  
   484  unittest
   485  {
   486  	auto e = new Evaluator;
   487  	enrollRuntimeLibrary(e);
   488  	auto r = assert_nothrow( e.evalString(`var x = 21; x + x*x;`) );
   489  	assert_eq( r, new IntValue(BigInt(21+21*21)) );
   490  	assert_eq( e.globalContext.get("x",ValueLayer), new IntValue(BigInt(21)) );
   491  	assert_nothrow( e.globalContext.get("x",ValueLayer) );
   492  	assert_throw!RuntimeException( e.globalContext.get("y",ValueLayer) );
   493  }
   494  
   495  unittest
   496  {
   497  	auto e = new Evaluator;
   498  	enrollRuntimeLibrary(e);
   499  	auto r = assert_nothrow( e.evalString(`var x = 21; var x = x + x*x;`) );
   500  	assert_eq( r, new IntValue(BigInt(21+21*21)) );
   501  	assert_eq( e.globalContext.get("x",ValueLayer), new IntValue(21+21*21) );
   502  	assert_nothrow( e.globalContext.get("x",ValueLayer) );
   503  	assert_throw!RuntimeException( e.globalContext.get("y",ValueLayer) );
   504  }
   505  unittest
   506  {
   507  	auto e = new Evaluator;
   508  	enrollRuntimeLibrary(e);
   509  	assert_eq( e.evalString(`let x=1; let y=(let x=2); x`), new IntValue(1) ); 
   510  	assert_eq( e.evalString(`let x=1; let y=(let x=2;fun(){x}); y()`), new IntValue(2) ); 
   511  }
   512  
   513  unittest
   514  {
   515  	auto e = new Evaluator;
   516  	enrollRuntimeLibrary(e);
   517  	assert_eq( e.evalString(`@a x=1; @b x=2; @a(x)`), new IntValue(BigInt(1)) );
   518  	assert_eq( e.evalString(`@a x=1; @b x=2; @b(x)`), new IntValue(BigInt(2)) );
   519  	assert_eq( e.evalString(`let x=1; let _ = (@a x=2;2); x`), new IntValue(BigInt(1)) );
   520  	e = new Evaluator;
   521  	assert_throw!Throwable( e.evalString(`let x=1; let _ = (@a x=2;2); @a(x)`) );
   522  }
   523  
   524  unittest
   525  {
   526  	auto e = new Evaluator;
   527  	enrollRuntimeLibrary(e);
   528  	assert_eq( e.evalString(`
   529  		@@s(x){x};
   530  		@s "+" = fun(x, y) {@value(
   531  			@s(x) - @s(y)
   532  		)};
   533  		@s(1 + 2)
   534  	`), new IntValue(BigInt(-1)) );
   535  }
   536  
   537  unittest
   538  {
   539  	auto e = new Evaluator;
   540  	enrollRuntimeLibrary(e);
   541  	assert_eq( e.evalString(`
   542  @@3(x){x};
   543  def incr(x) { x+1 };
   544  @ 3 incr(x) {@value( if @ 3(x)+1< 3 then @3(x)+1 else 0 )};
   545  def fb(n @value @3) { @3(n) };
   546  fb(incr(incr(incr(0))))
   547  	`), new IntValue(BigInt(0)) );
   548  }
   549  
   550  unittest
   551  {
   552  	auto e = new Evaluator;
   553  	enrollRuntimeLibrary(e);
   554  	assert_nothrow( e.evalString(`
   555  @macro twice(x) { x; x };
   556  def main() { twice(1) };
   557  main()
   558  	`) );
   559  }
   560  unittest
   561  {
   562  	auto e = new Evaluator;
   563  	enrollRuntimeLibrary(e);
   564  	assert_throw!RuntimeException( e.evalString(`case 1`) );
   565  	assert_nothrow( e.evalString(`case 1 when 1: 2`) );
   566  
   567  	// this is a shorthand for
   568  	//   @macro x = fun(){} in @macro(x)
   569  	// so it is ok to fail, but it is really incovenient on REPL
   570  	assert_nothrow( e.evalString(`@macro x=fun(){}`) );
   571  }
   572  
   573  unittest
   574  {
   575  	auto e = new Evaluator;
   576  	enrollRuntimeLibrary(e);
   577  	assert_throw!RuntimeException( e.evalString(`...`) );
   578  	assert_eq( e.evalString(`@@foo(x){x}; @foo(...)`), new BottomValue );
   579  }