Artifact Content
Not logged in

Artifact c85d31e67d8ceb4bbe8e3f21772ee40ad0a46687


     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 std.typecons;
    15  import std.stdio;
    16  
    17  ///
    18  Table createGlobalContext()
    19  {
    20  	auto ctx = new Table;
    21  	ctx.set("+", ValueLayer, native( (IntValue lhs, IntValue rhs){return new IntValue(lhs.data + rhs.data);} ));
    22  	ctx.set("-", ValueLayer, native( (IntValue lhs, IntValue rhs){return new IntValue(lhs.data - rhs.data);} ));
    23  	ctx.set("*", ValueLayer, native( (IntValue lhs, IntValue rhs){return new IntValue(lhs.data * rhs.data);} ));
    24  	ctx.set("/", ValueLayer, native( (IntValue lhs, IntValue rhs){return new IntValue(lhs.data / rhs.data);} ));
    25  	ctx.set("%", ValueLayer, native( (IntValue lhs, IntValue rhs){return new IntValue(lhs.data % rhs.data);} ));
    26  	ctx.set("||", ValueLayer, native( (IntValue lhs, IntValue rhs){return new IntValue(BigInt((lhs.data!=0) || (rhs.data!=0) ? 1:0));} ));
    27  	ctx.set("&&", ValueLayer, native( (IntValue lhs, IntValue rhs){return new IntValue(BigInt((lhs.data!=0) && (rhs.data!=0) ? 1:0));} ));
    28  	ctx.set("<", ValueLayer, native( (Value lhs, Value rhs){return new IntValue(BigInt(lhs < rhs ? 1: 0));} ));
    29  	ctx.set(">", ValueLayer, native( (Value lhs, Value rhs){return new IntValue(BigInt(lhs > rhs ? 1: 0));} ));
    30  	ctx.set("<=", ValueLayer, native( (Value lhs, Value rhs){return new IntValue(BigInt(lhs <= rhs ? 1: 0));} ));
    31  	ctx.set(">=", ValueLayer, native( (Value lhs, Value rhs){return new IntValue(BigInt(lhs >= rhs ? 1: 0));} ));
    32  	ctx.set("==", ValueLayer, native( (Value lhs, Value rhs){return new IntValue(BigInt(lhs == rhs ? 1: 0));} ));
    33  	ctx.set("!=", ValueLayer, native( (Value lhs, Value rhs){return new IntValue(BigInt(lhs != rhs ? 1: 0));} ));
    34  	ctx.set("print", ValueLayer, native( (Value a){
    35  		writeln(a);
    36  		return new IntValue(BigInt(0));
    37  	}));
    38  	ctx.set("if", ValueLayer, native( (IntValue x, FunValue ft, FunValue fe){
    39  		auto toRun = (x.data==0 ? fe : ft);
    40  		// [TODO] fill positional information
    41  		return toRun.invoke(null, ValueLayer, toRun.definitionContext());
    42  //		return toRun.invoke(pos, lay, toRun.definitionContext());
    43  	}));
    44  	ctx.set("_isint", ValueLayer, native( (Value v){return new IntValue(BigInt(cast(IntValue)v is null ? 0 : 1));} ));
    45  	ctx.set("_isstr", ValueLayer, native( (Value v){return new IntValue(BigInt(cast(StrValue)v is null ? 0 : 1));} ));
    46  	ctx.set("_isfun", ValueLayer, native( (Value v){return new IntValue(BigInt(cast(FunValue)v is null ? 0 : 1));} ));
    47  	ctx.set("_isundefined", ValueLayer, native( (Value v){return new IntValue(BigInt(cast(UndValue)v is null ? 0 : 1));} ));
    48  	ctx.set("_istable", ValueLayer, native( (Value v){return new IntValue(BigInt(cast(Table)v is null ? 0 : 1));} ));
    49  	ctx.set(".", ValueLayer, native( (Table t, StrValue s){
    50  		return (t.has(s.data, ValueLayer) ? t.get(s.data, ValueLayer) : new UndValue);
    51  	}) );
    52  	ctx.set(".?", ValueLayer, native( (Table t, StrValue s){
    53  		return new IntValue(BigInt(t.has(s.data, ValueLayer) ? 1 : 0));
    54  	}) );
    55  	ctx.set(".=", ValueLayer, native( (Table t, StrValue s, Value v){
    56  		auto t2 = new Table(t, Table.Kind.NotPropagateSet);
    57  		t2.set(s.data, ValueLayer, v);
    58  		return t2;
    59  	}) );
    60  	ctx.set("{}", ValueLayer, native( (){
    61  		return new Table;
    62  	}) );
    63  	return ctx;
    64  }
    65  
    66  /// Entry point of this module
    67  
    68  Tuple!(Value,"val",Table,"ctx") evalString(S,T...)(S str, T fn_ln_cn)
    69  {
    70  	return eval( polemy.parse.parseString(str, fn_ln_cn) );
    71  }
    72  
    73  /// Entry point of this module
    74  
    75  Tuple!(Value,"val",Table,"ctx") evalFile(S, T...)(S filename, T ln_cn)
    76  {
    77  	return eval( polemy.parse.parseFile(filename, ln_cn) );
    78  }
    79  
    80  /// Entry point of this module
    81  
    82  Tuple!(Value,"val",Table,"ctx") eval(AST e)
    83  {
    84  	Table ctx = createGlobalContext();
    85  	return typeof(return)(eval(e, ctx, false, ValueLayer), ctx);
    86  }
    87  
    88  Value invokeFunction(in LexPosition pos, Value _f, AST[] args, Table callerCtx, Layer lay, bool AlwaysMacro=false)
    89  {
    90  	if(auto f = cast(FunValue)_f)
    91  	{
    92  		Table ctx = new Table(f.definitionContext(), Table.Kind.NotPropagateSet);
    93  		foreach(i,p; f.params())
    94  			if( p.layers.empty )
    95  				if(lay==MacroLayer)
    96  					ctx.set(p.name, lay, macroEval(args[i], callerCtx, AlwaysMacro));
    97  				else
    98  					ctx.set(p.name, lay, eval(args[i], callerCtx, true, lay));
    99  			else
   100  				foreach(argLay; p.layers)
   101  					if(argLay==MacroLayer)
   102  						ctx.set(p.name, argLay, macroEval(args[i], callerCtx, AlwaysMacro));
   103  					else
   104  						ctx.set(p.name, argLay, eval(args[i], callerCtx, true, argLay));
   105  		return f.invoke(pos, lay, ctx);
   106  	}
   107  	throw genex!RuntimeException(pos, "tried to call non-function");
   108  }
   109  
   110  Value lift(in LexPosition pos, Value v, Layer lay, Table callerCtx)
   111  {
   112  	// similar to invoke Function, but with only one argument bound to ValueLayer
   113  	Value _f = callerCtx.get(lay, SystemLayer, pos);
   114  	if(auto f = cast(FunValue)_f)
   115  	{
   116  		Table ctx = new Table(f.definitionContext(), Table.Kind.NotPropagateSet);
   117  		auto ps = f.params();
   118  		if( ps.length != 1 )
   119  			throw genex!RuntimeException(pos, "lift function must take exactly one argument at "~ValueLayer~" layer");
   120  		if( ps[0].layers.length==0 || ps[0].layers.length==1 && ps[0].layers[0]==ValueLayer )
   121  		{
   122  			ctx.set(ps[0].name, ValueLayer, v);
   123  			return f.invoke(pos, ValueLayer, ctx);
   124  		}
   125  		else
   126  			throw genex!RuntimeException(pos, "lift function must take exactly one argument at "~ValueLayer~" layer");
   127  	}
   128  	throw genex!RuntimeException(pos, "tried to call non-function");
   129  }
   130  
   131  /// Entry point of this module
   132  /// If splitCtx = true, then inner variable declaration do not overwrite ctx.
   133  /// lay is the layer ID for evaluation (standard value semantics uses ValueLayer).
   134  
   135  Value eval(AST e, Table ctx, bool splitCtx, Layer lay)
   136  {
   137  	return e.match(
   138  		(StrLiteral e)
   139  		{
   140  			Value v = new StrValue(e.data);
   141  			if( lay == ValueLayer )
   142  				return v;
   143  			else
   144  				return lift(e.pos,v,lay,ctx);
   145  		},
   146  		(IntLiteral e)
   147  		{
   148  			Value v = new IntValue(e.data);
   149  			if( lay == ValueLayer )
   150  				return v;
   151  			else // rise
   152  				return lift(e.pos,v,lay,ctx);
   153  		},
   154  		(VarExpression e)
   155  		{
   156  			if( lay == ValueLayer )
   157  				return ctx.get(e.name, lay, e.pos);
   158  			try {
   159  				return ctx.get(e.name, lay, e.pos);
   160  			} catch( Throwable ) { // [TODO] more precise...
   161  				return lift(e.pos, ctx.get(e.name, ValueLayer, e.pos), lay, ctx);
   162  			}
   163  		},
   164  		(LayExpression e)
   165  		{
   166  			if( e.layer == MacroLayer )
   167  				return macroEval(e.expr, ctx, false);
   168  			else
   169  				return eval(e.expr, ctx, true, e.layer);
   170  		},
   171  		(LetExpression e)
   172  		{
   173  			// for letrec, we need this, but should avoid overwriting????
   174  			// ctx.set(e.var, ValueLayer, new UndefinedValue, e.pos);
   175  			if(splitCtx)
   176  				ctx = new Table(ctx, Table.Kind.NotPropagateSet);
   177  			Value v = eval(e.init, ctx, true, lay);
   178  			ctx.set(e.name, (e.layer.length ? e.layer : lay), v, e.pos);
   179  			return eval(e.expr, ctx, false, lay);
   180  		},
   181  		(FuncallExpression e)
   182  		{
   183  			return invokeFunction(e.pos, eval(e.fun, ctx, true, lay), e.args, ctx, lay);
   184  		},
   185  		(FunLiteral e)
   186  		{
   187  			Value[Value[]][Layer] memo;
   188  			AST macroMemo = null; // cache
   189  
   190  			// funvalue need not be rised
   191  			// no, need to be rised !!  suppose @t(fib)("int")
   192  			return new UserDefinedFunValue(e, ctx);
   193  		},
   194  		delegate Value (AST e)
   195  		{
   196  			throw genex!RuntimeException(e.pos, sprintf!"Unknown Kind of Expression %s"(typeid(e)));
   197  		}
   198  	);
   199  }
   200  
   201  // [TODO] Optimization
   202  Value macroEval(AST e, Table ctx, bool AlwaysMacro)
   203  {
   204  	Layer theLayer = ValueLayer;
   205  
   206  	Table makeCons(Value a, Value d)
   207  	{
   208  		Table t = new Table;
   209  		t.set("car", theLayer, a);
   210  		t.set("cdr", theLayer, d);
   211  		return t;
   212  	}
   213  
   214  	Table pos = new Table;
   215  	if( e.pos !is null ) {
   216  		pos.set("filename", theLayer, new StrValue(e.pos.filename));
   217  		pos.set("lineno",   theLayer, new IntValue(BigInt(e.pos.lineno)));
   218  		pos.set("column",   theLayer, new IntValue(BigInt(e.pos.column)));
   219  	} else {
   220  		pos.set("filename", theLayer, new StrValue("nullpos"));
   221  		pos.set("lineno",   theLayer, new IntValue(BigInt(0)));
   222  		pos.set("column",   theLayer, new IntValue(BigInt(0)));
   223  	}
   224  
   225  	return e.match(
   226  		(StrLiteral e)
   227  		{
   228  			Table t = new Table;
   229  			t.set("pos",  theLayer, pos);
   230  			t.set("is",   theLayer, new StrValue("str"));
   231  			t.set("data", theLayer, new StrValue(e.data));
   232  			return t;
   233  		},
   234  		(IntLiteral e)
   235  		{
   236  			Table t = new Table;
   237  			t.set("pos",  theLayer, pos);
   238  			t.set("is",   theLayer, new StrValue("int"));
   239  			t.set("data", theLayer, new IntValue(e.data));
   240  			return t;
   241  		},
   242  		(VarExpression e)
   243  		{
   244  			try {
   245  				return ctx.get(e.name, MacroLayer, e.pos);
   246  			} catch( Throwable ) {// [TODO] more precies...
   247  				Table t = new Table;
   248  				t.set("pos",  theLayer, pos);
   249  				t.set("is",   theLayer, new StrValue("var"));
   250  				t.set("name", theLayer, new StrValue(e.name));
   251  				return cast(Value)t;
   252  			}
   253  		},
   254  		(LayExpression e)
   255  		{
   256  			if( AlwaysMacro )
   257  			{
   258  				Table t = new Table;
   259  				t.set("pos",   theLayer, pos);
   260  				t.set("is",    theLayer, new StrValue("lay"));
   261  				t.set("layer", theLayer, new StrValue(e.layer));
   262  				t.set("expr",  theLayer, macroEval(e.expr,ctx,AlwaysMacro));
   263  				return cast(Value)t;
   264  			}
   265  			else
   266  			{
   267  				if( e.layer == MacroLayer )
   268  					return macroEval(e.expr, ctx, false);
   269  				else
   270  					return eval(e.expr, ctx, true, e.layer);
   271  			}
   272  		},
   273  		(LetExpression e)
   274  		{
   275  			Table t = new Table;
   276  			t.set("pos",  theLayer, pos);
   277  			t.set("is",   theLayer, new StrValue("let"));
   278  			t.set("name", theLayer, new StrValue(e.name));
   279  			t.set("init", theLayer, macroEval(e.init,ctx,AlwaysMacro));
   280  			t.set("expr", theLayer, macroEval(e.expr,ctx,AlwaysMacro));
   281  			return t;
   282  		},
   283  		(FuncallExpression e)
   284  		{
   285  			Value _f = macroEval(e.fun,ctx,AlwaysMacro);
   286  
   287  			if( auto f = cast(FunValue)_f )
   288  				return invokeFunction(e.pos, f, e.args, ctx, MacroLayer, AlwaysMacro);
   289  
   290  			Table t = new Table;
   291  			t.set("pos",  theLayer, pos);
   292  			t.set("is",   theLayer, new StrValue("app"));
   293  			t.set("fun",  theLayer, _f);
   294  			Table args = new Table;
   295  			foreach_reverse(a; e.args) {
   296  				Table cons = new Table;
   297  				cons.set("car",theLayer,macroEval(a,ctx,AlwaysMacro));
   298  				cons.set("cdr",theLayer,args);
   299  				args = cons;
   300  			}
   301  			t.set("args", theLayer, args);
   302  			return cast(Value)t;
   303  		},
   304  		(FunLiteral e)
   305  		{
   306  			Table t = new Table;
   307  			t.set("pos",   theLayer, pos);
   308  			t.set("is",    theLayer, new StrValue("fun"));
   309  			t.set("funbody",  theLayer, macroEval(e.funbody,ctx,AlwaysMacro));
   310  			Table params = new Table;
   311  			foreach_reverse(p; e.params)
   312  			{
   313  				Table lays = new Table;
   314  				foreach_reverse(lay; p.layers)
   315  					lays = makeCons(new StrValue(lay), lays);
   316  				Table kv = new Table;
   317  				kv.set("name", theLayer, new StrValue(p.name));
   318  				kv.set("layers", theLayer, lays);
   319  				Table cons = new Table;
   320  				params = makeCons(kv, params);
   321  			}
   322  			t.set("params", theLayer, params);
   323  			return t;
   324  		},
   325  		delegate Value (AST e)
   326  		{
   327  			throw genex!RuntimeException(e.pos, sprintf!"Unknown Kind of Expression %s"(typeid(e)));
   328  		}
   329  	);
   330  }
   331  
   332  unittest
   333  {
   334  	auto r = assert_nothrow( evalString(`var x = 21; x + x*x;`) );
   335  	assert_eq( r.val, new IntValue(BigInt(21+21*21)) );
   336  	assert_eq( r.ctx.get("x",ValueLayer), new IntValue(BigInt(21)) );
   337  	assert_nothrow( r.ctx.get("x",ValueLayer) );
   338  	assert_throw!RuntimeException( r.ctx.get("y",ValueLayer) );
   339  }
   340  unittest
   341  {
   342  	auto r = assert_nothrow( evalString(`var x = 21; var x = x + x*x;`) );
   343  	assert_eq( r.val, new IntValue(BigInt(21+21*21)) );
   344  	assert_eq( r.ctx.get("x",ValueLayer), new IntValue(BigInt(21+21*21)) );
   345  	assert_nothrow( r.ctx.get("x",ValueLayer) );
   346  	assert_throw!RuntimeException( r.ctx.get("y",ValueLayer) );
   347  }
   348  unittest
   349  {
   350  	assert_eq( evalString(`let x=1; let y=(let x=2); x`).val, new IntValue(BigInt(1)) ); 
   351  	assert_eq( evalString(`let x=1; let y=(let x=2;fun(){x}); y()`).val, new IntValue(BigInt(2)) ); 
   352  }
   353  unittest
   354  {
   355  	assert_eq( evalString(`@a x=1; @b x=2; @a(x)`).val, new IntValue(BigInt(1)) );
   356  	assert_eq( evalString(`@a x=1; @b x=2; @b(x)`).val, new IntValue(BigInt(2)) );
   357  	assert_eq( evalString(`let x=1; let _ = (@a x=2;2); x`).val, new IntValue(BigInt(1)) );
   358  	assert_throw!Throwable( evalString(`let x=1; let _ = (@a x=2;2); @a(x)`) );
   359  }
   360  /*
   361  unittest
   362  {
   363  	assert_eq( evalString(`var fac = fun(x){
   364  		if(x)
   365  			{ x*fac(x-1); }
   366  		else
   367  			{ 1; };
   368  	};
   369  	fac(10);`).val, new IntValue(BigInt(10*9*8*5040)));
   370  	assert_eq( evalString(`var fib = fun(x){
   371  		if(x<2)
   372  			{ 1; }
   373  		else
   374  			{ fib(x-1) + fib(x-2); };
   375  	};
   376  	fib(5);`).val, new IntValue(BigInt(8)));
   377  }
   378  
   379  unittest
   380  {
   381  	assert_throw!Throwable( evalString(`@@s(x){x}; @s "+"=fun(x,y){x-y};@s(1+2)`) );
   382  	assert_eq( evalString(`@@s(x){x}; @s "+"=fun(x,y){x-y};1+2`).val, new IntValue(BigInt(3)) );
   383  	assert_eq( evalString(`@@s(x){x}; @s "+"=fun(x,y){@value(@s(x)-@s(y))};1+2`).val, new IntValue(BigInt(3)) );
   384  	assert_eq( evalString(`@@s(x){x}; @s "+"=fun(x,y){@value(@s(x)-@s(y))};@s(1+2)`).val, new IntValue(BigInt(-1)) );
   385  }
   386  
   387  unittest
   388  {
   389  	assert_eq( evalString(`@@t = fun(x){x+1}; @t(123)`).val, new IntValue(BigInt(124)) );
   390  	// there was a bug that declaration in the first line of function definition
   391  	// cannot be recursive
   392  	assert_nothrow( evalString(`def foo() {
   393    def bar(y) { if(y<1) {0} else {bar(0)} };
   394    bar(1)
   395  }; foo()`) );
   396  }
   397  */