XCTLLexer.swift 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. //
  2. // XCTLLexer.swift
  3. // notebook
  4. //
  5. // Created by 邢铖 on 2023/5/18.
  6. //
  7. import Foundation
  8. internal class XCTLLexer {
  9. internal var position: Int = 0
  10. internal let document: [Character]
  11. internal var debugMode = true
  12. internal var paragraphTable = [String : XCTLStatement]()
  13. internal weak var lastStatement: XCTLStatement?
  14. init(document: String) {
  15. self.document = [Character](document)
  16. }
  17. internal func peek() throws -> XCTLToken {
  18. let position = self.position
  19. let value = try self._next()
  20. self.position = position
  21. return value
  22. }
  23. @discardableResult
  24. internal func next() throws -> XCTLToken {
  25. let token = try self._next()
  26. if self.debugMode {
  27. print("[LEX] \(token.rawValue): \(token.type.rawValue)")
  28. }
  29. return token
  30. }
  31. private func _next() throws -> XCTLToken {
  32. guard let char = peekChar() else {
  33. return .eof
  34. }
  35. position += 1
  36. if char.isLetter || char == "_" {
  37. var buffer = "\(char)"
  38. while true {
  39. guard let nextChar = peekChar(),
  40. !nextChar.isWhitespace,
  41. (nextChar.isLetter || nextChar.isNumber || nextChar == "_") else {
  42. break
  43. }
  44. position += 1
  45. buffer.append(nextChar)
  46. }
  47. switch buffer {
  48. case "import":
  49. return XCTLToken(type: .typeImport, rawValue: buffer)
  50. case "export":
  51. return XCTLToken(type: .typeExport, rawValue: buffer)
  52. case "true":
  53. return XCTLToken(type: .typeImmediateBool, rawValue: buffer)
  54. case "false":
  55. return XCTLToken(type: .typeImmediateBool, rawValue: buffer)
  56. case "switch":
  57. return XCTLToken(type: .typeSwitch, rawValue: buffer)
  58. case "lessthan":
  59. return XCTLToken(type: .typeLessthan, rawValue: buffer)
  60. case "morethan":
  61. return XCTLToken(type: .typeMorethan, rawValue: buffer)
  62. case "nextthan":
  63. return XCTLToken(type: .typeNextthan, rawValue: buffer)
  64. case "equalthan":
  65. return XCTLToken(type: .typeEqualthan, rawValue: buffer)
  66. case "paragraph":
  67. return XCTLToken(type: .typeParagraph, rawValue: buffer)
  68. case "function":
  69. return XCTLToken(type: .typeParagraph, rawValue: buffer)
  70. case "func":
  71. return XCTLToken(type: .typeParagraph, rawValue: buffer)
  72. case "set":
  73. return XCTLToken(type: .typeSet, rawValue: buffer)
  74. case "else":
  75. return XCTLToken(type: .typeElse, rawValue: buffer)
  76. case "return":
  77. return XCTLToken(type: .typeReturn, rawValue: buffer)
  78. case "for":
  79. return XCTLToken(type: .typeFor, rawValue: buffer)
  80. case "in":
  81. return XCTLToken(type: .typeIn, rawValue: buffer)
  82. case "break":
  83. return XCTLToken(type: .typeBreak, rawValue: buffer)
  84. case "continue":
  85. return XCTLToken(type: .typeContinue, rawValue: buffer)
  86. case "class":
  87. return XCTLToken(type: .typeClass, rawValue: buffer)
  88. default:
  89. return XCTLToken(type: .typeIdentifier, rawValue: buffer)
  90. }
  91. }
  92. if char.isNumber {
  93. var buffer = "\(char)"
  94. while true {
  95. guard let nextChar = peekChar(),
  96. "1234567890.".contains(nextChar) else {
  97. break
  98. }
  99. position += 1
  100. buffer.append(nextChar)
  101. }
  102. if Double(buffer) == nil {
  103. throw XCTLCompileTimeError.notValidNumber(string: buffer)
  104. }
  105. return XCTLToken(type: .typeImmediateNumber, rawValue: buffer)
  106. }
  107. if char == "\"" {
  108. var buffer = ""
  109. var ignoreNext = false
  110. while true {
  111. guard let nextChar = peekChar() else {
  112. throw XCTLCompileTimeError.stringNotTerminate(string: "\"\(buffer)")
  113. }
  114. position += 1
  115. if ignoreNext {
  116. ignoreNext = false
  117. } else {
  118. if nextChar == "\\" {
  119. ignoreNext = true
  120. }
  121. if nextChar == "\"" {
  122. break
  123. }
  124. }
  125. buffer.append(nextChar)
  126. }
  127. return XCTLToken(type: .typeImmediateString, rawValue: buffer)
  128. }
  129. if char.isWhitespace {
  130. return try self._next()
  131. }
  132. switch char {
  133. case "{":
  134. return XCTLToken(type: .typeOpenBrace, rawValue: "\(char)")
  135. case "}":
  136. return XCTLToken(type: .typeCloseBrace, rawValue: "\(char)")
  137. case "(":
  138. return XCTLToken(type: .typeOpenBracket, rawValue: "\(char)")
  139. case ")":
  140. return XCTLToken(type: .typeCloseBracket, rawValue: "\(char)")
  141. case "=":
  142. return XCTLToken(type: .typeEqual, rawValue: "\(char)")
  143. case "@":
  144. return XCTLToken(type: .typeAt, rawValue: "\(char)")
  145. case "^":
  146. return XCTLToken(type: .typeXOR, rawValue: "\(char)")
  147. case "$":
  148. return XCTLToken(type: .typeValue, rawValue: "\(char)")
  149. case ".":
  150. return XCTLToken(type: .typePoint, rawValue: "\(char)")
  151. case ":":
  152. return XCTLToken(type: .typeColon, rawValue: "\(char)")
  153. default:
  154. throw XCTLCompileTimeError.illegalCharacter(char: char)
  155. }
  156. }
  157. private func peekChar() -> Character? {
  158. if position >= 0 && position < document.count {
  159. return document[position]
  160. }
  161. return nil
  162. }
  163. }