Index: .poseidon
==================================================================
--- .poseidon
+++ .poseidon
@@ -34,10 +34,11 @@
polemy\_common.d
polemy\ast.d
polemy\lex.d
polemy\parse.d
polemy\tricks.d
+ polemy\value.d
Index: main.d
==================================================================
--- main.d
+++ main.d
@@ -7,14 +7,12 @@
import std.stdio;
version(unittest) static ~this()
{
- writeln( "***********************" );
- writeln( "* All Tests Passed <3 *" );
- writeln( "*********************** (Press Enter to finish)" );
+ writeln( "(Press Enter to finish)" );
readln();
}
void main( string[] args )
{
}
Index: polemy/ast.d
==================================================================
--- polemy/ast.d
+++ polemy/ast.d
@@ -36,10 +36,11 @@
}
class LetExpression : AST
{
string var;
+ string layer;
AST init;
AST expr;
mixin SimpleClass;
}
@@ -67,10 +68,10 @@
template genEast(T)
{ T genEast(P...)(P ps) { return new T(LexPosition.dummy, ps); } }
alias genEast!StrLiteral strl;
alias genEast!IntLiteral intl;
- auto fun(string[] xs, AST ps) { return genEast!FunLiteral(xs,ps); }
+ auto fun(string[] xs, AST ps) { return genEast!FunLiteral(xs,ps); } // to help type inference of D
alias genEast!VarExpression var;
alias genEast!LetExpression let;
alias genEast!FuncallExpression call;
}
Index: polemy/lex.d
==================================================================
--- polemy/lex.d
+++ polemy/lex.d
@@ -155,11 +155,11 @@
public static {
bool isSpace (dchar c) { return std.ctype.isspace(c)!=0; }
bool isSymbol (dchar c) { return 0x21<=c && c<=0x7f && !std.ctype.isalnum(c) && c!='_' && c!='\''; }
bool isSSymbol (dchar c) { return !find("()[]{};", c).empty; }
- bool isMSymbol (dchar c) { return isSymbol(c) && !isSSymbol(c); }
+ bool isMSymbol (dchar c) { return isSymbol(c) && !isSSymbol(c) && c!='"' && c!='#'; }
bool isLetter (dchar c) { return !isSpace(c) && !isSymbol(c); }
}
string readQuoted(const LexPosition pos){char[] buf; return readQuoted(pos,buf);}
string readQuoted(const LexPosition pos, ref char[] buf)
@@ -351,10 +351,18 @@
lex3.popFront;
assert( lex2.empty );
assert( !lex3.empty );
assert_eq( lex3.front.str, "5" );
}
+
+unittest
+{
+ auto lex = lexerFromString(`=""`);
+ assert_eq(lex.front.str, "="); lex.popFront;
+ assert_eq(lex.front.str, ""); lex.popFront;
+ assert( lex.empty );
+}
/// Forward range for reader character by character,
/// keeping track of position information and caring \r\n -> \n conversion.
private
Index: polemy/parse.d
==================================================================
--- polemy/parse.d
+++ polemy/parse.d
@@ -58,26 +58,30 @@
{
if( lex.empty || !lex.front.quoted && lex.front.str=="}" )
return doNothingExpression();
auto pos = lex.front.pos;
- if( tryEat("var") )
+ string kwd = lex.front.str;
+ if( tryEat("let") || tryEat("var") || tryEat("def") || tryEat("@") )
{
+ if( kwd == "@" )
+ kwd ~= eatId("after @");
immutable LexPosition varpos = (lex.empty ? null : lex.front.pos);
- string var = eatId("after var");
- eat("=", "after var");
+ string var = eatId("after "~kwd);
+ eat("=", "after "~kwd);
+ kwd = (kwd[0]=='@' ? kwd : "@val");
auto e = E(0);
if( tryEat(";") && !lex.empty && (lex.front.quoted || (lex.front.str!="}" && lex.front.str!=")")) )
- return new LetExpression(pos, var, e, Body());
+ return new LetExpression(pos, var, kwd, e, Body());
else
- return new LetExpression(pos, var, e, new VarExpression(varpos, var));
+ return new LetExpression(pos, var, kwd, e, new VarExpression(varpos, var));
}
else
{
auto e = E(0);
if( tryEat(";") && !lex.empty && (lex.front.quoted || (lex.front.str!="}" && lex.front.str!=")")) )
- return new LetExpression(pos, "_", e, Body());
+ return new LetExpression(pos, "_", "@val", e, Body());
else
return e;
}
}
@@ -185,11 +189,11 @@
cond,
new FunLiteral(thenPos, [], th),
new FunLiteral(elsePos, [], el)
);
}
- if( tryEat("fun") )
+ if( tryEat("fun") || tryEat("λ") )
{
eat("(", "after fun");
string[] params;
while( !tryEat(")") )
{
@@ -253,16 +257,19 @@
assert_eq(parseString(`123`), intl(123));
assert_eq(parseString(`"foo"`), strl("foo"));
assert_eq(parseString(`fun(){1}`), fun([],intl(1)));
assert_eq(parseString(`fun(x){1}`), fun(["x"],intl(1)));
- assert_eq(parseString(`1;2`), let("_",intl(1),intl(2)));
- assert_eq(parseString(`1;2;`), let("_",intl(1),intl(2)));
- assert_eq(parseString(`var x=1;2`), let("x",intl(1),intl(2)));
- assert_eq(parseString(`var x=1;2;`), let("x",intl(1),intl(2)));
- assert_eq(parseString(`var x=1`), let("x",intl(1),var("x")));
- assert_eq(parseString(`var x=1;`), let("x",intl(1),var("x")));
+ assert_eq(parseString(`λ(){1}`), fun([],intl(1)));
+ assert_eq(parseString(`λ(x){1}`), fun(["x"],intl(1)));
+ assert_eq(parseString(`1;2`), let("_","@val",intl(1),intl(2)));
+ assert_eq(parseString(`1;2;`), let("_","@val",intl(1),intl(2)));
+ assert_eq(parseString(`let x=1;2`), let("x","@val",intl(1),intl(2)));
+ assert_eq(parseString(`var x=1;2;`), let("x","@val",intl(1),intl(2)));
+ assert_eq(parseString(`def x=1`), let("x","@val",intl(1),var("x")));
+ assert_eq(parseString(`@val x=1;`), let("x","@val",intl(1),var("x")));
+ assert_eq(parseString(`@typ x="#int";`), let("x","@typ",strl("#int"),var("x")));
assert_eq(parseString(`f(1,2)`), call(var("f"),intl(1),intl(2)));
assert_eq(parseString(`if(1){2}`), call(var("if"),intl(1),fun([],intl(2)),fun([],intl(178))));
assert_eq(parseString(`if(1){2}else{3}`), call(var("if"),intl(1),fun([],intl(2)),fun([],intl(3))));
assert_eq(parseString(`if(1){}else{3}()()`),
call(call(call(var("if"),intl(1),fun([],intl(178)),fun([],intl(3))))));
@@ -270,22 +277,22 @@
assert_eq(parseString(`(1+2)*3`), call(var("*"),call(var("+"),intl(1),intl(2)),intl(3)));
assert_eq(parseString(`1*(2+3)`), call(var("*"),intl(1),call(var("+"),intl(2),intl(3))));
assert_eq(parseString(`1*2+3`), call(var("+"),call(var("*"),intl(1),intl(2)),intl(3)));
assert_eq(parseString(`
- var x = 100; #comment
- var y = 200; #comment!!!!!
+ let x = 100; #comment
+ let y = 200; #comment!!!!!
x+y
`),
- let("x", intl(100), let("y", intl(200), call(var("+"), var("x"), var("y"))))
+ let("x", "@val", intl(100), let("y", "@val", intl(200), call(var("+"), var("x"), var("y"))))
);
assert_eq(parseString(`
var fac = fun(x){ if(x <= 1) {1} else {x*fac(x-1)} };
fac(10)
`),
- let("fac", fun(["x"],
+ let("fac", "@val", fun(["x"],
call(var("if"),
call(var("<="), var("x"), intl(1)),
fun([], intl(1)),
fun([], call(var("*"), var("x"), call(var("fac"),call(var("-"),var("x"),intl(1)))))
)),
@@ -296,11 +303,11 @@
unittest
{
assert_throw!ParseException(parseString(`1+`));
assert_throw!ParseException(parseString(`1+2}`));
- assert_throw!ParseException(parseString(`var "x"`));
+ assert_throw!ParseException(parseString(`let "x"`));
assert_throw!ParseException(parseString(`var`));
- assert_throw!ParseException(parseString(`var x ==`));
+ assert_throw!ParseException(parseString(`@val x ==`));
assert_throw!ParseException(parseString(`if(){1}`));
assert_throw!ParseException(parseString(`f(`));
}
Index: polemy/value.d
==================================================================
--- polemy/value.d
+++ polemy/value.d
@@ -4,79 +4,137 @@
*
* Runtime data structures for Polemy programming language.
*/
module polemy.value;
import polemy._common;
-import polemy.lex : LexPosition;
-import std.stdio;
+import polemy.lex;
-class PolemyRuntimeException : Exception
+/// Raised when something went wrong in runtime
+
+class RuntimeException : Exception
{
- this(string msg) { super(msg); }
+ const LexPosition pos;
+
+ this( const LexPosition pos, string msg )
+ { super(sprintf!"%s [%s]"(msg, pos)); this.pos = pos; }
+ this( string msg ) { super(msg); this.pos = null; }
}
+
+/// Runtime values of Polemy
abstract class Value
{
}
-class UndefinedValue : Value
-{
- mixin SimpleConstructor;
- mixin SimpleCompare;
- override string toString() const { return "(undefined)"; }
-}
-
class IntValue : Value
{
BigInt data;
+
mixin SimpleConstructor;
mixin SimpleCompare;
- override string toString() const {
- return std.bigint.toDecimalString(cast(BigInt)data);
- }
+ override string toString() const { return std.bigint.toDecimalString(cast(BigInt)data); }
}
class StrValue : Value
{
string data;
+
mixin SimpleConstructor;
mixin SimpleCompare;
override string toString() const { return data; }
}
class FunValue : Value
{
Value delegate(immutable LexPosition pos, Value[]) data;
+
mixin SimpleConstructor;
- Value call(immutable LexPosition pos, Value[] args) { return data(pos,args); }
+ alias data call;
override string toString() const { return sprintf!"(function:%s:%s)"(data.ptr,data.funcptr); }
}
-import std.stdio;
-class Context
+
+/// Layer ID
+
+alias string Layer;
+
+/// Context (variable environment)
+/// Simlar to prototype chain of ECMAScript etc.
+/// But extended with the notion of "Layer"
+
+class Table : Value
{
- Context parent;
- Value[string] table;
- this(Context parent = null) { this.parent = parent; }
+ enum Kind {PropagateSet, NotPropagateSet};
+
+ this( Table proto=null, Kind k = Kind.PropagateSet )
+ { this.prototype = proto; this.kind = k; }
- void add(string i, Value v)
+ void set(string i, Layer lay, Value v)
{
- table[i] = v;
+ if( !setIfExist(i, lay, v) )
+ data[i][lay] = v;
}
-
- Value opIndex(string i)
+
+ Value get(string i, Layer lay)
{
- if( i in table )
- return table[i];
- if( parent is null )
- throw new PolemyRuntimeException(sprintf!"variable %s not found"(i));
- return parent[i];
+ if( i in data )
+ return data[i][lay];
+ if( prototype is null )
+ throw new RuntimeException(sprintf!"variable %s not found"(i));
+ return prototype.get(i, lay);
}
- void opIndexAssign(Value v, string i)
+private:
+ Table prototype;
+ Kind kind;
+ Value[Layer][string] data;
+
+ bool setIfExist(string i, Layer lay, Value v)
{
- if( i in table )
- return table[i] = v;
- if( parent is null )
- throw new PolemyRuntimeException(sprintf!"variable %s not found"(i));
- return parent[i] = 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", "@val", new IntValue(BigInt(12))) );
+ assert_throw!RuntimeException( c013.get("x", "@val") );
+ assert_nothrow( c013.set("x", "@val", new IntValue(BigInt(13))) );
+ assert_eq( c013.get("x", "@val"), new IntValue(BigInt(13)) );
+ assert_eq( c012.get("x", "@val"), new IntValue(BigInt(12)) );
+ assert_throw!RuntimeException( c01.get("x", "@val") );
+
+ assert_nothrow( c01.set("y", "@val", new IntValue(BigInt(1))) );
+ assert_eq( c013.get("y", "@val"), new IntValue(BigInt(1)) );
+ assert_eq( c012.get("y", "@val"), new IntValue(BigInt(1)) );
+ assert_eq( c01.get("y", "@val"), new IntValue(BigInt(1)) );
+
+ assert_nothrow( c0.set("z", "@val", new IntValue(BigInt(0))) );
+ assert_eq( c013.get("z", "@val"), new IntValue(BigInt(0)) );
+ assert_eq( c012.get("z", "@val"), new IntValue(BigInt(0)) );
+ assert_eq( c01.get("z", "@val"), new IntValue(BigInt(0)) );
+ assert_eq( c0.get("z", "@val"), new IntValue(BigInt(0)) );
+
+ assert_nothrow( c012.set("y", "@val", new IntValue(BigInt(444))) );
+ assert_eq( c013.get("y", "@val"), new IntValue(BigInt(444)) );
+ assert_eq( c012.get("y", "@val"), new IntValue(BigInt(444)) );
+ assert_eq( c01.get("y", "@val"), new IntValue(BigInt(444)) );
+
+ assert_nothrow( c012.set("z", "@val", new IntValue(BigInt(555))) );
+ assert_eq( c013.get("z", "@val"), new IntValue(BigInt(0)) );
+ assert_eq( c012.get("z", "@val"), new IntValue(BigInt(555)) );
+ assert_eq( c01.get("z", "@val"), new IntValue(BigInt(0)) );
+ assert_eq( c0.get("z", "@val"), new IntValue(BigInt(0)) );
+
+ // [TODO] define the semantics and test @layers
+}