A fork of Crisp for HARP
Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

137 Zeilen
2.9 KiB

  1. require "./types"
  2. require "./error"
  3. class Reader
  4. def initialize(@tokens)
  5. @pos = 0
  6. end
  7. def current_token
  8. @tokens[@pos] rescue nil
  9. end
  10. def peek
  11. t = current_token
  12. if t && t[0] == ';'
  13. @pos += 1
  14. peek
  15. else
  16. t
  17. end
  18. end
  19. def next
  20. peek
  21. ensure
  22. @pos += 1
  23. end
  24. def read_sequence(init, open, close)
  25. token = self.next
  26. parse_error "expected '#{open}', got EOF" unless token
  27. parse_error "expected '#{open}', got #{token}" unless token[0] == open
  28. loop do
  29. token = peek
  30. parse_error "expected '#{close}', got EOF" unless token
  31. break if token[0] == close
  32. init << read_form
  33. peek
  34. end
  35. self.next
  36. init
  37. end
  38. def read_list
  39. Mal::Type.new read_sequence(Mal::List.new, '(', ')')
  40. end
  41. def read_vector
  42. Mal::Type.new read_sequence(Mal::Vector.new, '[', ']')
  43. end
  44. def read_hashmap
  45. types = read_sequence([] of Mal::Type, '{', '}')
  46. parse_error "odd number of elements for hash-map: #{types.size}" if types.size.odd?
  47. map = Mal::HashMap.new
  48. types.each_slice(2) do |kv|
  49. k, v = kv[0].unwrap, kv[1]
  50. case k
  51. when String
  52. map[k] = v
  53. else
  54. parse_error("key of hash-map must be string or keyword")
  55. end
  56. end
  57. Mal::Type.new map
  58. end
  59. def read_atom
  60. token = self.next
  61. parse_error "expected Atom but got EOF" unless token
  62. Mal::Type.new case
  63. when token =~ /^-?\d+$/ then token.to_i
  64. when token == "true" then true
  65. when token == "false" then false
  66. when token == "nil" then nil
  67. when token[0] == '"' then token[1..-2].gsub(/\\"/, "\"")
  68. when token[0] == ':' then "\u029e#{token[1..-1]}"
  69. else Mal::Symbol.new token
  70. end
  71. end
  72. def list_of(symname)
  73. Mal::List.new << gen_type(Mal::Symbol, symname) << read_form
  74. end
  75. def read_form
  76. token = peek
  77. parse_error "unexpected EOF" unless token
  78. parse_error "unexpected comment" if token[0] == ';'
  79. Mal::Type.new case token
  80. when "(" then read_list
  81. when ")" then parse_error "unexpected ')'"
  82. when "[" then read_vector
  83. when "]" then parse_error "unexpected ']'"
  84. when "{" then read_hashmap
  85. when "}" then parse_error "unexpected '}'"
  86. when "'" then self.next; list_of("quote")
  87. when "`" then self.next; list_of("quasiquote")
  88. when "~" then self.next; list_of("unquote")
  89. when "~@" then self.next; list_of("splice-unquote")
  90. when "@" then self.next; list_of("deref")
  91. when "^"
  92. self.next
  93. meta = read_form
  94. list_of("with-meta") << meta
  95. else read_atom
  96. end
  97. end
  98. end
  99. def tokenize(str)
  100. regex = /[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"|;.*|[^\s\[\]{}('"`,;)]*)/
  101. str.scan(regex).map{|m| m[1]}.reject(&.empty?)
  102. end
  103. def read_str(str)
  104. r = Reader.new(tokenize(str))
  105. begin
  106. r.read_form
  107. ensure
  108. unless r.peek.nil?
  109. raise Mal::ParseException.new "expected EOF, got #{r.peek.to_s}"
  110. end
  111. end
  112. end