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 2.9KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  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. CORE = {
  41. "+" => Proc.new { |args| type_check(args, Float) || args.inject(:+) },
  42. "-" => Proc.new { |args| type_check(args, Float) || args.inject(:-) },
  43. "*" => Proc.new { |args| type_check(args, Float) || args.inject(:*) },
  44. "/" => Proc.new { |args| type_check(args, Float) || args.inject(:/) },
  45. "=" => Proc.new { |args| compare_pairs(args, :==) },
  46. "<" => Proc.new { |args| type_check(args, Float) || compare_pairs(args, :<) },
  47. ">" => Proc.new { |args| type_check(args, Float) || compare_pairs(args, :>) },
  48. "<=" => Proc.new { |args| type_check(args, Float) || compare_pairs(args, :<=) },
  49. ">=" => Proc.new { |args| type_check(args, Float) || compare_pairs(args, :>=) },
  50. "and" => Proc.new { |args| !(args.include?(false)) },
  51. "or" => Proc.new { |args| args.any? { |arg| !!arg == true } },
  52. "not" => Proc.new do |args|
  53. error = arity_check(args, 1)
  54. if error.nil?
  55. args.first == false ? true : false
  56. else
  57. error
  58. end
  59. end,
  60. "car" => Proc.new do |args|
  61. error = arity_check(args, 1)
  62. if error.nil?
  63. error = type_check(args, Array)
  64. if error.nil?
  65. if args.first.empty?
  66. Error.new("`car` expects a non-empty list")
  67. else
  68. args.first.first
  69. end
  70. else
  71. error
  72. end
  73. else
  74. error
  75. end
  76. end,
  77. "cdr" => Proc.new do |args|
  78. error = arity_check(args, 1)
  79. if error.nil?
  80. error = type_check(args, Array)
  81. if error.nil?
  82. args.first[1..-1]
  83. else
  84. error
  85. end
  86. else
  87. error
  88. end
  89. end
  90. }
  91. end
  92. end