A fork of Crisp for HARP
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

115 lines
2.9 KiB

  1. #! /usr/bin/env crystal run
  2. require "./readline"
  3. require "./reader"
  4. require "./printer"
  5. require "./types"
  6. require "./env"
  7. # Note:
  8. # Employed downcase names because Crystal prohibits uppercase names for methods
  9. def eval_error(msg)
  10. raise Mal::EvalException.new msg
  11. end
  12. def num_func(func)
  13. -> (args : Array(Mal::Type)) {
  14. x, y = args[0].unwrap, args[1].unwrap
  15. eval_error "invalid arguments" unless x.is_a?(Int32) && y.is_a?(Int32)
  16. Mal::Type.new func.call(x, y)
  17. }
  18. end
  19. $repl_env = Mal::Env.new nil
  20. $repl_env.set("+", Mal::Type.new num_func(->(x : Int32, y : Int32){ x + y }))
  21. $repl_env.set("-", Mal::Type.new num_func(->(x : Int32, y : Int32){ x - y }))
  22. $repl_env.set("*", Mal::Type.new num_func(->(x : Int32, y : Int32){ x * y }))
  23. $repl_env.set("/", Mal::Type.new num_func(->(x : Int32, y : Int32){ x / y }))
  24. def eval_ast(a, env)
  25. return a.map{|n| eval(n, env) } if a.is_a? Array
  26. Mal::Type.new case ast = a.unwrap
  27. when Mal::Symbol
  28. if e = env.get(ast.str)
  29. e
  30. else
  31. eval_error "'#{ast.str}' not found"
  32. end
  33. when Mal::List
  34. ast.each_with_object(Mal::List.new){|n, l| l << eval(n, env)}
  35. when Mal::Vector
  36. ast.each_with_object(Mal::Vector.new){|n, l| l << eval(n, env)}
  37. when Mal::HashMap
  38. new_map = Mal::HashMap.new
  39. ast.each{|k, v| new_map[k] = eval(v, env)}
  40. new_map
  41. else
  42. ast
  43. end
  44. end
  45. def read(str)
  46. read_str str
  47. end
  48. def eval(t, env)
  49. ast = t.unwrap
  50. return eval_ast(t, env) unless ast.is_a?(Mal::List)
  51. eval_error "empty list" if ast.empty?
  52. sym = ast.first.unwrap
  53. eval_error "first element of list must be a symbol" unless sym.is_a?(Mal::Symbol)
  54. Mal::Type.new case sym.str
  55. when "def!"
  56. eval_error "wrong number of argument for 'def!'" unless ast.size == 3
  57. a1 = ast[1].unwrap
  58. eval_error "1st argument of 'def!' must be symbol" unless a1.is_a?(Mal::Symbol)
  59. env.set(a1.str, eval(ast[2], env) as Mal::Type)
  60. when "let*"
  61. eval_error "wrong number of argument for 'def!'" unless ast.size == 3
  62. bindings = ast[1].unwrap
  63. eval_error "1st argument of 'let*' must be list or vector" unless bindings.is_a?(Array)
  64. eval_error "size of binding list must be even" unless bindings.size.even?
  65. new_env = Mal::Env.new env
  66. bindings.each_slice(2) do |binding|
  67. name, value = binding[0].unwrap, binding[1]
  68. eval_error "name of binding must be specified as symbol" unless name.is_a?(Mal::Symbol)
  69. new_env.set(name.str, eval(value, new_env))
  70. end
  71. eval(ast[2], new_env)
  72. else
  73. f = eval_ast(ast.first, env)
  74. ast.shift(1)
  75. args = eval_ast(ast, env)
  76. if f.is_a?(Mal::Type) && (f2 = f.unwrap).is_a?(Mal::Func)
  77. f2.call(args as Array(Mal::Type))
  78. else
  79. eval_error "expected function symbol as the first symbol of list"
  80. end
  81. end
  82. end
  83. def print(result)
  84. pr_str(result, true)
  85. end
  86. def rep(str)
  87. print(eval(read(str), $repl_env))
  88. end
  89. while line = my_readline("user> ")
  90. begin
  91. puts rep(line)
  92. rescue e
  93. STDERR.puts e
  94. end
  95. end