Artifact Content
Not logged in

Artifact 19366410c0eeb20926439c006d5302ad7d658878


     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(LexPosition pos, Layer lay, Table ctx);
    51  }
    52  
    53  /// Context (variable environment)
    54  /// Simlar to prototype chain of ECMAScript etc.
    55  /// But extended with the notion of "Layer"
    56  
    57  class Table : Value
    58  {
    59  	enum Kind {PropagateSet, NotPropagateSet};
    60  	bool kill = false; // to refactor
    61  
    62  	this( Table proto=null, Kind k = Kind.PropagateSet )
    63  		{ this.prototype = proto; this.kind = k; }
    64  
    65  	void set(string i, Layer lay, Value v, LexPosition pos=null)
    66  	{
    67  		if( setIfExist(i, lay, v) )
    68  			return;
    69  		data[i][lay] = v;
    70  	}
    71  
    72  	bool has(string i, Layer lay) const
    73  	{
    74  		if( i in data ) {
    75  			if( lay !in data[i] )
    76  				return false;
    77  			if(kill)
    78  				return false;
    79  			return true;
    80  		}
    81  		if( prototype is null )
    82  			return false;
    83  		return prototype.has(i, lay);
    84  	}
    85  	
    86  	Value get(string i, Layer lay, LexPosition pos=null)
    87  	{
    88  		if( i in data ) {
    89  			// [TODO] consider forwarding to proto also in this case
    90  			if( lay !in data[i] )
    91  				throw genex!RuntimeException(pos, sprintf!"'%s' is not set in layer %s"(i,lay));
    92  			if(kill)
    93  				throw genex!RuntimeException(pos, sprintf!"'%s' is killed in macro"(i));
    94  			return data[i][lay];
    95  		}
    96  		if( prototype is null )
    97  			throw new RuntimeException(pos, sprintf!"'%s' not found"(i));
    98  		return prototype.get(i, lay, pos);
    99  	}
   100  
   101  	T access(T,S...)( Layer lay, string path, S rest )
   102  	{
   103  		static if( rest.length == 0 )
   104  		{
   105  			if( this.has(path, lay) )
   106  				return cast(T) this.get(path, lay);
   107  		}
   108  		else
   109  		{
   110  			if(auto next = this.access!Table(lay,path))
   111  				return next.access!T(lay,rest);
   112  		}
   113  		return null;
   114  	}
   115  
   116  	string toStringWithoutParen() const
   117  	{
   118  		string result;
   119  		bool first = true;
   120  		foreach(k, l2d; data)
   121  			foreach(l,d; l2d)
   122  			{
   123  				if(first) first=false; else result~=", ";
   124  				result ~= k;
   125  				if( l.empty )
   126  					result ~= "(emptylayer)";
   127  				else if( l != ValueLayer )
   128  					result ~= l;
   129  				result ~= ":";
   130  				result ~= text(cast(Value)d);
   131  			}
   132  		if( prototype !is null )
   133  		{
   134  			result ~= " / ";
   135  			result ~= prototype.toStringWithoutParen();
   136  		}
   137  		return result;
   138  	}
   139  	
   140  	string toString() const
   141  	{
   142  		return "{" ~ toStringWithoutParen() ~ "}";
   143  	}
   144  
   145  private:
   146  	Table                prototype;
   147  	Kind                 kind;
   148  	Value[Layer][string] data;
   149  
   150  	bool setIfExist(string i, Layer lay, Value v)
   151  	{
   152  		if( i in data )
   153  		{
   154  			data[i][lay] = v;
   155  			return true;
   156  		}
   157  		if( kind==Kind.PropagateSet && prototype !is null )
   158  			return prototype.setIfExist(i, lay, v);
   159  		return false;
   160  	}
   161  }
   162  
   163  unittest
   164  {
   165  	Table c0 = new Table;
   166  	Table c01 = new Table(c0, Table.Kind.NotPropagateSet);
   167  	Table c012 = new Table(c01, Table.Kind.PropagateSet);
   168  	Table c013 = new Table(c01, Table.Kind.PropagateSet);
   169  
   170  	assert_nothrow( c012.set("x", ValueLayer, new IntValue(BigInt(12))) );
   171  	assert_throw!RuntimeException( c013.get("x", ValueLayer) );
   172  	assert_nothrow( c013.set("x", ValueLayer, new IntValue(BigInt(13))) );
   173  	assert_eq( c013.get("x", ValueLayer), new IntValue(BigInt(13)) );
   174  	assert_eq( c012.get("x", ValueLayer), new IntValue(BigInt(12)) );
   175  	assert_throw!RuntimeException( c01.get("x", ValueLayer) );
   176  
   177  	assert_nothrow( c01.set("y", ValueLayer, new IntValue(BigInt(1))) );
   178  	assert_eq( c013.get("y", ValueLayer), new IntValue(BigInt(1)) );
   179  	assert_eq( c012.get("y", ValueLayer), new IntValue(BigInt(1)) );
   180  	assert_eq( c01.get("y", ValueLayer), new IntValue(BigInt(1)) );
   181  
   182  	assert_nothrow( c0.set("z", ValueLayer, new IntValue(BigInt(0))) );
   183  	assert_eq( c013.get("z", ValueLayer), new IntValue(BigInt(0)) );
   184  	assert_eq( c012.get("z", ValueLayer), new IntValue(BigInt(0)) );
   185  	assert_eq( c01.get("z", ValueLayer), new IntValue(BigInt(0)) );
   186  	assert_eq( c0.get("z", ValueLayer), new IntValue(BigInt(0)) );
   187  
   188  	assert_nothrow( c012.set("y", ValueLayer, new IntValue(BigInt(444))) );
   189  	assert_eq( c013.get("y", ValueLayer), new IntValue(BigInt(444)) );
   190  	assert_eq( c012.get("y", ValueLayer), new IntValue(BigInt(444)) );
   191  	assert_eq( c01.get("y", ValueLayer), new IntValue(BigInt(444)) );
   192  
   193  	assert_nothrow( c012.set("z", ValueLayer, new IntValue(BigInt(555))) );
   194  	assert_eq( c013.get("z", ValueLayer), new IntValue(BigInt(0)) );
   195  	assert_eq( c012.get("z", ValueLayer), new IntValue(BigInt(555)) );
   196  	assert_eq( c01.get("z", ValueLayer), new IntValue(BigInt(0)) );
   197  	assert_eq( c0.get("z", ValueLayer), new IntValue(BigInt(0)) );
   198  
   199  	// [TODO] define the semantics and test @layers
   200  }
   201  
   202  immutable(LexPosition) extractPos( Table t )
   203  {
   204  	Layer theLayer = ValueLayer;
   205  	if(auto tt = t.access!Table(theLayer, "pos"))
   206  	{
   207  		auto fn = tt.access!StrValue(theLayer, "filename");
   208  		auto ln = tt.access!IntValue(theLayer, "lineno");
   209  		auto cl = tt.access!IntValue(theLayer, "column");
   210  		if(fn !is null && ln !is null && cl !is null)
   211  			return new immutable(LexPosition)(fn.data,cast(int)ln.data.toInt,cast(int)cl.data.toInt);
   212  	}
   213  	return null;
   214  }
   215  
   216  Value[] tableAsConsList( Layer theLayer, Table t )
   217  {
   218  	Value[] result;
   219  	while(t)
   220  		if(auto v  = t.access!Value(theLayer, "car"))
   221  		{
   222  			result ~= v;
   223  			t = t.access!Table(theLayer, "cdr");
   224  		}
   225  		else
   226  			break;
   227  	return result;
   228  }
   229  
   230  AST[] tableToASTList( Layer theLayer, Table t )
   231  {
   232  	AST[] result;
   233  	foreach(v; tableAsConsList(theLayer, t))
   234  		if(auto t = cast(Table)v)
   235  			result ~= tableToAST(theLayer,t);
   236  		else
   237  			throw genex!RuntimeException(cast(LexPosition)null, "Invalid AST (non-table in cons-list)");
   238  	return result;
   239  }
   240  
   241  AST tableToAST( Layer theLayer, Value vvvv )
   242  {
   243  	Table t = cast(Table)vvvv;
   244  	if( t is null )
   245  		throw genex!RuntimeException(cast(LexPosition)null, "Invalid AST (not a table)");
   246  
   247  	auto nodeType = t.access!StrValue(theLayer, "is");
   248  	if( nodeType is null )
   249  		throw genex!RuntimeException(cast(LexPosition)null, "Invalid AST {is:(not string)}");
   250  	auto pos = extractPos(t);
   251  	switch(nodeType.data)
   252  	{
   253  	case "int":
   254  		if(auto v = t.access!IntValue(theLayer, "data"))
   255  			return new Int(pos, v.data);
   256  		throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"int", data:(not int)}`);
   257  	case "str":
   258  		if(auto v = t.access!StrValue(theLayer, "data"))
   259  			return new Str(pos, v.data);
   260  		throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"str", data:(not string)}`);
   261  	case "var":
   262  		if(auto v = t.access!StrValue(theLayer, "name"))
   263  			return new Var(pos, v.data);
   264  		throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"var", name:(not string)}`);
   265  	case "lay":
   266  		if(auto v = t.access!StrValue(theLayer, "layer"))
   267  			if(auto e = t.access!Table(theLayer, "expr"))
   268  				return new Lay(pos, v.data, tableToAST(theLayer,e));
   269  			else
   270  				throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"lay", expr:(not table)}`);
   271  		throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"lay", layer:(not string)}`);
   272  	case "let":
   273  		if(auto n = t.access!StrValue(theLayer, "name"))
   274  		if(auto e = t.access!Table(theLayer, "init"))
   275  		if(auto b = t.access!Table(theLayer, "expr"))
   276  		{
   277  			string nn = n.data;
   278  			auto ee = tableToAST(theLayer, e);
   279  			auto bb = tableToAST(theLayer, b);
   280  			Layer lay="";
   281  			if(auto l = t.access!StrValue(theLayer, "layer"))
   282  				lay = l.data;
   283  			return new Let(pos, nn, lay, ee, bb);
   284  		}
   285  		throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"let", name:"???", init:"???", expr:"???"}`);
   286  	case "app":
   287  		if(auto f = t.access!Table(theLayer, "fun"))
   288  		if(auto a = t.access!Table(theLayer, "args"))
   289  			return new App(pos, tableToAST(theLayer,f), tableToASTList(theLayer,a));
   290  		throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"app", fun:???, args:???}`);
   291  	case "fun":
   292  		if(auto p = t.access!Table(theLayer, "params"))
   293  		if(auto b = t.access!Table(theLayer, "funbody"))
   294  		{
   295  			Parameter[] ps;
   296  			foreach(v; tableAsConsList(theLayer, p))
   297  			{
   298  				if(auto tt = cast(Table)v)
   299  				if(auto ss = tt.access!StrValue(theLayer, "name"))
   300  				if(auto ll = tt.access!Table(theLayer, "layers"))
   301  				{
   302  					Layer[] ls;
   303  					foreach(lll; tableAsConsList(theLayer, ll))
   304  						if(auto l = cast(StrValue)lll)
   305  							ls ~= l.data;
   306  						else
   307  							throw genex!RuntimeException(cast(LexPosition)null, sprintf!`Invalid AST {bad fun params %s}`(lll));
   308  					ps ~= new Parameter(ss.data, ls);
   309  					continue;
   310  				}
   311  				else
   312  				{
   313  					Layer[] emp;
   314  					ps ~= new Parameter(ss.data, emp);
   315  					continue;
   316  				}
   317  				throw genex!RuntimeException(cast(LexPosition)null, sprintf!`Invalid AST {bad fun params %s}`(v));
   318  			}
   319  			auto bb = tableToAST(theLayer, b);
   320  			return new Fun(pos,ps,bb);
   321  		}
   322  		throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"fun", param:???, body:???}`);
   323  	default:
   324  		throw genex!RuntimeException(cast(LexPosition)null, sprintf!`Invalid AST {is: "%s"} unknown`(nodeType.data));
   325  	}
   326  }
   327  
   328  Table makeCons(Value a, Value d)
   329  {
   330  	Table t = new Table;
   331  	t.set("car", ValueLayer, a);
   332  	t.set("cdr", ValueLayer, d);
   333  	return t;
   334  }
   335  
   336  Table fromPos(LexPosition pos)
   337  {
   338  	Table t = new Table;
   339  	if( pos !is null ) {
   340  		t.set("filename", ValueLayer, new StrValue(pos.filename));
   341  		t.set("lineno",   ValueLayer, new IntValue(BigInt(pos.lineno)));
   342  		t.set("column",   ValueLayer, new IntValue(BigInt(pos.column)));
   343  	} else {
   344  		t.set("filename", ValueLayer, new StrValue("nullpos"));
   345  		t.set("lineno",   ValueLayer, new IntValue(BigInt(0)));
   346  		t.set("column",   ValueLayer, new IntValue(BigInt(0)));
   347  	}
   348  	return t;
   349  }