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.

139 lignes
3.3 KiB

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