Artifact Content
Not logged in

Artifact 6fa9c2c94c01b27481bd421efbc9bd929b82b91f


     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  	override bool opEquals(Object rhs) { return 0==opCmp(rhs); }
    18  }
    19  
    20  ///
    21  class IntValue : Value
    22  {
    23  	BigInt data;
    24  
    25  	this(bool n)   { this.data = n?1:0; }
    26  	this(int n)    { this.data = n; }
    27  	this(long n)   { this.data = n; }
    28  	this(BigInt n) { this.data = n; }
    29  	this(string n) { this.data = BigInt(n); }
    30  	override string toString() const { return toDecimalString(cast(BigInt)data); }
    31  	override int opCmp(Object rhs) {
    32  		if(auto r = cast(IntValue)rhs) return data.opCmp(r.data);
    33  		if(auto r = cast(Value)rhs)    return typeid(this).opCmp(typeid(r));
    34  		throw genex!RuntimeException("comparison with value and somithing other");
    35  	}
    36  	mixin SimpleToHash;
    37  }
    38  
    39  ///
    40  class StrValue : Value
    41  {
    42  	string data;
    43  
    44  	mixin SimpleConstructor;
    45  	override string toString() const { return data; }
    46  	override int opCmp(Object rhs) {
    47  		if(auto r = cast(StrValue)rhs) return typeid(string).compare(&data, &r.data);
    48  		if(auto r = cast(Value)rhs)    return typeid(this).opCmp(typeid(r));
    49  		throw genex!RuntimeException("comparison with value and somithing other");
    50  	}
    51  	mixin SimpleToHash;
    52  }
    53  
    54  ///
    55  class UndefinedValue : Value
    56  {
    57  	mixin SimpleConstructor;
    58  	override string toString() const { return "<undefined>"; }
    59  	override int opCmp(Object rhs) {
    60  		if(auto r = cast(StrValue)rhs) return 0;
    61  		if(auto r = cast(Value)rhs)    return typeid(this).opCmp(typeid(r));
    62  		throw genex!RuntimeException("comparison with value and somithing other");
    63  	}
    64  	mixin SimpleToHash;
    65  }
    66  
    67  ///
    68  abstract class FunValue : Value
    69  {
    70  	const(Parameter[]) params();
    71  	Table definitionContext();
    72  	Value invoke(Layer lay, Table ctx, LexPosition pos);
    73  }
    74  
    75  /// Context (variable environment)
    76  /// Simlar to prototype chain of ECMAScript etc.
    77  /// But extended with the notion of "Layer"
    78  
    79  class Table : Value
    80  {
    81  	enum Kind {PropagateSet, NotPropagateSet};
    82  
    83  	this( Table proto=null, Kind k = Kind.PropagateSet )
    84  		{ this.prototype = proto; this.kind = k; }
    85  
    86  	/// Set the value v to the index i of layer lay
    87  	void set(string i, Layer lay, Value v)
    88  	{
    89  		if( setIfExist(i, lay, v) )
    90  			return;
    91  		data[i][lay] = v;
    92  	}
    93  
    94  	/// True if index i has value in layer lay
    95  	bool has(string i, Layer lay) const
    96  	{
    97  		if( i in data )
    98  			return !!(lay in data[i]);
    99  		if( prototype is null )
   100  			return false;
   101  		return prototype.has(i, lay);
   102  	}
   103  	
   104  	/// Return the value of index i at layer lay. Throws if it is not set
   105  	Value get(string i, Layer lay, LexPosition pos=null)
   106  	{
   107  		if( i in data ) {
   108  			// [TODO] consider forwarding to proto also in this case
   109  			if( lay !in data[i] )
   110  				throw genex!RuntimeException(pos, sprintf!"'%s' is not set in %s layer"(i,lay));
   111  			return data[i][lay];
   112  		}
   113  		if( prototype is null )
   114  			throw genex!RuntimeException(pos, sprintf!"'%s' not found in %s layer"(i,lay));
   115  		return prototype.get(i, lay, pos);
   116  	}
   117  
   118  	/// t.access!T(lay,a,b,...) returns t.get(a,lay).get(b,lay).... if exists
   119  	/// and has type T. Returns null otherwise
   120  	T access(T,S...)( Layer lay, string path, S rest )
   121  	{
   122  		static if( rest.length == 0 )
   123  		{
   124  			if( this.has(path, lay) )
   125  				return cast(T) this.get(path, lay);
   126  		}
   127  		else
   128  		{
   129  			if(auto next = this.access!Table(lay,path))
   130  				return next.access!T(lay,rest);
   131  		}
   132  		return null;
   133  	}
   134  
   135  	/// Is this an empty table?
   136  	bool empty()
   137  	{
   138  		return data.length==0 && (prototype is null || prototype.empty);
   139  	}
   140  	
   141  	/// Can be seen as a cons-list?
   142  	bool isList()
   143  	{
   144  		Table t = this;
   145  		while(t.has("car", ValueLayer) && t.has("cdr", ValueLayer))
   146  			if(auto tt = cast(Table)t.get("cdr", ValueLayer))
   147  				t = tt;
   148  			else
   149  				return false;
   150  		return t.empty;
   151  	}
   152  
   153  	/// Regard table as a cons-list and convert to an array
   154  	Value[] toList()
   155  	{
   156  		Value[] result;
   157  		Table t = this;
   158  		while(t.has("car", ValueLayer) && t.has("cdr", ValueLayer))
   159  		{
   160  			result ~= t.get("car", ValueLayer);
   161  			if(auto tt = cast(Table)t.get("cdr", ValueLayer))
   162  				t = tt;
   163  			else
   164  				throw genex!RuntimeException("this table is not a cons-list");
   165  		}
   166  		if( t.empty )
   167  			return result;
   168  		throw genex!RuntimeException("this table is not a cons-list");
   169  	}	
   170  
   171  	/// Get the list of direct entries ignoring prototypes in sorted order
   172  	Tuple!(string,Layer,Value)[] direct_entries()
   173  	{
   174  		Tuple!(string,Layer,Value)[] arr;
   175  		foreach(k, l2d; data)
   176  			foreach(l,d; l2d)
   177  				arr ~= tuple(k,l,d);
   178  		arr.sort();
   179  		return arr;
   180  	}
   181  	
   182  	/// Get the whole list of observable entries in unspecified order
   183  	Tuple!(string,Layer,Value)[] entries()
   184  	{
   185  		bool[string] hidden;
   186  		Tuple!(string,Layer,Value)[] arr;
   187  		enumerateEntries(hidden, arr);
   188  		return arr;
   189  	}
   190  
   191  	private void enumerateEntries( ref bool[string] hidden, ref Tuple!(string,Layer,Value)[] arr )
   192  	{
   193  		foreach(k, l2d; data)
   194  			if( k !in hidden )
   195  			{
   196  				foreach(l,d; l2d)
   197  					arr ~= tuple(k,l,d);
   198  				hidden[k] = true;
   199  			}
   200  		if(prototype !is null)
   201  			prototype.enumerateEntries(hidden, arr);
   202  	}
   203  
   204  	override string toString()
   205  	{
   206  		if( isList() )
   207  			return text(toList());
   208  		return "{" ~ toStringWithoutParen() ~ "}";
   209  	}
   210  
   211  	override int opCmp(Object rhs)
   212  	{
   213  		if(auto r = cast(Table)rhs) {
   214  			Tuple!(string,Layer,Value)[] ls = this.entries();
   215  			Tuple!(string,Layer,Value)[] rs = r.entries();
   216  			if( ls.length != rs.length )
   217  				return (ls.length < rs.length ? -1 : +1);
   218  			ls.sort();
   219  			rs.sort();
   220  			foreach(i,_; ls)
   221  				if(auto c = ls[i].opCmp(rs[i]))
   222  					return c;
   223  			return 0;
   224  		}
   225  		if(auto r = cast(Value)rhs) return typeid(this).opCmp(typeid(r));
   226  		throw genex!RuntimeException("comparison with value and somithing other");
   227  	}
   228  
   229  	override hash_t toHash()
   230  	{
   231  		Tuple!(string,Layer,Value)[] ls = this.entries();
   232  		ls.sort();
   233  		hash_t h;
   234  		foreach(e; ls)
   235  			h += structuralHash(e[0])+structuralHash(e[1])+structuralHash(e[2]);
   236  		return h;
   237  	}
   238  
   239  private:
   240  	Table                prototype;
   241  	Kind                 kind;
   242  	Value[Layer][string] data;
   243  
   244  	string toStringWithoutParen() const
   245  	{
   246  		string result;
   247  		bool first = true;
   248  		foreach(k, l2d; data)
   249  			foreach(l,d; l2d)
   250  			{
   251  				if(first) first=false; else result~=", ";
   252  				result ~= k;
   253  				if( l.empty )
   254  					result ~= "(emptylayer)";
   255  				else if( l != ValueLayer )
   256  					result ~= l;
   257  				result ~= ":";
   258  				result ~= text(cast(Value)d);
   259  			}
   260  		if( prototype !is null )
   261  		{
   262  			result ~= " / ";
   263  			result ~= prototype.toStringWithoutParen();
   264  		}
   265  		return result;
   266  	}
   267  
   268  	bool setIfExist(string i, Layer lay, Value v)
   269  	{
   270  		if( i in data )
   271  		{
   272  			data[i][lay] = v;
   273  			return true;
   274  		}
   275  		if( kind==Kind.PropagateSet && prototype !is null )
   276  			return prototype.setIfExist(i, lay, v);
   277  		return false;
   278  	}
   279  }
   280  
   281  unittest
   282  {
   283  	Table c0 = new Table;
   284  	Table c01 = new Table(c0, Table.Kind.NotPropagateSet);
   285  	Table c012 = new Table(c01, Table.Kind.PropagateSet);
   286  	Table c013 = new Table(c01, Table.Kind.PropagateSet);
   287  
   288  	assert_nothrow( c012.set("x", ValueLayer, new IntValue(12)) );
   289  	assert_throw!RuntimeException( c013.get("x", ValueLayer) );
   290  	assert_nothrow( c013.set("x", ValueLayer, new IntValue(13)) );
   291  	assert_eq( c013.get("x", ValueLayer), new IntValue(13) );
   292  	assert_eq( c012.get("x", ValueLayer), new IntValue(12) );
   293  	assert_throw!RuntimeException( c01.get("x", ValueLayer) );
   294  
   295  	assert_nothrow( c01.set("y", ValueLayer, new IntValue(1)) );
   296  	assert_eq( c013.get("y", ValueLayer), new IntValue(1) );
   297  	assert_eq( c012.get("y", ValueLayer), new IntValue(1) );
   298  	assert_eq( c01.get("y", ValueLayer), new IntValue(1) );
   299  
   300  	assert_nothrow( c0.set("z", ValueLayer, new IntValue(0)) );
   301  	assert_eq( c013.get("z", ValueLayer), new IntValue(0) );
   302  	assert_eq( c012.get("z", ValueLayer), new IntValue(0) );
   303  	assert_eq( c01.get("z", ValueLayer), new IntValue(0) );
   304  	assert_eq( c0.get("z", ValueLayer), new IntValue(0) );
   305  
   306  	assert_nothrow( c012.set("y", ValueLayer, new IntValue(444)) );
   307  	assert_eq( c013.get("y", ValueLayer), new IntValue(444) );
   308  	assert_eq( c012.get("y", ValueLayer), new IntValue(444) );
   309  	assert_eq( c01.get("y", ValueLayer), new IntValue(444) );
   310  
   311  	assert_nothrow( c012.set("z", ValueLayer, new IntValue(555)) );
   312  	assert_eq( c013.get("z", ValueLayer), new IntValue(0) );
   313  	assert_eq( c012.get("z", ValueLayer), new IntValue(555) );
   314  	assert_eq( c01.get("z", ValueLayer), new IntValue(0) );
   315  	assert_eq( c0.get("z", ValueLayer), new IntValue(0) );
   316  
   317  	// [TODO] define the semantics and test @layers
   318  }