XCTLRuntimeContext.swift 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. //
  2. // XCTLRuntimeContext.swift
  3. // notebook
  4. //
  5. // Created by 邢铖 on 2023/5/18.
  6. //
  7. import Foundation
  8. import UIKit
  9. internal class XCTLRuntimeContext: XCTLRuntimeAbstractContext {
  10. public let nativeObjectInstance: NSObject
  11. private let generators: [String : XCTLGenerateProtocol.Type]
  12. private var values = [String : XCTLRuntimeVariable]()
  13. internal private(set) var lazyRunStatements = [XCTLStatement]()
  14. internal let stdout = XCTLStream(onAppendBlock: {
  15. print($0, terminator: "")
  16. })
  17. internal init(nativeObjectInstance: NSObject,
  18. paragraphMembers: [String : XCTLStatement],
  19. generators: [String : XCTLGenerateProtocol.Type]) {
  20. self.nativeObjectInstance = nativeObjectInstance
  21. self.generators = generators
  22. self.setValue(XCTLRuntimeVariable(funcImpl: {
  23. if $0.count == 1,
  24. let val = $0.first {
  25. if val.type != .typeString {
  26. return .void
  27. }
  28. if let nativeImage = UIImage(named: val.stringValue) ?? UIImage(systemName: val.stringValue) {
  29. return XCTLRuntimeVariable(rawObject: nativeImage)
  30. }
  31. }
  32. return .void
  33. }), forName: "image:")
  34. self.setValue(XCTLRuntimeVariable(funcImpl: {
  35. if $0.count == 1,
  36. let val = $0.first {
  37. if val.type == .typeString {
  38. return XCTLRuntimeVariable(type: .typeString, rawValue: NSLocalizedString(val.stringValue, comment: ""))
  39. }
  40. }
  41. return .void
  42. }), forName: "string:")
  43. self.setValue(XCTLRuntimeVariable(funcImpl: {
  44. fatalError($0.first?.stringValue ?? "fatalError from XCT")
  45. }), forName: "appFatalError:")
  46. self.setValue(XCTLRuntimeVariable(funcImpl: {
  47. for (id, it) in $0.enumerated() {
  48. self.stdout.append(text: it.toString())
  49. if id != $0.count - 1 {
  50. self.stdout.append(text: " ")
  51. }
  52. }
  53. return .void
  54. }), forNames: "log", "log:", "log:_:", "log:_:_:", "log:_:_:_:",
  55. "log:_:_:_:_:", "log:_:_:_:_:_:", "log:_:_:_:_:_:_:", "log:_:_:_:_:_:_:_:")
  56. self.setValue(XCTLRuntimeVariable(funcImpl: {
  57. for (id, it) in $0.enumerated() {
  58. self.stdout.append(text: it.toString())
  59. if id != $0.count - 1 {
  60. self.stdout.append(text: " ")
  61. }
  62. }
  63. self.stdout.append(text: "\n")
  64. return .void
  65. }), forNames: "logn", "logn:", "logn:_:", "logn:_:_:", "logn:_:_:_:",
  66. "logn:_:_:_:_:", "logn:_:_:_:_:_:", "logn:_:_:_:_:_:_:", "logn:_:_:_:_:_:_:_:")
  67. self.setValue(XCTLRuntimeVariable(funcImpl: {
  68. var dest: Double = 0
  69. for it in $0 {
  70. if it.type == .typeNumber {
  71. dest += it.doubleValue
  72. }
  73. }
  74. return XCTLRuntimeVariable(type: .typeNumber, rawValue: dest.description)
  75. }), forNames: "add", "add:", "add:_:", "add:_:_:", "add:_:_:_:",
  76. "add:_:_:_:_:", "add:_:_:_:_:_:", "add:_:_:_:_:_:_:", "add:_:_:_:_:_:_:_:")
  77. self.setValue(XCTLRuntimeVariable(funcImpl: {
  78. var list = $0
  79. list = list.filter({ $0.type == .typeNumber })
  80. if list.isEmpty { return XCTLRuntimeVariable(type: .typeNumber, rawValue: "0") }
  81. var dest: Double = list.removeFirst().doubleValue
  82. for it in list {
  83. if it.type == .typeNumber {
  84. dest -= it.doubleValue
  85. }
  86. }
  87. return XCTLRuntimeVariable(type: .typeNumber, rawValue: dest.description)
  88. }), forNames: "minus", "minus:", "minus:_:", "minus:_:_:", "minus:_:_:_:",
  89. "minus:_:_:_:_:", "minus:_:_:_:_:_:", "minus:_:_:_:_:_:_:", "minus:_:_:_:_:_:_:_:")
  90. self.setValue(XCTLRuntimeVariable(funcImpl: {
  91. var dest: Double = 1
  92. for it in $0 {
  93. if it.type == .typeNumber {
  94. dest *= it.doubleValue
  95. }
  96. }
  97. return XCTLRuntimeVariable(type: .typeNumber, rawValue: dest.description)
  98. }), forNames: "mult", "mult:", "mult:_:", "mult:_:_:", "mult:_:_:_:",
  99. "mult:_:_:_:_:", "mult:_:_:_:_:_:", "mult:_:_:_:_:_:_:", "mult:_:_:_:_:_:_:_:")
  100. self.setValue(XCTLRuntimeVariable(funcImpl: {
  101. var list = $0
  102. list = list.filter({ $0.type == .typeNumber })
  103. if list.isEmpty { return XCTLRuntimeVariable(type: .typeNumber, rawValue: "0") }
  104. var dest: Double = list.removeFirst().doubleValue
  105. for it in list {
  106. if it.type == .typeNumber {
  107. dest /= it.doubleValue
  108. }
  109. }
  110. return XCTLRuntimeVariable(type: .typeNumber, rawValue: dest.description)
  111. }), forNames: "div", "div:", "div:_:", "div:_:_:", "div:_:_:_:",
  112. "div:_:_:_:_:", "div:_:_:_:_:_:", "div:_:_:_:_:_:_:", "div:_:_:_:_:_:_:_:")
  113. self.setValue(XCTLRuntimeVariable(funcImpl: {
  114. var begin: Double = 0
  115. var length: Double = 0
  116. var step: Double = 1
  117. let args = $0.filter({ $0.type == .typeNumber }).map({ $0.doubleValue })
  118. if args.count == 1 {
  119. length = args[0]
  120. }
  121. if args.count == 2 {
  122. begin = args[0]
  123. length = args[1]
  124. }
  125. if args.count == 3 {
  126. begin = args[0]
  127. length = args[1]
  128. step = args[2]
  129. }
  130. return XCTLRuntimeVariable(rawObject: XCTLRange(begin: begin, length: length, step: step))
  131. }), forNames: "range:", "range:_:", "range:_:_:")
  132. self.setValue(XCTLRuntimeVariable(type: .typeNumber, rawValue: "\(Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "-1")"), forName: "appBundleVersion")
  133. for it in paragraphMembers {
  134. self.setValue(XCTLRuntimeVariable(funcImplStmt: it.value), forName: it.key)
  135. }
  136. }
  137. private var importNames = Set<String>()
  138. private var exportNames = Set<String>()
  139. internal func valueDefined(_ name: String) -> Bool {
  140. if self.importNames.contains(name) {
  141. return true
  142. }
  143. if self.values[name] != nil {
  144. return true
  145. }
  146. return false
  147. }
  148. internal func value(forName name: String) -> XCTLRuntimeVariable? {
  149. if name == "self" {
  150. return XCTLRuntimeVariable(rawObject: self.nativeObjectInstance)
  151. }
  152. if let value = self.values[name] {
  153. return value
  154. }
  155. if importNames.contains(name),
  156. let valueFromNative = self.nativeObjectInstance.value(forKey: name) as? NSObject {
  157. let object = XCTLRuntimeVariable(rawObject: valueFromNative)
  158. self.values[name] = object
  159. return object
  160. }
  161. if let klass: AnyObject = NSClassFromString(name),
  162. let klass = klass as? NSObject {
  163. return XCTLRuntimeVariable(rawObject: klass)
  164. }
  165. return .void
  166. }
  167. internal func setValue(_ value: XCTLRuntimeVariable, forName name: String) {
  168. self.setValue(value, forNames: name)
  169. }
  170. internal func setValue(_ value: XCTLRuntimeVariable, forNames names: String...) {
  171. for name in names {
  172. self.values[name] = value
  173. if exportNames.contains(name) {
  174. self.nativeObjectInstance.setValue(value.nativeValue, forKey: name)
  175. }
  176. }
  177. }
  178. internal func setValueToRoot(_ value: XCTLRuntimeVariable, forName name: String) {
  179. self.values[name] = value
  180. self.nativeObjectInstance.setValue(value.nativeValue, forKey: name)
  181. }
  182. internal func setValueIgnoreParent(_ value: XCTLRuntimeVariable, forName name: String) {
  183. self.setValue(value, forName: name)
  184. }
  185. internal func addImport(name: String) {
  186. self.importNames.insert(name)
  187. }
  188. internal func addExport(name: String) {
  189. self.exportNames.insert(name)
  190. }
  191. internal func allocateObject(name: String, args: [XCTLRuntimeVariable]) throws -> XCTLRuntimeVariable {
  192. if let generator = self.generators[name] {
  193. let nativeObject = try generator.initWithXCT(args.compactMap({ $0.nativeValue }))
  194. let object = XCTLRuntimeVariable(rawObject: nativeObject)
  195. return object
  196. }
  197. if let klass: AnyObject = NSClassFromString(name),
  198. let klass = klass as? NSObject {
  199. let invocation = try Invocation(target: klass, selector: NSSelectorFromString("alloc"))
  200. invocation.invoke()
  201. if let obj = invocation.returnedObject as? NSObject {
  202. let invocation = try Invocation(target: obj, selector: NSSelectorFromString("init"))
  203. invocation.invoke()
  204. if let obj = invocation.returnedObject as? NSObject {
  205. return XCTLRuntimeVariable(rawObject: obj)
  206. }
  207. }
  208. }
  209. throw XCTLRuntimeError.generateProtocolNotFoundedError(name: name)
  210. }
  211. internal func addLazyStatement(_ stmt: XCTLStatement) {
  212. self.lazyRunStatements.append(stmt)
  213. }
  214. internal func makeSubContext() -> XCTLRuntimeAbstractContext {
  215. return XCTLRuntimeSubContext(parent: self)
  216. }
  217. private var conditionFrame: XCTLConditionParentStatementFrame?
  218. private var listFrame: XCTLListStatementFrame?
  219. private var forFrame: XCTLForStatementFrame?
  220. func findConditionFrame() -> XCTLConditionParentStatementFrame? {
  221. return self.conditionFrame
  222. }
  223. func findListFrame() -> XCTLListStatementFrame? {
  224. return self.listFrame
  225. }
  226. func recordListFrame(_ frame: XCTLListStatementFrame?) {
  227. self.listFrame = frame
  228. }
  229. func recordConditionFrame(_ frame: XCTLConditionParentStatementFrame?) {
  230. self.conditionFrame = frame
  231. }
  232. func findForFrame() -> XCTLForStatementFrame? {
  233. return self.forFrame
  234. }
  235. func recordForFrame(_ frame: XCTLForStatementFrame?) {
  236. self.forFrame = frame
  237. }
  238. func getParentContext() -> XCTLRuntimeAbstractContext? {
  239. return nil
  240. }
  241. var variableStack = XCTLRuntimeVariableStackFrame()
  242. }