Artifact Content
Not logged in

Artifact 3d0cc7f5519244935e30cd0ced0a827e2bb30fe2


     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  			if( lay !in data[i] )
   109  				throw genex!RuntimeException(pos, sprintf!"'%s' is not set in %s layer"(i,lay));
   110  			return data[i][lay];
   111  		}
   112  		if( prototype is null )
   113  			throw genex!RuntimeException(pos, sprintf!"'%s' not found in %s layer"(i,lay));
   114  		return prototype.get(i, lay, pos);
   115  	}
   116  
   117  	/// t.access!T(lay,a,b,...) returns t.get(a,lay).get(b,lay).... if exists
   118  	/// and has type T. Returns null otherwise
   119  	T access(T,S...)( Layer lay, string path, S rest )
   120  	{
   121  		static if( rest.length == 0 )
   122  		{
   123  			if( this.has(path, lay) )
   124  				return cast(T) this.get(path, lay);
   125  		}
   126  		else
   127  		{
   128  			if(auto next = this.access!Table(lay,path))
   129  				return next.access!T(lay,rest);
   130  		}
   131  		return null;
   132  	}
   133  
   134  	/// Is this an empty table?
   135  	bool empty()
   136  	{
   137  		return data.length==0 && (prototype is null || prototype.empty);
   138  	}
   139  	
   140  	/// Can be seen as a cons-list?
   141  	bool isList()
   142  	{
   143  		Table t = this;
   144  		while(t.has("car", ValueLayer) && t.has("cdr", ValueLayer))
   145  			if(auto tt = cast(Table)t.get("cdr", ValueLayer))
   146  				t = tt;
   147  			else
   148  				return false;
   149  		return t.empty;
   150  	}
   151  
   152  	/// Regard table as a cons-list and convert to an array
   153  	Value[] toList()
   154  	{
   155  		Value[] result;
   156  		Table t = this;
   157  		while(t.has("car", ValueLayer) && t.has("cdr", ValueLayer))
   158  		{
   159  			result ~= t.get("car", ValueLayer);
   160  			if(auto tt = cast(Table)t.get("cdr", ValueLayer))
   161  				t = tt;
   162  			else
   163  				throw genex!RuntimeException("this table is not a cons-list");
   164  		}
   165  		if( t.empty )
   166  			return result;
   167  		throw genex!RuntimeException("this table is not a cons-list");
   168  	}	
   169  
   170  	/// Get the list of direct entries ignoring prototypes in sorted order
   171  	Tuple!(string,Layer,Value)[] direct_entries()
   172  	{
   173  		Tuple!(string,Layer,Value)[] arr;
   174  		foreach(k, l2d; data)
   175  			foreach(l,d; l2d)
   176  				arr ~= tuple(k,l,d);
   177  		arr.sort();
   178  		return arr;
   179  	}
   180  	
   181  	/// Get the whole list of observable entries in unspecified order
   182  	Tuple!(string,Layer,Value)[] entries()
   183  	{
   184  		bool[string] hidden;
   185  		Tuple!(string,Layer,Value)[] arr;
   186  		enumerateEntries(hidden, arr);
   187  		return arr;
   188  	}
   189  
   190  	private void enumerateEntries( ref bool[string] hidden, ref Tuple!(string,Layer,Value)[] arr )
   191  	{
   192  		foreach(k, l2d; data)
   193  			if( k !in hidden )
   194  			{
   195  				foreach(l,d; l2d)
   196  					arr ~= tuple(k,l,d);
   197  				hidden[k] = true;
   198  			}
   199  		if(prototype !is null)
   200  			prototype.enumerateEntries(hidden, arr);
   201  	}
   202  
   203  	override string toString()
   204  	{
   205  		if( isList() )
   206  			return text(toList());
   207  		return "{" ~ toStringWithoutParen() ~ "}";
   208  	}
   209  
   210  	override int opCmp(Object rhs)
   211  	{
   212  		if(auto r = cast(Table)rhs) {
   213  			Tuple!(string,Layer,Value)[] ls = this.entries();
   214  			Tuple!(string,Layer,Value)[] rs = r.entries();
   215  			if( ls.length != rs.length )
   216  				return (ls.length < rs.length ? -1 : +1);
   217  			ls.sort();
   218  			rs.sort();
   219  			foreach(i,_; ls)
   220  				if(auto c = ls[i].opCmp(rs[i]))
   221  					return c;
   222  			return 0;
   223  		}
   224  		if(auto r = cast(Value)rhs) return typeid(this).opCmp(typeid(r));
   225  		throw genex!RuntimeException("comparison with value and somithing other");
   226  	}
   227  
   228  	override hash_t toHash()
   229  	{
   230  		Tuple!(string,Layer,Value)[] ls = this.entries();
   231  		ls.sort();
   232  		hash_t h;
   233  		foreach(e; ls)
   234  			h += structuralHash(e[0])+structuralHash(e[1])+structuralHash(e[2]);
   235  		return h;
   236  	}
   237  
   238  private:
   239  	Table                prototype;
   240  	Kind                 kind;
   241  	Value[Layer][string] data;
   242  
   243  	string toStringWithoutParen() const
   244  	{
   245  		string result;
   246  		bool first = true;
   247  		foreach(k, l2d; data)
   248  			foreach(l,d; l2d)
   249  			{
   250  				if(first) first=false; else result~=", ";
   251  				result ~= k;
   252  				if( l.empty )
   253  					result ~= "(emptylayer)";
   254  				else if( l != ValueLayer )
   255  					result ~= l;
   256  				result ~= ":";
   257  				result ~= text(cast(Value)d);
   258  			}
   259  		if( prototype !is null )
   260  		{
   261  			result ~= " / ";
   262  			result ~= prototype.toStringWithoutParen();
   263  		}
   264  		return result;
   265  	}
   266  
   267  	bool setIfExist(string i, Layer lay, Value v)
   268  	{
   269  		if( i in data )
   270  		{
   271  			data[i][lay] = v;
   272  			return true;
   273  		}
   274  		if( kind==Kind.PropagateSet && prototype !is null )
   275  			return prototype.setIfExist(i, lay, v);
   276  		return false;
   277  	}
   278  }
   279  
   280  unittest
   281  {
   282  	Table c0 = new Table;
   283  	Table c01 = new Table(c0, Table.Kind.NotPropagateSet);
   284  	Table c012 = new Table(c01, Table.Kind.PropagateSet);
   285  	Table c013 = new Table(c01, Table.Kind.PropagateSet);
   286  
   287  	assert_nothrow( c012.set("x", ValueLayer, new IntValue(12)) );
   288  	assert_throw!RuntimeException( c013.get("x", ValueLayer) );
   289  	assert_nothrow( c013.set("x", ValueLayer, new IntValue(13)) );
   290  	assert_eq( c013.get("x", ValueLayer), new IntValue(13) );
   291  	assert_eq( c012.get("x", ValueLayer), new IntValue(12) );
   292  	assert_throw!RuntimeException( c01.get("x", ValueLayer) );
   293  
   294  	assert_nothrow( c01.set("y", ValueLayer, new IntValue(1)) );
   295  	assert_eq( c013.get("y", ValueLayer), new IntValue(1) );
   296  	assert_eq( c012.get("y", ValueLayer), new IntValue(1) );
   297  	assert_eq( c01.get("y", ValueLayer), new IntValue(1) );
   298  
   299  	assert_nothrow( c0.set("z", ValueLayer, new IntValue(0)) );
   300  	assert_eq( c013.get("z", ValueLayer), new IntValue(0) );
   301  	assert_eq( c012.get("z", ValueLayer), new IntValue(0) );
   302  	assert_eq( c01.get("z", ValueLayer), new IntValue(0) );
   303  	assert_eq( c0.get("z", ValueLayer), new IntValue(0) );
   304  
   305  	assert_nothrow( c012.set("y", ValueLayer, new IntValue(444)) );
   306  	assert_eq( c013.get("y", ValueLayer), new IntValue(444) );
   307  	assert_eq( c012.get("y", ValueLayer), new IntValue(444) );
   308  	assert_eq( c01.get("y", ValueLayer), new IntValue(444) );
   309  
   310  	assert_nothrow( c012.set("z", ValueLayer, new IntValue(555)) );
   311  	assert_eq( c013.get("z", ValueLayer), new IntValue(0) );
   312  	assert_eq( c012.get("z", ValueLayer), new IntValue(555) );
   313  	assert_eq( c01.get("z", ValueLayer), new IntValue(0) );
   314  	assert_eq( c0.get("z", ValueLayer), new IntValue(0) );
   315  
   316  	// [TODO] define the semantics and test @layers
   317  }