A fork of Crisp for HARP
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

253 lignes
6.6 KiB

  1. #! /usr/bin/env crystal run
  2. require "./readline"
  3. require "./reader"
  4. require "./printer"
  5. require "./types"
  6. require "./env"
  7. require "./core"
  8. require "./error"
  9. # Note:
  10. # Employed downcase names because Crystal prohibits uppercase names for methods
  11. def func_of(env, binds, body)
  12. -> (args : Array(Mal::Type)) {
  13. new_env = Mal::Env.new(env, binds, args)
  14. eval(body, new_env)
  15. } as Mal::Func
  16. end
  17. def eval_ast(ast, env)
  18. return ast.map{|n| eval(n, env) as Mal::Type} if ast.is_a? Mal::List
  19. val = ast.unwrap
  20. Mal::Type.new case val
  21. when Mal::Symbol
  22. if e = env.get(val.str)
  23. e
  24. else
  25. eval_error "'#{val.str}' not found"
  26. end
  27. when Mal::List
  28. val.each_with_object(Mal::List.new){|n, l| l << eval(n, env)}
  29. when Mal::Vector
  30. val.each_with_object(Mal::Vector.new){|n, l| l << eval(n, env)}
  31. when Array(Mal::Type)
  32. val.map{|n| eval(n, env)}
  33. when Mal::HashMap
  34. val.each{|k, v| val[k] = eval(v, env)}
  35. val
  36. else
  37. val
  38. end
  39. end
  40. def read(str)
  41. read_str str
  42. end
  43. macro pair?(list)
  44. {{list}}.is_a?(Array) && !{{list}}.empty?
  45. end
  46. def quasiquote(ast)
  47. list = ast.unwrap
  48. unless pair?(list)
  49. return Mal::Type.new(
  50. Mal::List.new << gen_type(Mal::Symbol, "quote") << ast
  51. )
  52. end
  53. head = list.first.unwrap
  54. case
  55. # ("unquote" ...)
  56. when head.is_a?(Mal::Symbol) && head.str == "unquote"
  57. list[1]
  58. # (("splice-unquote" ...) ...)
  59. when pair?(head) && (arg0 = head.first.unwrap).is_a?(Mal::Symbol) && arg0.str == "splice-unquote"
  60. tail = Mal::Type.new list[1..-1].each_with_object(Mal::List.new){|e,l| l << e}
  61. Mal::Type.new(
  62. Mal::List.new << gen_type(Mal::Symbol, "concat") << head[1] << quasiquote(tail)
  63. )
  64. else
  65. tail = Mal::Type.new list[1..-1].each_with_object(Mal::List.new){|e,l| l << e}
  66. Mal::Type.new(
  67. Mal::List.new << gen_type(Mal::Symbol, "cons") << quasiquote(list.first) << quasiquote(tail)
  68. )
  69. end
  70. end
  71. def macro_call?(ast, env)
  72. list = ast.unwrap
  73. return false unless list.is_a? Mal::List
  74. sym = list.first.unwrap
  75. return false unless sym.is_a? Mal::Symbol
  76. func = env.find(sym.str).try(&.data[sym.str])
  77. return false unless func && func.macro?
  78. true
  79. end
  80. def macroexpand(ast, env)
  81. while macro_call?(ast, env)
  82. # Already checked in macro_call?
  83. list = ast.unwrap as Mal::List
  84. func_sym = list[0].unwrap as Mal::Symbol
  85. func = env.get(func_sym.str).unwrap
  86. case func
  87. when Mal::Func
  88. ast = func.call(list[1..-1])
  89. when Mal::Closure
  90. ast = func.fn.call(list[1..-1])
  91. else
  92. eval_error "macro '#{func_sym.str}' must be function: #{ast}"
  93. end
  94. end
  95. ast
  96. end
  97. macro invoke_list(l, env)
  98. f = eval({{l}}.first, {{env}}).unwrap
  99. args = eval_ast({{l}}[1..-1].each_with_object(Mal::List.new){|i, l| l << i}, {{env}})
  100. case f
  101. when Mal::Closure
  102. ast = f.ast
  103. {{env}} = Mal::Env.new(f.env, f.params, args)
  104. next # TCO
  105. when Mal::Func
  106. return f.call args
  107. else
  108. eval_error "expected function as the first argument: #{f}"
  109. end
  110. end
  111. def eval(ast, env)
  112. # 'next' in 'do...end' has a bug in crystal 0.7.1
  113. # https://github.com/manastech/crystal/issues/659
  114. while true
  115. return eval_ast(ast, env) unless ast.unwrap.is_a? Mal::List
  116. ast = macroexpand(ast, env)
  117. list = ast.unwrap
  118. return ast unless list.is_a? Mal::List
  119. return ast if list.empty?
  120. head = list.first.unwrap
  121. return invoke_list(list, env) unless head.is_a? Mal::Symbol
  122. return Mal::Type.new case head.str
  123. when "def!"
  124. eval_error "wrong number of argument for 'def!'" unless list.size == 3
  125. a1 = list[1].unwrap
  126. eval_error "1st argument of 'def!' must be symbol: #{a1}" unless a1.is_a? Mal::Symbol
  127. env.set(a1.str, eval(list[2], env))
  128. when "let*"
  129. eval_error "wrong number of argument for 'def!'" unless list.size == 3
  130. bindings = list[1].unwrap
  131. eval_error "1st argument of 'let*' must be list or vector" unless bindings.is_a? Array
  132. eval_error "size of binding list must be even" unless bindings.size.even?
  133. new_env = Mal::Env.new env
  134. bindings.each_slice(2) do |binding|
  135. key, value = binding
  136. name = key.unwrap
  137. eval_error "name of binding must be specified as symbol #{name}" unless name.is_a? Mal::Symbol
  138. new_env.set(name.str, eval(value, new_env))
  139. end
  140. ast, env = list[2], new_env
  141. next # TCO
  142. when "do"
  143. if list.empty?
  144. ast = Mal::Type.new nil
  145. next
  146. end
  147. eval_ast(list[1..-2].each_with_object(Mal::List.new){|i,l| l << i}, env)
  148. ast = list.last
  149. next # TCO
  150. when "if"
  151. ast = unless eval(list[1], env).unwrap
  152. list.size >= 4 ? list[3] : Mal::Type.new(nil)
  153. else
  154. list[2]
  155. end
  156. next # TCO
  157. when "fn*"
  158. params = list[1].unwrap
  159. unless params.is_a? Array
  160. eval_error "'fn*' parameters must be list or vector: #{params}"
  161. end
  162. Mal::Closure.new(list[2], params, env, func_of(env, params, list[2]))
  163. when "quote"
  164. list[1]
  165. when "quasiquote"
  166. ast = quasiquote list[1]
  167. next # TCO
  168. when "defmacro!"
  169. eval_error "wrong number of argument for 'defmacro!'" unless list.size == 3
  170. a1 = list[1].unwrap
  171. eval_error "1st argument of 'defmacro!' must be symbol: #{a1}" unless a1.is_a? Mal::Symbol
  172. env.set(a1.str, eval(list[2], env).tap{|n| n.is_macro = true})
  173. when "macroexpand"
  174. macroexpand(list[1], env)
  175. else
  176. invoke_list(list, env)
  177. end
  178. end
  179. end
  180. def print(result)
  181. pr_str(result, true)
  182. end
  183. def rep(str)
  184. print(eval(read(str), $repl_env))
  185. end
  186. $repl_env = Mal::Env.new nil
  187. Mal::NS.each{|k,v| $repl_env.set(k, Mal::Type.new(v))}
  188. $repl_env.set("eval", Mal::Type.new -> (args: Array(Mal::Type)){ eval(args[0], $repl_env) })
  189. rep "(def! not (fn* (a) (if a false true)))"
  190. rep "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"
  191. rep "(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))"
  192. rep "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))"
  193. $argv = Mal::List.new
  194. $repl_env.set("*ARGV*", Mal::Type.new $argv)
  195. unless ARGV.empty?
  196. if ARGV.size > 1
  197. ARGV[1..-1].each do |a|
  198. $argv << Mal::Type.new(a)
  199. end
  200. end
  201. begin
  202. rep "(load-file \"#{ARGV[0]}\")"
  203. rescue e
  204. STDERR.puts e
  205. end
  206. exit
  207. end
  208. while line = my_readline("user> ")
  209. begin
  210. puts rep(line)
  211. rescue e
  212. STDERR.puts e
  213. end
  214. end