Artifact Content
Not logged in

Artifact a7c677ad83621b477a9a927963083bf32ded52c8


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