Chervil is a toy Lisp interpreter written in Ruby
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.

core.rb 3.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. module Chervil
  2. module Core
  3. def self.compare_pairs(ary, method)
  4. pairs = Array.new
  5. ary.each_with_index do |el, i|
  6. unless ary[i + 1].nil?
  7. pairs.push([el, ary[i + 1]])
  8. end
  9. end
  10. !(pairs.map { |pair| pair[0].send(method, pair[1]) }.any?(false))
  11. end
  12. def self.display_class_name(cls)
  13. case cls.inspect
  14. when "Float"
  15. "number"
  16. when "Array"
  17. "list"
  18. when "String"
  19. "string"
  20. end
  21. end
  22. def self.type_check(args, cls)
  23. args_with_wrong_type = args.select { |arg| !arg.is_a?(cls) }
  24. if args_with_wrong_type.length.zero?
  25. nil
  26. else
  27. expected_type = display_class_name(cls)
  28. received_type = display_class_name(args_with_wrong_type.first.class)
  29. ::Chervil::Error.new("Expected an argument of type #{expected_type} but got #{received_type}")
  30. end
  31. end
  32. def self.arity_check(args, expected_count)
  33. if args.size != expected_count
  34. s = expected_count == 1 ? '' : 's'
  35. Error.new("Expected #{expected_count} argument#{s} but received #{args.size}")
  36. else
  37. nil
  38. end
  39. end
  40. def self.eval(source, env)
  41. lexer = Lexer.new(source)
  42. tree = Parser.new(lexer).parse
  43. if tree.is_a?(Error)
  44. return tree
  45. end
  46. interpreter = Interpreter.new(tree, env)
  47. interpreter.interpret.first
  48. end
  49. CORE = {
  50. "+" => Proc.new { |args| type_check(args, Float) || args.inject(:+) },
  51. "-" => Proc.new { |args| type_check(args, Float) || args.inject(:-) },
  52. "*" => Proc.new { |args| type_check(args, Float) || args.inject(:*) },
  53. "/" => Proc.new { |args| type_check(args, Float) || args.inject(:/) },
  54. "=" => Proc.new { |args| compare_pairs(args, :==) },
  55. "<" => Proc.new { |args| type_check(args, Float) || compare_pairs(args, :<) },
  56. ">" => Proc.new { |args| type_check(args, Float) || compare_pairs(args, :>) },
  57. "<=" => Proc.new { |args| type_check(args, Float) || compare_pairs(args, :<=) },
  58. ">=" => Proc.new { |args| type_check(args, Float) || compare_pairs(args, :>=) },
  59. "and" => Proc.new { |args| !(args.include?(false)) },
  60. "or" => Proc.new { |args| args.any? { |arg| !!arg == true } },
  61. "not" => Proc.new do |args|
  62. error = arity_check(args, 1)
  63. if error.nil?
  64. args.first == false ? true : false
  65. else
  66. error
  67. end
  68. end,
  69. "car" => Proc.new do |args|
  70. error = arity_check(args, 1)
  71. if error.nil?
  72. error = type_check(args, Array)
  73. if error.nil?
  74. if args.first.empty?
  75. Error.new("`car` expects a non-empty list")
  76. else
  77. args.first.first
  78. end
  79. else
  80. error
  81. end
  82. else
  83. error
  84. end
  85. end,
  86. "cdr" => Proc.new do |args|
  87. error = arity_check(args, 1)
  88. if error.nil?
  89. error = type_check(args, Array)
  90. if error.nil?
  91. args.first[1..-1]
  92. else
  93. error
  94. end
  95. else
  96. error
  97. end
  98. end,
  99. "cons" => Proc.new do |args|
  100. error = arity_check(args, 2)
  101. if error.nil?
  102. el, els = args
  103. error = type_check([els], Array)
  104. if error.nil?
  105. els.prepend(el)
  106. end
  107. end
  108. end,
  109. }
  110. NATIVE_CORE = {
  111. "map" => "(define (map f xs) (if (= xs '()) '() (cons (f (car xs)) (map f (cdr xs)))))"
  112. }
  113. end
  114. end