XCTLRuntimeContext.swift 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  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. }), forName: "log")
  55. self.setValue(XCTLRuntimeVariable(funcImpl: {
  56. for (id, it) in $0.enumerated() {
  57. self.stdout.append(text: it.toString())
  58. if id != $0.count - 1 {
  59. self.stdout.append(text: " ")
  60. }
  61. }
  62. self.stdout.append(text: "\n")
  63. return .void
  64. }), forName: "logn")
  65. self.setValue(XCTLRuntimeVariable(funcImpl: {
  66. var dest: Double = 0
  67. for it in $0 {
  68. if it.type == .typeNumber {
  69. dest += it.doubleValue
  70. }
  71. }
  72. return XCTLRuntimeVariable(type: .typeNumber, rawValue: dest.description)
  73. }), forName: "add")
  74. self.setValue(XCTLRuntimeVariable(funcImpl: {
  75. var list = $0
  76. list = list.filter({ $0.type == .typeNumber })
  77. if list.isEmpty { return XCTLRuntimeVariable(type: .typeNumber, rawValue: "0") }
  78. var dest: Double = list.removeFirst().doubleValue
  79. for it in list {
  80. if it.type == .typeNumber {
  81. dest -= it.doubleValue
  82. }
  83. }
  84. return XCTLRuntimeVariable(type: .typeNumber, rawValue: dest.description)
  85. }), forName: "minus")
  86. self.setValue(XCTLRuntimeVariable(funcImpl: {
  87. var dest: Double = 1
  88. for it in $0 {
  89. if it.type == .typeNumber {
  90. dest *= it.doubleValue
  91. }
  92. }
  93. return XCTLRuntimeVariable(type: .typeNumber, rawValue: dest.description)
  94. }), forName: "mult")
  95. self.setValue(XCTLRuntimeVariable(funcImpl: {
  96. var list = $0
  97. list = list.filter({ $0.type == .typeNumber })
  98. if list.isEmpty { return XCTLRuntimeVariable(type: .typeNumber, rawValue: "0") }
  99. var dest: Double = list.removeFirst().doubleValue
  100. for it in list {
  101. if it.type == .typeNumber {
  102. dest /= it.doubleValue
  103. }
  104. }
  105. return XCTLRuntimeVariable(type: .typeNumber, rawValue: dest.description)
  106. }), forName: "div")
  107. self.setValue(XCTLRuntimeVariable(funcImpl: {
  108. var begin: Double = 0
  109. var length: Double = 0
  110. var step: Double = 1
  111. let args = $0.filter({ $0.type == .typeNumber }).map({ $0.doubleValue })
  112. if args.count == 1 {
  113. length = args[0]
  114. }
  115. if args.count == 2 {
  116. begin = args[0]
  117. length = args[1]
  118. }
  119. if args.count == 3 {
  120. begin = args[0]
  121. length = args[1]
  122. step = args[2]
  123. }
  124. return XCTLRuntimeVariable(rawObject: XCTLRange(begin: begin, length: length, step: step))
  125. }), forName: "range")
  126. self.setValue(XCTLRuntimeVariable(type: .typeNumber, rawValue: "\(Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "-1")"), forName: "appBundleVersion")
  127. for it in paragraphMembers {
  128. self.setValue(XCTLRuntimeVariable(funcImplStmt: it.value), forName: it.key)
  129. }
  130. }
  131. private var importNames = Set<String>()
  132. private var exportNames = Set<String>()
  133. internal func valueDefined(_ name: String) -> Bool {
  134. if self.importNames.contains(name) {
  135. return true
  136. }
  137. if self.values[name] != nil {
  138. return true
  139. }
  140. return false
  141. }
  142. internal func value(forName name: String) -> XCTLRuntimeVariable? {
  143. if name == "self" {
  144. return XCTLRuntimeVariable(rawObject: self.nativeObjectInstance)
  145. }
  146. if let value = self.values[name] {
  147. return value
  148. }
  149. if importNames.contains(name),
  150. let valueFromNative = self.nativeObjectInstance.value(forKey: name) as? NSObject {
  151. let object = XCTLRuntimeVariable(rawObject: valueFromNative)
  152. self.values[name] = object
  153. return object
  154. }
  155. if let klass: AnyObject = NSClassFromString(name),
  156. let klass = klass as? NSObject {
  157. return XCTLRuntimeVariable(rawObject: klass)
  158. }
  159. return .void
  160. }
  161. internal func setValue(_ value: XCTLRuntimeVariable, forName name: String) {
  162. self.values[name] = value
  163. if exportNames.contains(name) {
  164. self.nativeObjectInstance.setValue(value.nativeValue, forKey: name)
  165. }
  166. }
  167. internal func setValueToRoot(_ value: XCTLRuntimeVariable, forName name: String) {
  168. self.values[name] = value
  169. self.nativeObjectInstance.setValue(value.nativeValue, forKey: name)
  170. }
  171. internal func setValueIgnoreParent(_ value: XCTLRuntimeVariable, forName name: String) {
  172. self.setValue(value, forName: name)
  173. }
  174. internal func addImport(name: String) {
  175. self.importNames.insert(name)
  176. }
  177. internal func addExport(name: String) {
  178. self.exportNames.insert(name)
  179. }
  180. internal func allocateObject(name: String, args: [XCTLRuntimeVariable]) throws -> XCTLRuntimeVariable {
  181. guard let generator = self.generators[name] else {
  182. throw XCTLRuntimeError.generateProtocolNotFoundedError(name: name)
  183. }
  184. let nativeObject = try generator.initWithXCT(args.compactMap({ $0.nativeValue }))
  185. let object = XCTLRuntimeVariable(rawObject: nativeObject)
  186. return object
  187. }
  188. internal func addLazyStatement(_ stmt: XCTLStatement) {
  189. self.lazyRunStatements.append(stmt)
  190. }
  191. internal func makeSubContext() -> XCTLRuntimeAbstractContext {
  192. return XCTLRuntimeSubContext(parent: self)
  193. }
  194. private var conditionFrame: XCTLConditionParentStatementFrame?
  195. private var listFrame: XCTLListStatementFrame?
  196. private var forFrame: XCTLForStatementFrame?
  197. func findConditionFrame() -> XCTLConditionParentStatementFrame? {
  198. return self.conditionFrame
  199. }
  200. func findListFrame() -> XCTLListStatementFrame? {
  201. return self.listFrame
  202. }
  203. func recordListFrame(_ frame: XCTLListStatementFrame?) {
  204. self.listFrame = frame
  205. }
  206. func recordConditionFrame(_ frame: XCTLConditionParentStatementFrame?) {
  207. self.conditionFrame = frame
  208. }
  209. func findForFrame() -> XCTLForStatementFrame? {
  210. return self.forFrame
  211. }
  212. func recordForFrame(_ frame: XCTLForStatementFrame?) {
  213. self.forFrame = frame
  214. }
  215. func getParentContext() -> XCTLRuntimeAbstractContext? {
  216. return nil
  217. }
  218. var variableStack = XCTLRuntimeVariableStackFrame()
  219. }