Artifact Content
Not logged in

Artifact ff42bce4fb7dc674c6010f6c33e084067f8fb7b6


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