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.

63 lines
1.6 KiB

  1. //
  2. // Lexer.swift
  3. // Kaleidoscope
  4. //
  5. // Created by Matthew Cheok on 15/11/15.
  6. // Copyright © 2015 Matthew Cheok. All rights reserved.
  7. //
  8. import Foundation
  9. public enum Token {
  10. case identifier(String, CountableRange<Int>)
  11. case number(Float, CountableRange<Int>)
  12. case parensOpen(CountableRange<Int>)
  13. case parensClose(CountableRange<Int>)
  14. case comma(CountableRange<Int>)
  15. case other(String, CountableRange<Int>)
  16. }
  17. typealias TokenGenerator = (String, CountableRange<Int>) -> Token?
  18. let tokenList: [(String, TokenGenerator)] = [
  19. ("[ \t\n]", { _, _ in nil }),
  20. ("[a-zA-Z][a-zA-Z0-9]*", { .identifier($0, $1) }),
  21. ("\\-?[0-9.]+", { .number(Float($0)!, $1) }),
  22. ("\\(", { .parensOpen($1) }),
  23. ("\\)", { .parensClose($1) }),
  24. (",", { .comma($1) })
  25. ]
  26. public class Lexer {
  27. let input: String
  28. public init(input: String) {
  29. self.input = input
  30. }
  31. public func tokenize() -> [Token] {
  32. var tokens = [Token]()
  33. var content = input
  34. while !content.isEmpty {
  35. var matched = false
  36. for (pattern, generator) in tokenList {
  37. if let (m, r) = content.match(regex: pattern) {
  38. if let t = generator(m, r) {
  39. tokens.append(t)
  40. }
  41. content = String(content[content.index(content.startIndex, offsetBy: m.count)...])
  42. matched = true
  43. break
  44. }
  45. }
  46. if !matched {
  47. let index = content.index(content.startIndex, offsetBy: 1)
  48. let intIndex = content.distance(from: content.startIndex, to: index)
  49. tokens.append(.other(String(content[..<index]), intIndex..<intIndex+1))
  50. content = String(content[index...])
  51. }
  52. }
  53. return tokens
  54. }
  55. }