Artifact Content
Not logged in

Artifact aeb12eeaab91afb4463090069bc5381a3f6d6e91


     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  
    55  ///
    56  class UserDefinedFunValue : FunValue
    57  {
    58  	FunLiteral ast;
    59  	Table      defCtx;
    60  	override const(Parameter[]) params() { return ast.params; }
    61  	override Table definitionContext() { return defCtx; }
    62  	override Value invoke(in LexPosition pos, Layer lay, Table ctx)
    63  	{
    64  		// TODO: only auto raised ones need memo? no?
    65  		// auto memoization
    66  /*
    67  		if( lay != ValueLayer && lay != MacroLayer )
    68  		{
    69  			if( auto memolay = lay in memo )
    70  				if( auto pv = args in *memolay )
    71  					return *pv;
    72  			memo[lay][args] = lift(e.pos,new UndValue,lay,ctx);
    73  		}
    74  				
    75  */
    76  		// @macro run!!!
    77  		if( lay == MacroLayer )
    78  			return macroEval(ast.funbody, ctx, false);
    79  /*TODO memo*/ AST macroMemo;
    80  		if( macroMemo is null ) {
    81  			// .prototype!, forced macro cannot access parameters
    82  			ctx.kill = true; scope(exit)ctx.kill=false;
    83  			auto tbl = macroEval(ast.funbody, ctx, true);
    84  			macroMemo = tableToAST(ValueLayer,tbl);
    85  		}
    86  		auto v = eval(macroMemo, ctx, true, lay);
    87  
    88  		//auto v = eval(e.funbody, ctxNeo, true, lay);
    89  		// auto memoization
    90  //		if( lay != ValueLayer && lay != MacroLayer )
    91  //			memo[lay][args] = v;
    92  		return v;
    93  	}
    94  
    95  	mixin SimpleClass;
    96  	override string toString() const { return sprintf!"(function:%x:%x)"(cast(void*)ast, cast(void*)defCtx); }
    97  }
    98  
    99  ///
   100  abstract class NativeFunValue : FunValue
   101  {
   102  	Parameter[] params_data;
   103  	override const(Parameter[]) params() { return params_data; }
   104  	override Table definitionContext() { return new Table; } // todo: cache	overrie
   105  }
   106  
   107  /// Named Constructor for FunValue
   108  
   109  FunValue native(R,T...)(R delegate (T) dg)
   110  {
   111  	return new class NativeFunValue {
   112  		this()
   113  		{
   114  			foreach(i, Ti; T)
   115  				params_data ~= new Parameter(text(i), []);
   116  		}
   117  		override Value invoke(in LexPosition pos, Layer lay, Table ctx)
   118  		{
   119  			if( lay != ValueLayer )
   120  				throw genex!RuntimeException(pos, "only "~ValueLayer~" layer can call native function");
   121  			T typed_args;
   122  			foreach(i, Ti; T) {
   123  				typed_args[i] = cast(Ti) ctx.get(text(i), ValueLayer);
   124  				if( typed_args[i] is null )
   125  					throw genex!RuntimeException(pos, sprintf!"type mismatch on the argument %d"(i+1));
   126  			}
   127  			try {
   128  				return dg(typed_args);
   129  			} catch( RuntimeException e ) {
   130  				throw e.pos is null ? new RuntimeException(pos, e.msg, e.file, e.line) : e;
   131  			}
   132  		}
   133  	};
   134  }
   135  
   136  /// Context (variable environment)
   137  /// Simlar to prototype chain of ECMAScript etc.
   138  /// But extended with the notion of "Layer"
   139  
   140  class Table : Value
   141  {
   142  	enum Kind {PropagateSet, NotPropagateSet};
   143  	bool kill = false; // to refactor
   144  
   145  	this( Table proto=null, Kind k = Kind.PropagateSet )
   146  		{ this.prototype = proto; this.kind = k; }
   147  
   148  	void set(string i, Layer lay, Value v, in LexPosition pos=null)
   149  	{
   150  		if( setIfExist(i, lay, v) )
   151  			return;
   152  		data[i][lay] = v;
   153  	}
   154  
   155  	bool has(string i, Layer lay, in LexPosition pos=null)
   156  	{
   157  		if( i in data ) {
   158  			if( lay !in data[i] )
   159  				return false;
   160  			if(kill)
   161  				return false;
   162  			return true;
   163  		}
   164  		if( prototype is null )
   165  			return false;
   166  		return prototype.has(i, lay, pos);
   167  	}
   168  	
   169  	Value get(string i, Layer lay, in LexPosition pos=null)
   170  	{
   171  		if( i in data ) {
   172  			// [TODO] consider forwarding to proto also in this case
   173  			if( lay !in data[i] )
   174  				throw genex!RuntimeException(pos, sprintf!"variable %s is not set in layer %s"(i,lay));
   175  			if(kill)
   176  				throw genex!RuntimeException(pos, sprintf!"variable %s is killed in macro"(i));
   177  			return data[i][lay];
   178  		}
   179  		if( prototype is null )
   180  			throw new RuntimeException(pos, sprintf!"variable %s not found"(i));
   181  		return prototype.get(i, lay, pos);
   182  	}
   183  
   184  	T access(T,S...)( Layer lay, string path, S rest )
   185  	{
   186  		static if( rest.length == 0 )
   187  		{
   188  			if( this.has(path, lay) )
   189  				return cast(T) this.get(path, lay);
   190  		}
   191  		else
   192  		{
   193  			if(auto next = this.access!Table(lay,path))
   194  				return next.access!T(lay,rest);
   195  		}
   196  		return null;
   197  	}
   198  
   199  	string toStringWithoutParen() const
   200  	{
   201  		string result;
   202  		bool first = true;
   203  		foreach(k, l2d; data)
   204  			foreach(l,d; l2d)
   205  			{
   206  				if(first) first=false; else result~=", ";
   207  				result ~= k;
   208  				result ~= l;
   209  				result ~= ":";
   210  				result ~= text(cast(Value)d);
   211  			}
   212  		if( prototype !is null )
   213  		{
   214  			result ~= " / ";
   215  			result ~= prototype.toStringWithoutParen();
   216  		}
   217  		return result;
   218  	}
   219  	
   220  	string toString() const
   221  	{
   222  		return "{" ~ toStringWithoutParen() ~ "}";
   223  	}
   224  
   225  private:
   226  	Table                prototype;
   227  	Kind                 kind;
   228  	Value[Layer][string] data;
   229  
   230  	bool setIfExist(string i, Layer lay, Value v)
   231  	{
   232  		if( i in data )
   233  		{
   234  			data[i][lay] = v;
   235  			return true;
   236  		}
   237  		if( kind==Kind.PropagateSet && prototype !is null )
   238  			return prototype.setIfExist(i, lay, v);
   239  		return false;
   240  	}
   241  }
   242  
   243  unittest
   244  {
   245  	Table c0 = new Table;
   246  	Table c01 = new Table(c0, Table.Kind.NotPropagateSet);
   247  	Table c012 = new Table(c01, Table.Kind.PropagateSet);
   248  	Table c013 = new Table(c01, Table.Kind.PropagateSet);
   249  
   250  	assert_nothrow( c012.set("x", ValueLayer, new IntValue(BigInt(12))) );
   251  	assert_throw!RuntimeException( c013.get("x", ValueLayer) );
   252  	assert_nothrow( c013.set("x", ValueLayer, new IntValue(BigInt(13))) );
   253  	assert_eq( c013.get("x", ValueLayer), new IntValue(BigInt(13)) );
   254  	assert_eq( c012.get("x", ValueLayer), new IntValue(BigInt(12)) );
   255  	assert_throw!RuntimeException( c01.get("x", ValueLayer) );
   256  
   257  	assert_nothrow( c01.set("y", ValueLayer, new IntValue(BigInt(1))) );
   258  	assert_eq( c013.get("y", ValueLayer), new IntValue(BigInt(1)) );
   259  	assert_eq( c012.get("y", ValueLayer), new IntValue(BigInt(1)) );
   260  	assert_eq( c01.get("y", ValueLayer), new IntValue(BigInt(1)) );
   261  
   262  	assert_nothrow( c0.set("z", ValueLayer, new IntValue(BigInt(0))) );
   263  	assert_eq( c013.get("z", ValueLayer), new IntValue(BigInt(0)) );
   264  	assert_eq( c012.get("z", ValueLayer), new IntValue(BigInt(0)) );
   265  	assert_eq( c01.get("z", ValueLayer), new IntValue(BigInt(0)) );
   266  	assert_eq( c0.get("z", ValueLayer), new IntValue(BigInt(0)) );
   267  
   268  	assert_nothrow( c012.set("y", ValueLayer, new IntValue(BigInt(444))) );
   269  	assert_eq( c013.get("y", ValueLayer), new IntValue(BigInt(444)) );
   270  	assert_eq( c012.get("y", ValueLayer), new IntValue(BigInt(444)) );
   271  	assert_eq( c01.get("y", ValueLayer), new IntValue(BigInt(444)) );
   272  
   273  	assert_nothrow( c012.set("z", ValueLayer, new IntValue(BigInt(555))) );
   274  	assert_eq( c013.get("z", ValueLayer), new IntValue(BigInt(0)) );
   275  	assert_eq( c012.get("z", ValueLayer), new IntValue(BigInt(555)) );
   276  	assert_eq( c01.get("z", ValueLayer), new IntValue(BigInt(0)) );
   277  	assert_eq( c0.get("z", ValueLayer), new IntValue(BigInt(0)) );
   278  
   279  	// [TODO] define the semantics and test @layers
   280  }
   281  
   282  immutable(LexPosition) extractPos( Table t )
   283  {
   284  	Layer theLayer = ValueLayer;
   285  	if(auto tt = t.access!Table(theLayer, "pos"))
   286  	{
   287  		auto fn = tt.access!StrValue(theLayer, "filename");
   288  		auto ln = tt.access!IntValue(theLayer, "lineno");
   289  		auto cl = tt.access!IntValue(theLayer, "column");
   290  		if(fn !is null && ln !is null && cl !is null)
   291  			return new immutable(LexPosition)(fn.data,cast(int)ln.data.toInt,cast(int)cl.data.toInt);
   292  	}
   293  	return null;
   294  }
   295  
   296  Value[] tableAsConsList( Layer theLayer, Table t )
   297  {
   298  	Value[] result;
   299  	while(t)
   300  		if(auto v  = t.access!Value(theLayer, "car"))
   301  		{
   302  			result ~= v;
   303  			t = t.access!Table(theLayer, "cdr");
   304  		}
   305  		else
   306  			break;
   307  	return result;
   308  }
   309  
   310  AST[] tableToASTList( Layer theLayer, Table t )
   311  {
   312  	AST[] result;
   313  	foreach(v; tableAsConsList(theLayer, t))
   314  		if(auto t = cast(Table)v)
   315  			result ~= tableToAST(theLayer,t);
   316  		else
   317  			throw genex!RuntimeException(cast(LexPosition)null, "Invalid AST (non-table in cons-list)");
   318  	return result;
   319  }
   320  
   321  AST tableToAST( Layer theLayer, Value vvvv )
   322  {
   323  	Table t = cast(Table)vvvv;
   324  	if( t is null )
   325  		throw genex!RuntimeException(cast(LexPosition)null, "Invalid AST (not a table)");
   326  
   327  	auto nodeType = t.access!StrValue(theLayer, "is");
   328  	if( nodeType is null )
   329  		throw genex!RuntimeException(cast(LexPosition)null, "Invalid AST {is:(not string)}");
   330  	auto pos = extractPos(t);
   331  	switch(nodeType.data)
   332  	{
   333  	case "int":
   334  		if(auto v = t.access!IntValue(theLayer, "data"))
   335  			return new IntLiteral(pos, v.data);
   336  		throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"int", data:(not int)}`);
   337  	case "str":
   338  		if(auto v = t.access!StrValue(theLayer, "data"))
   339  			return new StrLiteral(pos, v.data);
   340  		throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"str", data:(not string)}`);
   341  	case "var":
   342  		if(auto v = t.access!StrValue(theLayer, "name"))
   343  			return new VarExpression(pos, v.data);
   344  		throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"var", name:(not string)}`);
   345  	case "lay":
   346  		if(auto v = t.access!StrValue(theLayer, "layer"))
   347  			if(auto e = t.access!Table(theLayer, "expr"))
   348  				return new LayExpression(pos, v.data, tableToAST(theLayer,e));
   349  			else
   350  				throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"lay", expr:(not table)}`);
   351  		throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"lay", layer:(not string)}`);
   352  	case "let":
   353  		if(auto n = t.access!StrValue(theLayer, "name"))
   354  		if(auto e = t.access!Table(theLayer, "init"))
   355  		if(auto b = t.access!Table(theLayer, "expr"))
   356  		{
   357  			string nn = n.data;
   358  			auto ee = tableToAST(theLayer, e);
   359  			auto bb = tableToAST(theLayer, b);
   360  			Layer lay="";
   361  			if(auto l = t.access!StrValue(theLayer, "layer"))
   362  				lay = l.data;
   363  			return new LetExpression(pos, nn, lay, ee, bb);
   364  		}
   365  		throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"let", name:"???", init:"???", expr:"???"}`);
   366  	case "app":
   367  		if(auto f = t.access!Table(theLayer, "fun"))
   368  		if(auto a = t.access!Table(theLayer, "args"))
   369  			return new FuncallExpression(pos, tableToAST(theLayer,f), tableToASTList(theLayer,a));
   370  		throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"app", fun:???, args:???}`);
   371  	case "fun":
   372  		if(auto p = t.access!Table(theLayer, "params"))
   373  		if(auto b = t.access!Table(theLayer, "funbody"))
   374  		{
   375  			Parameter[] ps;
   376  			foreach(v; tableAsConsList(theLayer, p))
   377  			{
   378  				if(auto tt = cast(Table)v)
   379  				if(auto ss = tt.access!StrValue(theLayer, "name"))
   380  				if(auto ll = tt.access!Table(theLayer, "layers"))
   381  				{
   382  					Layer[] ls;
   383  					foreach(lll; tableAsConsList(theLayer, ll))
   384  						if(auto l = cast(StrValue)lll)
   385  							ls ~= l.data;
   386  						else
   387  							throw genex!RuntimeException(cast(LexPosition)null, sprintf!`Invalid AST {bad fun params %s}`(lll));
   388  					ps ~= new Parameter(ss.data, ls);
   389  					continue;
   390  				}
   391  				else
   392  				{
   393  					Layer[] emp;
   394  					ps ~= new Parameter(ss.data, emp);
   395  					continue;
   396  				}
   397  				throw genex!RuntimeException(cast(LexPosition)null, sprintf!`Invalid AST {bad fun params %s}`(v));
   398  			}
   399  			auto bb = tableToAST(theLayer, b);
   400  			return new FunLiteral(pos,ps,bb);
   401  		}
   402  		throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"fun", param:???, body:???}`);
   403  	default:
   404  		throw genex!RuntimeException(cast(LexPosition)null, sprintf!`Invalid AST {is: "%s"} unknown`(nodeType.data));
   405  	}
   406  }