Index: .poseidon
==================================================================
--- .poseidon
+++ .poseidon
@@ -33,10 +33,11 @@
main.d
polemy\_common.d
polemy\ast.d
polemy\eval.d
polemy\failure.d
+ polemy\layer.d
polemy\lex.d
polemy\parse.d
polemy\value.d
tricks\test.d
tricks\tricks.d
Index: doc/candydoc/modules.ddoc
==================================================================
--- doc/candydoc/modules.ddoc
+++ doc/candydoc/modules.ddoc
@@ -2,10 +2,11 @@
$(MODULE main)
$(MODULE tricks.tricks)
$(MODULE tricks.test)
$(MODULE polemy._common)
$(MODULE polemy.failure)
+ $(MODULE polemy.layer)
$(MODULE polemy.lex)
$(MODULE polemy.parse)
$(MODULE polemy.ast)
$(MODULE polemy.eval)
$(MODULE polemy.value)
Index: main.d
==================================================================
--- main.d
+++ main.d
@@ -11,10 +11,11 @@
import polemy.value;
import polemy.failure;
import polemy.parse;
import polemy.ast;
import polemy.eval;
+import polemy.layer;
enum VersionNoMajor = 0;
enum VersionNoMinor = 1;
enum VersionNoRev = 0;
@@ -35,11 +36,11 @@
}
/// Run one file on the global scope
void runFile(string filename)
{
- eval(parseFile(filename), ctx, false, "@v");
+ eval(parseFile(filename), ctx, false, ValueLayer);
}
/// Repeat the singleInteraction
void replLoop()
{
@@ -75,11 +76,11 @@
{ buf = ""; lineno = nextlineno; }
buf ~= s;
nextlineno ++;
try
- { lastVal = eval(parseString(buf, "", lineno), ctx, false, "@v"); }
+ { lastVal = eval(parseString(buf, "", lineno), ctx, false, ValueLayer); }
catch( UnexpectedEOF )
{ return false; } // wait
buf = "";
lineno = nextlineno;
return true;
Index: polemy/eval.d
==================================================================
--- polemy/eval.d
+++ polemy/eval.d
@@ -8,56 +8,57 @@
import polemy._common;
import polemy.failure;
import polemy.ast;
import polemy.parse;
import polemy.value;
+import polemy.layer;
import std.typecons;
import std.stdio;
///
Table createGlobalContext()
{
auto ctx = new Table;
- ctx.set("+", "@v", native( (IntValue lhs, IntValue rhs){return new IntValue(lhs.data + rhs.data);} ));
- ctx.set("-", "@v", native( (IntValue lhs, IntValue rhs){return new IntValue(lhs.data - rhs.data);} ));
- ctx.set("*", "@v", native( (IntValue lhs, IntValue rhs){return new IntValue(lhs.data * rhs.data);} ));
- ctx.set("/", "@v", native( (IntValue lhs, IntValue rhs){return new IntValue(lhs.data / rhs.data);} ));
- ctx.set("%", "@v", native( (IntValue lhs, IntValue rhs){return new IntValue(lhs.data % rhs.data);} ));
- ctx.set("||", "@v", native( (IntValue lhs, IntValue rhs){return new IntValue(BigInt((lhs.data!=0) || (rhs.data!=0) ? 1:0));} ));
- ctx.set("&&", "@v", native( (IntValue lhs, IntValue rhs){return new IntValue(BigInt((lhs.data!=0) && (rhs.data!=0) ? 1:0));} ));
- ctx.set("<", "@v", native( (Value lhs, Value rhs){return new IntValue(BigInt(lhs < rhs ? 1: 0));} ));
- ctx.set(">", "@v", native( (Value lhs, Value rhs){return new IntValue(BigInt(lhs > rhs ? 1: 0));} ));
- ctx.set("<=", "@v", native( (Value lhs, Value rhs){return new IntValue(BigInt(lhs <= rhs ? 1: 0));} ));
- ctx.set(">=", "@v", native( (Value lhs, Value rhs){return new IntValue(BigInt(lhs >= rhs ? 1: 0));} ));
- ctx.set("==", "@v", native( (Value lhs, Value rhs){return new IntValue(BigInt(lhs == rhs ? 1: 0));} ));
- ctx.set("!=", "@v", native( (Value lhs, Value rhs){return new IntValue(BigInt(lhs != rhs ? 1: 0));} ));
- ctx.set("print", "@v", native( (Value a){
+ ctx.set("+", ValueLayer, native( (IntValue lhs, IntValue rhs){return new IntValue(lhs.data + rhs.data);} ));
+ ctx.set("-", ValueLayer, native( (IntValue lhs, IntValue rhs){return new IntValue(lhs.data - rhs.data);} ));
+ ctx.set("*", ValueLayer, native( (IntValue lhs, IntValue rhs){return new IntValue(lhs.data * rhs.data);} ));
+ ctx.set("/", ValueLayer, native( (IntValue lhs, IntValue rhs){return new IntValue(lhs.data / rhs.data);} ));
+ ctx.set("%", ValueLayer, native( (IntValue lhs, IntValue rhs){return new IntValue(lhs.data % rhs.data);} ));
+ ctx.set("||", ValueLayer, native( (IntValue lhs, IntValue rhs){return new IntValue(BigInt((lhs.data!=0) || (rhs.data!=0) ? 1:0));} ));
+ ctx.set("&&", ValueLayer, native( (IntValue lhs, IntValue rhs){return new IntValue(BigInt((lhs.data!=0) && (rhs.data!=0) ? 1:0));} ));
+ ctx.set("<", ValueLayer, native( (Value lhs, Value rhs){return new IntValue(BigInt(lhs < rhs ? 1: 0));} ));
+ ctx.set(">", ValueLayer, native( (Value lhs, Value rhs){return new IntValue(BigInt(lhs > rhs ? 1: 0));} ));
+ ctx.set("<=", ValueLayer, native( (Value lhs, Value rhs){return new IntValue(BigInt(lhs <= rhs ? 1: 0));} ));
+ ctx.set(">=", ValueLayer, native( (Value lhs, Value rhs){return new IntValue(BigInt(lhs >= rhs ? 1: 0));} ));
+ ctx.set("==", ValueLayer, native( (Value lhs, Value rhs){return new IntValue(BigInt(lhs == rhs ? 1: 0));} ));
+ ctx.set("!=", ValueLayer, native( (Value lhs, Value rhs){return new IntValue(BigInt(lhs != rhs ? 1: 0));} ));
+ ctx.set("print", ValueLayer, native( (Value a){
writeln(a);
return new IntValue(BigInt(178));
}));
- ctx.set("if", "@v", native( (IntValue x, FunValue ft, FunValue fe){
+ ctx.set("if", ValueLayer, native( (IntValue x, FunValue ft, FunValue fe){
auto toRun = (x.data==0 ? fe : ft);
- return toRun.invoke(null, "@v", toRun.definitionContext());
+ return toRun.invoke(null, ValueLayer, toRun.definitionContext());
// return toRun.invoke(pos, lay, toRun.definitionContext());
}));
- ctx.set("_isint", "@v", native( (Value v){return new IntValue(BigInt(cast(IntValue)v is null ? 0 : 1));} ));
- ctx.set("_isstr", "@v", native( (Value v){return new IntValue(BigInt(cast(StrValue)v is null ? 0 : 1));} ));
- ctx.set("_isfun", "@v", native( (Value v){return new IntValue(BigInt(cast(FunValue)v is null ? 0 : 1));} ));
- ctx.set("_isundefined", "@v", native( (Value v){return new IntValue(BigInt(cast(UndValue)v is null ? 0 : 1));} ));
- ctx.set("_istable", "@v", native( (Value v){return new IntValue(BigInt(cast(Table)v is null ? 0 : 1));} ));
- ctx.set(".", "@v", native( (Table t, StrValue s){
- return (t.has(s.data, "@v") ? t.get(s.data, "@v") : new UndValue);
+ ctx.set("_isint", ValueLayer, native( (Value v){return new IntValue(BigInt(cast(IntValue)v is null ? 0 : 1));} ));
+ ctx.set("_isstr", ValueLayer, native( (Value v){return new IntValue(BigInt(cast(StrValue)v is null ? 0 : 1));} ));
+ ctx.set("_isfun", ValueLayer, native( (Value v){return new IntValue(BigInt(cast(FunValue)v is null ? 0 : 1));} ));
+ ctx.set("_isundefined", ValueLayer, native( (Value v){return new IntValue(BigInt(cast(UndValue)v is null ? 0 : 1));} ));
+ ctx.set("_istable", ValueLayer, native( (Value v){return new IntValue(BigInt(cast(Table)v is null ? 0 : 1));} ));
+ ctx.set(".", ValueLayer, native( (Table t, StrValue s){
+ return (t.has(s.data, ValueLayer) ? t.get(s.data, ValueLayer) : new UndValue);
+ }) );
+ ctx.set(".?", ValueLayer, native( (Table t, StrValue s){
+ return new IntValue(BigInt(t.has(s.data, ValueLayer) ? 1 : 0));
}) );
- ctx.set(".?", "@v", native( (Table t, StrValue s){
- return new IntValue(BigInt(t.has(s.data, "@v") ? 1 : 0));
- }) );
- ctx.set(".=", "@v", native( (Table t, StrValue s, Value v){
+ ctx.set(".=", ValueLayer, native( (Table t, StrValue s, Value v){
auto t2 = new Table(t, Table.Kind.NotPropagateSet);
- t2.set(s.data, "@v", v);
+ t2.set(s.data, ValueLayer, v);
return t2;
}) );
- ctx.set("{}", "@v", native( (){
+ ctx.set("{}", ValueLayer, native( (){
return new Table;
}) );
return ctx;
}
@@ -78,27 +79,27 @@
/// Entry point of this module
Tuple!(Value,"val",Table,"ctx") eval(AST e)
{
Table ctx = createGlobalContext();
- return typeof(return)(eval(e, ctx, false, "@v"), ctx);
+ return typeof(return)(eval(e, ctx, false, ValueLayer), ctx);
}
Value invokeFunction(in LexPosition pos, Value _f, AST[] args, Table callerCtx, Layer lay, bool AlwaysMacro=false)
{
if(auto f = cast(FunValue)_f)
{
Table ctx = new Table(f.definitionContext(), Table.Kind.NotPropagateSet);
foreach(i,p; f.params())
if( p.layers.empty )
- if(lay=="@macro")
+ if(lay==MacroLayer)
ctx.set(p.name, lay, macroEval(args[i], callerCtx, AlwaysMacro));
else
ctx.set(p.name, lay, eval(args[i], callerCtx, true, lay));
else
foreach(argLay; p.layers)
- if(argLay=="@macro")
+ if(argLay==MacroLayer)
ctx.set(p.name, argLay, macroEval(args[i], callerCtx, AlwaysMacro));
else
ctx.set(p.name, argLay, eval(args[i], callerCtx, true, argLay));
return f.invoke(pos, lay, ctx);
}
@@ -105,73 +106,73 @@
throw genex!RuntimeException(pos, "tried to call non-function");
}
Value lift(in LexPosition pos, Value v, Layer lay, Table callerCtx)
{
- // similar to invoke Function, but with only one argument bound to @v
- Value _f = callerCtx.get(lay, "(system)", pos);
+ // similar to invoke Function, but with only one argument bound to ValueLayer
+ Value _f = callerCtx.get(lay, SystemLayer, pos);
if(auto f = cast(FunValue)_f)
{
Table ctx = new Table(f.definitionContext(), Table.Kind.NotPropagateSet);
auto ps = f.params();
if( ps.length != 1 )
- throw genex!RuntimeException(pos, "lift function must take exactly one argument at @v layer");
- if( ps[0].layers.length==0 || ps[0].layers.length==1 && ps[0].layers[0]=="@v" )
+ throw genex!RuntimeException(pos, "lift function must take exactly one argument at "~ValueLayer~" layer");
+ if( ps[0].layers.length==0 || ps[0].layers.length==1 && ps[0].layers[0]==ValueLayer )
{
- ctx.set(ps[0].name, "@v", v);
- return f.invoke(pos, "@v", ctx);
+ ctx.set(ps[0].name, ValueLayer, v);
+ return f.invoke(pos, ValueLayer, ctx);
}
else
- throw genex!RuntimeException(pos, "lift function must take exactly one argument at @v layer");
+ throw genex!RuntimeException(pos, "lift function must take exactly one argument at "~ValueLayer~" layer");
}
throw genex!RuntimeException(pos, "tried to call non-function");
}
/// Entry point of this module
/// If splitCtx = true, then inner variable declaration do not overwrite ctx.
-/// lay is the layer ID for evaluation (standard value semantics uses "@v").
+/// lay is the layer ID for evaluation (standard value semantics uses ValueLayer).
Value eval(AST e, Table ctx, bool splitCtx, Layer lay)
{
return e.match(
(StrLiteral e)
{
Value v = new StrValue(e.data);
- if( lay == "@v" )
+ if( lay == ValueLayer )
return v;
else
return lift(e.pos,v,lay,ctx);
},
(IntLiteral e)
{
Value v = new IntValue(e.data);
- if( lay == "@v" )
+ if( lay == ValueLayer )
return v;
else // rise
return lift(e.pos,v,lay,ctx);
},
(VarExpression e)
{
- if( lay == "@v" )
+ if( lay == ValueLayer )
return ctx.get(e.var, lay, e.pos);
try {
return ctx.get(e.var, lay, e.pos);
} catch( Throwable ) { // [TODO] more precise...
- return lift(e.pos, ctx.get(e.var, "@v", e.pos), lay, ctx);
+ return lift(e.pos, ctx.get(e.var, ValueLayer, e.pos), lay, ctx);
}
},
(LayeredExpression e)
{
- if( e.lay == "@macro" )
+ if( e.lay == MacroLayer )
return macroEval(e.expr, ctx, false);
else
return eval(e.expr, ctx, true, e.lay);
},
(LetExpression e)
{
// for letrec, we need this, but should avoid overwriting????
- // ctx.set(e.var, "@v", new UndefinedValue, e.pos);
+ // ctx.set(e.var, ValueLayer, new UndefinedValue, e.pos);
if(splitCtx)
ctx = new Table(ctx, Table.Kind.NotPropagateSet);
Value v = eval(e.init, ctx, true, lay);
ctx.set(e.var, (e.layer.length ? e.layer : lay), v, e.pos);
return eval(e.expr, ctx, false, lay);
@@ -197,11 +198,11 @@
}
// [TODO] Optimization
Value macroEval(AST e, Table ctx, bool AlwaysMacro)
{
- Layer theLayer = "@v";
+ Layer theLayer = ValueLayer;
Table pos = new Table;
pos.set("filename", theLayer, new StrValue(e.pos.filename));
pos.set("lineno", theLayer, new IntValue(BigInt(e.pos.lineno)));
pos.set("column", theLayer, new IntValue(BigInt(e.pos.column)));
@@ -223,11 +224,11 @@
return t;
},
(VarExpression e)
{
try {
- return ctx.get(e.var, "@macro", e.pos);
+ return ctx.get(e.var, MacroLayer, e.pos);
} catch( Throwable ) {// [TODO] more precies...
Table t = new Table;
t.set("pos", theLayer, pos);
t.set("is", theLayer, new StrValue("var"));
t.set("name", theLayer, new StrValue(e.var));
@@ -245,11 +246,11 @@
t.set("expr", theLayer, macroEval(e.expr,ctx,AlwaysMacro));
return cast(Value)t;
}
else
{
- if( e.lay == "@macro" )
+ if( e.lay == MacroLayer )
return macroEval(e.expr, ctx, false);
else
return eval(e.expr, ctx, true, e.lay);
}
},
@@ -266,11 +267,11 @@
(FuncallExpression e)
{
Value _f = macroEval(e.fun,ctx,AlwaysMacro);
if( auto f = cast(FunValue)_f )
- return invokeFunction(e.pos, f, e.args, ctx, "@macro", AlwaysMacro);
+ return invokeFunction(e.pos, f, e.args, ctx, MacroLayer, AlwaysMacro);
Table t = new Table;
t.set("pos", theLayer, pos);
t.set("is", theLayer, new StrValue("app"));
t.set("fun", theLayer, _f);
@@ -319,21 +320,21 @@
unittest
{
auto r = assert_nothrow( evalString(`var x = 21; x + x*x;`) );
assert_eq( r.val, new IntValue(BigInt(21+21*21)) );
- assert_eq( r.ctx.get("x","@v"), new IntValue(BigInt(21)) );
- assert_nothrow( r.ctx.get("x","@v") );
- assert_throw!RuntimeException( r.ctx.get("y","@v") );
+ assert_eq( r.ctx.get("x",ValueLayer), new IntValue(BigInt(21)) );
+ assert_nothrow( r.ctx.get("x",ValueLayer) );
+ assert_throw!RuntimeException( r.ctx.get("y",ValueLayer) );
}
unittest
{
auto r = assert_nothrow( evalString(`var x = 21; var x = x + x*x;`) );
assert_eq( r.val, new IntValue(BigInt(21+21*21)) );
- assert_eq( r.ctx.get("x","@v"), new IntValue(BigInt(21+21*21)) );
- assert_nothrow( r.ctx.get("x","@v") );
- assert_throw!RuntimeException( r.ctx.get("y","@v") );
+ assert_eq( r.ctx.get("x",ValueLayer), new IntValue(BigInt(21+21*21)) );
+ assert_nothrow( r.ctx.get("x",ValueLayer) );
+ assert_throw!RuntimeException( r.ctx.get("y",ValueLayer) );
}
unittest
{
assert_eq( evalString(`let x=1; let y=(let x=2); x`).val, new IntValue(BigInt(1)) );
assert_eq( evalString(`let x=1; let y=(let x=2;fun(){x}); y()`).val, new IntValue(BigInt(2)) );
@@ -366,12 +367,12 @@
unittest
{
assert_throw!Throwable( evalString(`@@s(x){x}; @s "+"=fun(x,y){x-y};@s(1+2)`) );
assert_eq( evalString(`@@s(x){x}; @s "+"=fun(x,y){x-y};1+2`).val, new IntValue(BigInt(3)) );
- assert_eq( evalString(`@@s(x){x}; @s "+"=fun(x,y){@v(@s(x)-@s(y))};1+2`).val, new IntValue(BigInt(3)) );
- assert_eq( evalString(`@@s(x){x}; @s "+"=fun(x,y){@v(@s(x)-@s(y))};@s(1+2)`).val, new IntValue(BigInt(-1)) );
+ assert_eq( evalString(`@@s(x){x}; @s "+"=fun(x,y){@value(@s(x)-@s(y))};1+2`).val, new IntValue(BigInt(3)) );
+ assert_eq( evalString(`@@s(x){x}; @s "+"=fun(x,y){@value(@s(x)-@s(y))};@s(1+2)`).val, new IntValue(BigInt(-1)) );
}
unittest
{
assert_eq( evalString(`@@t = fun(x){x+1}; @t(123)`).val, new IntValue(BigInt(124)) );
@@ -380,6 +381,5 @@
assert_nothrow( evalString(`def foo() {
def bar(y) { if(y<1) {0} else {bar(0)} };
bar(1)
}; foo()`) );
}
-
Index: polemy/parse.d
==================================================================
--- polemy/parse.d
+++ polemy/parse.d
@@ -7,10 +7,11 @@
module polemy.parse;
import polemy._common;
import polemy.failure;
import polemy.lex;
import polemy.ast;
+import polemy.layer;
/// Parse a string and return its AST
/// Throws: ParseException, LexException, UnexpectedEOF
AST parseString(S, T...)(S str, T fn_ln_cn)
@@ -100,13 +101,13 @@
auto e = tryEat("(")
? parseLambdaAfterOpenParen(pos) // let var ( ...
: (eat("=", "after "~kwd), E(0)); // let var = ...
if( moreDeclarationExists() )
- return new LetExpression(pos, var, "(system)", e, Body());
+ return new LetExpression(pos, var, SystemLayer, e, Body());
else
- return new LetExpression(pos, var, "(system)", e, new VarExpression(pos, var));
+ return new LetExpression(pos, var, SystemLayer, e, new VarExpression(pos, var));
}
else
{
string kwd = layer;
if( layer.empty && !tryEat(kwd="let") && !tryEat(kwd="var") && !tryEat(kwd="def") )
@@ -473,14 +474,14 @@
fun(["x"], call(var("+"), var("x"), intl(1))),
var("foo"))
);
assert_eq(parseString(`@@type ( x ) { x }`),
- let("@type", "(system)", fun(["x"], var("x")), var("@type")) );
+ let("@type", SystemLayer, fun(["x"], var("x")), var("@type")) );
assert_eq(parseString(`{}`), call(var("{}")));
assert_eq(parseString(`{foo:1,"bar":2}`),
call(var(".="), call(var(".="), call(var("{}")), strl("foo"), intl(1)), strl("bar"), intl(2)));
assert_eq(parseString(`{}.foo`), call(var("."),call(var("{}")),strl("foo")));
assert_eq(parseString(`{}.?foo`), call(var(".?"),call(var("{}")),strl("foo")));
assert_eq(parseString(`x{y:1}`), call(var(".="),var("x"),strl("y"),intl(1)));
}
Index: polemy/value.d
==================================================================
--- polemy/value.d
+++ polemy/value.d
@@ -6,10 +6,11 @@
*/
module polemy.value;
import polemy._common;
import polemy.failure;
import polemy.ast;
+import polemy.layer;
/// Runtime values of Polemy
abstract class Value
{
@@ -61,33 +62,33 @@
override Value invoke(in LexPosition pos, Layer lay, Table ctx)
{
// TODO: only auto raised ones need memo? no?
// auto memoization
/*
- if( lay != "@v" && lay != "@macro" )
+ if( lay != ValueLayer && lay != MacroLayer )
{
if( auto memolay = lay in memo )
if( auto pv = args in *memolay )
return *pv;
memo[lay][args] = lift(e.pos,new UndValue,lay,ctx);
}
*/
// @macro run!!!
- if( lay == "@macro" )
+ if( lay == MacroLayer )
return macroEval(ast.funbody, ctx, false);
/*TODO memo*/ AST macroMemo;
if( macroMemo is null ) {
// .prototype!, forced macro cannot access parameters
ctx.kill = true; scope(exit)ctx.kill=false;
- macroMemo = tableToAST("@v",macroEval(ast.funbody, ctx, true));
+ macroMemo = tableToAST(ValueLayer,macroEval(ast.funbody, ctx, true));
}
auto v = eval(macroMemo, ctx, true, lay);
//auto v = eval(e.funbody, ctxNeo, true, lay);
// auto memoization
-// if( lay != "@v" && lay != "@macro" )
+// if( lay != ValueLayer && lay != MacroLayer )
// memo[lay][args] = v;
return v;
}
mixin SimpleClass;
@@ -112,15 +113,15 @@
foreach(i, Ti; T)
params_data ~= new Parameter(text(i), []);
}
override Value invoke(in LexPosition pos, Layer lay, Table ctx)
{
- if( lay != "@v" )
- throw genex!RuntimeException(pos, "only @v layer can call native function");
+ if( lay != ValueLayer )
+ throw genex!RuntimeException(pos, "only "~ValueLayer~" layer can call native function");
T typed_args;
foreach(i, Ti; T) {
- typed_args[i] = cast(Ti) ctx.get(text(i), "@v");
+ typed_args[i] = cast(Ti) ctx.get(text(i), ValueLayer);
if( typed_args[i] is null )
throw genex!RuntimeException(pos, sprintf!"type mismatch on the argument %d"(i+1));
}
try {
return dg(typed_args);
@@ -129,14 +130,10 @@
}
}
};
}
-/// 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
@@ -247,45 +244,45 @@
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", "@v", new IntValue(BigInt(12))) );
- assert_throw!RuntimeException( c013.get("x", "@v") );
- assert_nothrow( c013.set("x", "@v", new IntValue(BigInt(13))) );
- assert_eq( c013.get("x", "@v"), new IntValue(BigInt(13)) );
- assert_eq( c012.get("x", "@v"), new IntValue(BigInt(12)) );
- assert_throw!RuntimeException( c01.get("x", "@v") );
+ assert_nothrow( c012.set("x", ValueLayer, new IntValue(BigInt(12))) );
+ assert_throw!RuntimeException( c013.get("x", ValueLayer) );
+ assert_nothrow( c013.set("x", ValueLayer, new IntValue(BigInt(13))) );
+ assert_eq( c013.get("x", ValueLayer), new IntValue(BigInt(13)) );
+ assert_eq( c012.get("x", ValueLayer), new IntValue(BigInt(12)) );
+ assert_throw!RuntimeException( c01.get("x", ValueLayer) );
+
+ assert_nothrow( c01.set("y", ValueLayer, new IntValue(BigInt(1))) );
+ assert_eq( c013.get("y", ValueLayer), new IntValue(BigInt(1)) );
+ assert_eq( c012.get("y", ValueLayer), new IntValue(BigInt(1)) );
+ assert_eq( c01.get("y", ValueLayer), new IntValue(BigInt(1)) );
- assert_nothrow( c01.set("y", "@v", new IntValue(BigInt(1))) );
- assert_eq( c013.get("y", "@v"), new IntValue(BigInt(1)) );
- assert_eq( c012.get("y", "@v"), new IntValue(BigInt(1)) );
- assert_eq( c01.get("y", "@v"), new IntValue(BigInt(1)) );
+ assert_nothrow( c0.set("z", ValueLayer, new IntValue(BigInt(0))) );
+ assert_eq( c013.get("z", ValueLayer), new IntValue(BigInt(0)) );
+ assert_eq( c012.get("z", ValueLayer), new IntValue(BigInt(0)) );
+ assert_eq( c01.get("z", ValueLayer), new IntValue(BigInt(0)) );
+ assert_eq( c0.get("z", ValueLayer), new IntValue(BigInt(0)) );
- assert_nothrow( c0.set("z", "@v", new IntValue(BigInt(0))) );
- assert_eq( c013.get("z", "@v"), new IntValue(BigInt(0)) );
- assert_eq( c012.get("z", "@v"), new IntValue(BigInt(0)) );
- assert_eq( c01.get("z", "@v"), new IntValue(BigInt(0)) );
- assert_eq( c0.get("z", "@v"), new IntValue(BigInt(0)) );
+ assert_nothrow( c012.set("y", ValueLayer, new IntValue(BigInt(444))) );
+ assert_eq( c013.get("y", ValueLayer), new IntValue(BigInt(444)) );
+ assert_eq( c012.get("y", ValueLayer), new IntValue(BigInt(444)) );
+ assert_eq( c01.get("y", ValueLayer), new IntValue(BigInt(444)) );
- assert_nothrow( c012.set("y", "@v", new IntValue(BigInt(444))) );
- assert_eq( c013.get("y", "@v"), new IntValue(BigInt(444)) );
- assert_eq( c012.get("y", "@v"), new IntValue(BigInt(444)) );
- assert_eq( c01.get("y", "@v"), new IntValue(BigInt(444)) );
-
- assert_nothrow( c012.set("z", "@v", new IntValue(BigInt(555))) );
- assert_eq( c013.get("z", "@v"), new IntValue(BigInt(0)) );
- assert_eq( c012.get("z", "@v"), new IntValue(BigInt(555)) );
- assert_eq( c01.get("z", "@v"), new IntValue(BigInt(0)) );
- assert_eq( c0.get("z", "@v"), new IntValue(BigInt(0)) );
+ assert_nothrow( c012.set("z", ValueLayer, new IntValue(BigInt(555))) );
+ assert_eq( c013.get("z", ValueLayer), new IntValue(BigInt(0)) );
+ assert_eq( c012.get("z", ValueLayer), new IntValue(BigInt(555)) );
+ assert_eq( c01.get("z", ValueLayer), new IntValue(BigInt(0)) );
+ assert_eq( c0.get("z", ValueLayer), new IntValue(BigInt(0)) );
// [TODO] define the semantics and test @layers
}
immutable(LexPosition) extractPos( Table t )
{
- Layer theLayer = "@v";
+ Layer theLayer = ValueLayer;
if(auto tt = t.access!Table(theLayer, "pos"))
{
auto fn = tt.access!StrValue(theLayer, "filename");
auto ln = tt.access!IntValue(theLayer, "lineno");
auto cl = tt.access!IntValue(theLayer, "column");
Index: readme.txt
==================================================================
--- readme.txt
+++ readme.txt
@@ -42,93 +42,316 @@
<>
> polemy
starts REPL
- > polemy foo.pmy
- executes foo.pmy
-
- > polemy -l foo.pmy
- after executing foo.pmy, starts REPL
-
- > polemy -l foo.pmy -l bar.pmy buz.pmy
- executes foo.pmy, bar.bmy, and then buz.pmy
-
-
-
-<>
-
- Comment is "# ... \n"
-
- E ::=
- // declaration
- | ("var"|"let"|"def"|LAYER) ID "=" E (";"|"in") E
- | ("var"|"let"|"def"|LAYER) ID "(" PARAMS ")" "{" E "}" (";"|"in") E
- | ("var"|"let"|"def"|LAYER) ID "=" E
- | ("var"|"let"|"def"|LAYER) ID "(" PARAMS ")" "{" E "}"
- // literal
- | INTEGER
- | STRING
- | "{" ENTRYS "}"
- | "fun" "(" PARAMS ")" "{" E "}"
+ > polemy foo.pmy
+ executes foo.pmy
+
+ > polemy -l foo.pmy
+ after executing foo.pmy, starts REPL
+
+ > polemy -l foo.pmy -l bar.pmy buz.pmy
+ executes foo.pmy, bar.bmy, and then buz.pmy
+
+
+
+<>
+
+ Comment is "# ... \n"
+
+ E ::=
+ // declaration
+ | ("var"|"let"|"def"|LAYER) ID "=" E (";"|"in") E
+ | ("var"|"let"|"def"|LAYER) ID "(" PARAMS ")" "{" E "}" (";"|"in") E
+ | ("var"|"let"|"def"|LAYER) ID "=" E
+ | ("var"|"let"|"def"|LAYER) ID "(" PARAMS ")" "{" E "}"
+ // literal
+ | INTEGER
+ | STRING
+ | "{" ENTRYS "}" // table
+ | "fun" "(" PARAMS ")" "{" E "}" // anonymous function
// function call
- | E "(" ARGS")"
- where ARGS ::= E "," ... "," E
- PARAMS ::= ID LAYER* "," ... "," ID LAYER*
- ENTRYS ::= ID ":" E "," ... "," ID ":" E
- ID ::= 'a-zA-Z0-9_...'+
- LAYER ::= "@" ID
- // operators
- | "(" E ")"
- | E "." ID
- | E ".?" ID
- | E BINOP E
- | "if" "(" E ")" "{" E "}"
- | "if" "(" E ")" "{" E "}" "else "{" E "}"
- // layered exec
- | LAYER "(" E ")"
-
+ | E "(" ARGS")"
+ where ARGS ::= E "," ... "," E
+ PARAMS ::= ID LAYER* "," ... "," ID LAYER*
+ ENTRYS ::= ID ":" E "," ... "," ID ":" E
+ ID ::= 'a-zA-Z0-9_...'+
+ LAYER ::= "@" ID
+ // operators
+ | "(" E ")"
+ | E "." ID // table field access
+ | E ".?" ID // table field existence check
+ | E "{" ENTRYS "}" // table extend (pure functionally)
+ | E BINOP E
+ | "if" "(" E ")" "{" E "}"
+ | "if" "(" E ")" "{" E "}" "else "{" E "}"
+ // layered exec
+ | LAYER "(" E ")"
+
The following are actually rewritten to function calls:
-
- - if (E) then{E} else{E} ==> if( E, fun(){E}, fun(){E} )
- - E BINOP E ==> BINOP(E, E)
+
+ - if (E) then{E} else{E} ==> if( E, fun(){E}, fun(){E} )
+ - E BINOP E ==> BINOP(E, E)
- E.ID ==> . (E, ID)
- - E.?ID ==> .?(E, ID)
- - {} ==> {}()
- - {ID:E, ...} ==> .=({...}, ID, E)
-
-Several styles of variable declaration can be used:
-
- - fun(x){ fun(y){x} } # K-combinator
- - fun(x){ let f = fun(y){x} in f } # let-in style
- - fun(x){ var f = fun(y){x}; f } # var-; style
- - fun(x){ def f = fun(y){x} in f } # you can use any combination of (let|var|def)-(;|in)
- - fun(x){ def f(y){x} in f } # syntax sugar for function declaration
- - fun(x){ let f(y){x}; f } # this is also ok
- - fun(x){ var f(y){x} } # omitting (;|in) returns the last declared object directly
- - fun(x,y){x} #< this is not equal to the above ones. functions are no curried.
-
-NOTE: Theres no "let rec" syntax, but still recursive definition works
- def f(x) { if(x==0){1}else{x*f(x-1)} } in f(10) #=> 3628800
- yet still the code below also works
- def x=21 in def x=x+x in x #=> 42.
- The internal scoping mechanism is a little tricky (this is for coping with
- the "layer" feature explained below), but I hope that it works as everyone
- expects in most cases, as long as you don't use the same-name-variables heavily :).
-
-
-
-<>
-
- Polemy is an untyped functional programming language that has
- - integers: 0, 123, 456666666666666666666666666666666666666789, ...
- - strings: "hello, world!\n", ...
- - tables: {car: 1, cdr: {car: 2, cdr: {}}}
- - functions: fun(x){x+1}
- as primitive datatypes. Functions capture lexical closures.
- It is almost 'pure' (except the primitve function "print" and some
- trick inside scoping mechanisms).
-
-
-<>
-
- to be written
+ - E.?ID ==> .?(E, ID)
+ - {} ==> {}()
+ - { ENTRIES } ==> {}{ ENTRIES }
+ - E {ID:E, ...} ==> (.=(E, ID, E)) { ... }
+
+Several styles of variable declaration can be used:
+
+ - fun(x){ fun(y){x} } # K-combinator
+ - fun(x){ let f = fun(y){x} in f } # let-in style
+ - fun(x){ var f = fun(y){x}; f } # var-; style
+ - fun(x){ def f = fun(y){x} in f } # you can use any combination of (let|var|def)-(;|in)
+ - fun(x){ def f(y){x} in f } # syntax sugar for function declaration
+ - fun(x){ let f(y){x}; f } # this is also ok
+ - fun(x){ var f(y){x} } # omitting (;|in) returns the last declared object directly
+ - fun(x,y){x} #< this is not equal to the above ones. functions are no curried.
+
+NOTE: Theres no "let rec" syntax, but still recursive definition works
+ def f(x) { if(x==0){1}else{x*f(x-1)} } in f(10) #=> 3628800
+ yet still the code below also works
+ def x=21 in def x=x+x in x #=> 42.
+ The internal scoping mechanism is a little tricky (this is for coping with
+ the "layer" feature explained below), but I hope that it works as everyone
+ expects in most cases, as long as you don't use the same-name-variables heavily :).
+
+
+
+<>
+
+ Polemy is an untyped functional programming language that has
+ - integers: 0, 123, 456666666666666666666666666666666666666789, ...
+ - strings: "hello, world!\n", ...
+ - tables: {car: 1, cdr: {car: 2, cdr: {}}}
+ - functions: fun(x){x+1}
+ as primitive datatypes. Functions capture lexical closures.
+ It is almost 'pure' (except the primitve function "print" and some
+ trick inside scoping mechanisms).
+
+
+<>
+
+ Polemy's runtime environment has many "layer"s.
+ Usual execution run in the @value layer.
+
+ >> 1 + 2
+ 3
+ >> @value( 1 + 2 )
+ 3
+
+ Here you can see that @LayerName( Expression ) executes the inner Expression in
+ the @LayerName layer. Other than @value, one other predefined layer exists: @macro.
+
+ >> @macro( 1+2 )
+ {pos@value:{lineno@value:3, column@value:9, filename@value:},
+ is@value:app,
+ arg@value:{car@value:{pos@value:{lineno@value:3, column@value:9, filename@value:},
+ is@value:int,
+ data@value:1},
+ cdr@value:{
+ car@value:{pos@value:{lineno@value:3, column@value:11, filename@value:},
+ is@value:int,
+ data@value:2},
+ cdr@value:{}}},
+ fun@value:{pos@value:{lineno@value:3, column@value:10, filename@value:},
+ is@value:var,
+ name@value:+}}
+
+ (Sorry, this pretty printing is not available on the actual interpreter...)
+ This evaluates the expression 1+2 in the @macro layer. In this layer, the meaning of
+ the program is its abstract syntax tree.
+
+ You can interleave layers.
+ The root node of the abstract syntax tree is function "app"lication.
+
+ >> @value(@macro( 1+2 ).is)
+ app
+
+
+
+<>
+
+ To define a new layer, you should first tell how to "lift" existing values two the new layer.
+ Let us define the "@type" layer, where the meaning of programs is their static type.
+
+ >> @@type = fun(x) {
+ >> if( _isint(x) ) { "int" } else {
+ >> if( _isfun(x) ) { x } else { "unknown" } }
+ >> }
+ (Note: polemy REPL may warn some exception here but please ignore)
+
+ For simplicity, I here deal only with integers.
+ _isint is a primitive function of Polemy that checks the dynamic type of a value.
+ For function, leaving it untouched works well for almost all layers.
+
+ >> @type( 1 )
+ int
+ >> @type( 2 )
+ int
+ >> @type( "foo" )
+ unknown
+
+ Fine! Let's try to type 1+2.
+
+ >> @type( 1 + 2 )
+ ...\value.d(119): [:6:8] only @value layer can call native function
+
+ Note that the behavior of this program is
+ - run 1+2 in the @type layer
+ and NOT
+ - run 1+2 in @value and obtain 3 and run 3 in the @type.
+ The problem is, the variable "+" is defined only in the @value layer.
+ To carry out computation in the @type layer. We need to define it also
+ in the @type layer.
+
+ To define some variable in a specific layer, use @LayerName in place of
+ (let|var|def)s.
+
+ >> let x = 2
+ >> @value x = 2
+ >> @type x = "int"
+ >> @hoge x = "fuga"
+
+ For "+", do it like this.
+
+ >> @type "+" = fun(x,y) {@value(
+ >> if( @type(x)=="int" && @type(y)=="int" ) { "int" } else { "typeerror" }
+ >> )}
+ polemy.value.native!(IntValue,IntValue,IntValue).native.__anonclass24
+
+ It is just computing the return type from the input type.
+ Not here that the intended "meaning" of if-then-else is the runtime-branching,
+ and the meaning of "==" is the value-comparison. These are the @value layer
+ behavior. So we have defined the function body inside @value layer.
+ But when we refer the variables x and y, we need its @type layer meaning.
+ Hence we use @type() there.
+
+ Now we get it.
+
+ >> @type( 1 + 2 )
+ int
+
+ Well, but do we have to define the @type layer meaning for every variables???
+ No. After you defined @type "+", you'll automatically get the following:
+
+ >> def double(x) { x + x }
+ (function:17e4740:1789720)
+
+ >> @type( double(123) )
+ int
+
+ Every user-defined functions are automatically "lift"ed to the appropriate layer.
+ Only primitive functions like "+" requires @yourNewLayer annotation.
+
+
+
+<>
+
+ let|var|def is to define a variable in the "current" layer.
+ Not necessary to the @value layer.
+
+ >> @value( let x = 1 in @value(x) )
+ 1
+
+ >> @macro( let x = 1 in @value(x) )
+ polemy.failure.RuntimeException: [:14:29] variable x not found
+
+ >> @macro( let x = 1 in @macro(x) )
+ {pos@value:{lineno@value:15, ...
+
+
+
+<>
+
+ >> def foo(x @macro @value) { {fst: x, snd: @macro(x)} }
+ (function:1730360:1789720)
+
+ If you annotate function parameters by @LayerNames, when you invoke the function...
+
+ >> foo(1+2)
+ {snd@value: {pos@value:{lineno@value:17, column@value:5, filename@value:},
+ is@value:app, arg@value:{...
+ /fst@value:3
+ /}
+
+ its corresponding arguments are evaluated in the layer and passed to it.
+ If you specify multiple layers, the argument expression is run multiple times.
+ If you do not specify any layer for a parameter, it works in the neutral layer.
+
+
+
+<<@macro layer>>
+
+ When function is invoked, it first run in the @macro layer, and after that,
+ it run in the neutral layer. Here is an example.
+
+ >> @macro twice(x) { x; x }
+ >> def f() { twice(print("Hello")); 999 }
+ (function:173b6a0:1789720)
+ >> f()
+ Hello
+ Hello
+ 999
+
+ When the interpreter evaluates f(), it first executes
+ "twice(print("Hello")); 999"
+ in the @macro layer. Basically what it does is to just construct its syntax tree.
+ But, since we have defined the "twice" function in the @macro layer, it is
+ execute as a function. Resulting syntax tree is
+ "print("Hello"); print("Hello"); 999"
+ and this is executed on the neutral (in this example, @value) layer.
+ This is the reason why you see two "Hello"s.
+
+
+
+ [[limitations]]
+
+ This @macro layer is a very primitive one, and not a perfect macro language.
+ Two major limitations are seen in the following "it" example.
+
+ >> @macro LetItBe(x, y) { let it = x in y };
+
+ The variable name is not hygenic, and so without any effort, the syntax tree "y"
+ can access the outer variable "it".
+
+ >> def foo() { LetItBe( 1+2+3, it*it ) }
+ >> foo()
+ 36
+
+ Of course, this is not just a limitation; it can sometimes allow us to write
+ many interesting macros.
+
+ The other problem is that the macro expansion is only done at function startup.
+ So
+
+ >> LetItBe( 1+2+3, it*it )
+ ...\value.d(173): [:24:1] variable LetItBe is not set in layer @value
+
+ you cannot directly use the macro in the same scope as the definition.
+ You need to wrap it up in a function (like the foo() in the above example).
+
+
+
+ [[quote and unquote]]
+
+ Here is more involved example of code genration.
+ From "x", it generates "x*x*x*x*x*x*x*x*x*x".
+
+ @macro pow10(x) {
+ @value(
+ def pow(x, n) {
+ if( n == 1 ) { x }
+ else {
+ @macro( @value(x) * @value(pow(x,n-1)) )
+ }
+ }
+ in
+ pow(@macro(x),10)
+ )
+ };
+
+ Here, x is a syntax tree but n is an actual integer. If you read carefully,
+ you should get what is going on. Basically, @macro can be considered like
+ quasiquoting and @value to be an escape from it.
Index: sample/macro.pmy
==================================================================
--- sample/macro.pmy
+++ sample/macro.pmy
@@ -9,20 +9,18 @@
};
@macro maxBad(x,y) {
if(x