Artifact Content
Not logged in

Artifact 0386a7dd31807f874b171fecf43c2d9cfc2c9ee5


     1  /**
     2   * Authors: k.inaba
     3   * License: NYSL 0.9982 http://www.kmonos.net/nysl/
     4   *
     5   * Runtime data structures for Polemy programming language.
     6   */
     7  module polemy.value;
     8  import polemy._common;
     9  import polemy.failure;
    10  import polemy.ast;
    11  import polemy.layer;
    12  
    13  /// Runtime values of Polemy
    14  
    15  abstract class Value
    16  {
    17  }
    18  
    19  ///
    20  class IntValue : Value
    21  {
    22  	BigInt data;
    23  
    24  	mixin SimpleClass;
    25  	override string toString() const { return std.bigint.toDecimalString(cast(BigInt)data); }
    26  }
    27  
    28  ///
    29  class StrValue : Value
    30  {
    31  	string data;
    32  
    33  	mixin SimpleClass;
    34  	override string toString() const { return data; }
    35  }
    36  
    37  ///
    38  class UndValue : Value
    39  {
    40  	mixin SimpleClass;
    41  	override string toString() const { return "<undefined>"; }
    42  }
    43  
    44  
    45  ///
    46  abstract class FunValue : Value
    47  {
    48  	const(Parameter[]) params();
    49  	Table definitionContext();
    50  	Value invoke(in LexPosition pos, Layer lay, Table ctx);
    51  }
    52  
    53  import polemy.eval; // circular...
    54  version = MacroCache;
    55  //version = AutoMemoization;
    56  //version = AutoRerun;
    57  
    58  ///
    59  class UserDefinedFunValue : FunValue
    60  {
    61  	FunLiteral ast;
    62  	Table      defCtx;
    63  
    64  	this(FunLiteral ast, Table defCtx) { this.ast=ast; this.defCtx=defCtx; }
    65  	override string toString() const { return sprintf!"(function:%x:%x)"(cast(void*)ast, cast(void*)defCtx); }
    66  	override bool opEquals(Object rhs_) const /// member-by-member equality
    67  	{
    68  		if( auto rhs = cast(typeof(this))rhs_ )
    69  			return this.ast==rhs.ast && this.defCtx==rhs.defCtx;
    70  		assert(false, sprintf!"Cannot compare %s with %s"(typeid(this), typeid(rhs_)));
    71  	}
    72  	override hash_t toHash() const /// member-by-member hash
    73  	{
    74  		return typeid(this.ast).getHash(&this.ast) + typeid(this.defCtx).getHash(&this.defCtx);
    75  	}
    76  	override int opCmp(Object rhs_) /// member-by-member compare
    77  	{
    78  		if( auto rhs = cast(typeof(this))rhs_ )
    79  		{
    80  			if(auto i = this.ast.opCmp(rhs.ast))
    81  				return i;
    82  			return this.defCtx.opCmp(rhs.defCtx);
    83  		}
    84  		assert(false, sprintf!"Cannot compare %s with %s"(typeid(this), typeid(rhs_)));
    85  	}
    86  
    87  	private AST preprocessed_funbody;
    88  	private Value[Value[]][Layer] memo;
    89  
    90  	override const(Parameter[]) params() { return ast.params; }
    91  	override Table definitionContext() { return defCtx; }
    92  	override Value invoke(in LexPosition pos, Layer lay, Table ctx)
    93  	{
    94  		// TODO: only auto raised ones need memo? no?
    95  		// how can we integrate re-run ??
    96  		version(AutoMemoization)
    97  		{
    98  			Value[] memokey;
    99  			if( lay != ValueLayer && lay != MacroLayer )
   100  			{
   101  				foreach(i,p; ast.params)
   102  					memokey ~= ctx.get(p.name, lay); // lay?
   103  				if( auto memolay = lay in memo )
   104  					if( auto pv = memokey in *memolay )
   105  						return *pv;
   106  				memo[lay][memokey] = lift(ast.pos,new UndValue,lay,ctx);
   107  			}
   108  		}
   109  
   110  		// @macro run!!!
   111  		if( lay == MacroLayer )
   112  			return macroEval(ast.funbody, ctx, false);
   113  
   114  		version(MacroCache) {
   115  			if( preprocessed_funbody is null ) {
   116  				// .prototype!, forced macro cannot access parameters
   117  				ctx.kill = true; scope(exit)ctx.kill=false;
   118  				preprocessed_funbody = tableToAST(ValueLayer,macroEval(ast.funbody, ctx, true));
   119  			}
   120  		} else {
   121  			if( preprocessed_funbody is null ) {
   122  				// .prototype!, forced macro cannot access parameters
   123  				ctx.kill = true; scope(exit)ctx.kill=false;
   124  				preprocessed_funbody = tableToAST(ValueLayer,macroEval(ast.funbody, ctx, true));
   125  			}
   126  		}
   127  
   128  		auto v = eval(preprocessed_funbody, ctx, true, lay);
   129  		version(AutoMemoization)
   130  		{
   131  			if( lay != ValueLayer && lay != MacroLayer )
   132  			{
   133  				memo[lay][memokey] = v;
   134  				version(AutoReRun)
   135  				memo[lay][memokey] = eval(preprocessed_funbody, ctx, true, lay); // re-Run!!
   136  			}
   137  		}
   138  		return v;
   139  	}
   140  }
   141  
   142  ///
   143  abstract class NativeFunValue : FunValue
   144  {
   145  	Parameter[] params_data;
   146  	override const(Parameter[]) params() { return params_data; }
   147  	override Table definitionContext() { return new Table; } // todo: cache	overrie
   148  }
   149  
   150  /// Named Constructor for FunValue
   151  
   152  FunValue native(R,T...)(R delegate (T) dg)
   153  {
   154  	return new class NativeFunValue {
   155  		this()
   156  		{
   157  			foreach(i, Ti; T)
   158  				params_data ~= new Parameter(text(i), []);
   159  		}
   160  		override Value invoke(in LexPosition pos, Layer lay, Table ctx)
   161  		{
   162  			if( lay != ValueLayer )
   163  				throw genex!RuntimeException(pos, "only "~ValueLayer~" layer can call native function");
   164  			T typed_args;
   165  			foreach(i, Ti; T) {
   166  				typed_args[i] = cast(Ti) ctx.get(text(i), ValueLayer);
   167  				if( typed_args[i] is null )
   168  					throw genex!RuntimeException(pos, sprintf!"type mismatch on the argument %d"(i+1));
   169  			}
   170  			try {
   171  				return dg(typed_args);
   172  			} catch( RuntimeException e ) {
   173  				throw e.pos is null ? new RuntimeException(pos, e.msg, e.file, e.line) : e;
   174  			}
   175  		}
   176  	};
   177  }
   178  
   179  /// Context (variable environment)
   180  /// Simlar to prototype chain of ECMAScript etc.
   181  /// But extended with the notion of "Layer"
   182  
   183  class Table : Value
   184  {
   185  	enum Kind {PropagateSet, NotPropagateSet};
   186  	bool kill = false; // to refactor
   187  
   188  	this( Table proto=null, Kind k = Kind.PropagateSet )
   189  		{ this.prototype = proto; this.kind = k; }
   190  
   191  	void set(string i, Layer lay, Value v, in LexPosition pos=null)
   192  	{
   193  		if( setIfExist(i, lay, v) )
   194  			return;
   195  		data[i][lay] = v;
   196  	}
   197  
   198  	bool has(string i, Layer lay, in LexPosition pos=null)
   199  	{
   200  		if( i in data ) {
   201  			if( lay !in data[i] )
   202  				return false;
   203  			if(kill)
   204  				return false;
   205  			return true;
   206  		}
   207  		if( prototype is null )
   208  			return false;
   209  		return prototype.has(i, lay, pos);
   210  	}
   211  	
   212  	Value get(string i, Layer lay, in LexPosition pos=null)
   213  	{
   214  		if( i in data ) {
   215  			// [TODO] consider forwarding to proto also in this case
   216  			if( lay !in data[i] )
   217  				throw genex!RuntimeException(pos, sprintf!"'%s' is not set in layer %s"(i,lay));
   218  			if(kill)
   219  				throw genex!RuntimeException(pos, sprintf!"'%s' is killed in macro"(i));
   220  			return data[i][lay];
   221  		}
   222  		if( prototype is null )
   223  			throw new RuntimeException(pos, sprintf!"'%s' not found"(i));
   224  		return prototype.get(i, lay, pos);
   225  	}
   226  
   227  	T access(T,S...)( Layer lay, string path, S rest )
   228  	{
   229  		static if( rest.length == 0 )
   230  		{
   231  			if( this.has(path, lay) )
   232  				return cast(T) this.get(path, lay);
   233  		}
   234  		else
   235  		{
   236  			if(auto next = this.access!Table(lay,path))
   237  				return next.access!T(lay,rest);
   238  		}
   239  		return null;
   240  	}
   241  
   242  	string toStringWithoutParen() const
   243  	{
   244  		string result;
   245  		bool first = true;
   246  		foreach(k, l2d; data)
   247  			foreach(l,d; l2d)
   248  			{
   249  				if(first) first=false; else result~=", ";
   250  				result ~= k;
   251  				result ~= l;
   252  				result ~= ":";
   253  				result ~= text(cast(Value)d);
   254  			}
   255  		if( prototype !is null )
   256  		{
   257  			result ~= " / ";
   258  			result ~= prototype.toStringWithoutParen();
   259  		}
   260  		return result;
   261  	}
   262  	
   263  	string toString() const
   264  	{
   265  		return "{" ~ toStringWithoutParen() ~ "}";
   266  	}
   267  
   268  private:
   269  	Table                prototype;
   270  	Kind                 kind;
   271  	Value[Layer][string] data;
   272  
   273  	bool setIfExist(string i, Layer lay, Value v)
   274  	{
   275  		if( i in data )
   276  		{
   277  			data[i][lay] = v;
   278  			return true;
   279  		}
   280  		if( kind==Kind.PropagateSet && prototype !is null )
   281  			return prototype.setIfExist(i, lay, v);
   282  		return false;
   283  	}
   284  }
   285  
   286  unittest
   287  {
   288  	Table c0 = new Table;
   289  	Table c01 = new Table(c0, Table.Kind.NotPropagateSet);
   290  	Table c012 = new Table(c01, Table.Kind.PropagateSet);
   291  	Table c013 = new Table(c01, Table.Kind.PropagateSet);
   292  
   293  	assert_nothrow( c012.set("x", ValueLayer, new IntValue(BigInt(12))) );
   294  	assert_throw!RuntimeException( c013.get("x", ValueLayer) );
   295  	assert_nothrow( c013.set("x", ValueLayer, new IntValue(BigInt(13))) );
   296  	assert_eq( c013.get("x", ValueLayer), new IntValue(BigInt(13)) );
   297  	assert_eq( c012.get("x", ValueLayer), new IntValue(BigInt(12)) );
   298  	assert_throw!RuntimeException( c01.get("x", ValueLayer) );
   299  
   300  	assert_nothrow( c01.set("y", ValueLayer, new IntValue(BigInt(1))) );
   301  	assert_eq( c013.get("y", ValueLayer), new IntValue(BigInt(1)) );
   302  	assert_eq( c012.get("y", ValueLayer), new IntValue(BigInt(1)) );
   303  	assert_eq( c01.get("y", ValueLayer), new IntValue(BigInt(1)) );
   304  
   305  	assert_nothrow( c0.set("z", ValueLayer, new IntValue(BigInt(0))) );
   306  	assert_eq( c013.get("z", ValueLayer), new IntValue(BigInt(0)) );
   307  	assert_eq( c012.get("z", ValueLayer), new IntValue(BigInt(0)) );
   308  	assert_eq( c01.get("z", ValueLayer), new IntValue(BigInt(0)) );
   309  	assert_eq( c0.get("z", ValueLayer), new IntValue(BigInt(0)) );
   310  
   311  	assert_nothrow( c012.set("y", ValueLayer, new IntValue(BigInt(444))) );
   312  	assert_eq( c013.get("y", ValueLayer), new IntValue(BigInt(444)) );
   313  	assert_eq( c012.get("y", ValueLayer), new IntValue(BigInt(444)) );
   314  	assert_eq( c01.get("y", ValueLayer), new IntValue(BigInt(444)) );
   315  
   316  	assert_nothrow( c012.set("z", ValueLayer, new IntValue(BigInt(555))) );
   317  	assert_eq( c013.get("z", ValueLayer), new IntValue(BigInt(0)) );
   318  	assert_eq( c012.get("z", ValueLayer), new IntValue(BigInt(555)) );
   319  	assert_eq( c01.get("z", ValueLayer), new IntValue(BigInt(0)) );
   320  	assert_eq( c0.get("z", ValueLayer), new IntValue(BigInt(0)) );
   321  
   322  	// [TODO] define the semantics and test @layers
   323  }
   324  
   325  immutable(LexPosition) extractPos( Table t )
   326  {
   327  	Layer theLayer = ValueLayer;
   328  	if(auto tt = t.access!Table(theLayer, "pos"))
   329  	{
   330  		auto fn = tt.access!StrValue(theLayer, "filename");
   331  		auto ln = tt.access!IntValue(theLayer, "lineno");
   332  		auto cl = tt.access!IntValue(theLayer, "column");
   333  		if(fn !is null && ln !is null && cl !is null)
   334  			return new immutable(LexPosition)(fn.data,cast(int)ln.data.toInt,cast(int)cl.data.toInt);
   335  	}
   336  	return null;
   337  }
   338  
   339  Value[] tableAsConsList( Layer theLayer, Table t )
   340  {
   341  	Value[] result;
   342  	while(t)
   343  		if(auto v  = t.access!Value(theLayer, "car"))
   344  		{
   345  			result ~= v;
   346  			t = t.access!Table(theLayer, "cdr");
   347  		}
   348  		else
   349  			break;
   350  	return result;
   351  }
   352  
   353  AST[] tableToASTList( Layer theLayer, Table t )
   354  {
   355  	AST[] result;
   356  	foreach(v; tableAsConsList(theLayer, t))
   357  		if(auto t = cast(Table)v)
   358  			result ~= tableToAST(theLayer,t);
   359  		else
   360  			throw genex!RuntimeException(cast(LexPosition)null, "Invalid AST (non-table in cons-list)");
   361  	return result;
   362  }
   363  
   364  AST tableToAST( Layer theLayer, Value vvvv )
   365  {
   366  	Table t = cast(Table)vvvv;
   367  	if( t is null )
   368  		throw genex!RuntimeException(cast(LexPosition)null, "Invalid AST (not a table)");
   369  
   370  	auto nodeType = t.access!StrValue(theLayer, "is");
   371  	if( nodeType is null )
   372  		throw genex!RuntimeException(cast(LexPosition)null, "Invalid AST {is:(not string)}");
   373  	auto pos = extractPos(t);
   374  	switch(nodeType.data)
   375  	{
   376  	case "int":
   377  		if(auto v = t.access!IntValue(theLayer, "data"))
   378  			return new IntLiteral(pos, v.data);
   379  		throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"int", data:(not int)}`);
   380  	case "str":
   381  		if(auto v = t.access!StrValue(theLayer, "data"))
   382  			return new StrLiteral(pos, v.data);
   383  		throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"str", data:(not string)}`);
   384  	case "var":
   385  		if(auto v = t.access!StrValue(theLayer, "name"))
   386  			return new VarExpression(pos, v.data);
   387  		throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"var", name:(not string)}`);
   388  	case "lay":
   389  		if(auto v = t.access!StrValue(theLayer, "layer"))
   390  			if(auto e = t.access!Table(theLayer, "expr"))
   391  				return new LayExpression(pos, v.data, tableToAST(theLayer,e));
   392  			else
   393  				throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"lay", expr:(not table)}`);
   394  		throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"lay", layer:(not string)}`);
   395  	case "let":
   396  		if(auto n = t.access!StrValue(theLayer, "name"))
   397  		if(auto e = t.access!Table(theLayer, "init"))
   398  		if(auto b = t.access!Table(theLayer, "expr"))
   399  		{
   400  			string nn = n.data;
   401  			auto ee = tableToAST(theLayer, e);
   402  			auto bb = tableToAST(theLayer, b);
   403  			Layer lay="";
   404  			if(auto l = t.access!StrValue(theLayer, "layer"))
   405  				lay = l.data;
   406  			return new LetExpression(pos, nn, lay, ee, bb);
   407  		}
   408  		throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"let", name:"???", init:"???", expr:"???"}`);
   409  	case "app":
   410  		if(auto f = t.access!Table(theLayer, "fun"))
   411  		if(auto a = t.access!Table(theLayer, "args"))
   412  			return new FuncallExpression(pos, tableToAST(theLayer,f), tableToASTList(theLayer,a));
   413  		throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"app", fun:???, args:???}`);
   414  	case "fun":
   415  		if(auto p = t.access!Table(theLayer, "params"))
   416  		if(auto b = t.access!Table(theLayer, "funbody"))
   417  		{
   418  			Parameter[] ps;
   419  			foreach(v; tableAsConsList(theLayer, p))
   420  			{
   421  				if(auto tt = cast(Table)v)
   422  				if(auto ss = tt.access!StrValue(theLayer, "name"))
   423  				if(auto ll = tt.access!Table(theLayer, "layers"))
   424  				{
   425  					Layer[] ls;
   426  					foreach(lll; tableAsConsList(theLayer, ll))
   427  						if(auto l = cast(StrValue)lll)
   428  							ls ~= l.data;
   429  						else
   430  							throw genex!RuntimeException(cast(LexPosition)null, sprintf!`Invalid AST {bad fun params %s}`(lll));
   431  					ps ~= new Parameter(ss.data, ls);
   432  					continue;
   433  				}
   434  				else
   435  				{
   436  					Layer[] emp;
   437  					ps ~= new Parameter(ss.data, emp);
   438  					continue;
   439  				}
   440  				throw genex!RuntimeException(cast(LexPosition)null, sprintf!`Invalid AST {bad fun params %s}`(v));
   441  			}
   442  			auto bb = tableToAST(theLayer, b);
   443  			return new FunLiteral(pos,ps,bb);
   444  		}
   445  		throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"fun", param:???, body:???}`);
   446  	default:
   447  		throw genex!RuntimeException(cast(LexPosition)null, sprintf!`Invalid AST {is: "%s"} unknown`(nodeType.data));
   448  	}
   449  }