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 +}