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
11 /// Raised when something went wrong in runtime
12
13 class RuntimeException : Exception
14 {
15 mixin ExceptionWithPosition;
16 }
17
18 /// Runtime values of Polemy
19
20 abstract class Value
21 {
22 }
23
24 ///
25 class IntValue : Value
26 {
27 BigInt data;
28
29 mixin SimpleClass;
30 override string toString() const { return std.bigint.toDecimalString(cast(BigInt)data); }
31 }
32
33 ///
34 class StrValue : Value
35 {
36 string data;
37
38 mixin SimpleClass;
39 override string toString() const { return data; }
40 }
41
42 ///
43 class FunValue : Value
44 {
45 Value delegate(immutable LexPosition pos, string lay, Value[]) data;
46
47 mixin SimpleConstructor;
48 alias data call;
49 override string toString() const { return sprintf!"(function:%s:%s)"(data.ptr,data.funcptr); }
50 }
51
52 ///
53 class UndValue : Value
54 {
55 mixin SimpleClass;
56 override string toString() const { return "<undefined>"; }
57 }
58
59 /// Named Constructor for FunValue
60
61 FunValue nativef(Value delegate(immutable LexPosition pos, Layer lay, Value[] args) dg)
62 {
63 return new FunValue(dg);
64 }
65
66 /// Named Constructor for FunValue
67
68 FunValue native(R,T...)(R delegate (T) dg)
69 {
70 return nativef( delegate Value(immutable LexPosition pos, Layer lay, Value[] args) {
71 if( lay != "@v" )
72 throw genex!RuntimeException(pos, "only @v layer can call native function");
73 if( T.length != args.length )
74 throw genex!RuntimeException(pos, "argument number mismatch!");
75 T typed_args;
76 foreach(i, Ti; T)
77 {
78 typed_args[i] = cast(Ti) args[i];
79 if( typed_args[i] is null )
80 throw genex!RuntimeException(pos, sprintf!"type mismatch on the argument %d"(i+1));
81 }
82 try {
83 return dg(typed_args);
84 } catch( RuntimeException e ) {
85 throw e.pos is null ? new RuntimeException(pos, e.msg, e.file, e.line) : e;
86 }
87 });
88 }
89
90 /// Layer ID
91
92 alias string Layer;
93
94 /// Context (variable environment)
95 /// Simlar to prototype chain of ECMAScript etc.
96 /// But extended with the notion of "Layer"
97
98 class Table : Value
99 {
100 enum Kind {PropagateSet, NotPropagateSet};
101
102 this( Table proto=null, Kind k = Kind.PropagateSet )
103 { this.prototype = proto; this.kind = k; }
104
105 void set(string i, Layer lay, Value v, in LexPosition pos=null)
106 {
107 if( setIfExist(i, lay, v) )
108 return;
109 data[i][lay] = v;
110 }
111
112 bool has(string i, Layer lay, in LexPosition pos=null)
113 {
114 if( i in data ) {
115 if( lay !in data[i] )
116 return false;
117 return true;
118 }
119 if( prototype is null )
120 return false;
121 return prototype.has(i, lay, pos);
122 }
123
124 Value get(string i, Layer lay, in LexPosition pos=null)
125 {
126 if( i in data ) {
127 // [TODO] consider forwarding to proto also in this case
128 if( lay !in data[i] )
129 throw genex!RuntimeException(pos, sprintf!"variable %s is not set in layer %s"(i,lay));
130 return data[i][lay];
131 }
132 if( prototype is null )
133 throw new RuntimeException(pos, sprintf!"variable %s not found"(i));
134 return prototype.get(i, lay, pos);
135 }
136
137 private:
138 Table prototype;
139 Kind kind;
140 Value[Layer][string] data;
141
142 bool setIfExist(string i, Layer lay, Value v)
143 {
144 if( i in data )
145 {
146 data[i][lay] = v;
147 return true;
148 }
149 if( kind==Kind.PropagateSet && prototype !is null )
150 return prototype.setIfExist(i, lay, v);
151 return false;
152 }
153 }
154
155 unittest
156 {
157 Table c0 = new Table;
158 Table c01 = new Table(c0, Table.Kind.NotPropagateSet);
159 Table c012 = new Table(c01, Table.Kind.PropagateSet);
160 Table c013 = new Table(c01, Table.Kind.PropagateSet);
161
162 assert_nothrow( c012.set("x", "@v", new IntValue(BigInt(12))) );
163 assert_throw!RuntimeException( c013.get("x", "@v") );
164 assert_nothrow( c013.set("x", "@v", new IntValue(BigInt(13))) );
165 assert_eq( c013.get("x", "@v"), new IntValue(BigInt(13)) );
166 assert_eq( c012.get("x", "@v"), new IntValue(BigInt(12)) );
167 assert_throw!RuntimeException( c01.get("x", "@v") );
168
169 assert_nothrow( c01.set("y", "@v", new IntValue(BigInt(1))) );
170 assert_eq( c013.get("y", "@v"), new IntValue(BigInt(1)) );
171 assert_eq( c012.get("y", "@v"), new IntValue(BigInt(1)) );
172 assert_eq( c01.get("y", "@v"), new IntValue(BigInt(1)) );
173
174 assert_nothrow( c0.set("z", "@v", new IntValue(BigInt(0))) );
175 assert_eq( c013.get("z", "@v"), new IntValue(BigInt(0)) );
176 assert_eq( c012.get("z", "@v"), new IntValue(BigInt(0)) );
177 assert_eq( c01.get("z", "@v"), new IntValue(BigInt(0)) );
178 assert_eq( c0.get("z", "@v"), new IntValue(BigInt(0)) );
179
180 assert_nothrow( c012.set("y", "@v", new IntValue(BigInt(444))) );
181 assert_eq( c013.get("y", "@v"), new IntValue(BigInt(444)) );
182 assert_eq( c012.get("y", "@v"), new IntValue(BigInt(444)) );
183 assert_eq( c01.get("y", "@v"), new IntValue(BigInt(444)) );
184
185 assert_nothrow( c012.set("z", "@v", new IntValue(BigInt(555))) );
186 assert_eq( c013.get("z", "@v"), new IntValue(BigInt(0)) );
187 assert_eq( c012.get("z", "@v"), new IntValue(BigInt(555)) );
188 assert_eq( c01.get("z", "@v"), new IntValue(BigInt(0)) );
189 assert_eq( c0.get("z", "@v"), new IntValue(BigInt(0)) );
190
191 // [TODO] define the semantics and test @layers
192 }