/**
* Authors: k.inaba
* License: NYSL 0.9982 http://www.kmonos.net/nysl/
*
* Runtime data structures for Polemy programming language.
*/
module polemy.value;
import polemy._common;
import polemy.failure;
import polemy.ast;
import polemy.layer;
/// Runtime values of Polemy
abstract class Value
{
override bool opEquals(Object rhs) { return 0==opCmp(rhs); }
}
///
class IntValue : Value
{
BigInt data;
this(bool n) { this.data = n?1:0; }
this(int n) { this.data = n; }
this(long n) { this.data = n; }
this(BigInt n) { this.data = n; }
this(string n) { this.data = BigInt(n); }
override string toString() const { return toDecimalString(cast(BigInt)data); }
override int opCmp(Object rhs) {
if(auto r = cast(IntValue)rhs) return data.opCmp(r.data);
if(auto r = cast(Value)rhs) return typeid(this).opCmp(typeid(r));
throw genex!RuntimeException("comparison with value and somithing other");
}
mixin SimpleToHash;
}
///
class StrValue : Value
{
string data;
mixin SimpleConstructor;
override string toString() const { return data; }
override int opCmp(Object rhs) {
if(auto r = cast(StrValue)rhs) return typeid(string).compare(&data, &r.data);
if(auto r = cast(Value)rhs) return typeid(this).opCmp(typeid(r));
throw genex!RuntimeException("comparison with value and somithing other");
}
mixin SimpleToHash;
}
///
class UndefinedValue : Value
{
mixin SimpleConstructor;
override string toString() const { return "<undefined>"; }
override int opCmp(Object rhs) {
if(auto r = cast(StrValue)rhs) return 0;
if(auto r = cast(Value)rhs) return typeid(this).opCmp(typeid(r));
throw genex!RuntimeException("comparison with value and somithing other");
}
mixin SimpleToHash;
}
///
abstract class FunValue : Value
{
const(Parameter[]) params();
Table definitionContext();
Value invoke(Layer lay, Table ctx, LexPosition pos);
}
/// Context (variable environment)
/// Simlar to prototype chain of ECMAScript etc.
/// But extended with the notion of "Layer"
class Table : Value
{
enum Kind {PropagateSet, NotPropagateSet};
this( Table proto=null, Kind k = Kind.PropagateSet )
{ this.prototype = proto; this.kind = k; }
void set(string i, Layer lay, Value v)
{
if( setIfExist(i, lay, v) )
return;
data[i][lay] = v;
}
bool has(string i, Layer lay) const
{
if( i in data )
return !!(lay in data[i]);
if( prototype is null )
return false;
return prototype.has(i, lay);
}
Value get(string i, Layer lay, LexPosition pos=null)
{
if( i in data ) {
// [TODO] consider forwarding to proto also in this case
if( lay !in data[i] )
throw genex!RuntimeException(pos, sprintf!"'%s' is not set in %s layer"(i,lay));
return data[i][lay];
}
if( prototype is null )
throw genex!RuntimeException(pos, sprintf!"'%s' not found in %s layer"(i,lay));
return prototype.get(i, lay, pos);
}
T access(T,S...)( Layer lay, string path, S rest )
{
static if( rest.length == 0 )
{
if( this.has(path, lay) )
return cast(T) this.get(path, lay);
}
else
{
if(auto next = this.access!Table(lay,path))
return next.access!T(lay,rest);
}
return null;
}
string toStringWithoutParen() const
{
string result;
bool first = true;
foreach(k, l2d; data)
foreach(l,d; l2d)
{
if(first) first=false; else result~=", ";
result ~= k;
if( l.empty )
result ~= "(emptylayer)";
else if( l != ValueLayer )
result ~= l;
result ~= ":";
result ~= text(cast(Value)d);
}
if( prototype !is null )
{
result ~= " / ";
result ~= prototype.toStringWithoutParen();
}
return result;
}
string toString()
{
if( isList() )
return text(toList());
return "{" ~ toStringWithoutParen() ~ "}";
}
public:
/// Is this an empty table?
bool empty()
{
return data.length==0 && (prototype is null || prototype.empty);
}
/// Can be seen as a cons-list?
bool isList()
{
Table t = this;
while(t.has("car", ValueLayer) && t.has("cdr", ValueLayer))
if(auto tt = cast(Table)t.get("cdr", ValueLayer))
t = tt;
else
return false;
return t.empty;
}
/// Regard table as a cons-list and convert to an array
Value[] toList()
{
Value[] result;
Table t = this;
while(t.has("car", ValueLayer) && t.has("cdr", ValueLayer))
{
result ~= t.get("car", ValueLayer);
if(auto tt = cast(Table)t.get("cdr", ValueLayer))
t = tt;
else
throw genex!RuntimeException("this table is not a cons-list");
}
if( t.empty )
return result;
throw genex!RuntimeException("this table is not a cons-list");
}
private:
Table prototype;
Kind kind;
Value[Layer][string] data;
bool setIfExist(string i, Layer lay, Value v)
{
if( i in data )
{
data[i][lay] = v;
return true;
}
if( kind==Kind.PropagateSet && prototype !is null )
return prototype.setIfExist(i, lay, v);
return false;
}
}
unittest
{
Table c0 = new Table;
Table c01 = new Table(c0, Table.Kind.NotPropagateSet);
Table c012 = new Table(c01, Table.Kind.PropagateSet);
Table c013 = new Table(c01, Table.Kind.PropagateSet);
assert_nothrow( c012.set("x", ValueLayer, new IntValue(12)) );
assert_throw!RuntimeException( c013.get("x", ValueLayer) );
assert_nothrow( c013.set("x", ValueLayer, new IntValue(13)) );
assert_eq( c013.get("x", ValueLayer), new IntValue(13) );
assert_eq( c012.get("x", ValueLayer), new IntValue(12) );
assert_throw!RuntimeException( c01.get("x", ValueLayer) );
assert_nothrow( c01.set("y", ValueLayer, new IntValue(1)) );
assert_eq( c013.get("y", ValueLayer), new IntValue(1) );
assert_eq( c012.get("y", ValueLayer), new IntValue(1) );
assert_eq( c01.get("y", ValueLayer), new IntValue(1) );
assert_nothrow( c0.set("z", ValueLayer, new IntValue(0)) );
assert_eq( c013.get("z", ValueLayer), new IntValue(0) );
assert_eq( c012.get("z", ValueLayer), new IntValue(0) );
assert_eq( c01.get("z", ValueLayer), new IntValue(0) );
assert_eq( c0.get("z", ValueLayer), new IntValue(0) );
assert_nothrow( c012.set("y", ValueLayer, new IntValue(444)) );
assert_eq( c013.get("y", ValueLayer), new IntValue(444) );
assert_eq( c012.get("y", ValueLayer), new IntValue(444) );
assert_eq( c01.get("y", ValueLayer), new IntValue(444) );
assert_nothrow( c012.set("z", ValueLayer, new IntValue(555)) );
assert_eq( c013.get("z", ValueLayer), new IntValue(0) );
assert_eq( c012.get("z", ValueLayer), new IntValue(555) );
assert_eq( c01.get("z", ValueLayer), new IntValue(0) );
assert_eq( c0.get("z", ValueLayer), new IntValue(0) );
// [TODO] define the semantics and test @layers
}