diff --git a/impls/livescript/env.ls b/impls/livescript/env.ls index 594048012d..ec7f3caf24 100644 --- a/impls/livescript/env.ls +++ b/impls/livescript/env.ls @@ -6,16 +6,7 @@ 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.find symbol diff --git a/impls/livescript/step2_eval.ls b/impls/livescript/step2_eval.ls index aa4124da97..af898262b1 100644 --- a/impls/livescript/step2_eval.ls +++ b/impls/livescript/step2_eval.ls @@ -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] diff --git a/impls/livescript/step3_env.ls b/impls/livescript/step3_env.ls index b6100f2d1b..629214b7fb 100644 --- a/impls/livescript/step3_env.ls +++ b/impls/livescript/step3_env.ls @@ -27,19 +27,28 @@ list-to-pairs = (list) -> [0 to (list.length - 2) by 2] \ |> map (idx) -> [list[idx], list[idx+1]] +is-thruthy = ({type, value}) -> + type != \const or value not in [\nil \false] + + +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_simple = (env, {type, value}: ast) -> switch type | \symbol => env.get value - | \list, \vector => do + or throw new Error "'#{value}' not found" + |> return + | \list => + # Proceed after this switch + | \vector => do type: type value: value |> map eval_ast env | otherwise => ast + |> return - -eval_ast = (env, {type, value}: ast) --> - if type != \list then eval_simple env, ast - else if value.length == 0 then ast + if value.length == 0 then ast else if value[0].type == \symbol params = value[1 to] switch value[0].value diff --git a/impls/livescript/step4_if_fn_do.ls b/impls/livescript/step4_if_fn_do.ls index 61b74fd00a..733b5ea343 100644 --- a/impls/livescript/step4_if_fn_do.ls +++ b/impls/livescript/step4_if_fn_do.ls @@ -15,17 +15,24 @@ fmap-ast = (fn, {type, value}: ast) --> {type: type, value: fn value} -eval_simple = (env, {type, value}: 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}" + switch type | \symbol => env.get value - | \list, \vector => ast |> fmap-ast map eval_ast env + or throw new Error "'#{value}' not found" + |> return + | \list => + # Proceed after this switch + | \vector => return (ast |> fmap-ast map eval_ast env) | \map => ast |> fmap-ast Obj.map eval_ast env + |> return | otherwise => ast + |> return - -eval_ast = (env, {type, value}: ast) --> - if type != \list then eval_simple env, ast - else if value.length == 0 then ast + if value.length == 0 then ast else if value[0].type == \symbol params = value[1 to] switch value[0].value diff --git a/impls/livescript/step5_tco.ls b/impls/livescript/step5_tco.ls index 10c845c60e..defd498973 100644 --- a/impls/livescript/step5_tco.ls +++ b/impls/livescript/step5_tco.ls @@ -21,20 +21,27 @@ fmap-ast = (fn, {type, value}: ast) --> {type: type, value: fn value} -eval_simple = (env, {type, value}: 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}" + switch type | \symbol => env.get value - | \list, \vector => ast |> fmap-ast map eval_ast env + or throw new Error "'#{value}' not found" + |> return + | \list => + # Proceed after this switch + | \vector => return (ast |> fmap-ast map eval_ast env) | \map => ast |> fmap-ast Obj.map eval_ast env + |> return | otherwise => ast + |> return - -eval_ast = (env, {type, value}: ast) --> - loop - if type != \list - return eval_simple env, ast - else if value.length == 0 + if value.length == 0 return ast + else result = if value[0].type == \symbol params = value[1 to] diff --git a/impls/livescript/step6_file.ls b/impls/livescript/step6_file.ls index fac53c29d9..2787940e1d 100644 --- a/impls/livescript/step6_file.ls +++ b/impls/livescript/step6_file.ls @@ -22,20 +22,27 @@ fmap-ast = (fn, {type, value}: ast) --> {type: type, value: fn value} -eval_simple = (env, {type, value}: 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}" + switch type | \symbol => env.get value - | \list, \vector => ast |> fmap-ast map eval_ast env + or throw new Error "'#{value}' not found" + |> return + | \list => + # Proceed after this switch + | \vector => return (ast |> fmap-ast map eval_ast env) | \map => ast |> fmap-ast Obj.map eval_ast env + |> return | otherwise => ast + |> return - -eval_ast = (env, {type, value}: ast) --> - loop - if type != \list - return eval_simple env, ast - else if value.length == 0 + if value.length == 0 return ast + else result = if value[0].type == \symbol params = value[1 to] diff --git a/impls/livescript/step7_quote.ls b/impls/livescript/step7_quote.ls index 9b83bb47c0..c8e8f4ecf6 100644 --- a/impls/livescript/step7_quote.ls +++ b/impls/livescript/step7_quote.ls @@ -28,20 +28,27 @@ 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) -> +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}" + switch type | \symbol => env.get value - | \list, \vector => ast |> fmap-ast map eval_ast env + or throw new Error "'#{value}' not found" + |> return + | \list => + # Proceed after this switch + | \vector => return (ast |> fmap-ast map eval_ast env) | \map => ast |> fmap-ast Obj.map eval_ast env + |> return | otherwise => ast + |> return - -eval_ast = (env, {type, value}: ast) --> - loop - if type != \list - return eval_simple env, ast - else if value.length == 0 + if value.length == 0 return ast + else result = if value[0].type == \symbol params = value[1 to] @@ -52,7 +59,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 diff --git a/impls/livescript/step8_macros.ls b/impls/livescript/step8_macros.ls index 337e678cd5..5534388241 100644 --- a/impls/livescript/step8_macros.ls +++ b/impls/livescript/step8_macros.ls @@ -28,24 +28,27 @@ 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) -> +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}" + switch type | \symbol => env.get value - | \list, \vector => ast |> fmap-ast map eval_ast env + or throw new Error "'#{value}' not found" + |> return + | \list => + # Proceed after this switch + | \vector => return (ast |> fmap-ast map eval_ast env) | \map => ast |> fmap-ast Obj.map eval_ast env + |> return | otherwise => ast + |> return - -eval_ast = (env, ast) --> - loop - if ast.type != \list - return eval_simple env, ast - - ast = macroexpand env, ast - if ast.type != \list - return eval_simple env, ast - else if ast.value.length == 0 + if value.length == 0 return ast + else result = if ast.value[0].type == \symbol params = ast.value[1 to] @@ -56,10 +59,8 @@ 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 @@ -198,10 +199,15 @@ eval_fn = (env, params) -> eval_apply = (env, list) -> - [fn, ...args] = list |> map eval_ast env + [first, ...raw_args] = list + fn = eval_ast env first 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 = map eval_ast env, raw_args fn.value.apply env, args @@ -280,30 +286,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 diff --git a/impls/livescript/step9_try.ls b/impls/livescript/step9_try.ls index 7945a17884..8a7d313d2c 100644 --- a/impls/livescript/step9_try.ls +++ b/impls/livescript/step9_try.ls @@ -28,24 +28,27 @@ 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) -> +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}" + switch type | \symbol => env.get value - | \list, \vector => ast |> fmap-ast map eval_ast env + or throw new Error "'#{value}' not found" + |> return + | \list => + # Proceed after this switch + | \vector => return (ast |> fmap-ast map eval_ast env) | \map => ast |> fmap-ast Obj.map eval_ast env + |> return | otherwise => ast + |> return - -eval_ast = (env, ast) --> - loop - if ast.type != \list - return eval_simple env, ast - - ast = macroexpand env, ast - if ast.type != \list - return eval_simple env, ast - else if ast.value.length == 0 + if value.length == 0 return ast + else result = if ast.value[0].type == \symbol params = ast.value[1 to] @@ -56,10 +59,8 @@ 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 | 'try*' => eval_try env, params | otherwise => eval_apply env, ast.value else @@ -199,10 +200,15 @@ eval_fn = (env, params) -> eval_apply = (env, list) -> - [fn, ...args] = list |> map eval_ast env + [first, ...raw_args] = list + fn = eval_ast env first 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 = map eval_ast env, raw_args fn.value.apply env, args @@ -281,30 +287,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] - - eval_try = (env, params) -> if params.length > 2 runtime-error "'try*' expected 1 or 2 parameters, diff --git a/impls/livescript/stepA_mal.ls b/impls/livescript/stepA_mal.ls index 7ee72908f0..2810ee54f9 100644 --- a/impls/livescript/stepA_mal.ls +++ b/impls/livescript/stepA_mal.ls @@ -28,24 +28,27 @@ 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) -> +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}" + switch type | \symbol => env.get value - | \list, \vector => ast |> fmap-ast map eval_ast env + or throw new Error "'#{value}' not found" + |> return + | \list => + # Proceed after this switch + | \vector => return (ast |> fmap-ast map eval_ast env) | \map => ast |> fmap-ast Obj.map eval_ast env + |> return | otherwise => ast + |> return - -eval_ast = (env, ast) --> - loop - if ast.type != \list - return eval_simple env, ast - - ast = macroexpand env, ast - if ast.type != \list - return eval_simple env, ast - else if ast.value.length == 0 + if value.length == 0 return ast + else result = if ast.value[0].type == \symbol params = ast.value[1 to] @@ -56,10 +59,8 @@ 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 | 'try*' => eval_try env, params | otherwise => eval_apply env, ast.value else @@ -199,10 +200,15 @@ eval_fn = (env, params) -> eval_apply = (env, list) -> - [fn, ...args] = list |> map eval_ast env + [first, ...raw_args] = list + fn = eval_ast env first 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 = map eval_ast env, raw_args fn.value.apply env, args @@ -281,30 +287,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] - - eval_try = (env, params) -> if params.length > 2 runtime-error "'try*' expected 1 or 2 parameters,