Artifact Content
Not logged in

Artifact f1a01bb8ba2daf2647894690281908ad807b7672


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