Skip to content

Commit

Permalink
fantom: merge eval_ast and macroexpand into EVAL, implement DEBUG-EVAL
Browse files Browse the repository at this point in the history
Various trivial changes minimizing the diff between steps.
Hence, this commit should be reviewed with git -b.

Change the type of the key argument in env.{get,set} from MAL symbol
to fantom Str.
Return null instead of raising an exception.
Both changes help DEBUG-EVAL.

For readability, merge env.find into env.get.
  • Loading branch information
asarhaddon authored and kanaka committed Oct 29, 2024
1 parent ac666a1 commit c14f871
Show file tree
Hide file tree
Showing 10 changed files with 578 additions and 600 deletions.
10 changes: 2 additions & 8 deletions impls/fantom/src/mallib/fan/env.fan
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,8 @@ class MalEnv
return value
}

MalEnv? find(MalSymbol key)
MalVal? get(Str key)
{
return data.containsKey(key.value) ? this : outer?.find(key)
}

MalVal get(MalSymbol key)
{
foundEnv := find(key) ?: throw Err("'$key.value' not found")
return (MalVal)foundEnv.data[key.value]
return data.containsKey(key) ? data[key] : outer?.get(key)
}
}
58 changes: 27 additions & 31 deletions impls/fantom/src/step2_eval/fan/main.fan
Original file line number Diff line number Diff line change
Expand Up @@ -7,51 +7,47 @@ class Main
return Reader.read_str(s)
}

static MalVal eval_ast(MalVal ast, Str:MalFunc env)
static MalVal EVAL(MalVal ast, Str:MalVal env)
{
switch (ast.typeof)
{
case MalSymbol#:
varName := (ast as MalSymbol).value
varVal := env[varName] ?: throw Err("'$varName' not found")
return (MalVal)varVal
case MalList#:
newElements := (ast as MalList).value.map |MalVal v -> MalVal| { EVAL(v, env) }
return MalList(newElements)
case MalVector#:
newElements := (ast as MalVector).value.map |MalVal v -> MalVal| { EVAL(v, env) }
return MalVector(newElements)
case MalHashMap#:
newElements := (ast as MalHashMap).value.map |MalVal v -> MalVal| { EVAL(v, env) }
return MalHashMap.fromMap(newElements)
default:
return ast
}
}
switch (ast.typeof)
{
case MalSymbol#:
varName := (ast as MalSymbol).value
return env[varName] ?: throw Err("'$varName' not found")
case MalVector#:
newElements := (ast as MalVector).value.map |MalVal v -> MalVal| { EVAL(v, env) }
return MalVector(newElements)
case MalHashMap#:
newElements := (ast as MalHashMap).value.map |MalVal v -> MalVal| { EVAL(v, env) }
return MalHashMap.fromMap(newElements)
case MalList#:
astList := ast as MalList
if (astList.isEmpty) return ast

static MalVal EVAL(MalVal ast, Str:MalFunc env)
{
if (!(ast is MalList)) return eval_ast(ast, env)
astList := ast as MalList
if (astList.isEmpty) return ast
evaled_ast := eval_ast(ast, env) as MalList
f := evaled_ast[0] as MalFunc
return f.call(evaled_ast[1..-1])
f := EVAL(astList[0], env)
args := astList.value[1..-1].map |MalVal v -> MalVal| { EVAL(v, env) }

malfunc := f as MalFunc
return malfunc.call(args)

default:
return ast
}
}

static Str PRINT(MalVal exp)
{
return exp.toString(true)
}

static Str REP(Str s, Str:MalFunc env)
static Str REP(Str s, Str:MalVal env)
{
return PRINT(EVAL(READ(s), env))
}

static Void main()
{
env := [
repl_env := [
"+": MalFunc { MalInteger((it[0] as MalInteger).value + (it[1] as MalInteger).value) },
"-": MalFunc { MalInteger((it[0] as MalInteger).value - (it[1] as MalInteger).value) },
"*": MalFunc { MalInteger((it[0] as MalInteger).value * (it[1] as MalInteger).value) },
Expand All @@ -62,7 +58,7 @@ class Main
if (line == null) break
if (line.isSpace) continue
try
echo(REP(line, env))
echo(REP(line, repl_env))
catch (Err e)
echo("Error: $e.msg")
}
Expand Down
76 changes: 41 additions & 35 deletions impls/fantom/src/step3_env/fan/main.fan
Original file line number Diff line number Diff line change
Expand Up @@ -7,46 +7,52 @@ class Main
return Reader.read_str(s)
}

static MalVal eval_ast(MalVal ast, MalEnv env)
static Void debug_eval(MalVal ast, MalEnv env)
{
switch (ast.typeof)
{
case MalSymbol#:
return env.get(ast)
case MalList#:
newElements := (ast as MalList).value.map |MalVal v -> MalVal| { EVAL(v, env) }
return MalList(newElements)
case MalVector#:
newElements := (ast as MalVector).value.map |MalVal v -> MalVal| { EVAL(v, env) }
return MalVector(newElements)
case MalHashMap#:
newElements := (ast as MalHashMap).value.map |MalVal v -> MalVal| { EVAL(v, env) }
return MalHashMap.fromMap(newElements)
default:
return ast
}
value := env.get("DEBUG-EVAL")
if ((value != null) && !(value is MalFalseyVal))
echo("EVAL: ${PRINT(ast)}")
}

static MalVal EVAL(MalVal ast, MalEnv env)
{
if (!(ast is MalList)) return eval_ast(ast, env)
astList := ast as MalList
if (astList.isEmpty) return ast
switch ((astList[0] as MalSymbol).value)
{
case "def!":
return env.set(astList[1], EVAL(astList[2], env))
case "let*":
let_env := MalEnv(env)
varList := (astList[1] as MalSeq)
for (i := 0; i < varList.count; i += 2)
let_env.set(varList[i], EVAL(varList[i + 1], let_env))
return EVAL(astList[2], let_env)
default:
evaled_ast := eval_ast(ast, env) as MalList
f := evaled_ast[0] as MalFunc
return f.call(evaled_ast[1..-1])
}
debug_eval(ast, env)
switch (ast.typeof)
{
case MalSymbol#:
varName := (ast as MalSymbol).value
return env.get(varName) ?: throw Err("'$varName' not found")
case MalVector#:
newElements := (ast as MalVector).value.map |MalVal v -> MalVal| { EVAL(v, env) }
return MalVector(newElements)
case MalHashMap#:
newElements := (ast as MalHashMap).value.map |MalVal v -> MalVal| { EVAL(v, env) }
return MalHashMap.fromMap(newElements)
case MalList#:
astList := ast as MalList
if (astList.isEmpty) return ast
switch ((astList[0] as MalSymbol)?.value)
{
case "def!":
value := EVAL(astList[2], env)
return env.set(astList[1], value)
case "let*":
let_env := MalEnv(env)
varList := astList[1] as MalSeq
for (i := 0; i < varList.count; i += 2)
let_env.set(varList[i], EVAL(varList[i + 1], let_env))
return EVAL(astList[2], let_env)
default:
f := EVAL(astList[0], env)
args := astList.value[1..-1].map |MalVal v -> MalVal| { EVAL(v, env) }

malfunc := f as MalFunc
return malfunc.call(args)

}
default:
return ast
}
}

static Str PRINT(MalVal exp)
Expand Down
101 changes: 56 additions & 45 deletions impls/fantom/src/step4_if_fn_do/fan/main.fan
Original file line number Diff line number Diff line change
Expand Up @@ -7,56 +7,67 @@ class Main
return Reader.read_str(s)
}

static MalVal eval_ast(MalVal ast, MalEnv env)
static Void debug_eval(MalVal ast, MalEnv env)
{
switch (ast.typeof)
{
case MalSymbol#:
return env.get(ast)
case MalList#:
newElements := (ast as MalList).value.map |MalVal v -> MalVal| { EVAL(v, env) }
return MalList(newElements)
case MalVector#:
newElements := (ast as MalVector).value.map |MalVal v -> MalVal| { EVAL(v, env) }
return MalVector(newElements)
case MalHashMap#:
newElements := (ast as MalHashMap).value.map |MalVal v -> MalVal| { EVAL(v, env) }
return MalHashMap.fromMap(newElements)
default:
return ast
}
value := env.get("DEBUG-EVAL")
if ((value != null) && !(value is MalFalseyVal))
echo("EVAL: ${PRINT(ast)}")
}

static MalVal EVAL(MalVal ast, MalEnv env)
{
if (!(ast is MalList)) return eval_ast(ast, env)
astList := ast as MalList
if (astList.isEmpty) return ast
switch ((astList[0] as MalSymbol)?.value)
{
case "def!":
return env.set(astList[1], EVAL(astList[2], env))
case "let*":
let_env := MalEnv(env)
varList := astList[1] as MalSeq
for (i := 0; i < varList.count; i += 2)
let_env.set(varList[i], EVAL(varList[i + 1], let_env))
return EVAL(astList[2], let_env)
case "do":
eval_ast(MalList(astList[1..-2]), env)
return EVAL(astList[-1], env)
case "if":
if (EVAL(astList[1], env) is MalFalseyVal)
return astList.count > 3 ? EVAL(astList[3], env) : MalNil.INSTANCE
else
return EVAL(astList[2], env)
case "fn*":
return MalFunc { EVAL(astList[2], MalEnv(env, (astList[1] as MalSeq), MalList(it))) }
default:
evaled_ast := eval_ast(ast, env) as MalList
f := evaled_ast[0] as MalFunc
return f.call(evaled_ast[1..-1])
}
debug_eval(ast, env)
switch (ast.typeof)
{
case MalSymbol#:
varName := (ast as MalSymbol).value
return env.get(varName) ?: throw Err("'$varName' not found")
case MalVector#:
newElements := (ast as MalVector).value.map |MalVal v -> MalVal| { EVAL(v, env) }
return MalVector(newElements)
case MalHashMap#:
newElements := (ast as MalHashMap).value.map |MalVal v -> MalVal| { EVAL(v, env) }
return MalHashMap.fromMap(newElements)
case MalList#:
astList := ast as MalList
if (astList.isEmpty) return ast
switch ((astList[0] as MalSymbol)?.value)
{
case "def!":
value := EVAL(astList[2], env)
return env.set(astList[1], value)
case "let*":
let_env := MalEnv(env)
varList := astList[1] as MalSeq
for (i := 0; i < varList.count; i += 2)
let_env.set(varList[i], EVAL(varList[i + 1], let_env))
return EVAL(astList[2], let_env)
case "do":
for (i:=1; i<astList.count-1; i+=1)
EVAL(astList[i], env);
return EVAL(astList[-1], env)
case "if":
if (EVAL(astList[1], env) is MalFalseyVal)
return astList.count > 3 ? EVAL(astList[3], env) : MalNil.INSTANCE
else
return EVAL(astList[2], env)
case "fn*":
return MalFunc { EVAL(astList[2], MalEnv(env, (astList[1] as MalSeq), MalList(it))) }
default:
f := EVAL(astList[0], env)
args := astList.value[1..-1].map |MalVal v -> MalVal| { EVAL(v, env) }
switch (f.typeof)
{
case MalFunc#:
malfunc := f as MalFunc
return malfunc.call(args)
default:
throw Err("Unknown type")
}
}
default:
return ast
}
}

static Str PRINT(MalVal exp)
Expand Down
Loading

0 comments on commit c14f871

Please sign in to comment.