Skip to content

Commit

Permalink
livescript: merge eval and eval_ast, fix some regressions
Browse files Browse the repository at this point in the history
 * map evaluation in step3
 * do in step 5
  • Loading branch information
asarhaddon committed Oct 12, 2024
1 parent 5c87c69 commit e45b45d
Show file tree
Hide file tree
Showing 10 changed files with 147 additions and 196 deletions.
14 changes: 2 additions & 12 deletions impls/livescript/env.ls
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,6 @@ export class Env
set: (symbol, ast) ->
@data[symbol] = ast

find: (symbol) ->
if symbol of @data then @
else if @outer? then @outer.find symbol

get: (symbol) ->
result = @try-get symbol
if not result
then throw new Error "'#{symbol}' not found"
else result

try-get: (symbol) ->
env = @find symbol
if env then env.data[symbol]
if symbol of @data then @data[symbol]
else if @outer? then @outer.get symbol
3 changes: 3 additions & 0 deletions impls/livescript/step2_eval.ls
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ repl_env = do
value: (a, b) -> {type: \int, value: parseInt(a.value / b.value)}

eval_ast = (repl_env, {type, value}: ast) -->

# console.log "EVAL: #{pr_str ast}"

switch type
| \symbol =>
result = repl_env[value]
Expand Down
28 changes: 17 additions & 11 deletions impls/livescript/step3_env.ls
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
readline = require './node_readline'
{id, map, each} = require 'prelude-ls'
{id, map, Obj, each} = require 'prelude-ls'
{read_str} = require './reader'
{pr_str} = require './printer'
{Env} = require './env'
Expand Down Expand Up @@ -27,19 +27,25 @@ list-to-pairs = (list) ->
[0 to (list.length - 2) by 2] \
|> map (idx) -> [list[idx], list[idx+1]]


eval_simple = (env, {type, value}: ast) ->
switch type
| \symbol => env.get value
| \list, \vector => do
type: type
value: value |> map eval_ast env
| otherwise => ast
is-thruthy = ({type, value}) ->
type != \const or value not in [\nil \false]


eval_ast = (env, {type, value}: ast) -->
if type != \list then eval_simple env, ast
else if value.length == 0 then ast

dbgeval = env.get "DEBUG-EVAL"
if dbgeval and is-thruthy dbgeval then console.log "EVAL: #{pr_str ast}"

switch type
| \symbol => return (env.get value
or throw new Error "'#{value}' not found")
| \list =>
# Proceed after this switch
| \vector => return {type: \vector, value: value |> map eval_ast env}
| \map => return {type: \map, value: value |> Obj.map eval_ast env}
| otherwise => return ast

if value.length == 0 then ast
else if value[0].type == \symbol
params = value[1 to]
switch value[0].value
Expand Down
22 changes: 13 additions & 9 deletions impls/livescript/step4_if_fn_do.ls
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,21 @@ fmap-ast = (fn, {type, value}: ast) -->
{type: type, value: fn value}


eval_simple = (env, {type, value}: ast) ->
switch type
| \symbol => env.get value
| \list, \vector => ast |> fmap-ast map eval_ast env
| \map => ast |> fmap-ast Obj.map eval_ast env
| otherwise => ast
eval_ast = (env, {type, value}: ast) -->

dbgeval = env.get "DEBUG-EVAL"
if dbgeval and is-thruthy dbgeval then console.log "EVAL: #{pr_str ast}"

eval_ast = (env, {type, value}: ast) -->
if type != \list then eval_simple env, ast
else if value.length == 0 then ast
switch type
| \symbol => return (env.get value
or throw new Error "'#{value}' not found")
| \list =>
# Proceed after this switch
| \vector => return (ast |> fmap-ast map eval_ast env)
| \map => return (ast |> fmap-ast Obj.map eval_ast env)
| otherwise => return ast

if value.length == 0 then ast
else if value[0].type == \symbol
params = value[1 to]
switch value[0].value
Expand Down
28 changes: 16 additions & 12 deletions impls/livescript/step5_tco.ls
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,24 @@ fmap-ast = (fn, {type, value}: ast) -->
{type: type, value: fn value}


eval_simple = (env, {type, value}: ast) ->
switch type
| \symbol => env.get value
| \list, \vector => ast |> fmap-ast map eval_ast env
| \map => ast |> fmap-ast Obj.map eval_ast env
| otherwise => ast
eval_ast = (env, {type, value}: ast) -->
loop

dbgeval = env.get "DEBUG-EVAL"
if dbgeval and is-thruthy dbgeval then console.log "EVAL: #{pr_str ast}"

eval_ast = (env, {type, value}: ast) -->
loop
if type != \list
return eval_simple env, ast
else if value.length == 0
switch type
| \symbol => return (env.get value
or throw new Error "'#{value}' not found")
| \list =>
# Proceed after this switch
| \vector => return (ast |> fmap-ast map eval_ast env)
| \map => return (ast |> fmap-ast Obj.map eval_ast env)
| otherwise => return ast

if value.length == 0
return ast
else

result = if value[0].type == \symbol
params = value[1 to]
Expand Down Expand Up @@ -112,7 +116,7 @@ eval_do = (env, params) ->

[...rest, last-param] = params
rest |> each eval_ast env
tco env, last-param
defer-tco env, last-param


eval_if = (env, params) ->
Expand Down
26 changes: 15 additions & 11 deletions impls/livescript/step6_file.ls
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,24 @@ fmap-ast = (fn, {type, value}: ast) -->
{type: type, value: fn value}


eval_simple = (env, {type, value}: ast) ->
switch type
| \symbol => env.get value
| \list, \vector => ast |> fmap-ast map eval_ast env
| \map => ast |> fmap-ast Obj.map eval_ast env
| otherwise => ast
eval_ast = (env, {type, value}: ast) -->
loop

dbgeval = env.get "DEBUG-EVAL"
if dbgeval and is-thruthy dbgeval then console.log "EVAL: #{pr_str ast}"

eval_ast = (env, {type, value}: ast) -->
loop
if type != \list
return eval_simple env, ast
else if value.length == 0
switch type
| \symbol => return (env.get value
or throw new Error "'#{value}' not found")
| \list =>
# Proceed after this switch
| \vector => return (ast |> fmap-ast map eval_ast env)
| \map => return (ast |> fmap-ast Obj.map eval_ast env)
| otherwise => return ast

if value.length == 0
return ast
else

result = if value[0].type == \symbol
params = value[1 to]
Expand Down
27 changes: 15 additions & 12 deletions impls/livescript/step7_quote.ls
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,24 @@ make-call = (name, params) -> make-list [make-symbol name] ++ params
is-symbol = (ast, name) -> ast.type == \symbol and ast.value == name


eval_simple = (env, {type, value}: ast) ->
switch type
| \symbol => env.get value
| \list, \vector => ast |> fmap-ast map eval_ast env
| \map => ast |> fmap-ast Obj.map eval_ast env
| otherwise => ast
eval_ast = (env, {type, value}: ast) -->
loop

dbgeval = env.get "DEBUG-EVAL"
if dbgeval and is-thruthy dbgeval then console.log "EVAL: #{pr_str ast}"

eval_ast = (env, {type, value}: ast) -->
loop
if type != \list
return eval_simple env, ast
else if value.length == 0
switch type
| \symbol => return (env.get value
or throw new Error "'#{value}' not found")
| \list =>
# Proceed after this switch
| \vector => return (ast |> fmap-ast map eval_ast env)
| \map => return (ast |> fmap-ast Obj.map eval_ast env)
| otherwise => return ast

if value.length == 0
return ast
else

result = if value[0].type == \symbol
params = value[1 to]
Expand All @@ -52,7 +56,6 @@ eval_ast = (env, {type, value}: ast) -->
| 'if' => eval_if env, params
| 'fn*' => eval_fn env, params
| 'quote' => eval_quote env, params
| 'quasiquoteexpand' => eval_quasiquoteexpand params
| 'quasiquote' => eval_quasiquote env, params
| otherwise => eval_apply env, value
else
Expand Down
65 changes: 22 additions & 43 deletions impls/livescript/step8_macros.ls
Original file line number Diff line number Diff line change
Expand Up @@ -28,24 +28,24 @@ make-call = (name, params) -> make-list [make-symbol name] ++ params
is-symbol = (ast, name) -> ast.type == \symbol and ast.value == name


eval_simple = (env, {type, value}: ast) ->
switch type
| \symbol => env.get value
| \list, \vector => ast |> fmap-ast map eval_ast env
| \map => ast |> fmap-ast Obj.map eval_ast env
| otherwise => ast

eval_ast = (env, {type, value}: ast) -->
loop

eval_ast = (env, ast) -->
loop
if ast.type != \list
return eval_simple env, ast
dbgeval = env.get "DEBUG-EVAL"
if dbgeval and is-thruthy dbgeval then console.log "EVAL: #{pr_str ast}"

ast = macroexpand env, ast
if ast.type != \list
return eval_simple env, ast
else if ast.value.length == 0
switch type
| \symbol => return (env.get value
or throw new Error "'#{value}' not found")
| \list =>
# Proceed after this switch
| \vector => return (ast |> fmap-ast map eval_ast env)
| \map => return (ast |> fmap-ast Obj.map eval_ast env)
| otherwise => return ast

if value.length == 0
return ast
else

result = if ast.value[0].type == \symbol
params = ast.value[1 to]
Expand All @@ -56,16 +56,15 @@ eval_ast = (env, ast) -->
| 'if' => eval_if env, params
| 'fn*' => eval_fn env, params
| 'quote' => eval_quote env, params
| 'quasiquoteexpand' => eval_quasiquoteexpand params
| 'quasiquote' => eval_quasiquote env, params
| 'defmacro!' => eval_defmacro env, params
| 'macroexpand' => eval_macroexpand env, params
| otherwise => eval_apply env, ast.value
else
eval_apply env, ast.value

if result.type == \tco
{env, ast} = result
env = result.env
{type, value}: ast = result.ast
else
return result

Expand Down Expand Up @@ -198,10 +197,14 @@ eval_fn = (env, params) ->


eval_apply = (env, list) ->
[fn, ...args] = list |> map eval_ast env
[first, ...raw_args] = list
fn = first |> eval_ast env
if fn.type != \function
runtime-error "#{fn.value} is not a function, got a #{fn.type}"

if fn.is_macro
return (defer-tco env, (unpack-tco (fn.value.apply env, raw_args)))
args = raw_args |> map eval_ast env
fn.value.apply env, args


Expand Down Expand Up @@ -280,30 +283,6 @@ eval_defmacro = (env, params) ->
env.set name.value, macro_fn


get-macro-fn = (env, ast) ->
if ast.type == \list and
ast.value.length != 0 and
ast.value[0].type == \symbol
fn = env.try-get ast.value[0].value
if fn and fn.type == \function and fn.is_macro
then fn


macroexpand = (env, ast) ->
loop # until ast is not a macro function call.
macro_fn = get-macro-fn env, ast
if not macro_fn then return ast
ast = unpack-tco <| macro_fn.value.apply env, ast.value[1 to]


eval_macroexpand = (env, params) ->
if params.length != 1
runtime-error "'macroexpand' expected 1 parameter,
got #{params.length}"

macroexpand env, params[0]


repl_env = new Env
for symbol, value of ns
repl_env.set symbol, value
Expand Down
Loading

0 comments on commit e45b45d

Please sign in to comment.