├── .gitignore ├── Example ├── HTNSwift.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── HTNSwift │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Base.lproj │ │ └── Main.storyboard │ ├── HTNSwift.entitlements │ ├── Info.plist │ ├── Test.swift │ ├── ViewController.swift │ └── VueTest.swift └── Podfile ├── HTN.podspec ├── HTNCLI ├── .gitignore ├── Package.resolved ├── Package.swift ├── README.md ├── Sources │ └── HTNCLI │ │ ├── Build.swift │ │ ├── HTNCLI.swift │ │ └── main.swift └── install.sh ├── HTNExamples ├── .gitignore ├── Podfile ├── Podfile.lock ├── Sample.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── Sample.xcworkspace │ └── contents.xcworkspacedata └── Sample │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Base.lproj │ └── LaunchScreen.storyboard │ ├── Flexbox.h │ ├── Flexbox.m │ ├── Info.plist │ ├── MainViewController.h │ ├── MainViewController.m │ ├── UIColor+Extension.h │ ├── UIColor+Extension.m │ └── main.m ├── LICENSE ├── Package.swift ├── README.md ├── Sources ├── Core │ ├── CSS │ │ ├── CSSDeclaration.swift │ │ ├── CSSDefault.swift │ │ ├── CSSParser.swift │ │ ├── CSSProperty.swift │ │ ├── CSSRule.swift │ │ ├── CSSSelector.swift │ │ └── CSSStyleSheet.swift │ ├── HTML │ │ ├── Attribute.swift │ │ ├── Document.swift │ │ ├── Element.swift │ │ ├── HTMLToken.swift │ │ ├── HTMLTokenizer.swift │ │ └── HTMLTreeBuilder.swift │ ├── HTNFundation │ │ ├── HTNMultilingualism.swift │ │ ├── HTNStateMachine.swift │ │ ├── Node.swift │ │ ├── SMNetWorking.swift │ │ └── StringExtension.swift │ ├── JavaScript │ │ ├── Archive │ │ │ ├── JSNode.swift │ │ │ ├── JSToken.swift │ │ │ ├── JSTokenizer.swift │ │ │ └── JSTreeBuilder.swift │ │ ├── CodeGeneratorFromJSToOC.swift │ │ ├── JNode.swift │ │ ├── JParser.swift │ │ ├── JState.swift │ │ ├── JToken.swift │ │ ├── JTokenizer.swift │ │ ├── JTransformer.swift │ │ ├── JTraverser.swift │ │ └── babylonAll.js │ ├── Layout │ │ ├── HTMLToTexture.swift │ │ ├── JSONToFrame.swift │ │ ├── LayoutElement.swift │ │ ├── RenderObject.swift │ │ └── StyleResolver.swift │ └── OC │ │ ├── OCAST.swift │ │ ├── OCArithmetics.swift │ │ ├── OCInterpreter.swift │ │ ├── OCLexer.swift │ │ ├── OCParser.swift │ │ ├── OCToken.swift │ │ └── StaticAnalyzer │ │ ├── OCStaticAnalyzer.swift │ │ ├── OCSymbol.swift │ │ ├── OCSymbolTable.swift │ │ └── OCVisitor.swift └── H5Editor │ ├── H5EditorDatasource.swift │ ├── H5EditorObjc.swift │ ├── H5EditorStruct.swift │ └── H5EditorToFrame.swift ├── deploy.sh └── htnbluemap.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | # Package.pins 40 | .build/ 41 | 42 | # CocoaPods 43 | # 44 | # We recommend against adding the Pods directory to your .gitignore. However 45 | # you should judge for yourself, the pros and cons are mentioned at: 46 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 47 | # 48 | Pods/ 49 | HTNSwift.xcworkspace/ 50 | Podfile.lock 51 | 52 | # Carthage 53 | # 54 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 55 | # Carthage/Checkouts 56 | 57 | Carthage/Build 58 | 59 | # fastlane 60 | # 61 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 62 | # screenshots whenever they are needed. 63 | # For more information about the recommended setup visit: 64 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 65 | 66 | fastlane/report.xml 67 | fastlane/Preview.html 68 | fastlane/screenshots 69 | fastlane/test_output 70 | -------------------------------------------------------------------------------- /Example/HTNSwift.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/HTNSwift/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // HTNSwift 4 | // 5 | // Created by DaiMing on 2017/10/11. 6 | // Copyright © 2017年 Starming. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | @NSApplicationMain 12 | class AppDelegate: NSObject, NSApplicationDelegate { 13 | 14 | 15 | 16 | func applicationDidFinishLaunching(_ aNotification: Notification) { 17 | // Insert code here to initialize your application 18 | } 19 | 20 | func applicationWillTerminate(_ aNotification: Notification) { 21 | // Insert code here to tear down your application 22 | } 23 | 24 | 25 | } 26 | 27 | -------------------------------------------------------------------------------- /Example/HTNSwift/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "size" : "16x16", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "size" : "16x16", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "size" : "32x32", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "size" : "32x32", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "size" : "128x128", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "size" : "128x128", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "size" : "256x256", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "size" : "256x256", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "size" : "512x512", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "size" : "512x512", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /Example/HTNSwift/HTNSwift.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Example/HTNSwift/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | Copyright © 2017年 Starming. All rights reserved. 27 | NSMainStoryboardFile 28 | Main 29 | NSPrincipalClass 30 | NSApplication 31 | NSAppTransportSecurity 32 | 33 | NSAllowsArbitraryLoads 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /Example/HTNSwift/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // HTNSwift 4 | // 5 | // Created by DaiMing on 2017/10/11. 6 | // Copyright © 2017年 Starming. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import HTN 11 | 12 | 13 | struct Data { 14 | let a: String 15 | } 16 | 17 | 18 | @available(OSX 10.13, *) 19 | @available(OSX 10.13, *) 20 | class ViewController: NSViewController { 21 | 22 | @IBOutlet weak var inputLb: NSTextField! 23 | 24 | @IBOutlet var nativeCodeLb: NSTextView! 25 | @IBOutlet weak var inputTypeSelect: NSPopUpButton! 26 | @IBOutlet weak var toNativeBt: NSButton! 27 | 28 | @IBAction func toNativeAction(_ sender: Any) { 29 | guard let item = inputTypeSelect.selectedItem else { 30 | return 31 | } 32 | switch item.title { 33 | case "HTML 转 Texture": 34 | htmlToTexture() 35 | case "JSON 转 Frame": 36 | jsonToFrame() 37 | case "Javascript 测试": 38 | javascriptTest() 39 | default: 40 | htmlToTexture() 41 | } 42 | 43 | } 44 | 45 | func jsonToFrame() { 46 | guard inputLb.stringValue.count > 0 else { 47 | return; 48 | } 49 | //请求地址在输入框输入 50 | SMNetWorking().requestJSON(inputLb.stringValue) { (jsonModel) in 51 | guard let model = jsonModel else { 52 | return 53 | } 54 | let reStr = H5EditorToFrame(H5EditorObjc()).convert(model) 55 | // print(reStr) 56 | DispatchQueue.main.async { 57 | self.nativeCodeLb.string = reStr.0 + "\n\n" + reStr.1 58 | } 59 | } 60 | } 61 | public func javascriptTest() { 62 | 63 | } 64 | 65 | 66 | 67 | //递归所有子节点 68 | public func htmlToTexture() { 69 | let treeBuilder = HTMLTreeBuilder(inputLb.stringValue) 70 | _ = treeBuilder.parse() 71 | let cssStyle = CSSParser(treeBuilder.doc.allStyle()).parseSheet() 72 | let document = StyleResolver().resolver(treeBuilder.doc, styleSheet: cssStyle) 73 | document.des() //打印包含样式信息的 DOM 树 74 | 75 | //转 Textrue 76 | let layoutElement = LayoutElement().createRenderer(doc: document) 77 | _ = HTMLToTexture(nodeName:"Flexbox").converter(layoutElement); 78 | nativeCodeLb.string = "" 79 | } 80 | 81 | override func viewDidLoad() { 82 | super.viewDidLoad() 83 | self.nativeCodeLb.font = NSFont.userFont(ofSize: 16) 84 | 85 | var caseStr = "31 + 8 / 2 * 3" 86 | 87 | caseStr = "31 + (4 + 5 - (3 + 3)) * 4 - (1 + (51 - 4))" 88 | caseStr = "4 + 3 * 2" 89 | //caseStr = "4 + 3 - 2" 90 | caseStr = "4 + - 3 * 2" 91 | caseStr = """ 92 | @interface OurClass 93 | // 定义属性 94 | @property (nonatomic, assign) NSUInteger pa; 95 | @property (nonatomic, assign) CGFloat pb; 96 | 97 | @end 98 | 99 | @implementation OurClass 100 | 101 | /* 开始运算 */ 102 | - (void)run { 103 | CGFloat ta = 4.3; 104 | pb = 13.3; 105 | pa = (3 + 2) + pb * 2 - ta; 106 | } 107 | 108 | @end 109 | """ 110 | 111 | let interperter = OCInterpreter(caseStr) 112 | 113 | print(interperter) 114 | 115 | // VueTest().LetTestBegin() 116 | 117 | // let jsonStringClear = justTest().replacingOccurrences(of: "\n", with: "") 118 | // let jsonData = jsonStringClear.data(using: .utf8)! 119 | // 120 | // do { 121 | // let a = try JSONSerialization.jsonObject(with: jsonData, options: JSONSerialization.ReadingOptions(rawValue: 0)) as! [Dictionary] 122 | // for c in a { 123 | // print(c["type"] ?? "") 124 | // } 125 | // } catch let error as NSError { print(error) } 126 | 127 | } 128 | 129 | } 130 | 131 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /Example/HTNSwift/VueTest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // VueTest.swift 3 | // HTNSwift 4 | // 5 | // Created by DaiMing on 2018/5/10. 6 | // Copyright © 2018年 Starming. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import HTN 11 | 12 | class VueTest { 13 | var isPrintable = true 14 | let _case_tmp = """ 15 | const PI = 3.141593 16 | PI > 3.0 17 | """ 18 | let _case_1 = """ 19 | const tokens = [45,34.5,"this","is","case1 content is const tokens = [\\"bulabula\\"]"]; 20 | if (/[0-9]/.test(currentChar)) { 21 | var num = 1244.7 % 889; 22 | } 23 | """ 24 | let _case_1_hash = """ 25 | const|const:tokens|name:=|eq:[|braceL:45|float:,|comma:34.5|float:,|comma:this|string:,|comma:is|string:,|comma:case1 content is const tokens = [slash"bulabulaslash"]|string:]|braceR:|eof:if|if:(|parenL:/[0-9]/|regular:.|dot:test|name:(|parenL:currentChar|name:)|parenR:)|parenR:{|braceL:|eof:var|var:num|name:=|eq:1244.7|float:%|modulo:889|float:|eof:}|braceR: 26 | """ 27 | 28 | func LetTestBegin() { 29 | checkCase_1() 30 | checkCase_2() 31 | 32 | } 33 | 34 | // 检查 Case 35 | // Case2 36 | func checkCase_2() { 37 | let re = JParser(_case_tmp).parser() 38 | 39 | } 40 | // Case1 包含了字符串,正则,数字还有基本的 token 的测试 41 | func checkCase_1() { 42 | let tks = JTokenizer(_case_1).tokenizer() 43 | if isPrintable { 44 | print("Case1 String is:\(_case_1)") 45 | } 46 | let hash = hashFrom(tokens: tks) 47 | caseResultPrint(hash: hash, rightHash: _case_1_hash, caseStr: "case1") 48 | } 49 | 50 | // 打印的方法 51 | func caseResultPrint(hash: String, rightHash: String, caseStr: String) { 52 | if hash == rightHash { 53 | print("\(caseStr) ✅") 54 | } else { 55 | print("\(caseStr) ❌") 56 | } 57 | } 58 | // 打印 token 并返回 hash 的 token 59 | func hashFrom(tokens:[JToken]) -> String { 60 | var hash = "" 61 | for tk in tokens { 62 | if isPrintable { 63 | print("\(tk.value) ::: \(tk.type.rawValue)") 64 | } 65 | hash.append("\(tk.value)|\(tk.type.rawValue):") 66 | } 67 | let reHash = hash.replacingOccurrences(of: "\\", with: "slash") 68 | if isPrintable { 69 | print(reHash) 70 | } 71 | return reHash 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | source 'https://github.com/CocoaPods/Specs.git' 2 | platform :osx, '10.11' 3 | 4 | target 'HTNSwift' do 5 | use_frameworks! 6 | pod 'HTN', :path => '../' 7 | end 8 | -------------------------------------------------------------------------------- /HTN.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod lib lint HTN.podspec' to ensure this is a 3 | # valid spec before submitting. 4 | # 5 | # Any lines starting with a # are optional, but their use is encouraged 6 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | s.name = 'HTN' 11 | s.version = '0.1.0' 12 | s.summary = 'HTML to Native like swift and objective-c' 13 | 14 | # This description is used to generate tags and improve search results. 15 | # * Think: What does it do? Why did you write it? What is the focus? 16 | # * Try to keep it short, snappy and to the point. 17 | # * Write the description between the DESC delimiters below. 18 | # * Finally, don't worry about the indent, CocoaPods strips it! 19 | 20 | s.description = <<-DESC 21 | HTML to Native like swift and objective-c. 22 | DESC 23 | 24 | s.homepage = 'https://github.com/ming1016/HTN' 25 | # s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2' 26 | s.license = { :type => 'MIT', :file => 'LICENSE' } 27 | s.author = { 'daiming' => 'daiming@didichuxing.com' } 28 | s.source = { :git => 'https://github.com/ming1016/HTN.git', :tag => s.version.to_s } 29 | # s.social_media_url = 'https://twitter.com/' 30 | 31 | s.osx.deployment_target = '10.10' 32 | s.requires_arc = true 33 | #s.swift_version = '4.0' 34 | s.pod_target_xcconfig = { 'SWIFT_VERSION' => '4.0'} 35 | 36 | # s.resource_bundles = { 37 | # 'HTNSwift' => ['HTNSwift/Assets/*.png'] 38 | # } 39 | # s.dependency 'Alamofire', '~> 4.7' 40 | 41 | # Core 42 | s.subspec 'Core' do |core| 43 | core.source_files = 'Sources/Core/**/*' 44 | end 45 | 46 | # H5Editor 47 | s.subspec 'H5Editor' do |h5Editor| 48 | h5Editor.source_files = 'Sources/H5Editor/**/*' 49 | h5Editor.dependency 'HTN/Core' 50 | end 51 | 52 | end -------------------------------------------------------------------------------- /HTNCLI/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | -------------------------------------------------------------------------------- /HTNCLI/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "HTN", 6 | "repositoryURL": "https://github.com/ming1016/HTN", 7 | "state": { 8 | "branch": null, 9 | "revision": "c1a2cff12195c6273805f7c00c9c8ca704c670a9", 10 | "version": "0.1.0" 11 | } 12 | }, 13 | { 14 | "package": "PathKit", 15 | "repositoryURL": "https://github.com/kylef/PathKit", 16 | "state": { 17 | "branch": null, 18 | "revision": "fa81fa9e3a9f59645159c4ea45c0c46ee6558f71", 19 | "version": "0.9.1" 20 | } 21 | }, 22 | { 23 | "package": "Rainbow", 24 | "repositoryURL": "https://github.com/onevcat/Rainbow", 25 | "state": { 26 | "branch": null, 27 | "revision": "7737750b25a9cb4174c7567bef11d175715a824f", 28 | "version": "3.1.2" 29 | } 30 | }, 31 | { 32 | "package": "Spectre", 33 | "repositoryURL": "https://github.com/kylef/Spectre.git", 34 | "state": { 35 | "branch": null, 36 | "revision": "e34d5687e1e9d865e3527dd58bc2f7464ef6d936", 37 | "version": "0.8.0" 38 | } 39 | }, 40 | { 41 | "package": "SwiftCLI", 42 | "repositoryURL": "https://github.com/jakeheis/SwiftCLI", 43 | "state": { 44 | "branch": null, 45 | "revision": "88aa58faaea2ad78f62fbb427f8bafa93589c7f4", 46 | "version": "4.3.0" 47 | } 48 | } 49 | ] 50 | }, 51 | "version": 1 52 | } 53 | -------------------------------------------------------------------------------- /HTNCLI/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:4.0 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "HTNCLI", 8 | products: [ 9 | .executable(name: "htn", targets: ["HTNCLI"]) 10 | ], 11 | dependencies: [ 12 | .package(url: "https://github.com/ming1016/HTN", from: "0.1.0"), 13 | // .package(url: "../../HTN", .branch("master")), 14 | .package(url: "https://github.com/jakeheis/SwiftCLI", from: "4.2.0"), 15 | .package(url: "https://github.com/onevcat/Rainbow", from: "3.1.1"), 16 | .package(url: "https://github.com/kylef/PathKit", from: "0.9.1") 17 | ], 18 | targets: [ 19 | .target(name: "HTNCLI", dependencies: ["HTN", "SwiftCLI", "Rainbow", "PathKit"]) 20 | ] 21 | ) 22 | -------------------------------------------------------------------------------- /HTNCLI/README.md: -------------------------------------------------------------------------------- 1 | # HTNCLI 2 | 3 | A description of this package. 4 | -------------------------------------------------------------------------------- /HTNCLI/Sources/HTNCLI/Build.swift: -------------------------------------------------------------------------------- 1 | import SwiftCLI 2 | 3 | class BuildCommand: Command { 4 | 5 | let name = "build" 6 | let shortDescription = "Build H5Editor Url to Native file." 7 | let type = OptionalParameter() 8 | let urlFlag = Key("-u", "--urls") 9 | let outputFlag = Key("-o", "--output") 10 | 11 | public func execute() throws { 12 | let buildType = type.value ?? "h5editor" //默认是H5Editor的json类型 13 | let urls = (urlFlag.value ?? "").split(separator: ",").map(String.init).filter{ $0.count > 0} 14 | let outputPath = outputFlag.value ?? "." 15 | 16 | let htn = HTNCLI() 17 | if buildType == "h5editor" { 18 | //convert h5editor 19 | htn.buildH5Editor(urls: urls, outputPath: outputPath) 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /HTNCLI/Sources/HTNCLI/HTNCLI.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HTNCLI.swift 3 | // HTNCLI 4 | // 5 | // Created by 陈爱彬 on 2018/4/17. Maintain by 陈爱彬 6 | // Description 7 | // 8 | 9 | import Foundation 10 | import HTN 11 | import PathKit 12 | import Dispatch 13 | 14 | public class HTNCLI { 15 | public static let version = "0.1.0" 16 | 17 | let sema = DispatchSemaphore(value: 0) 18 | 19 | public init() {} 20 | 21 | //build h5editor urls and export to outputPath 22 | public func buildH5Editor(urls: [String], outputPath: String) { 23 | let path = Path(outputPath) 24 | for url in urls { 25 | print("Request url:\(url)") 26 | SMNetWorking().requestJSON(url) { (jsonModel) in 27 | guard let model = jsonModel else { 28 | self.sema.signal() 29 | return 30 | } 31 | let converter = H5EditorToFrame(H5EditorObjc()) 32 | let reStr = converter.convert(model) 33 | // print(reStr) 34 | // print(converter.m.pageId) 35 | let hPath = path + Path(converter.m.pageId + ".h") 36 | let mPath = path + Path(converter.m.pageId + ".m") 37 | self.writeFileToPath(hPath, content: reStr.0) 38 | self.writeFileToPath(mPath, content: reStr.1) 39 | self.sema.signal() 40 | } 41 | sema.wait() 42 | } 43 | } 44 | //write response to path 45 | func writeFileToPath(_ path: Path, content: String) { 46 | try? path.write(content) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /HTNCLI/Sources/HTNCLI/main.swift: -------------------------------------------------------------------------------- 1 | import SwiftCLI 2 | import Rainbow 3 | 4 | Rainbow.enabled = Term.isTTY 5 | 6 | let _verbose = Flag("-v", "--verbose") 7 | extension Command { 8 | var verbose: Flag { 9 | return _verbose 10 | } 11 | 12 | func verboseLog(_ content: String) { 13 | if verbose.value { 14 | stdout <<< content 15 | } 16 | } 17 | } 18 | 19 | let htn = CLI(name: "htn", version: HTNCLI.version, description: "HTN toolkit") 20 | htn.commands = [ 21 | BuildCommand() 22 | ] 23 | htn.globalOptions = [_verbose] 24 | htn.goAndExit() 25 | -------------------------------------------------------------------------------- /HTNCLI/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | swift package clean 3 | swift build -c release 4 | cp .build/release/htn /usr/local/bin/htn 5 | -------------------------------------------------------------------------------- /HTNExamples/.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | # Package.pins 40 | .build/ 41 | 42 | # CocoaPods 43 | # 44 | # We recommend against adding the Pods directory to your .gitignore. However 45 | # you should judge for yourself, the pros and cons are mentioned at: 46 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 47 | # 48 | Pods/ 49 | 50 | # Carthage 51 | # 52 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 53 | # Carthage/Checkouts 54 | 55 | Carthage/Build 56 | 57 | # fastlane 58 | # 59 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 60 | # screenshots whenever they are needed. 61 | # For more information about the recommended setup visit: 62 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 63 | 64 | fastlane/report.xml 65 | fastlane/Preview.html 66 | fastlane/screenshots 67 | fastlane/test_output 68 | -------------------------------------------------------------------------------- /HTNExamples/Podfile: -------------------------------------------------------------------------------- 1 | source 'https://github.com/CocoaPods/Specs.git' 2 | platform :ios, '8.0' 3 | target 'Sample' do 4 | pod 'Texture' , '2.5.1' 5 | pod 'ReactiveCocoa', '2.3.1' 6 | end 7 | 8 | -------------------------------------------------------------------------------- /HTNExamples/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - PINCache (3.0.1-beta.5): 3 | - PINCache/Arc-exception-safe (= 3.0.1-beta.5) 4 | - PINCache/Core (= 3.0.1-beta.5) 5 | - PINCache/Arc-exception-safe (3.0.1-beta.5): 6 | - PINCache/Core 7 | - PINCache/Core (3.0.1-beta.5): 8 | - PINOperation (= 1.0.3) 9 | - PINOperation (1.0.3) 10 | - PINRemoteImage/Core (3.0.0-beta.12): 11 | - PINOperation 12 | - PINRemoteImage/iOS (3.0.0-beta.12): 13 | - PINRemoteImage/Core 14 | - PINRemoteImage/PINCache (3.0.0-beta.12): 15 | - PINCache (= 3.0.1-beta.5) 16 | - PINRemoteImage/Core 17 | - ReactiveCocoa (2.3.1): 18 | - ReactiveCocoa/UI (= 2.3.1) 19 | - ReactiveCocoa/Core (2.3.1): 20 | - ReactiveCocoa/no-arc 21 | - ReactiveCocoa/no-arc (2.3.1) 22 | - ReactiveCocoa/UI (2.3.1): 23 | - ReactiveCocoa/Core 24 | - Texture (2.5.1): 25 | - Texture/PINRemoteImage (= 2.5.1) 26 | - Texture/Core (2.5.1) 27 | - Texture/PINRemoteImage (2.5.1): 28 | - PINRemoteImage/iOS (= 3.0.0-beta.12) 29 | - PINRemoteImage/PINCache 30 | - Texture/Core 31 | 32 | DEPENDENCIES: 33 | - ReactiveCocoa (= 2.3.1) 34 | - Texture (= 2.5.1) 35 | 36 | SPEC CHECKSUMS: 37 | PINCache: 98e7b1ef782824ad231ade51987c218b758c30d8 38 | PINOperation: ac23db44796d4a564ecf9b5ea7163510f579b71d 39 | PINRemoteImage: 0cefe720c2612960bd360710efbd6c73a1991d5f 40 | ReactiveCocoa: 3fe9b233ca6db810e86681b5749488564c1179a1 41 | Texture: c3f06e344aa091667b7a56002e525cde674d021a 42 | 43 | PODFILE CHECKSUM: 8b2a75f66332744932242f3409be56dee897cfa7 44 | 45 | COCOAPODS: 1.3.1 46 | -------------------------------------------------------------------------------- /HTNExamples/Sample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /HTNExamples/Sample.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /HTNExamples/Sample/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // Sample 4 | // 5 | // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. 6 | // This source code is licensed under the BSD-style license found in the 7 | // LICENSE file in the root directory of this source tree. An additional grant 8 | // of patent rights can be found in the PATENTS file in the same directory. 9 | // 10 | 11 | #import 12 | 13 | @interface AppDelegate : UIResponder 14 | @property (strong, nonatomic) UIWindow *window; 15 | @end 16 | 17 | -------------------------------------------------------------------------------- /HTNExamples/Sample/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // Sample 4 | // 5 | // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. 6 | // This source code is licensed under the BSD-style license found in the 7 | // LICENSE file in the root directory of this source tree. An additional grant 8 | // of patent rights can be found in the PATENTS file in the same directory. 9 | // 10 | 11 | #import "AppDelegate.h" 12 | #import "MainViewController.h" 13 | 14 | @implementation AppDelegate 15 | 16 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 17 | 18 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 19 | self.window.backgroundColor = [UIColor whiteColor]; 20 | self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:[MainViewController new]]; 21 | [self.window makeKeyAndVisible]; 22 | 23 | // [[UINavigationBar appearance] setBarTintColor:[UIColor colorWithRed:47/255.0 green:184/255.0 blue:253/255.0 alpha:1.0]]; 24 | // [[UINavigationBar appearance] setTintColor:[UIColor whiteColor]]; 25 | // [[UINavigationBar appearance] setTitleTextAttributes:@{NSForegroundColorAttributeName : [UIColor whiteColor]}];; 26 | 27 | return YES; 28 | } 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /HTNExamples/Sample/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /HTNExamples/Sample/Flexbox.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface Flexbox : ASDisplayNode 4 | 5 | @end 6 | -------------------------------------------------------------------------------- /HTNExamples/Sample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | Sample 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | NSAllowsArbitraryLoads 30 | 31 | 32 | UILaunchStoryboardName 33 | LaunchScreen 34 | UIRequiredDeviceCapabilities 35 | 36 | armv7 37 | 38 | UISupportedInterfaceOrientations 39 | 40 | UIInterfaceOrientationPortrait 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /HTNExamples/Sample/MainViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // MainViewController.h 3 | // Sample 4 | // 5 | // Created by sunshinelww on 2017/11/1. 6 | // Copyright © 2017年 AsyncDisplayKit. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface MainViewController : ASViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /HTNExamples/Sample/MainViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // MainViewController.m 3 | // Sample 4 | // 5 | // Created by sunshinelww on 2017/11/1. 6 | // Copyright © 2017年 AsyncDisplayKit. All rights reserved. 7 | // 8 | 9 | #import "MainViewController.h" 10 | #import "Flexbox.h" 11 | 12 | @interface MainViewController () 13 | 14 | @property (strong, nonatomic)Flexbox *flexBoxNode; 15 | 16 | @end 17 | 18 | @implementation MainViewController 19 | 20 | - (instancetype)init{ 21 | ASDisplayNode *node = [ASDisplayNode new]; 22 | _flexBoxNode = [[Flexbox alloc] init]; 23 | __weak typeof(self) weakSelf = self; 24 | node.automaticallyManagesSubnodes = YES; 25 | node.layoutSpecBlock = ^ASLayoutSpec * _Nonnull(__kindof ASDisplayNode * _Nonnull node, ASSizeRange constrainedSize) { 26 | return [ASCenterLayoutSpec centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringY sizingOptions:ASCenterLayoutSpecSizingOptionMinimumX child:weakSelf.flexBoxNode]; 27 | }; 28 | self=[super initWithNode:node]; 29 | return self; 30 | } 31 | 32 | - (void)viewDidLoad { 33 | [super viewDidLoad]; 34 | // Do any additional setup after loading the view. 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /HTNExamples/Sample/UIColor+Extension.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor+Extension.h 3 | // HTNSample 4 | // 5 | // Created by sunshinelww on 2017/4/6. 6 | // Copyright © 2017年 sunshinelww. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UIColor (Extension) 12 | 13 | +(UIColor *)colorWithHexString:(NSString *)hexString; 14 | 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /HTNExamples/Sample/UIColor+Extension.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor+Extension.m 3 | // HTNSample 4 | // 5 | // Created by sunshinelww on 2017/4/6. 6 | // Copyright © 2017年 sunshinelww. All rights reserved. 7 | // 8 | 9 | #import "UIColor+Extension.h" 10 | 11 | @implementation UIColor (Extension) 12 | 13 | + (UIColor *)colorWithHexString:(NSString *)colorHexString { 14 | CGFloat alpha, red, blue, green; 15 | 16 | NSString *colorString = [[colorHexString stringByReplacingOccurrencesOfString:@"#" withString:@""] uppercaseString]; 17 | switch ([colorString length]) { 18 | case 3: // #RGB 19 | alpha = 1.0f; 20 | red = colorComponentFromHexString(colorString, 0, 1); 21 | green = colorComponentFromHexString(colorString, 1, 1); 22 | blue = colorComponentFromHexString(colorString, 2, 1); 23 | break; 24 | 25 | case 6: // #RRGGBB 26 | alpha = 1.0f; 27 | red = colorComponentFromHexString(colorString, 0, 2); 28 | green = colorComponentFromHexString(colorString, 2, 2); 29 | blue = colorComponentFromHexString(colorString, 4, 2); 30 | break; 31 | 32 | default: 33 | return nil; 34 | } 35 | return [UIColor colorWithRed:red green:green blue:blue alpha:alpha]; 36 | } 37 | 38 | CGFloat colorComponentFromHexString(NSString *string, NSUInteger start, NSUInteger length) { 39 | NSString *substring = [string substringWithRange:NSMakeRange(start, length)]; 40 | NSString *hexString = length == 2 ? substring : [NSString stringWithFormat: @"%@%@", substring, substring]; 41 | 42 | unsigned hexNum; 43 | NSScanner *scanner=[NSScanner scannerWithString:hexString]; 44 | [scanner scanHexInt: &hexNum]; 45 | return hexNum / 255.0; 46 | } 47 | @end 48 | -------------------------------------------------------------------------------- /HTNExamples/Sample/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // Sample 4 | // 5 | // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. 6 | // This source code is licensed under the BSD-style license found in the 7 | // LICENSE file in the root directory of this source tree. An additional grant 8 | // of patent rights can be found in the PATENTS file in the same directory. 9 | // 10 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 11 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 12 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 13 | // FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 14 | // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 15 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | // 17 | 18 | #import 19 | #import "AppDelegate.h" 20 | 21 | int main(int argc, char * argv[]) { 22 | @autoreleasepool { 23 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:4.0 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "HTN", 8 | products: [ 9 | .library(name: "HTN",targets: [ "HTN" ]), 10 | ], 11 | dependencies: [], 12 | targets: [ 13 | .target(name: "HTN", dependencies: [], path: "Sources"), 14 | ] 15 | ) 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HTN 2 | 3 | ![htnbluemap](https://github.com/ming1016/HTN/blob/master/htnbluemap.png?raw=true) 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /Sources/Core/CSS/CSSDeclaration.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CSSDeclaration.swift 3 | // HTNSwift 4 | // 5 | // Created by DaiMing on 2017/10/15. 6 | // Copyright © 2017年 Starming. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class CSSDeclaration { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /Sources/Core/CSS/CSSParser.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CSSParser.swift 3 | // HTNSwift 4 | // 5 | // Created by DaiMing on 2017/10/15. 6 | // Copyright © 2017年 Starming. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class CSSParser { 12 | 13 | public var styleSheet: CSSStyleSheet 14 | 15 | private var _input: String 16 | private var _index: String.Index 17 | private var _bufferStr: String 18 | private var _currentSelector: CSSSelector 19 | private var _currentProperty: CSSProperty 20 | private var _currentRule: CSSRule 21 | 22 | public init(_ input: String) { 23 | self.styleSheet = CSSStyleSheet() 24 | //过滤注释 25 | var newStr = "" 26 | let annotationBlockPattern = "/\\*[\\s\\S]*?\\*/" //匹配/*...*/这样的注释 27 | let regexBlock = try! NSRegularExpression(pattern: annotationBlockPattern, options: NSRegularExpression.Options(rawValue:0)) 28 | newStr = regexBlock.stringByReplacingMatches(in: input, options: NSRegularExpression.MatchingOptions(rawValue:0), range: NSMakeRange(0, input.count), withTemplate: "") 29 | //默认样式 30 | //TODO: 考虑后面需要考虑样式属性需要支持优先级,这里的处理还需要考虑记录更多信息支持后面根据类型判断优先级 31 | let defaultCSSString = CSSDefault.htnCSSDefault() 32 | // let defaultCSSString = "" 33 | newStr = defaultCSSString + newStr 34 | _input = newStr 35 | 36 | //初始化 37 | _index = _input.startIndex 38 | _bufferStr = "" 39 | _currentSelector = CSSSelector() 40 | _currentProperty = CSSProperty() 41 | _currentRule = CSSRule() 42 | } 43 | 44 | //解析 CSS 样式表 45 | public func parseSheet() -> CSSStyleSheet { 46 | 47 | let stateMachine = HTNStateMachine(S.UnknownState) 48 | 49 | stateMachine.listen(E.CommaEvent, transit: [S.UnknownState, S.SelectorState], to: S.SelectorState) { (t) in 50 | self._currentSelector.path = self._bufferStr 51 | self.addSelector() 52 | self.advanceIndexAndResetCurrentStr() 53 | } 54 | stateMachine.listen(E.BraceLeftEvent, transit: [S.UnknownState, S.SelectorState], to: S.PropertyKeyState) { (t) in 55 | self._currentSelector.path = self._bufferStr 56 | self.addSelector() 57 | self.advanceIndexAndResetCurrentStr() 58 | } 59 | stateMachine.listen(E.ColonEvent, transit: S.PropertyKeyState, to: S.PropertyValueState) { (t) in 60 | self._currentProperty.key = self._bufferStr.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines).lowercased() 61 | self.advanceIndexAndResetCurrentStr() 62 | } 63 | stateMachine.listen(E.SemicolonEvent, transit: S.PropertyValueState, to: S.PropertyKeyState) { (t) in 64 | self._currentProperty.value = self._bufferStr.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines).lowercased() 65 | self.addProperty() 66 | self.advanceIndexAndResetCurrentStr() 67 | } 68 | //加上 PropertyValueState 的原因是需要支持 property 最后一个不需要写 ; 的情况 69 | stateMachine.listen(E.BraceRightEvent, transit: [S.PropertyKeyState,S.PropertyValueState], to: S.UnknownState) { (t) in 70 | if self._bufferStr.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines).count > 0 { 71 | self._currentProperty.value = self._bufferStr.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines).lowercased() 72 | self.addProperty() 73 | } 74 | self.addRule() 75 | self.advanceIndexAndResetCurrentStr() 76 | } 77 | 78 | while let aChar = currentChar { 79 | let aStr = aChar.description 80 | var hasAStrTrigger = false 81 | //deal with aStr 82 | if aStr == "," { hasAStrTrigger = stateMachine.trigger(E.CommaEvent) } 83 | if aStr == "." { hasAStrTrigger = stateMachine.trigger(E.DotEvent) } 84 | if aStr == "#" { hasAStrTrigger = stateMachine.trigger(E.HashTagEvent) } 85 | if aStr == "{" { hasAStrTrigger = stateMachine.trigger(E.BraceLeftEvent) } 86 | if aStr == "}" { hasAStrTrigger = stateMachine.trigger(E.BraceRightEvent) } 87 | if aStr == ":" { hasAStrTrigger = stateMachine.trigger(E.ColonEvent) } 88 | if aStr == ";" { hasAStrTrigger = stateMachine.trigger(E.SemicolonEvent) } 89 | 90 | if !hasAStrTrigger { 91 | addBufferStr(aStr) 92 | advanceIndex() 93 | } 94 | } 95 | 96 | return self.styleSheet 97 | } 98 | 99 | //parser tool 100 | var currentChar: Character? { 101 | return _index < _input.endIndex ? _input[_index] : nil 102 | } 103 | func addBufferStr(_ bufferStr: String) { 104 | _bufferStr += bufferStr 105 | } 106 | func advanceIndex() { 107 | _input.formIndex(after: &_index) 108 | } 109 | func advanceIndexAndResetCurrentStr() { 110 | _bufferStr = "" 111 | advanceIndex() 112 | } 113 | //add 114 | func addSelector() { 115 | let selector = _currentSelector 116 | _currentRule.selectorList.append(selector) 117 | _currentSelector = CSSSelector() 118 | } 119 | func addProperty() { 120 | let property = _currentProperty 121 | _currentRule.propertyList.append(property) 122 | _currentRule.propertyMap[property.key] = property.value 123 | _currentProperty = CSSProperty() 124 | } 125 | func addRule() { 126 | let rule = _currentRule 127 | styleSheet.ruleList.append(rule) 128 | _currentRule = CSSRule() 129 | } 130 | 131 | enum S: HTNStateType { 132 | case UnknownState // 133 | case SelectorState // 比如 div p, #id 134 | case PropertyKeyState // 属性的 key 135 | case PropertyValueState // 属性的 value 136 | 137 | //TODO:以下后期支持,优先级2 138 | case PseudoClass // :nth-child(2) 139 | case PseudoElement // ::first-line 140 | 141 | //TODO:以下后期支持,优先级3 142 | case PagePseudoClass 143 | case AttributeExact // E[attr] 144 | case AttributeSet // E[attr|="value"] 145 | case AttributeHyphen // E[attr~="value"] 146 | case AttributeList // E[attr*="value"] 147 | case AttributeContain // E[attr^="value"] 148 | case AttributeBegin // E[attr$="value"] 149 | case AttributeEnd 150 | //TODO:@media 这类 @规则 ,后期支持,优先级4 151 | } 152 | enum E: HTNEventType { 153 | case SpaceEvent //空格 154 | case CommaEvent // , 155 | case DotEvent // . 156 | case HashTagEvent // # 157 | case BraceLeftEvent // { 158 | case BraceRightEvent // } 159 | case ColonEvent // : 160 | case SemicolonEvent // ; 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /Sources/Core/CSS/CSSProperty.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CSSProperty.swift 3 | // HTNSwift 4 | // 5 | // Created by DaiMing on 2017/10/15. 6 | // Copyright © 2017年 Starming. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class CSSProperty { 12 | public var key = "" 13 | public var value = "" 14 | } 15 | -------------------------------------------------------------------------------- /Sources/Core/CSS/CSSRule.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CSSRule.swift 3 | // HTNSwift 4 | // 5 | // Created by DaiMing on 2017/10/15. 6 | // Copyright © 2017年 Starming. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class CSSRule { 12 | public var selectorList = [CSSSelector]() 13 | public var propertyList = [CSSProperty]() 14 | public var propertyMap = [String:String]() 15 | } 16 | -------------------------------------------------------------------------------- /Sources/Core/CSS/CSSSelector.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CSSSelector.swift 3 | // HTNSwift 4 | // 5 | // Created by DaiMing on 2017/10/15. 6 | // Copyright © 2017年 Starming. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class CSSSelector { 12 | public var path : String { 13 | get { 14 | return String() 15 | } 16 | set(newPath) { 17 | self.identifier = newPath.lowercased().trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) 18 | self.matchList = self.identifier.components(separatedBy: " ") 19 | } 20 | } 21 | public var matchList = [String]() 22 | public var identifier = "" 23 | } 24 | -------------------------------------------------------------------------------- /Sources/Core/CSS/CSSStyleSheet.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CSSStyleSheet.swift 3 | // HTNSwift 4 | // 5 | // Created by DaiMing on 2017/10/15. 6 | // Copyright © 2017年 Starming. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class CSSStyleSheet { 12 | public var ruleList = [CSSRule]() 13 | } 14 | -------------------------------------------------------------------------------- /Sources/Core/HTML/Attribute.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Attribute.swift 3 | // HTNSwift 4 | // 5 | // Created by DaiMing on 2017/10/11. 6 | // Copyright © 2017年 Starming. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | open class Attribute { 12 | public var name: String 13 | public var value: String 14 | init() { 15 | self.name = "" 16 | self.value = "" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Sources/Core/HTML/Document.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Document.swift 3 | // HTNSwift 4 | // 5 | // Created by DaiMing on 2017/10/11. 6 | // Copyright © 2017年 Starming. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class Document : Element { 12 | public var styleList = [Element]() 13 | public var scriptList = [Element]() 14 | 15 | public func allStyle() -> String { 16 | var allStyle = "" 17 | for style in styleList { 18 | guard let str = style.charToken?.data else { 19 | continue 20 | } 21 | allStyle += str 22 | } 23 | return allStyle 24 | } 25 | 26 | public func allScript() -> String { 27 | var allScript = "" 28 | for script in scriptList { 29 | guard let str = script.charToken?.data else { 30 | continue 31 | } 32 | allScript += str 33 | } 34 | return allScript 35 | } 36 | 37 | //打印 Dom 树 38 | public func des() { 39 | for elm in self.children { 40 | desElement(elm: elm as! Element, level: 0) 41 | } 42 | } 43 | 44 | private func desElement(elm:Element, level:Int) { 45 | var propertyStr = "" 46 | var attributeStr = "" 47 | if elm.startTagToken != nil { 48 | if elm.startTagToken!.attributeList.count > 0 { 49 | attributeStr = "[ATTR]:" 50 | for attr in (elm.startTagToken?.attributeList)! { 51 | attributeStr += " \(attr.name):\(attr.value)" 52 | } 53 | } 54 | } 55 | if elm.propertyMap.count > 0 { 56 | propertyStr = "[CSS]:" 57 | for property in elm.propertyMap { 58 | propertyStr += " \(property.key):\(property.value)" 59 | } 60 | } 61 | var frontStr = ""; 62 | for _ in 0...level { 63 | if level > 0 { 64 | frontStr += " " 65 | } 66 | } 67 | print("\(frontStr)[\(elm.startTagToken?.data.uppercased() ?? "CHAR")] \(attributeStr) \(propertyStr)") 68 | if elm.children.count > 0 { 69 | for child in elm.children { 70 | self.desElement(elm: child as! Element, level: level + 1) 71 | } 72 | } 73 | } 74 | 75 | } 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /Sources/Core/HTML/Element.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Element.swift 3 | // HTNSwift 4 | // 5 | // Created by DaiMing on 2017/10/11. 6 | // Copyright © 2017年 Starming. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class Element : Node { 12 | public var startTagToken: HTMLToken? 13 | public var charToken: HTMLToken? 14 | public var propertyMap = [String:String]() //属性映射表 15 | public var renderer:RenderObject? //代表元素的渲染对象 16 | 17 | override init() { 18 | 19 | } 20 | 21 | init(token: HTMLToken) { 22 | 23 | if token.type == .StartTag { 24 | self.startTagToken = token 25 | } 26 | if token.type == .Char { 27 | self.charToken = token 28 | } 29 | } 30 | 31 | public func createRenderObject (){ 32 | renderer=RenderObject(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/Core/HTML/HTMLToken.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HTMLToken.swift 3 | // HTNSwift 4 | // 5 | // Created by DaiMing on 2017/10/11. 6 | // Copyright © 2017年 Starming. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class HTMLToken { 12 | public var type: tokenType 13 | public var data: String 14 | public var selfClosing: Bool 15 | public var attributeList: [Attribute] 16 | public var attributeDic: [String:String] 17 | public var currentAttribute: Attribute 18 | 19 | init() { 20 | self.type = tokenType.DocType 21 | self.data = "" 22 | self.selfClosing = false 23 | self.attributeList = [Attribute]() 24 | self.attributeDic = [String:String]() 25 | self.currentAttribute = Attribute() 26 | } 27 | 28 | public enum tokenType { 29 | case DocType 30 | case StartTag 31 | case EndTag 32 | case Comment 33 | case Char 34 | case EOF 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Sources/Core/HTML/HTMLTokenizer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HTMLTokenizer.swift 3 | // HTNSwift 4 | // 5 | // Created by DaiMing on 2017/10/11. 6 | // Copyright © 2017年 Starming. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class HTMLTokenizer { 12 | private let _input: String 13 | private var _index: String.Index 14 | private var _bufferStr: String 15 | private var _bufferToken: HTMLToken 16 | private var _tks: [HTMLToken] 17 | //TODO: add doctypeData for DOCTYPE 18 | public init(_ input: String) { 19 | _input = input 20 | _index = input.startIndex 21 | _bufferStr = "" 22 | _bufferToken = HTMLToken() 23 | _tks = [HTMLToken]() 24 | } 25 | public func parse() -> [HTMLToken]{ 26 | //初始化状态机 27 | let stateMachine = HTNStateMachine(S.DataState) 28 | 29 | //状态机监听 state 30 | //不同事件在来源是集合状态的转成统一状态的处理 31 | //TODO:根据 w3c 标准 https://dev.w3.org/html5/spec-preview/tokenization.html 来完善状态机,添加更多状态的处理 32 | let anglebracketRightEventFromStatesArray = [S.DOCTYPEState, 33 | S.CommentEndState, 34 | S.TagOpenState, 35 | S.EndTagOpenState, 36 | S.AfterAttributeValueQuotedState, 37 | S.BeforeDOCTYPENameState, 38 | S.AfterDOCTYPEPublicIdentifierState] 39 | stateMachine.listen(E.AngleBracketRight, transit: anglebracketRightEventFromStatesArray, to: S.DataState) { (t) in 40 | if t.fromState == S.TagOpenState || t.fromState == S.EndTagOpenState { 41 | if self._bufferStr.count > 0 { 42 | self._bufferToken.data = self._bufferStr.lowercased() 43 | } 44 | } 45 | self.addHTMLToken() 46 | self.advanceIndexAndResetCurrentStr() 47 | } 48 | 49 | //DataState 50 | stateMachine.listen(E.AngleBracketLeft, transit: S.DataState, to: S.TagOpenState) { (t) in 51 | self._bufferStr = self._bufferStr.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) 52 | if self._bufferStr.count > 0 { 53 | self._bufferToken.type = .Char 54 | self._bufferToken.data = self._bufferStr 55 | self.addHTMLToken() 56 | } 57 | self._bufferToken.type = .StartTag 58 | self.advanceIndexAndResetCurrentStr() 59 | } 60 | stateMachine.listen(E.And, transit: S.DataState, to: S.CharacterReferenceInDataState) { (t) in 61 | self.advanceIndexAndResetCurrentStr() 62 | } 63 | //TagOpenState 64 | stateMachine.listen(E.Exclamation, transit: S.TagOpenState, to: S.MarkupDeclarationOpenState) { (t) in 65 | self.advanceIndexAndResetCurrentStr() 66 | } 67 | //TODO:补上这种需要判断开始时字母,或者特定首字母集进入的状态,比如 TagNameState 和 BeforeAttributeNameState,但需要权衡下,如果不加这个状态会少不少检查 68 | stateMachine.listen(E.Space, transit: S.TagOpenState, to: S.AttributeNameState) { (t) in 69 | self._bufferToken.data = self._bufferStr.lowercased() 70 | self.advanceIndexAndResetCurrentStr() 71 | } 72 | stateMachine.listen(E.Slash, transit: S.TagOpenState, to: S.EndTagOpenState) { (t) in 73 | if self._bufferStr.count == 0 { 74 | self._bufferToken.type = .EndTag 75 | } else { 76 | self._bufferToken.data = self._bufferStr.lowercased() 77 | self._bufferToken.selfClosing = true 78 | } 79 | self.advanceIndexAndResetCurrentStr() 80 | } 81 | //AttributeNameState 82 | stateMachine.listen(E.Equal, transit: [S.AttributeNameState,S.AfterAttributeValueQuotedState], to: S.BeforeAttributeValueState) { (t) in 83 | self._bufferToken.currentAttribute.name = self._bufferStr.lowercased().trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) 84 | self.advanceIndexAndResetCurrentStr() 85 | } 86 | //BeforeAttributeValueState 87 | stateMachine.listen(E.Quotation, transit: S.BeforeAttributeValueState, to: S.AttributeValueDoubleQuotedState) { (t) in 88 | self.advanceIndexAndResetCurrentStr() 89 | } 90 | //AttributeValueDoubleQuotedState 91 | stateMachine.listen(E.Quotation, transit: S.AttributeValueDoubleQuotedState, to: S.AfterAttributeValueQuotedState) { (t) in 92 | self._bufferToken.currentAttribute.value = self._bufferStr 93 | self._bufferToken.attributeList.append(self._bufferToken.currentAttribute) 94 | self._bufferToken.attributeDic[self._bufferToken.currentAttribute.name] = self._bufferToken.currentAttribute.value 95 | self._bufferToken.currentAttribute = Attribute() 96 | self.advanceIndexAndResetCurrentStr() 97 | } 98 | //AfterAttributeValueQuotedState 99 | stateMachine.listen(E.Slash, transit: S.AfterAttributeValueQuotedState, to: S.EndTagOpenState) { (t) in 100 | self._bufferToken.selfClosing = true 101 | self.advanceIndexAndResetCurrentStr() 102 | } 103 | stateMachine.listen(E.InStyleOrScript, transit: S.AfterAttributeValueQuotedState, to: S.PLAINTEXTState) { (t) in 104 | self.addHTMLToken() 105 | self.advanceIndexAndResetCurrentStr() 106 | } 107 | //MarkupDeclarationOpenState 108 | stateMachine.listen(E.DocType, transit: S.MarkupDeclarationOpenState, to: S.DOCTYPEState) { (t) in 109 | self._bufferToken.type = .DocType 110 | self.advanceIndexAndResetCurrentStr() 111 | } 112 | stateMachine.listen(E.DoubleMinus, transit: S.MarkupDeclarationOpenState, to: S.CommentStartState) { (t) in 113 | self._bufferToken.type = .Comment 114 | self.advanceIndexAndResetCurrentStr() 115 | } 116 | //CommentStartState 117 | stateMachine.listen(E.Minus, transit: S.CommentStartState, to: S.CommentEndDashState) { (t) in 118 | self._bufferToken.data = self._bufferStr 119 | self.advanceIndexAndResetCurrentStr() 120 | } 121 | //CommentEndDashState 122 | stateMachine.listen(E.Minus, transit: S.CommentEndDashState, to: S.CommentEndState) { (t) in 123 | self.advanceIndexAndResetCurrentStr() 124 | } 125 | //DOCTYPEState 126 | stateMachine.listen(E.Space, transit: S.DOCTYPEState, to: S.BeforeDOCTYPENameState) { (t) in 127 | self.advanceIndexAndResetCurrentStr() 128 | } 129 | //BeforeDOCTYPENameState 130 | stateMachine.listen(E.Space, transit: S.BeforeDOCTYPENameState, to: S.AfterDOCTYPENameState) { (t) in 131 | self._bufferToken.data = self._bufferStr 132 | self.advanceIndexAndResetCurrentStr() 133 | } 134 | //AfterDOCTYPENameState 135 | stateMachine.listen(E.Public, transit: S.AfterDOCTYPENameState, to: S.AfterDOCTYPEPublicKeywordState) { (t) in 136 | self.advanceIndexAndResetCurrentStr() 137 | } 138 | //AfterDOCTYPEPublicKeywordState 139 | stateMachine.listen(E.Space, transit: S.AfterDOCTYPEPublicKeywordState, to: S.BeforeDOCTYPEPublicIdentifierState) { (t) in 140 | self.advanceIndexAndResetCurrentStr() 141 | } 142 | //BeforeDOCTYPEPublicIdentifierState 143 | //暂时仅仅记录 DoubleQuoted 状态,不区分单引号和双引号 144 | stateMachine.listen(E.Quotation, transit: S.BeforeDOCTYPEPublicIdentifierState, to: S.DOCTYPEPublicIdentifierDoubleQuotedState) { (t) in 145 | self.advanceIndexAndResetCurrentStr() 146 | } 147 | //DOCTYPEPublicIdentifierDoubleQuotedState 148 | stateMachine.listen(E.Quotation, transit: S.DOCTYPEPublicIdentifierDoubleQuotedState, to: S.AfterDOCTYPEPublicIdentifierState) { (t) in 149 | //TODO:记录 doctypeData 150 | self.advanceIndexAndResetCurrentStr() 151 | } 152 | //PLAINTEXTState 153 | stateMachine.listen(E.AngleBracketLeft, transit: S.PLAINTEXTState, to: S.TagOpenState) { (t) in 154 | if self._bufferStr.count > 0 { 155 | self._bufferToken.type = .Char 156 | self._bufferToken.data = self._bufferStr 157 | self.addHTMLToken() 158 | } 159 | self._bufferToken.type = .StartTag 160 | self.advanceIndexAndResetCurrentStr() 161 | } 162 | 163 | while let aChar = currentChar { 164 | 165 | let aStr = aChar.description 166 | var hasAStrTrigger = false 167 | var hasBufferStrTrigger = false 168 | //deal with aStr 169 | if aStr == "!" { 170 | hasAStrTrigger = stateMachine.trigger(E.Exclamation) 171 | } 172 | if aStr == "<" { 173 | hasAStrTrigger = stateMachine.trigger(E.AngleBracketLeft) 174 | } 175 | if aStr == ">" { 176 | if _bufferToken.type == .StartTag && (_bufferToken.data.lowercased() == "style" || _bufferToken.data.lowercased() == "script") { 177 | hasAStrTrigger = stateMachine.trigger(E.InStyleOrScript) 178 | } else { 179 | hasAStrTrigger = stateMachine.trigger(E.AngleBracketRight) 180 | } 181 | 182 | } 183 | if aStr == " " { 184 | hasAStrTrigger = stateMachine.trigger(E.Space) 185 | } 186 | if self.isQuotation(s: aStr) { 187 | hasAStrTrigger = stateMachine.trigger(E.Quotation) 188 | } 189 | if aStr == "-" { 190 | hasAStrTrigger = stateMachine.trigger(E.Minus) 191 | } 192 | if aStr == "=" { 193 | hasAStrTrigger = stateMachine.trigger(E.Equal) 194 | } 195 | if aStr == "/" { 196 | hasAStrTrigger = stateMachine.trigger(E.Slash) 197 | } 198 | 199 | //deal with bufferStr 200 | if !hasAStrTrigger { 201 | addBufferStr(aStr) 202 | } else { 203 | continue 204 | } 205 | 206 | if _bufferStr.lowercased() == "doctype" { 207 | hasBufferStrTrigger = stateMachine.trigger(E.DocType) 208 | } 209 | if _bufferStr.lowercased() == "public" { 210 | hasBufferStrTrigger = stateMachine.trigger(E.Public) 211 | } 212 | if _bufferStr.lowercased() == "--" { 213 | hasBufferStrTrigger = stateMachine.trigger(E.DoubleMinus) 214 | } 215 | if !hasBufferStrTrigger { 216 | self.advanceIndex() 217 | } 218 | } 219 | return _tks 220 | } 221 | 222 | //tiny tool 223 | var currentChar: Character? { 224 | return _index < _input.endIndex ? _input[_index] : nil 225 | } 226 | func addBufferStr(_ bufferStr: String) { 227 | _bufferStr += bufferStr 228 | } 229 | func isQuotation(s:String) -> Bool { 230 | if s == "\"" || s == "'" { 231 | return true 232 | } 233 | return false 234 | } 235 | //添加 token 236 | func addHTMLToken() { 237 | let tk = _bufferToken 238 | _tks.append(tk) 239 | _bufferToken = HTMLToken() 240 | } 241 | func advanceIndex() { 242 | _input.formIndex(after: &_index) 243 | } 244 | func advanceIndexAndResetCurrentStr() { 245 | _bufferStr = "" 246 | advanceIndex() 247 | } 248 | 249 | //枚举 250 | enum S: HTNStateType { 251 | case DataState //half done 252 | case CharacterReferenceInDataState 253 | case RCDATAState 254 | case CharacterReferenceInRCDATAState 255 | case RAWTEXTState 256 | case ScriptDataState 257 | case PLAINTEXTState 258 | case TagOpenState //half done 259 | case EndTagOpenState 260 | case TagNameState //half done 261 | 262 | case RCDATALessThanSignState 263 | case RCDATAEndTagOpenState 264 | case RCDATAEndTagNameState 265 | 266 | case RAWTEXTLessThanSignState 267 | case RAWTEXTEndTagOpenState 268 | case RAWTEXTEndTagNameState 269 | 270 | //Script 271 | case ScriptDataLessThanSignState 272 | case ScriptDataEndTagOpenState 273 | case ScriptDataEndTagNameState 274 | case ScriptDataEscapeStartState 275 | case ScriptDataEscapeStartDashState 276 | case ScriptDataEscapedState 277 | case ScriptDataEscapedDashState 278 | case ScriptDataEscapedDashDashState 279 | case ScriptDataEscapedLessThanSignState 280 | case ScriptDataEscapedEndTagOpenState 281 | case ScriptDataEscapedEndTagNameState 282 | case ScriptDataDoubleEscapeStartState 283 | case ScriptDataDoubleEscapedState 284 | case ScriptDataDoubleEscapedDashState 285 | case ScriptDataDoubleEscapedDashDashState 286 | case ScriptDataDoubleEscapedLessThanSignState 287 | case ScriptDataDoubleEscapeEndState 288 | 289 | //Tag 290 | case BeforeAttributeNameState 291 | case AttributeNameState //half done 292 | case AfterAttributeNameState 293 | case BeforeAttributeValueState 294 | case AttributeValueDoubleQuotedState //half done 295 | case AttributeValueSingleQuotedState 296 | case AttributeValueUnquotedState 297 | case CharacterReferenceInAttributeValueState 298 | case AfterAttributeValueQuotedState //half done 299 | case SelfClosingStartTagState 300 | case BogusCommentState 301 | case ContinueBogusCommentState 302 | case MarkupDeclarationOpenState //half done 303 | 304 | //Comment 305 | case CommentStartState //half done 306 | case CommentStartDashState 307 | case CommentState 308 | case CommentEndDashState //half done 309 | case CommentEndState //half done 310 | case CommentEndBangState 311 | 312 | //DOCTYPE 313 | case DOCTYPEState //half done 314 | case BeforeDOCTYPENameState //half done 315 | case DOCTYPENameState 316 | case AfterDOCTYPENameState //half done 317 | case AfterDOCTYPEPublicKeywordState //half done 318 | case BeforeDOCTYPEPublicIdentifierState //half done 319 | case DOCTYPEPublicIdentifierDoubleQuotedState //half done 320 | case DOCTYPEPublicIdentifierSingleQuotedState 321 | case AfterDOCTYPEPublicIdentifierState //half done 322 | case BetweenDOCTYPEPublicAndSystemIdentifiersState 323 | case AfterDOCTYPESystemKeywordState 324 | case BeforeDOCTYPESystemIdentifierState 325 | case DOCTYPESystemIdentifierDoubleQuotedState 326 | case DOCTYPESystemIdentifierSingleQuotedState 327 | case AfterDOCTYPESystemIdentifierState 328 | case BogusDOCTYPEState 329 | 330 | case CDATASectionState 331 | case CDATASectionRightSquareBracketState 332 | case CDATASectionDoubleRightSquareBracketState 333 | } 334 | enum E: HTNEventType { 335 | case Advance 336 | case AngleBracketLeft // < 337 | case AngleBracketRight // > 338 | case And // & 339 | case Space // 空格 340 | case Exclamation // ! 341 | case DocType // doctype 342 | case Public // public 343 | case Quotation // ' 或 " 344 | case DoubleMinus // -- 345 | case Minus // - 346 | case Equal // = 347 | case Slash // / 348 | case InStyleOrScript // 349 | case MarkEndTagForSure //TODO: fix script 里可能出现 < 符号的问题,这个事件触发发生在 _bufferStr == " [HTMLToken] { 28 | 29 | let tks = tokenizer.parse() //词法分析 30 | 31 | var stackElement = [Element]() //为了父子级别而记录深度的堆栈 32 | let stateMachine = HTNStateMachine(S.BeforeHTMLState) 33 | 34 | stateMachine.listen(E.StartTagEvent, transit: S.InitialModeState, to: S.BeforeHTMLState) { (t) in 35 | //TODO:暂时只支持 html 标签内,所以外部的定义先不处理 36 | } 37 | stateMachine.listen(E.StartTagEvent, transit: S.BeforeHTMLState, to: S.BeforeHeadState) { (t) in 38 | //TODO:根 Node Document 39 | } 40 | stateMachine.listen(E.StartTagEvent, transit: S.BeforeHeadState, to: S.InHeadState) { (t) in 41 | // 42 | } 43 | stateMachine.listen(E.CharEvent, transit: S.InHeadState, to: S.InHeadState) { (t) in 44 | //在 head 里 45 | if self._currentParent?.startTagToken?.data == "style" { 46 | self.doc.styleList.append(self._currentElement) 47 | } 48 | if self._currentParent?.startTagToken?.data == "script" { 49 | self.doc.scriptList.append(self._currentElement) 50 | } 51 | } 52 | 53 | //InHeadState 54 | stateMachine.listen(E.EndHeadTagEvent, transit: S.InHeadState, to: S.AfterHeadState) { (t) in 55 | // 56 | } 57 | 58 | //AfterHeadState 59 | stateMachine.listen(E.StartTagEvent, transit: S.AfterHeadState, to: S.InBodyState) { (t) in 60 | // 61 | } 62 | stateMachine.listen(E.StartTagEvent, transit: S.InBodyState, to: S.InBodyState) { (t) in 63 | //TODO: 处理 inline style 64 | } 65 | 66 | //TODO: AfterBodyState 和 AfterAfterBodyState 的情况 67 | 68 | for tk in tks { 69 | var hasTrigger = false 70 | //TODO:现将无关的过滤之,以后再做处理 71 | if tk.type == .StartTag || tk.type == .Char || tk.type == .EndTag { 72 | } else { 73 | continue 74 | } 75 | _currentElement = Element(token: tk) 76 | //根元素的处理 77 | if tk.data == "html" && tk.type == .StartTag { 78 | _currentElement = Document(token: tk) 79 | doc = _currentElement as! Document 80 | } 81 | 82 | //StartTag 83 | if tk.type == .StartTag { 84 | stackElement.append(_currentElement) //堆栈添加 85 | 86 | //子关闭标签的情况 87 | if tk.selfClosing { 88 | _ = stackElement.popLast() 89 | _currentParent = stackElement.last 90 | self.parentAppendChild() 91 | } else { 92 | self.parentAppendChild() 93 | _currentParent = _currentElement 94 | } 95 | 96 | hasTrigger = stateMachine.trigger(E.StartTagEvent) 97 | } 98 | 99 | //Char 100 | if tk.type == .Char { 101 | //添加子结点 102 | self.parentAppendChild() 103 | hasTrigger = stateMachine.trigger(E.CharEvent) 104 | } 105 | 106 | //EndTag 107 | if tk.type == .EndTag { 108 | //pop 出堆栈 109 | _ = stackElement.popLast() 110 | _currentParent = stackElement.last 111 | if tk.data == "head" { 112 | hasTrigger = stateMachine.trigger(E.EndHeadTagEvent) 113 | } else { 114 | hasTrigger = stateMachine.trigger(E.EndTagEvent) 115 | } 116 | } 117 | if hasTrigger { 118 | 119 | } 120 | } 121 | 122 | return tks 123 | } 124 | 125 | func parentAppendChild() { 126 | _currentElement.parent = _currentParent 127 | _currentParent?.children.append(_currentElement) 128 | } 129 | 130 | //TODO: 按照 w3c 的状态来。 131 | //w3c 的定义:https://www.w3.org/TR/html5/syntax.html#html-parser 132 | enum S: HTNStateType { 133 | case InitialModeState 134 | case BeforeHTMLState 135 | case BeforeHeadState 136 | case InHeadState 137 | case AfterHeadState 138 | case InBodyState 139 | case AfterBodyState 140 | case AfterAfterBodyState 141 | } 142 | 143 | enum E: HTNEventType { 144 | case StartTagEvent 145 | case CharEvent 146 | case EndTagEvent 147 | case EndHeadTagEvent // 148 | case EndBodyTagEvent //TODO: 先不处理 标签后的情况 149 | } 150 | 151 | } 152 | -------------------------------------------------------------------------------- /Sources/Core/HTNFundation/HTNMultilingualism.swift: -------------------------------------------------------------------------------- 1 | // 2 | // H5EditorMultilingualism.swift 3 | // HTNSwift 4 | // 5 | // Created by DaiMing on 2018/3/21. 6 | // Copyright © 2018年 Starming. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | //多语言协议规范 12 | public protocol HTNMultilingualismSpecification { 13 | var pageId: String {get set} //页面 id 14 | var id: String {get set} //动态 id 15 | var selfId: String {get} //self.id 16 | var selfStr: String {get} //self 17 | var selfPtStr: String {get} //self. 18 | func validIdStr(id: String) -> String //对 id 字符串的合法化 19 | 20 | func newEqualStr(vType:HTNMt.ViewType,id:String) -> String //初始化一个类型对象 21 | 22 | func viewPtToStrStruct(vpt:HTNMt.ViewPt) -> HTNMt.ViewStrStruct //视图属性转视图结构 23 | 24 | func viewTypeClassStr(vt:HTNMt.ViewType) -> String //视图类型类名的映射 25 | func ptEqualToStr(pe:HTNMt.PtEqual) -> String //属性表达式 26 | func addSubViewStr(host: String,sub: String) -> String //添加 subView 27 | func impFile(impf:HTNMt.ImpFile) -> String //实现文件的内容 28 | func interfaceFile(intf:HTNMt.InterfaceFile) -> String //接口文件的内容 29 | func idProperty(pt: HTNMt.WgPt, idPar: String, prefix: String, equalT: HTNMt.EqualType) -> String //属性 30 | 31 | func flowViewLayout(fl:HTNMt.Flowly) -> String //flow 布局 32 | func scale(_ v:Float) -> String //适应屏幕尺寸 scale 转换 33 | 34 | func sizeToFit(elm:String) -> String //处理label sizeToFit 35 | } 36 | extension HTNMultilingualismSpecification { 37 | public func flowViewLayout(fl:HTNMt.Flowly) -> String { 38 | let cId = id + "Container" 39 | var lyStr = "" 40 | //UIView *myViewContainer = [UIView new]; 41 | lyStr += newEqualStr(vType: .view, id: cId) + "\n" 42 | 43 | //属性拼装 44 | lyStr += HTNMt.PtEqualC().accumulatorLine({ (pe) -> String in 45 | return self.ptEqualToStr(pe: pe) 46 | }).once({ (p) in 47 | p.left(.top).leftId(cId).end() 48 | if fl.isFirst { 49 | //myViewContainer.top = 0.0; 50 | p.rightType(.float).rightFloat(0).add() 51 | } else { 52 | //myViewContainer.top = lastView.bottom; 53 | p.rightId(fl.lastId + "Container").rightType(.pt).right(.bottom).add() 54 | } 55 | }).once({ (p) in 56 | //myViewContainer.left = 0.0; 57 | p.leftId(cId).left(.left).rightType(.float).rightFloat(0).add() 58 | }).once({ (p) in 59 | //myViewContainer.width = self.myView.width; 60 | p.leftId(cId).left(.width).rightType(.pt).rightIdPrefix(selfPtStr).rightId(id).right(.width).add() 61 | 62 | //myViewContainer.height = self.myView.height; 63 | p.left(.height).right(.height).add() 64 | }).once({ (p) in 65 | //self.myView.width -= 16 * 2; 66 | p.left(.width).leftId(id).leftIdPrefix(selfPtStr).rightType(.float).rightFloat(fl.viewPt.padding.left * 2).equalType(.decrease).add() 67 | 68 | //self.myView.height -= 8 * 2; 69 | p.left(.height).rightFloat(fl.viewPt.padding.top * 2).add() 70 | 71 | //self.myView.top = 8; 72 | p.equalType(.normal).left(.top).rightType(.float).rightFloat(fl.viewPt.padding.top).add() 73 | 74 | //属性 verticalAlign 或 horizontalAlign 是 padding 和其它排列时的区别处理 75 | if fl.viewPt.horizontalAlign == .padding { 76 | //self.myView.left = 16; 77 | p.left(.left).rightFloat(fl.viewPt.padding.left).add() 78 | } else { 79 | //[self.myView sizeToFit]; 80 | p.add(sizeToFit(elm: "\(selfId)")) 81 | p.left(.height).rightType(.pt).rightId(cId).right(.height).add() 82 | switch fl.viewPt.horizontalAlign { 83 | case .center: 84 | p.left(HTNMt.WgPt.center).right(.center).add() 85 | case .left: 86 | p.left(.left).right(.left).add() 87 | case .right: 88 | p.left(.right).right(.right).add() 89 | default: 90 | () 91 | } 92 | } 93 | 94 | 95 | }).mutiEqualStr 96 | 97 | //[myViewContainer addSubview:self.myView]; 98 | lyStr += addSubViewStr(host: cId, sub: "\(selfId)") + "\n" 99 | //[self addSubview:myViewContainer]; 100 | lyStr += addSubViewStr(host: selfStr, sub: cId) + "\n" 101 | 102 | return lyStr 103 | } 104 | } 105 | /*-----------协议所需结构和枚举------------*/ 106 | public struct HTNMt { 107 | //flow布局 108 | public struct Flowly { 109 | var id = "" 110 | var lastId = "" 111 | var isFirst = false 112 | var viewPt = ViewPt() 113 | } 114 | struct Padding { 115 | var top:Float = 0 116 | var left:Float = 0 117 | var bottom:Float = 0 118 | var right:Float = 0 119 | } 120 | //视图类型 121 | public enum ViewType { 122 | case view,label,image,button,scrollView 123 | } 124 | enum LayoutType { 125 | case normal,flow 126 | } 127 | //视图属性数据结构 128 | public struct ViewPt { 129 | var id = "" 130 | var viewType:ViewType = .label 131 | var layoutType:LayoutType = .normal 132 | var isNormal = false 133 | var text = "" 134 | var fontSize:Float = 32 135 | var textColor = "" 136 | var top:Float = 0 137 | var left:Float = 0 138 | var width:Float = 0 139 | var height:Float = 0 140 | var padding = Padding() 141 | var verticalAlign:VerticalAlign = .padding 142 | var horizontalAlign:HorizontalAlign = .padding 143 | var imageUrl = "" //图片链接 144 | var bgColor = "" //背景色 145 | var radius:Float = 0 //圆角 146 | var borderColor = "" //边框颜色 147 | var borderWidth:Float = 0 //边框宽度 148 | var hasBorder = false //是否有边框 149 | var redirectUrl = "" //点击跳转地址 150 | } 151 | enum VerticalAlign { 152 | case padding 153 | case middle 154 | case top 155 | case bottom 156 | } 157 | enum HorizontalAlign: Int { 158 | case left = 0 159 | case center = 1 160 | case right = 2 161 | case padding = 3 162 | } 163 | 164 | public struct ViewStrStruct { 165 | let propertyStr: String 166 | let initStr: String 167 | let getterStr: String 168 | let layoutStr: String 169 | var viewPt: ViewPt 170 | } 171 | 172 | //属性类型定义 173 | public enum WgPt { 174 | case none 175 | case new 176 | case top,bottom,left,right,center,frame //位置相关属性 177 | case width,height,tag,bgColor,radius,borderColor,borderWidth,masksToBounds,clips,enableClick //通用属性 178 | case text,font,textColor,lineBreakMode,numberOfLines,alignment //label 相关属性 179 | case title,titleFont,titleColor,racCommand //button 相关属性 180 | case contentSize,bounces,pagingEnabled,showHIndicator,showVIndicator //scrollView 相关属性 181 | } 182 | enum PtEqualRightType { 183 | case pt,float,int,string,color,text,new,font,size,frame,racCommand 184 | } 185 | //表达式所需结构 186 | public struct PtEqual { 187 | var leftId = "" 188 | var left = WgPt.none 189 | var leftIdPrefix = "" //左前缀 190 | var rightType = PtEqualRightType.pt 191 | var rightId = "" 192 | var rightIdPrefix = "" 193 | var right = WgPt.none 194 | var rightFloat:Float = 0 195 | var rightInt:Int = 0 196 | var rightSize:(Float,Float) = (0,0) 197 | var rightFrame:(Float,Float,Float,Float) = (0,0,0,0) 198 | var rightColor = "" 199 | var rightText = "" 200 | var rightString = "" 201 | var rightSuffix = "" 202 | 203 | var equalType = EqualType.normal 204 | } 205 | class PtEqualC { 206 | typealias MutiClosure = (_ pe: PtEqual) -> String 207 | typealias FilterClosure = () -> Bool 208 | var pe:PtEqual = PtEqual() 209 | var accumulatorLineClosure:MutiClosure = {_ in return ""} 210 | var filterBl = true 211 | var mutiEqualStr = "" //累加的字符串 212 | var mutiEqualLineMark = "\n" //换行标识 213 | //设置 PtEqual 结构体 214 | func once(_ closure:(_ pc: PtEqualC) -> Void) -> PtEqualC{ 215 | if filterBl { 216 | closure(self) 217 | } 218 | _ = resetPe() 219 | _ = resetFilter() 220 | return self 221 | } 222 | //累计设置的 PtEqual 字符串 223 | func accumulatorLine(_ closure:@escaping MutiClosure) -> PtEqualC { 224 | self.accumulatorLineClosure = closure 225 | return self 226 | } 227 | //执行累加动作 228 | func add() { 229 | if filterBl { 230 | self.mutiEqualStr += accumulatorLineClosure(self.pe) + self.mutiEqualLineMark 231 | } 232 | _ = resetFilter() 233 | } 234 | //累加自定义的字符串 235 | func add(_ str:String) { 236 | if filterBl { 237 | self.mutiEqualStr += str + self.mutiEqualLineMark 238 | } 239 | _ = resetFilter() 240 | } 241 | //过滤条件 242 | func filter(_ closure: FilterClosure) -> PtEqualC { 243 | filterBl = closure() 244 | return self 245 | } 246 | //重置 PtEqual 247 | func resetPe() -> PtEqualC { 248 | self.pe = PtEqual() 249 | return self 250 | } 251 | //重置 filter 252 | func resetFilter() -> PtEqualC { 253 | filterBl = true 254 | return self 255 | } 256 | //结束时无返回,表示全部结束设置 257 | func end() { 258 | _ = resetFilter() 259 | } 260 | func left(_ wp:WgPt) -> PtEqualC { 261 | filterBl ? self.pe.left = wp : () 262 | return self 263 | } 264 | func leftId(_ str:String) -> PtEqualC { 265 | filterBl ? self.pe.leftId = str : () 266 | return self 267 | } 268 | func leftIdPrefix(_ str:String) -> PtEqualC { 269 | filterBl ? self.pe.leftIdPrefix = str : () 270 | return self 271 | } 272 | func rightType(_ t:PtEqualRightType) -> PtEqualC { 273 | filterBl ? self.pe.rightType = t : () 274 | return self 275 | } 276 | func rightId(_ str:String) -> PtEqualC { 277 | filterBl ? self.pe.rightId = str : () 278 | return self 279 | } 280 | func rightIdPrefix(_ str:String) -> PtEqualC { 281 | filterBl ? self.pe.rightIdPrefix = str : () 282 | return self 283 | } 284 | func right(_ wp:WgPt) -> PtEqualC { 285 | filterBl ? self.pe.right = wp : () 286 | return self 287 | } 288 | func rightFloat(_ f:Float) -> PtEqualC { 289 | filterBl ? self.pe.rightFloat = f : () 290 | return self 291 | } 292 | func rightInt(_ i:Int) -> PtEqualC { 293 | filterBl ? self.pe.rightInt = i : () 294 | return self 295 | } 296 | func rightSize(w:Float, h:Float) -> PtEqualC { 297 | filterBl ? self.pe.rightSize = (w,h) : () 298 | return self 299 | } 300 | func rightFrame(l:Float, t:Float, w:Float, h:Float) -> PtEqualC { 301 | filterBl ? self.pe.rightFrame = (l,t,w,h) : () 302 | return self 303 | } 304 | func rightColor(_ str:String) -> PtEqualC { 305 | filterBl ? self.pe.rightColor = str : () 306 | return self 307 | } 308 | func rightText(_ str:String) -> PtEqualC { 309 | filterBl ? self.pe.rightText = str : () 310 | return self 311 | } 312 | func rightString(_ str:String) -> PtEqualC { 313 | filterBl ? self.pe.rightString = str : () 314 | return self 315 | } 316 | func rightSuffix(_ str:String) -> PtEqualC { 317 | filterBl ? self.pe.rightSuffix = str : () 318 | return self 319 | } 320 | func equalType(_ et:EqualType) -> PtEqualC { 321 | filterBl ? self.pe.equalType = et : () 322 | return self 323 | } 324 | 325 | } 326 | //等号类型 327 | public enum EqualType { 328 | case normal,accumulation,decrease,set 329 | } 330 | //实现文件所需结构 331 | public struct ImpFile { 332 | var properties = "" 333 | var initContent = "" 334 | var getters = "" 335 | var layouts = "" 336 | } 337 | //接口文件所需结构 338 | public struct InterfaceFile { 339 | 340 | } 341 | } 342 | 343 | /*--------------------------------*/ 344 | /*---------具体语言实现------------*/ 345 | /*--------------------------------*/ 346 | //Objective-C 347 | //见H5EditorObjc.swift 348 | 349 | ////Swift 350 | //struct H5EditorSwift: HTNMultilingualismSpecification { 351 | // var id = "" 352 | // var left = "left" 353 | // var right = "right" 354 | // var top = "top" 355 | // var bottom = "bottom" 356 | // func flowTopSetNormal(lastWidgetId: String) -> String { 357 | // return "\(id).top = \(lastWidgetId).bottom;" 358 | // } 359 | //} 360 | 361 | -------------------------------------------------------------------------------- /Sources/Core/HTNFundation/HTNStateMachine.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HTNStateMachine.swift 3 | // HTNSwift 4 | // 5 | // Created by DaiMing on 2017/10/12. 6 | // Copyright © 2017年 Starming. All rights reserved. 7 | // Description 状态机 8 | 9 | import Foundation 10 | 11 | protocol HTNStateType: Hashable {} 12 | protocol HTNEventType: Hashable {} 13 | 14 | struct HTNTransition { 15 | let event: E 16 | let fromState: S 17 | let toState: S 18 | 19 | init(event: E, fromState: S, toState: S) { 20 | self.event = event 21 | self.fromState = fromState 22 | self.toState = toState 23 | } 24 | } 25 | 26 | class HTNStateMachine { 27 | //需要处理事件的结构 28 | private struct Operation { 29 | let transition: HTNTransition 30 | let triggerCallback: (HTNTransition) -> Void 31 | } 32 | private var routes = [S: [E: Operation]]() //字典结构做记录 33 | private(set) var currentState: S 34 | private(set) var lastState: S? 35 | 36 | init(_ currentState: S) { 37 | self.currentState = currentState 38 | } 39 | 40 | //一组状态监听 41 | func listen(_ event: E, transit fromStates:[S], to toState: S, callback: @escaping (HTNTransition) -> Void) { 42 | for fromState in fromStates { 43 | listen(event, transit: fromState, to: toState, callback: callback) 44 | } 45 | } 46 | //当前状态是什么保持状态不变 47 | func listen(_ event: E, callback: @escaping (HTNTransition) -> Void) { 48 | listen(event, transit: currentState, to: currentState, callback: callback) 49 | } 50 | //fromStates 是集合,当前集合里的具体是哪个 to 就是那个 51 | func listen(_ event: E, tansitHasSameFromAndToStates fromStates:[S], callback: @escaping (HTNTransition) -> Void) { 52 | for fromState in fromStates { 53 | listen(event, transit: fromState, to: fromState, callback: callback) 54 | } 55 | } 56 | //单个监听 57 | func listen(_ event: E, transit fromState: S, to toState: S, callback: @escaping (HTNTransition) -> Void) { 58 | var route = routes[fromState] ?? [:] 59 | let transition = HTNTransition(event: event, fromState: fromState, toState: toState) 60 | let operation = Operation(transition: transition, triggerCallback: callback) 61 | route[event] = operation 62 | routes[fromState] = route 63 | } 64 | 65 | func trigger(_ event: E) -> Bool { 66 | guard let operation = routes[currentState]?[event] else { 67 | return false 68 | } 69 | lastState = currentState 70 | currentState = operation.transition.toState 71 | operation.triggerCallback(operation.transition) 72 | return true 73 | } 74 | //适应相同动作在相同的当前状态下转成不同的toState 75 | func changeCurrentState(_ state: S) { 76 | currentState = state 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Sources/Core/HTNFundation/Node.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Node.swift 3 | // HTNSwift 4 | // 5 | // Created by DaiMing on 2017/10/11. 6 | // Copyright © 2017年 Starming. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class Node { 12 | public weak var parent: Node? 13 | public var children = [Node]() 14 | 15 | init() { 16 | 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Sources/Core/HTNFundation/SMNetWorking.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HTNNetworking.swift 3 | // HTNSwift 4 | // 5 | // Created by DaiMing on 2018/3/29. 6 | // Copyright © 2018年 Starming. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class SMNetWorking { 12 | var op:Optionals = Optionals() 13 | 14 | open let session:URLSession 15 | 16 | //处理数据的 block 17 | public typealias CompletionJSONClosure = (_ data:T?) -> Void 18 | var completionJSONClosure:CompletionJSONClosure = {_ in } 19 | 20 | //配置 request 21 | typealias ConfigRequestClosure = (_ request:URLRequest) -> Void 22 | var configRequestClosure:ConfigRequestClosure = {_ in } 23 | 24 | public init() { 25 | self.session = URLSession.shared 26 | } 27 | 28 | //JSON的请求 29 | public func requestJSON(_ url: SMURLNetWorking, 30 | doneClosure:@escaping CompletionJSONClosure 31 | ) { 32 | self.completionJSONClosure = doneClosure 33 | var request:URLRequest = NSURLRequest.init(url: url.asURL()) as URLRequest 34 | request.httpMethod = op.httpMethod.rawValue 35 | self.configRequestClosure(request) //block 方式自定义 request 的属性 36 | let task = self.session.dataTask(with: request) { (data, res, error) in 37 | if (error == nil) { 38 | let decoder = JSONDecoder() 39 | do { 40 | print("解析 JSON 成功") 41 | let jsonModel = try decoder.decode(T.self, from: data!) 42 | self.completionJSONClosure(jsonModel) 43 | } catch { 44 | print("解析 JSON 失败") 45 | self.completionJSONClosure(nil) 46 | } 47 | } 48 | } 49 | task.resume() 50 | } 51 | 52 | //链式方法 53 | //HTTPMethod 的设置 54 | func method(_ md:HTTPMethod) -> SMNetWorking { 55 | self.op.httpMethod = md 56 | return self 57 | } 58 | func configRequest(_ c:@escaping ConfigRequestClosure) -> SMNetWorking { 59 | self.configRequestClosure = c 60 | return self 61 | } 62 | 63 | //struct 64 | struct Optionals { 65 | var httpMethod:HTTPMethod = .GET 66 | //后期继续添加更多 67 | //... 68 | } 69 | 70 | //enum 71 | enum HTTPMethod: String { 72 | case GET,OPTIONS,HEAD,POST,PUT,PATCH,DELETE,TRACE,CONNECT 73 | } 74 | 75 | } 76 | 77 | /*----------Protocol----------*/ 78 | public protocol SMURLNetWorking { 79 | func asURL() -> URL 80 | } 81 | 82 | /*----------Extension---------*/ 83 | extension String: SMURLNetWorking { 84 | public func asURL() -> URL { 85 | guard let url = URL(string:self) else { 86 | return URL(string:"http:www.starming.com")! 87 | } 88 | return url 89 | } 90 | } 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /Sources/Core/HTNFundation/StringExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StringExtension.swift 3 | // HTNSwift 4 | // 5 | // Created by DaiMing on 2018/3/26. 6 | // Copyright © 2018年 Starming. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension String { 12 | // let s = "hello" 13 | // s[0..<3] // "hel" 14 | // s[3..) -> String { 16 | let idx1 = index(startIndex, offsetBy: range.lowerBound) 17 | let idx2 = index(startIndex, offsetBy: range.upperBound) 18 | return String(self[idx1.. String { 31 | return self.replacingOccurrences(of: "\"", with: "\\\"") 32 | } 33 | 34 | // 过滤注释 35 | func filterAnnotationBlock() -> String { 36 | //过滤注释 37 | var newStr = "" 38 | let annotationBlockPattern = "/\\*[\\s\\S]*?\\*/" //匹配/*...*/这样的注释 39 | let regexBlock = try! NSRegularExpression(pattern: annotationBlockPattern, options: NSRegularExpression.Options(rawValue:0)) 40 | newStr = regexBlock.stringByReplacingMatches(in: self, options: NSRegularExpression.MatchingOptions(rawValue:0), range: NSMakeRange(0, self.count), withTemplate: "") 41 | return newStr 42 | } 43 | // 判断是否是整数 44 | func isInt() -> Bool { 45 | let scan:Scanner = Scanner(string: self) 46 | var val:Int = 0 47 | return scan.scanInt(&val) && scan.isAtEnd 48 | } 49 | // 判断是否是 Float 50 | func isFloat() -> Bool { 51 | let scan:Scanner = Scanner(string: self) 52 | var val:Float = 0 53 | return scan.scanFloat(&val) && scan.isAtEnd 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /Sources/Core/JavaScript/Archive/JSToken.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JSToken.swift 3 | // HTNSwift 4 | // 5 | // Created by DaiMing on 2017/10/30. 6 | // Copyright © 2017年 Starming. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class JSToken { 12 | public var type = tokenType.Unknown 13 | public var data = "" 14 | 15 | public enum tokenType { 16 | case Unknown 17 | case KeyWords 18 | case Char 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/Core/JavaScript/Archive/JSTokenizer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JSTokenizer.swift 3 | // HTNSwift 4 | // 5 | // Created by DaiMing on 2017/10/30. 6 | // Copyright © 2017年 Starming. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class JSTokenizer { 12 | private var _input: String 13 | private var _index: String.Index 14 | private var _bufferStr: String 15 | private var _bufferToken: JSToken 16 | private var _tks: [JSToken] 17 | 18 | public init(_ input: String) { 19 | _input = input 20 | _index = input.startIndex 21 | _bufferStr = "" 22 | _bufferToken = JSToken() 23 | _tks = [JSToken]() 24 | } 25 | //TODO: 解决最后一个 char 如果不是关键字会被忽略掉 26 | public func parse() -> [JSToken] { 27 | let newStr = dislodgeAnnotaion(content: _input) 28 | _input = newStr 29 | _index = newStr.startIndex 30 | 31 | 32 | let stateMachine = HTNStateMachine(S.Data) 33 | 34 | stateMachine.listen(E.SignleKeywordEvent, transit: S.Data, to: S.Data) { (t) in 35 | self._bufferToken.type = .Char 36 | self._bufferToken.data = self._bufferStr.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) 37 | if self._bufferToken.data != "" { 38 | self.addToken() 39 | } 40 | self.advanceIndexAndResetCurrentBuffer() 41 | } 42 | stateMachine.listen(E.MultiKeywordEvent, transit: S.Data, to: S.Data) { (t) in 43 | self._bufferToken.type = .KeyWords 44 | self._bufferToken.data = self._bufferStr.lowercased().trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) 45 | self.addToken() 46 | self.advanceIndexAndResetCurrentBuffer() 47 | } 48 | let singleCharKeywordArray = [",",".",":",";","?","(",")","[","]","{","}","|","^","&","<",">","+","-","*","/","%","~","=","\"","'","!","\n"] 49 | let multiCharKeywordArray = ["instance","in","delete","void","typeof","var","new","function","do","while","for","in","continue","break","import","return","with","switch","case","default","throw","try","finally","catch","if","else","const"]; 50 | 51 | while let aChar = currentChar { 52 | let aStr = aChar.description 53 | if singleCharKeywordArray.contains(aStr) { 54 | //添加 bufferStr 55 | if multiCharKeywordArray.contains(_bufferStr.lowercased().trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)) { 56 | _ = stateMachine.trigger(E.MultiKeywordEvent) 57 | } else { 58 | _ = stateMachine.trigger(E.SignleKeywordEvent) 59 | } 60 | //添加 keyword 61 | _bufferToken.data = aStr 62 | _bufferToken.type = .KeyWords 63 | _tks.append(_bufferToken) 64 | _bufferToken = JSToken() 65 | continue 66 | } else { 67 | addBufferStr(aStr) 68 | } 69 | 70 | if aStr == " " { 71 | //处理 for in 里的 in 关键字的情况 72 | if _bufferStr.hasSuffix(" in ") { 73 | self._bufferToken.data = _bufferStr.dropLast(4).description 74 | self._bufferToken.type = .Char 75 | _tks.append(_bufferToken) 76 | _bufferToken = JSToken() 77 | 78 | self._bufferToken.data = "in" 79 | self._bufferToken.type = .KeyWords 80 | _tks.append(_bufferToken) 81 | _bufferToken = JSToken() 82 | 83 | self.advanceIndexAndResetCurrentBuffer() 84 | continue 85 | } 86 | //处理多字符关键字情况 87 | if multiCharKeywordArray.contains(_bufferStr.lowercased().trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)) { 88 | _ = stateMachine.trigger(E.MultiKeywordEvent) 89 | continue 90 | } 91 | } 92 | 93 | self.advanceIndex() 94 | } 95 | return _tks 96 | } 97 | 98 | //tinyTool 99 | // 清理注释 100 | func dislodgeAnnotaion(content:String) -> String { 101 | 102 | let annotationBlockPattern = "/\\*[\\s\\S]*?\\*/" //匹配/*...*/这样的注释 103 | let annotationLinePattern = "//.*?\\n" //匹配//这样的注释 104 | 105 | let regexBlock = try! NSRegularExpression(pattern: annotationBlockPattern, options: NSRegularExpression.Options(rawValue:0)) 106 | let regexLine = try! NSRegularExpression(pattern: annotationLinePattern, options: NSRegularExpression.Options(rawValue:0)) 107 | var newStr = "" 108 | newStr = regexLine.stringByReplacingMatches(in: content, options: NSRegularExpression.MatchingOptions(rawValue:0), range: NSMakeRange(0, content.count), withTemplate: "") 109 | newStr = regexBlock.stringByReplacingMatches(in: newStr, options: NSRegularExpression.MatchingOptions(rawValue:0), range: NSMakeRange(0, newStr.count), withTemplate: "") 110 | return newStr 111 | } 112 | var currentChar: Character? { 113 | return _index < _input.endIndex ? _input[_index] : nil 114 | } 115 | func addBufferStr(_ bufferStr: String) { 116 | _bufferStr += bufferStr 117 | } 118 | func isQuotation(s: String) -> Bool { 119 | if s == "\"" || s == "'" { 120 | return true 121 | } 122 | return false 123 | } 124 | 125 | //添加 token 126 | func addToken() { 127 | let tk = _bufferToken 128 | _tks.append(tk) 129 | _bufferToken = JSToken() 130 | } 131 | func advanceIndex() { 132 | _input.formIndex(after: &_index) 133 | } 134 | func advanceIndexAndResetCurrentBuffer() { 135 | _bufferStr = "" 136 | advanceIndex() 137 | } 138 | 139 | // 140 | enum S: HTNStateType { 141 | case Data 142 | case StartQuotation 143 | 144 | } 145 | enum E: HTNEventType { 146 | case SignleKeywordEvent 147 | case MultiKeywordEvent 148 | // case CommaEvent // , expression 里区分不同的 expression 149 | // case DotEvent // . 150 | // case ColonEvent // : 151 | // case SemicolonEvent // ; 152 | // case QuestionMarkEvent // ? 153 | // case RoundBracketLeftEvent // ( 154 | // case RoundBracketRightEvent // ) 155 | // case BracketLeftEvent // [ 156 | // case BracketRightEvent // ] 157 | // case BraceLeftEvent // { 158 | // case BraceRightEvent // } 159 | 160 | // case DoubleVerticalLineEvent // || 161 | // case DoubleAmpersandEvent // && 162 | // case VerticalLineEvent // | 163 | // case CaretEvent // ^ 164 | // case AmpersandEvent // & 165 | // case DoubleEqualEvent // == 166 | // case ExclamationMarkEqualEvent // != 167 | // case TripleEqualEvent // === 168 | // case ExclamationMarkDoubleEqualEvent // !== 169 | // case AngleBracketLeftEvent // < 170 | // case AngleBracketRightEvent // > 171 | // case AngleBracketLeftEqualEvent // <= 172 | // case AngleBracketRightEqualEvent // >= 173 | // case instanceofEvent // instance 174 | // case inEvent // in 175 | // case DoubleAngleBracketLeftEvent // << 176 | // case DoubleAngleBracketRIghtEvent // >> 177 | // case TripleAngleBracketRightEvent // >>> 178 | // case AddEvent // + 179 | // case MinusEvent // - 180 | // case AsteriskEvent // * 181 | // case SlashEvent // / 182 | // case PercentEvent // % 183 | // case DeleteEvent // delete 184 | // case VoidEvent // void 185 | // case TypeofEvent // typeof 186 | // case DoubleAddEvent // ++ 187 | // case DoubleMinusEvent // -- 188 | // case TildeEvent // ~ 189 | // case VarEvent // var 190 | 191 | // case EqualEvent // = 192 | // case AsteriskEqualEvent // *= 193 | // case SlashAssignEvent // /= 194 | // case PercentEqualEvent // %= 195 | // case AddEqualEvent // += 196 | // case MinusEqualEvent // -= 197 | // case DoubleAngleBracketLeftEqualEvent // <<= 198 | // case DoubleAngleBracketRightEqualEvent // >>= 199 | // case TripleAngleBracketRightEqualEvent // >>>= 200 | // case AmpersandEqualEvent // &= 201 | // case CaretEqualEvent // ^= 202 | // case VerticalLineEqualEvent // |= 203 | 204 | // case NewEvent // new 205 | // case FunctionEvent // function 206 | // case DoEvent // do 207 | // case WhileEvent // while 208 | // case ForEvent // for 209 | // case InEvent // in 210 | // case ContinueEvent // continue 211 | // case BreakEvent // break 212 | // case ImportEvent // import 213 | // case ReturnEvent // return 214 | // case WithEvent // with 215 | // case SwitchEvent // switch 216 | // case CaseEvent // case 217 | // case DefaultEvent // default 218 | // case ThrowEvent // throw 219 | // case TryEvent // try 220 | // case FinallyEvent // finally 221 | // case catchEvent // catch 222 | } 223 | } 224 | 225 | 226 | 227 | -------------------------------------------------------------------------------- /Sources/Core/JavaScript/CodeGeneratorFromJSToOC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CodeGeneratorFromJSToOC.swift 3 | // HTN 4 | // 5 | // Created by DaiMing on 2018/5/2. 6 | // 7 | 8 | import Foundation 9 | 10 | //public class CodeGeneratorFromJSToOC { 11 | // public var code = "" 12 | // public init(_ input:String) { 13 | // print("Input code is:") 14 | // print(input) 15 | // let ast = JTransformer(input).ast 16 | // for aNode in ast { 17 | // code.append(recGeneratorCode(aNode)) 18 | // } 19 | // print("The code generated:") 20 | // print(code) 21 | // } 22 | // 23 | // public func recGeneratorCode(_ node:JNode) -> String { 24 | // var code = "" 25 | // if node.type == .ExpressionStatement { 26 | // for aExp in node.expressions { 27 | // code.append(recGeneratorCode(aExp)) 28 | // } 29 | // } 30 | // if node.type == .CallExpression { 31 | // code.append(node.callee.name) 32 | // code.append("(") 33 | // if node.arguments.count > 0 { 34 | // for (index,arg) in node.arguments.enumerated() { 35 | // code.append(recGeneratorCode(arg)) 36 | // if index != node.arguments.count - 1 { 37 | // code.append(", ") 38 | // } 39 | // } 40 | // } 41 | // code.append(")") 42 | // } 43 | // if node.type == .Identifier { 44 | // code.append(node.name) 45 | // } 46 | // if node.type == .NumberLiteral { 47 | // switch node.numberType { 48 | // case .float: 49 | // code.append(String(node.floatValue)) 50 | // case .int: 51 | // code.append(String(node.intValue)) 52 | // } 53 | // } 54 | // 55 | // return code 56 | // } 57 | //} 58 | -------------------------------------------------------------------------------- /Sources/Core/JavaScript/JState.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JState.swift 3 | // HTN 4 | // 5 | // Created by DaiMing on 2018/5/16. 6 | // 7 | 8 | import Foundation 9 | 10 | class JState { 11 | var input:String = "" 12 | 13 | var inMethod = false 14 | var inFunction = false 15 | var inParameters = false 16 | var maybeInArrowParameters = false 17 | var inGenerator = false 18 | var inAsync = false 19 | var inPropertyName = false 20 | var inType = false 21 | var inClassProperty = false 22 | var isIterator = false 23 | } 24 | -------------------------------------------------------------------------------- /Sources/Core/JavaScript/JToken.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // HTN 4 | // 5 | // Created by DaiMing on 2018/5/9. 6 | // 7 | 8 | import Foundation 9 | 10 | // ES 标准 11 | public enum JTokenType:String { 12 | case none 13 | 14 | case float 15 | case int 16 | case bigint 17 | 18 | case string // 字符 19 | case name // 命名 20 | case eof // 间隔,换行 21 | case regular // 正则 22 | 23 | // 标点符号 24 | case bracketL // [ 25 | case bracketR // ] 26 | case braceL // { 27 | case braceBarL // {| 28 | case braceR // } 29 | case braceBarR // |} 30 | case parenL // ( 31 | case parenR // ) 32 | case comma // , 33 | case semi // ; 34 | case colon // : 35 | case doubleColon // :: 36 | case dot // . 37 | case question // ? 38 | case questiondot // ?. 39 | case arrow // => 40 | case ellipsis // ... 41 | case backQuote // ` 42 | case dollarBraceL // ${ 43 | case at // @ 44 | case hash // # 45 | 46 | // 操作符 47 | case eq // = 48 | case assign // _= 49 | case incDec // ++ / -- 50 | case bang // ! 51 | case tilde // ~ 52 | 53 | // 有优先级的操作符 54 | case pipleline // |> 55 | case nullishCoalescing // ?? 56 | case logicalOR // || 57 | case logicalAND // && 58 | case bitwiseOR // | 59 | case bitwiseXOR // ^ 60 | case bitwiseAND // & 61 | case equality // == / != 62 | case relational // < / > 63 | case bitShift // << / >> 64 | case plusMin // + / - 65 | case modulo // % 66 | case star // * 67 | case slash // / 68 | case exponent // ** 69 | 70 | // 关键字 71 | case template // template 72 | case `break` 73 | case `case` 74 | case `catch` 75 | case `continue` 76 | case `debugger` 77 | case `default` 78 | case `do` 79 | case `else` 80 | case `finally` 81 | case `for` 82 | case `function` 83 | case `if` 84 | case `return` 85 | case `switch` 86 | case `throw` 87 | case `try` 88 | case `var` 89 | case `let` 90 | case `const` 91 | case `while` 92 | case `with` 93 | case `new` 94 | case `this` 95 | case `super` 96 | case `class` 97 | case `extends` 98 | case `export` 99 | case `import` 100 | case `yield` 101 | case `null` 102 | case `true` 103 | case `false` 104 | case `in` 105 | case `instanceof` 106 | case `typeof` 107 | case `void` 108 | case `delete` 109 | } 110 | 111 | -------------------------------------------------------------------------------- /Sources/Core/JavaScript/JTransformer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JTransformer.swift 3 | // HTN 4 | // 5 | // Created by DaiMing on 2018/5/2. 6 | // 7 | 8 | import Foundation 9 | 10 | //public class JTransformer { 11 | // public var ast:[JNode] 12 | // 13 | // public init(_ input:String) { 14 | // ast = [JNode]() 15 | // var currentParent = JNode() //当前的父节点 16 | // let numberLiteralClosure:VisitorClosure = { (node,parent) in 17 | // if currentParent.type == .ExpressionStatement { 18 | // currentParent.expressions[0].arguments.append(node) 19 | // } 20 | // if currentParent.type == .CallExpression { 21 | // currentParent.arguments.append(node) 22 | // } 23 | // } 24 | // let callExpressionClosure:VisitorClosure = { (node,parent) in 25 | // let exp = JNode() 26 | // exp.type = .CallExpression 27 | // 28 | // let callee = JNodeCallee() 29 | // callee.type = .Identifier 30 | // callee.name = node.name 31 | // exp.callee = callee 32 | // 33 | // if parent.type != .CallExpression { 34 | // let exps = JNode() 35 | // exps.type = .ExpressionStatement 36 | // exps.expressions.append(exp) 37 | // if parent.type == .Root { 38 | // self.ast.append(exps) 39 | // } 40 | // currentParent = exps 41 | // } else { 42 | // currentParent.expressions[0].arguments.append(exp) 43 | // currentParent = exp 44 | // } 45 | // } 46 | // let vDic = ["NumberLiteral": numberLiteralClosure, "CallExpression" : callExpressionClosure] 47 | // 48 | // JTraverser(input).traverser(visitor: vDic) 49 | // print("After transform AST:") 50 | // JParser.astPrintable(ast) 51 | // } 52 | //} 53 | -------------------------------------------------------------------------------- /Sources/Core/JavaScript/JTraverser.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JTraverser.swift 3 | // HTN 4 | // 5 | // Created by DaiMing on 2018/5/2. 6 | // 7 | 8 | import Foundation 9 | 10 | //public typealias VisitorClosure = (_ node:JNode,_ parent:JNode) -> Void 11 | // 12 | //public class JTraverser { 13 | // 14 | // private var _ast: [JNode] 15 | // public init(_ input:String) { 16 | // _ast = JParser(input).parser() 17 | // } 18 | // public func traverser(visitor:[String:VisitorClosure]) { 19 | // 20 | // func traverseChildNode(childrens:[JNode], parent:JNode) { 21 | // for child in childrens { 22 | // traverseNode(node: child, parent: parent) 23 | // } 24 | // } 25 | // 26 | // func traverseNode(node:JNode, parent:JNode) { 27 | // //TODO:改一下 visitor 的结构成 [String:[String:VisitorClosure]]。这样可以添加更多的执行,比如刚进来 enter 的 key 和离开节点的 exit 28 | // //会执行外部传入的 Closure 29 | // //enter: 30 | // if visitor.keys.contains(node.type.rawValue) { 31 | // if let closure:VisitorClosure = visitor[node.type.rawValue] { 32 | // closure(node,parent) 33 | // } 34 | // } 35 | // //看是否有子节点需要继续遍历 36 | // if node.params.count > 0 { 37 | // traverseChildNode(childrens: node.params, parent: node) 38 | // } 39 | // //exit: 40 | // } 41 | // let rootNode = JNode() 42 | // rootNode.type = .Root 43 | // traverseChildNode(childrens: _ast, parent: rootNode) 44 | // } 45 | //} 46 | -------------------------------------------------------------------------------- /Sources/Core/Layout/JSONToFrame.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JSONToFrame.swift 3 | // HTNSwift 4 | // 5 | // Created by DaiMing on 2018/3/13. 6 | // Copyright © 2018年 Starming. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class JSONToFrame { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /Sources/Core/Layout/LayoutElement.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LayoutElement.swift 3 | // HTNSwift 4 | // 5 | // Created by sunshinelww on 2017/10/25. 6 | // Copyright © 2017年 Starming. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | /** 11 | 布局类 12 | **/ 13 | public class LayoutElement { 14 | public init() { 15 | 16 | } 17 | 18 | public func createRenderer(doc:Document)->Document { 19 | layoutBlockChildren(doc) 20 | return doc 21 | } 22 | 23 | /** 24 | 给每个需要显示的节点创建RenderObject 25 | **/ 26 | private func layoutBlockChildren(_ elem:Element){ 27 | if(shouldCreateRenderer(elem)){ 28 | elem.createRenderObject(); //创建RenderObject 29 | parseMargin(elem) //解析margin 30 | parsePadding(elem) //解析padding 31 | parseBorder(elem) 32 | if elem.children.count > 0{ 33 | for child in elem.children{ 34 | let e = child as! Element; 35 | layoutBlockChildren(e); //递归渲染子元素 36 | } 37 | } 38 | } 39 | } 40 | 41 | /** 42 | 判断是否需要给节点创建Render 43 | **/ 44 | private func shouldCreateRenderer(_ elem:Element) -> Bool{ 45 | //Css display:none的元素不需要 46 | if let property = elem.propertyMap["display"] { 47 | if property == "none"{ 48 | return false 49 | } 50 | } 51 | return true 52 | } 53 | 54 | private func parseMargin(_ elem :Element){ 55 | if let propertyValue = elem.propertyMap["margin"] { 56 | let results = propertyValue.split(separator: " ") 57 | if results.count == 1 {//只有一个值 58 | if let value=Double(cutNumberMark(str: String(results[0]))){ 59 | elem.renderer?.margin_top = Double(value); 60 | elem.renderer?.margin_left = Double(value); 61 | elem.renderer?.margin_bottom = Double(value); 62 | elem.renderer?.margin_right = Double(value); 63 | } 64 | } 65 | else if results.count == 2{ 66 | if let value=Double(cutNumberMark(str: String(results[0]))){ 67 | elem.renderer?.margin_top = Double(value); 68 | elem.renderer?.margin_bottom = Double(value); 69 | } 70 | if let value=Double(cutNumberMark(str: String(results[1]))){ 71 | elem.renderer?.margin_left = Double(value); 72 | elem.renderer?.margin_right = Double(value); 73 | } 74 | } 75 | else if results.count == 3{ 76 | if let value=Double(cutNumberMark(str: String(results[0]))){ 77 | elem.renderer?.margin_top = Double(value); 78 | } 79 | if let value=Double(cutNumberMark(str: String(results[1]))){ 80 | elem.renderer?.margin_left = Double(value); 81 | elem.renderer?.margin_right = Double(value); 82 | } 83 | if let value=Double(cutNumberMark(str: String(results[2]))){ 84 | elem.renderer?.margin_bottom = Double(value); 85 | } 86 | } 87 | else if results.count == 4{ 88 | if let value=Double(cutNumberMark(str: String(results[0]))){ 89 | elem.renderer?.margin_top = Double(value); 90 | } 91 | if let value=Double(cutNumberMark(str: String(results[1]))){ 92 | elem.renderer?.margin_right = Double(value); 93 | } 94 | if let value=Double(cutNumberMark(str: String(results[2]))){ 95 | elem.renderer?.margin_bottom = Double(value); 96 | } 97 | if let value=Double(cutNumberMark(str: String(results[3]))){ 98 | elem.renderer?.margin_left = Double(value); 99 | } 100 | } 101 | } 102 | else if let propertyValue = elem.propertyMap["margin-top"]{ 103 | if let value = Double(cutNumberMark(str: String(propertyValue))){ 104 | elem.renderer?.margin_top = value; 105 | } 106 | } 107 | else if let propertyValue = elem.propertyMap["margin-left"]{ 108 | if let value = Double(cutNumberMark(str: String(propertyValue))){ 109 | elem.renderer?.margin_left = value; 110 | } 111 | } 112 | else if let propertyValue = elem.propertyMap["margin-bottom"]{ 113 | if let value = Double(cutNumberMark(str: String(propertyValue))){ 114 | elem.renderer?.margin_bottom = value; 115 | } 116 | } 117 | else if let propertyValue = elem.propertyMap["margin-right"]{ 118 | if let value = Double(cutNumberMark(str: String(propertyValue))){ 119 | elem.renderer?.margin_right = value; 120 | } 121 | } 122 | 123 | } 124 | 125 | private func parsePadding(_ elem :Element){ 126 | if let propertyValue = elem.propertyMap["padding"]{ 127 | let results = propertyValue.split(separator: " ") 128 | if results.count == 1 {//只有一个值 129 | if let value=Double(cutNumberMark(str: String(results[0]))){ 130 | elem.renderer?.padding_top = Double(value); 131 | elem.renderer?.padding_right = Double(value); 132 | elem.renderer?.padding_bottom = Double(value); 133 | elem.renderer?.padding_left = Double(value); 134 | } 135 | } 136 | else if results.count == 4{ 137 | if let value=Double(cutNumberMark(str: String(results[0]))){ 138 | elem.renderer?.padding_top = Double(value); 139 | } 140 | if let value=Double(cutNumberMark(str: String(results[1]))){ 141 | elem.renderer?.padding_right = Double(value); 142 | } 143 | if let value=Double(cutNumberMark(str: String(results[2]))){ 144 | elem.renderer?.padding_bottom = Double(value); 145 | } 146 | if let value=Double(cutNumberMark(str: String(results[3]))){ 147 | elem.renderer?.padding_left = Double(value); 148 | } 149 | } 150 | } 151 | else if let propertyValue = elem.propertyMap["padding-top"]{ 152 | if let value = Double(cutNumberMark(str: String(propertyValue))){ 153 | elem.renderer?.padding_top = value; 154 | } 155 | } 156 | else if let propertyValue = elem.propertyMap["padding-left"]{ 157 | if let value = Double(cutNumberMark(str: String(propertyValue))){ 158 | elem.renderer?.padding_left = value; 159 | } 160 | } 161 | else if let propertyValue = elem.propertyMap["padding-bottom"]{ 162 | if let value = Double(cutNumberMark(str: String(propertyValue))){ 163 | elem.renderer?.padding_bottom = value; 164 | } 165 | } 166 | else if let propertyValue = elem.propertyMap["padding-right"]{ 167 | if let value = Double(cutNumberMark(str: String(propertyValue))){ 168 | elem.renderer?.padding_right = value; 169 | } 170 | } 171 | } 172 | 173 | private func parseBorder(_ elem: Element) { 174 | if let propertyValue = elem.propertyMap["border"]{ 175 | let valueArr = propertyValue.split(separator: " ").map(String.init) 176 | elem.renderer?.borderWidth = Double(cutNumberMark(str: valueArr[0])) ?? 0.0 177 | elem.renderer?.borderColor = valueArr[2] 178 | } 179 | if let propertyValue = elem.propertyMap["border-radius"]{ 180 | elem.renderer?.borderRadius = Double(cutNumberMark(str: propertyValue)) ?? 0.0 181 | } 182 | } 183 | 184 | func cutNumberMark(str:String) -> String { 185 | var re = str.replacingOccurrences(of: "pt", with: "") 186 | re = re.replacingOccurrences(of: "px", with: "") 187 | return re 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /Sources/Core/Layout/RenderObject.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RenderObject.swift 3 | // HTNSwift 4 | // 5 | // Created by DaiMing on 2017/10/15. 6 | // Copyright © 2017年 Starming. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | public class RenderObject { 11 | 12 | public var x : Double? //代表元素的一个布局位置,暂时只考虑这四个属性,以后再添加 13 | public var y : Double? 14 | public var width : Double 15 | public var height : Double 16 | 17 | //主要设置盒子模型的padding属性 18 | public var padding_top : Double = 0 19 | public var padding_left :Double = 0 20 | public var padding_bottom : Double = 0 21 | public var padding_right : Double = 0 22 | 23 | //盒子模型的border属性 24 | public var border_top : Double = 0 25 | public var border_left : Double = 0 26 | public var border_bottom : Double = 0 27 | public var border_right : Double = 0 28 | 29 | //盒子模型的marginInfo,注意父节点的marginInfo需要根据所有子节点信息的marginInfo来共同决定 30 | public var margin_top : Double = 0 31 | public var margin_left : Double = 0 32 | public var margin_bottom : Double = 0 33 | public var margin_right : Double = 0 34 | 35 | public var backgroundColor : String? //背景颜色 36 | public var borderColor :String? //边框颜色 37 | public var borderWidth :Double = 0 //边框宽度 38 | public var borderRadius :Double = 0 //边框圆角 39 | 40 | init() { 41 | width=0 42 | height=0 43 | } 44 | 45 | public func setPadding(_ top :Double, _ left : Double, _ bottom: Double, _ right: Double){ 46 | padding_top = top 47 | padding_left = left 48 | padding_bottom = bottom 49 | padding_right = right 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/Core/Layout/StyleResolver.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StyleResolver.swift 3 | // HTNSwift 4 | // 5 | // Created by DaiMing on 2017/10/15. 6 | // Copyright © 2017年 Starming. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class StyleResolver { 12 | public init() { 13 | 14 | } 15 | public func resolver(_ doc:Document, styleSheet:CSSStyleSheet) -> Document{ 16 | //样式映射表 17 | //这种结构能够支持多级 Selector 18 | var matchMap = [String:[String:[String:String]]]() 19 | for rule in styleSheet.ruleList { 20 | for selector in rule.selectorList { 21 | guard let matchLast = selector.matchList.last else { 22 | continue 23 | } 24 | var matchDic = matchMap[matchLast] 25 | if matchDic == nil { 26 | matchDic = [String:[String:String]]() 27 | matchMap[matchLast] = matchDic 28 | } 29 | 30 | //这里可以按照后加入 rulelist 的优先级更高的原则进行覆盖操作 31 | if matchMap[matchLast]![selector.identifier] == nil { 32 | matchMap[matchLast]![selector.identifier] = [String:String]() 33 | } 34 | for a in rule.propertyList { 35 | matchMap[matchLast]![selector.identifier]![a.key] = a.value 36 | } 37 | } 38 | } 39 | for elm in doc.children { 40 | self.attach(elm as! Element, matchMap: matchMap) 41 | } 42 | 43 | return doc 44 | } 45 | //递归将样式属性都加上 46 | func attach(_ element:Element, matchMap:[String:[String:[String:String]]]) { 47 | guard let token = element.startTagToken else { 48 | return 49 | } 50 | if matchMap[token.data] != nil { 51 | //TODO: 还不支持 selector 里多个标签名组合,后期加上 52 | addProperty(token.data, matchMap: matchMap, element: element) 53 | } 54 | 55 | //增加 property 通过处理 token 里的属性列表里的 class 和 id 在 matchMap 里找 56 | for attr in token.attributeList { 57 | if attr.name == "class" { 58 | addProperty("." + attr.value.lowercased(), matchMap: matchMap, element: element) 59 | } 60 | if attr.name == "id" { 61 | addProperty("#" + attr.value.lowercased(), matchMap: matchMap, element: element) 62 | } 63 | } 64 | 65 | if element.children.count > 0 { 66 | for element in element.children { 67 | self.attach(element as! Element, matchMap: matchMap) 68 | } 69 | } 70 | } 71 | 72 | func addProperty(_ key:String, matchMap:[String:[String:[String:String]]], element:Element) { 73 | guard let dic = matchMap[key] else { 74 | return 75 | } 76 | for aDic in dic { 77 | var selectorArr = aDic.key.components(separatedBy: " ") 78 | if selectorArr.count > 1 { 79 | //带多个 selector 的情况 80 | selectorArr.removeLast() 81 | if !recursionSelectorMatch(selectorArr, parentElement: element.parent as! Element) { 82 | continue 83 | } 84 | } 85 | guard let ruleDic = dic[aDic.key] else { 86 | continue 87 | } 88 | //将属性加入 element 的属性列表里 89 | for property in ruleDic { 90 | element.propertyMap[property.key] = property.value 91 | } 92 | } 93 | 94 | } 95 | 96 | //递归找出匹配的多路径 97 | func recursionSelectorMatch(_ selectors:[String], parentElement:Element) -> Bool { 98 | var selectorArr = selectors 99 | guard var last = selectorArr.last else { 100 | //表示全匹配了 101 | return true 102 | } 103 | guard let parent = parentElement.parent else { 104 | return false 105 | } 106 | 107 | var isMatch = false 108 | 109 | if last.hasPrefix(".") { 110 | last.removeFirst() 111 | //TODO:这里还需要考虑attribute 空格多个 class 名的情况 112 | guard let startTagToken = parentElement.startTagToken else { 113 | return false 114 | } 115 | if startTagToken.attributeDic["class"] == last { 116 | isMatch = true 117 | } 118 | } else if last.hasPrefix("#") { 119 | last.removeFirst() 120 | guard let startTagToken = parentElement.startTagToken else { 121 | return false 122 | } 123 | if startTagToken.attributeDic["id"] == last { 124 | isMatch = true 125 | } 126 | } else { 127 | guard let startTagToken = parentElement.startTagToken else { 128 | return false 129 | } 130 | if startTagToken.data == last { 131 | isMatch = true 132 | } 133 | } 134 | 135 | if isMatch { 136 | //匹配到会继续往前去匹配 137 | selectorArr.removeLast() 138 | } 139 | return recursionSelectorMatch(selectorArr, parentElement: parent as! Element) 140 | 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /Sources/Core/OC/OCAST.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OCAST.swift 3 | // HTN 4 | // 5 | // Created by DaiMing on 2018/6/10. 6 | // 7 | 8 | import Foundation 9 | 10 | public protocol OCAST {} 11 | public protocol OCDeclaration: OCAST {} 12 | 13 | class OCProgram: OCAST { 14 | let interface: OCInterface 15 | let implementation: OCImplementation 16 | init(interface:OCInterface, implementation: OCImplementation) { 17 | self.interface = interface 18 | self.implementation = implementation 19 | } 20 | } 21 | 22 | class OCInterface: OCAST { 23 | let name: String 24 | let propertyList: [OCPropertyDeclaration] 25 | init(name: String, propertyList: [OCPropertyDeclaration]) { 26 | self.name = name 27 | self.propertyList = propertyList 28 | } 29 | } 30 | 31 | class OCPropertyDeclaration: OCAST { 32 | let propertyAttributesList: [OCPropertyAttribute] 33 | let type: String 34 | let name: String 35 | init(propertyAttributesList: [OCPropertyAttribute], type: String, name: String) { 36 | self.propertyAttributesList = propertyAttributesList 37 | self.type = type 38 | self.name = name 39 | } 40 | } 41 | 42 | class OCPropertyAttribute: OCAST { 43 | let name :String 44 | init(name: String) { 45 | self.name = name 46 | } 47 | } 48 | 49 | class OCImplementation: OCAST { 50 | let name: String 51 | let methodList: [OCMethod] 52 | init(name: String, methodList: [OCMethod]) { 53 | self.name = name 54 | self.methodList = methodList 55 | } 56 | } 57 | 58 | class OCMethod: OCAST { 59 | let returnIdentifier: String 60 | let methodName: String 61 | let statements: [OCAST] 62 | init(returnIdentifier: String, methodName: String, statements:[OCAST]) { 63 | self.returnIdentifier = returnIdentifier 64 | self.methodName = methodName 65 | self.statements = statements 66 | } 67 | } 68 | 69 | class OCCompoundStatement: OCAST { 70 | let children: [OCAST] 71 | init(children: [OCAST]) { 72 | self.children = children 73 | } 74 | } 75 | 76 | class OCVariableDeclaration: OCDeclaration { 77 | let variable: OCVar 78 | let type: String 79 | let right: OCAST 80 | 81 | init(variable: OCVar, type: String, right: OCAST) { 82 | self.variable = variable 83 | self.type = type 84 | self.right = right 85 | } 86 | } 87 | 88 | class OCIdentifier: OCAST { 89 | let identifier: String 90 | init(identifier: String) { 91 | self.identifier = identifier 92 | } 93 | } 94 | 95 | class OCAssign: OCAST { 96 | let left: OCVar 97 | let right: OCAST 98 | 99 | init(left: OCVar, right: OCAST) { 100 | self.left = left 101 | self.right = right 102 | } 103 | } 104 | 105 | class OCVar: OCAST { 106 | let name: String 107 | 108 | init(name: String) { 109 | self.name = name 110 | } 111 | } 112 | 113 | class OCNoOp: OCAST {} 114 | 115 | /*--------- 运算符 ---------*/ 116 | public enum OCBinOpType { 117 | case plus 118 | case minus 119 | case mult 120 | case intDiv 121 | } 122 | 123 | public enum OCUnaryOperationType { 124 | case plus 125 | case minus 126 | } 127 | 128 | public enum OCNumber: OCAST { 129 | case integer(Int) 130 | case float(Float) 131 | } 132 | 133 | class OCUnaryOperation: OCAST { 134 | let operation: OCUnaryOperationType 135 | let operand: OCAST 136 | 137 | init(operation: OCUnaryOperationType, operand: OCAST) { 138 | self.operation = operation 139 | self.operand = operand 140 | } 141 | } 142 | 143 | class OCBinOp: OCAST { 144 | let left: OCAST 145 | let operation: OCBinOpType 146 | let right: OCAST 147 | 148 | init(left: OCAST, operation: OCBinOpType, right: OCAST) { 149 | self.left = left 150 | self.operation = operation 151 | self.right = right 152 | } 153 | } 154 | 155 | extension OCNumber: Equatable { 156 | public static func == (lhs: OCNumber, rhs: OCNumber) -> Bool { 157 | switch (lhs, rhs) { 158 | case let (.integer(left), .integer(right)): 159 | return left == right 160 | case let (.float(left), .float(right)): 161 | return left == right 162 | case let (.float(left), .integer(right)): 163 | return left == Float(right) 164 | case let (.integer(left), .float(right)): 165 | return Float(left) == right 166 | } 167 | } 168 | } 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | -------------------------------------------------------------------------------- /Sources/Core/OC/OCArithmetics.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OCArithmetics.swift 3 | // HTN 4 | // 5 | // Created by DaiMing on 2018/6/11. 6 | // 7 | 8 | import Foundation 9 | 10 | extension OCNumber { 11 | // Unary 12 | static prefix func + (left: OCNumber) -> OCNumber { 13 | switch left { 14 | case let .integer(value): 15 | return .integer(+value) 16 | case let .float(value): 17 | return .float(+value) 18 | } 19 | } 20 | 21 | static prefix func - (left: OCNumber) -> OCNumber { 22 | switch left { 23 | case let .integer(value): 24 | return .integer(-value) 25 | case let .float(value): 26 | return .float(-value) 27 | } 28 | } 29 | 30 | 31 | // binOp 32 | static func + (left: OCNumber, right: OCNumber) -> OCNumber { 33 | switch (left, right) { 34 | case let (.integer(left), .integer(right)): 35 | return .integer(left + right) 36 | case let (.float(left), .float(right)): 37 | return .float(left + right) 38 | case let (.float(left), .integer(right)): 39 | return .float(left + Float(right)) 40 | case let (.integer(left), .float(right)): 41 | return .float(Float(left) + right) 42 | } 43 | } 44 | 45 | static func - (left: OCNumber, right: OCNumber) -> OCNumber { 46 | switch (left, right) { 47 | case let (.integer(left), .integer(right)): 48 | return .integer(left - right) 49 | case let (.float(left), .float(right)): 50 | return .float(left - right) 51 | case let (.float(left), .integer(right)): 52 | return .float(left - Float(right)) 53 | case let (.integer(left), .float(right)): 54 | return .float(Float(left) - right) 55 | } 56 | } 57 | 58 | static func * (left: OCNumber, right: OCNumber) -> OCNumber { 59 | switch (left, right) { 60 | case let (.integer(left), .integer(right)): 61 | return .integer(left * right) 62 | case let (.float(left), .float(right)): 63 | return .float(left * right) 64 | case let (.float(left), .integer(right)): 65 | return .float(left * Float(right)) 66 | case let (.integer(left), .float(right)): 67 | return .float(Float(left) * right) 68 | } 69 | } 70 | 71 | static func / (left: OCNumber, right: OCNumber) -> OCNumber { 72 | switch (left, right) { 73 | case let (.integer(left), .integer(right)): 74 | return .integer(left / right) 75 | case let (.float(left), .float(right)): 76 | return .float(left / right) 77 | case let (.float(left), .integer(right)): 78 | return .float(left / Float(right)) 79 | case let (.integer(left), .float(right)): 80 | return .float(Float(left) / right) 81 | } 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /Sources/Core/OC/OCInterpreter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OCInterpreter.swift 3 | // HTN 4 | // 5 | // Created by DaiMing on 2018/6/5. 6 | // 7 | 8 | import Foundation 9 | 10 | public class OCInterpreter { 11 | 12 | private let ast: OCAST 13 | private var scopes: [String: OCValue] 14 | 15 | public init(_ input: String) { 16 | let parser = OCParser(input) 17 | ast = parser.parse() 18 | scopes = [String: OCValue]() 19 | print(ast) 20 | eval(node: ast) 21 | print("scope is:") 22 | print(scopes) 23 | 24 | let sa = OCStaticAnalyzer() 25 | let symtb = sa.analyze(node: ast) 26 | 27 | print(symtb) 28 | } 29 | 30 | @discardableResult public func eval(node: OCAST) -> OCValue { 31 | switch node { 32 | case let program as OCProgram: 33 | return eval(program: program) 34 | case let implementation as OCImplementation: 35 | return eval(implementation: implementation) 36 | case let method as OCMethod: 37 | return eval(method: method) 38 | case let assign as OCAssign: 39 | return eval(assign: assign) 40 | case let variable as OCVar: 41 | return eval(variable: variable) 42 | case let variableDeclaration as OCVariableDeclaration: 43 | return eval(variableDeclaration: variableDeclaration) 44 | case let number as OCNumber: 45 | return eval(number: number) 46 | case let unaryOperation as OCUnaryOperation: 47 | return eval(unaryOperation: unaryOperation) 48 | case let binOp as OCBinOp: 49 | return eval(binOp: binOp) 50 | default: 51 | return .none 52 | } 53 | } 54 | 55 | func eval(program: OCProgram) -> OCValue { 56 | return eval(implementation: program.implementation) 57 | } 58 | 59 | func eval(implementation: OCImplementation) -> OCValue { 60 | for method in implementation.methodList { 61 | eval(method: method) 62 | } 63 | return .none 64 | } 65 | 66 | @discardableResult func eval(method: OCMethod) -> OCValue { 67 | for statement in method.statements { 68 | eval(node: statement) 69 | } 70 | return .none 71 | } 72 | 73 | func eval(assign: OCAssign) -> OCValue { 74 | scopes[assign.left.name] = eval(node: assign.right) 75 | return .none 76 | } 77 | 78 | func eval(variable: OCVar) -> OCValue { 79 | guard let value = scopes[variable.name] else { 80 | fatalError("Error: eval var") 81 | } 82 | return value 83 | } 84 | 85 | func eval(variableDeclaration: OCVariableDeclaration) -> OCValue { 86 | scopes[variableDeclaration.variable.name] = eval(node: variableDeclaration.right) 87 | return .none 88 | } 89 | 90 | /*--------- eval 运算符 ----------*/ 91 | func eval(number: OCNumber) -> OCValue { 92 | return .number(number) 93 | } 94 | 95 | func eval(binOp: OCBinOp) -> OCValue { 96 | guard case let .number(leftResult) = eval(node: binOp.left), case let .number(rightResult) = eval(node: binOp.right) else { 97 | fatalError("Error! binOp is wrong") 98 | } 99 | 100 | switch binOp.operation { 101 | case .plus: 102 | return .number(leftResult + rightResult) 103 | case .minus: 104 | return .number(leftResult - rightResult) 105 | case .mult: 106 | return .number(leftResult * rightResult) 107 | case .intDiv: 108 | return .number(leftResult / rightResult) 109 | } 110 | } 111 | 112 | func eval(unaryOperation: OCUnaryOperation) -> OCValue { 113 | guard case let .number(result) = eval(node: unaryOperation.operand) else { 114 | fatalError("Error: eval unaryOperation") 115 | } 116 | switch unaryOperation.operation { 117 | case .plus: 118 | return .number(+result) 119 | case .minus: 120 | return .number(-result) 121 | } 122 | } 123 | 124 | } 125 | -------------------------------------------------------------------------------- /Sources/Core/OC/OCLexer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OCLexer.swift 3 | // HTN 4 | // 5 | // Created by DaiMing on 2018/6/10. 6 | // 7 | 8 | import Foundation 9 | 10 | public class OCLexer { 11 | private let text: String 12 | private var currentIndex: Int 13 | private var currentCharacter: Character? 14 | 15 | private let keywords: [String: OCToken] = [ 16 | "return": .return 17 | ] 18 | 19 | public init(_ input: String) { 20 | if input.count == 0 { 21 | fatalError("Error! input can't be empty") 22 | } 23 | self.text = input 24 | currentIndex = 0 25 | currentCharacter = text[text.startIndex] 26 | } 27 | 28 | // 流程函数 29 | func nextTk() -> OCToken { 30 | // 到文件末 31 | if currentIndex > self.text.count - 1 { 32 | return .eof 33 | } 34 | 35 | // 空格换行 36 | if CharacterSet.whitespacesAndNewlines.contains((currentCharacter?.unicodeScalars.first!)!) { 37 | skipWhiteSpaceAndNewLines() 38 | //return .whiteSpaceAndNewLine 39 | } 40 | 41 | // 数字 42 | if CharacterSet.decimalDigits.contains((currentCharacter?.unicodeScalars.first!)!) { 43 | return number() 44 | } 45 | 46 | // identifier 47 | if CharacterSet.alphanumerics.contains((currentCharacter?.unicodeScalars.first!)!) { 48 | return id() 49 | } 50 | 51 | // 符号 52 | if currentCharacter == "+" { 53 | advance() 54 | return .operation(.plus) 55 | } 56 | if currentCharacter == "-" { 57 | advance() 58 | return .operation(.minus) 59 | } 60 | if currentCharacter == "*" { 61 | advance() 62 | return .operation(.mult) 63 | } 64 | if currentCharacter == "/" { 65 | // 处理可能的注释的情况 66 | if peek() == "/" { 67 | advance() 68 | advance() 69 | return commentsFromDoubleSlash() 70 | } else if peek() == "*" { 71 | advance() 72 | advance() 73 | return commentsFromSlashAsterisk() 74 | } else { 75 | advance() 76 | return .operation(.intDiv) 77 | } 78 | 79 | } 80 | if currentCharacter == "(" { 81 | advance() 82 | return .paren(.left) 83 | } 84 | if currentCharacter == ")" { 85 | advance() 86 | return .paren(.right) 87 | } 88 | if currentCharacter == "@" { 89 | return at() 90 | } 91 | if currentCharacter == ";" { 92 | advance() 93 | return .semi 94 | } 95 | if currentCharacter == "=" { 96 | advance() 97 | return .assign 98 | } 99 | if currentCharacter == "{" { 100 | advance() 101 | return .brace(.left) 102 | } 103 | if currentCharacter == "}" { 104 | advance() 105 | return .brace(.right) 106 | } 107 | if currentCharacter == "*" { 108 | advance() 109 | return .asterisk 110 | } 111 | if currentCharacter == "," { 112 | advance() 113 | return .comma 114 | } 115 | advance() 116 | return .eof 117 | } 118 | 119 | // identifier and keywords 120 | private func id() -> OCToken { 121 | var idStr = "" 122 | while let character = currentCharacter, CharacterSet.alphanumerics.contains(character.unicodeScalars.first!) { 123 | idStr += String(character) 124 | advance() 125 | } 126 | 127 | // 关键字 128 | if let token = keywords[idStr] { 129 | return token 130 | } 131 | 132 | return .id(idStr) 133 | } 134 | 135 | // @符号的处理 136 | private func at() -> OCToken { 137 | advance() 138 | var atStr = "" 139 | while let character = currentCharacter, CharacterSet.alphanumerics.contains((character.unicodeScalars.first!)) { 140 | atStr += String(character) 141 | advance() 142 | } 143 | if atStr == "interface" { 144 | return .interface 145 | } 146 | if atStr == "end" { 147 | return .end 148 | } 149 | if atStr == "implementation" { 150 | return .implementation 151 | } 152 | if atStr == "property" { 153 | return .property 154 | } 155 | 156 | fatalError("Error: at string not support") 157 | } 158 | // 数字处理 159 | private func number() -> OCToken { 160 | var numStr = "" 161 | while let character = currentCharacter, CharacterSet.decimalDigits.contains(character.unicodeScalars.first!) { 162 | numStr += String(character) 163 | advance() 164 | } 165 | 166 | if let character = currentCharacter, character == ".", peek() != "." { 167 | numStr += "." 168 | advance() 169 | while let character = currentCharacter, CharacterSet.decimalDigits.contains(character.unicodeScalars.first!) { 170 | numStr += String(character) 171 | advance() 172 | } 173 | return .constant(.float(Float(numStr)!)) 174 | } 175 | 176 | return .constant(.integer(Int(numStr)!)) 177 | } 178 | 179 | // --------- 辅助函数 -------- 180 | private func advance() { 181 | currentIndex += 1 182 | guard currentIndex < text.count else { 183 | currentCharacter = nil 184 | return 185 | } 186 | currentCharacter = text[text.index(text.startIndex, offsetBy: currentIndex)] 187 | } 188 | 189 | // 往前探一个字符,不改变当前字符 190 | private func peek() -> Character? { 191 | let peekIndex = currentIndex + 1 192 | guard peekIndex < text.count else { 193 | return nil 194 | } 195 | return text[text.index(text.startIndex, offsetBy: peekIndex)] 196 | } 197 | 198 | // 过滤 // 这种注释 199 | private func commentsFromDoubleSlash() -> OCToken { 200 | var cStr = "" 201 | while let character = currentCharacter, !CharacterSet.newlines.contains(character.unicodeScalars.first!) { 202 | advance() 203 | cStr += String(character) 204 | } 205 | return .comments(cStr) 206 | } 207 | 208 | // 过滤 /* */ 这样的注释 209 | private func commentsFromSlashAsterisk() -> OCToken { 210 | var cStr = "" 211 | while let character = currentCharacter { 212 | if character == "*" && peek() == "/" { 213 | advance() 214 | advance() 215 | break 216 | } else { 217 | advance() 218 | cStr += String(character) 219 | } 220 | 221 | } 222 | return .comments(cStr) 223 | } 224 | 225 | private func skipWhiteSpaceAndNewLines() { 226 | while let character = currentCharacter, CharacterSet.whitespacesAndNewlines.contains(character.unicodeScalars.first!) { 227 | advance() 228 | } 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /Sources/Core/OC/OCParser.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OCParser.swift 3 | // HTN 4 | // 5 | // Created by DaiMing on 2018/7/4. 6 | // 7 | 8 | import Foundation 9 | 10 | public class OCParser { 11 | private var tkIndex = 0 12 | private var tks: [OCToken] 13 | 14 | private var currentTk: OCToken { 15 | return tks[tkIndex] 16 | } 17 | private var nextTk: OCToken { 18 | return tks[tkIndex + 1] 19 | } 20 | 21 | public init(_ input: String) { 22 | let lexer = OCLexer(input) 23 | var tk = lexer.nextTk() 24 | var all = [tk] 25 | while tk != .eof { 26 | tk = lexer.nextTk() 27 | switch tk { 28 | case let .comments(cmStr): 29 | print(cmStr) 30 | default: 31 | all.append(tk) 32 | } 33 | } 34 | tks = all 35 | } 36 | 37 | public func debug() { 38 | // while currentTk != .eof { 39 | // print(currentTk) 40 | // eat(currentTk) 41 | // } 42 | // eat(.whiteSpaceAndNewLine) 43 | let pgm = program() 44 | print(pgm) 45 | 46 | } 47 | 48 | public func parse() -> OCAST { 49 | let node = program() 50 | if currentTk != .eof { 51 | fatalError("Error: no reached end") 52 | } 53 | return node 54 | } 55 | 56 | private func program() -> OCProgram { 57 | return OCProgram(interface: interface(), implementation: implementation()) 58 | } 59 | 60 | private func interface() -> OCInterface { 61 | eat(.interface) 62 | guard case let .id(name) = currentTk else { 63 | fatalError("Error interface") 64 | } 65 | eat(.id(name)) 66 | let pl = propertyList() 67 | eat(.end) 68 | return OCInterface(name: name, propertyList: pl) 69 | } 70 | 71 | private func propertyList() -> [OCPropertyDeclaration] { 72 | var properties = [OCPropertyDeclaration]() 73 | while currentTk == .property { 74 | eat(.property) 75 | eat(.paren(.left)) 76 | let pa = propertyAttributes() 77 | eat(.paren(.right)) 78 | guard case let .id(pType) = currentTk else { 79 | fatalError("Error: property type wrong") 80 | } 81 | eat(.id(pType)) 82 | guard case let .id(name) = currentTk else { 83 | fatalError("Error: property name wrong") 84 | } 85 | eat(.id(name)) 86 | let pd = OCPropertyDeclaration(propertyAttributesList: pa, type: pType, name: name) 87 | properties.append(pd) 88 | eat(.semi) 89 | } 90 | return properties 91 | } 92 | 93 | private func propertyAttributes() -> [OCPropertyAttribute] { 94 | let p = propertyAttribute() 95 | var pa = [p] 96 | while currentTk == .comma { 97 | eat(.comma) 98 | pa.append(propertyAttribute()) 99 | } 100 | return pa 101 | } 102 | 103 | private func propertyAttribute() -> OCPropertyAttribute { 104 | guard case let .id(name) = currentTk else { 105 | fatalError("Error: propertyAttribute wrong") 106 | } 107 | eat(.id(name)) 108 | return OCPropertyAttribute(name: name) 109 | } 110 | 111 | private func implementation() -> OCImplementation { 112 | eat(.implementation) 113 | guard case let .id(name) = currentTk else { 114 | fatalError("Error implementation") 115 | } 116 | eat(.id(name)) 117 | let methodListNode = methodList() 118 | eat(.end) 119 | return OCImplementation(name: name, methodList: methodListNode) 120 | } 121 | 122 | private func methodList() -> [OCMethod] { 123 | var methods = [OCMethod]() 124 | while currentTk == .operation(.plus) || currentTk == .operation(.minus) { 125 | eat(currentTk) 126 | methods.append(method()) 127 | } 128 | return methods 129 | } 130 | 131 | private func method() -> OCMethod { 132 | eat(.paren(.left)) 133 | guard case let .id(reStr) = currentTk else { 134 | fatalError("Error reStr") 135 | } 136 | eat(.id(reStr)) 137 | eat(.paren(.right)) 138 | guard case let .id(methodName) = currentTk else { 139 | fatalError("Error methodName") 140 | } 141 | eat(.id(methodName)) 142 | eat(.brace(.left)) 143 | let statementsNode = statements() 144 | eat(.brace(.right)) 145 | return OCMethod(returnIdentifier: reStr, methodName: methodName, statements: statementsNode) 146 | } 147 | 148 | private func statements() -> [OCAST] { 149 | let sNode = statement() 150 | var statements = [sNode] 151 | while currentTk == .semi { 152 | eat(.semi) 153 | statements.append(statement()) 154 | } 155 | return statements 156 | } 157 | 158 | private func statement() -> OCAST { 159 | switch currentTk { 160 | case .id: 161 | if case .id = nextTk { 162 | guard case let .id(name) = currentTk else { 163 | fatalError("Error: wrong") 164 | } 165 | eat(.id(name)) 166 | let v = variable() 167 | if currentTk == .assign { 168 | eat(.assign) 169 | let right = expr() 170 | return OCVariableDeclaration(variable: v, type: name, right: right) 171 | } else { 172 | fatalError("Error: wrong") 173 | } 174 | } 175 | return assignStatement() 176 | default: 177 | return empty() 178 | } 179 | } 180 | 181 | private func assignStatement() -> OCAssign { 182 | let left = variable() 183 | eat(.assign) 184 | let right = expr() 185 | return OCAssign(left: left, right: right) 186 | } 187 | 188 | private func variable() -> OCVar { 189 | guard case let .id(name) = currentTk else { 190 | fatalError("Error: var was wrong") 191 | } 192 | eat(.id(name)) 193 | 194 | return OCVar(name: name) 195 | } 196 | 197 | private func empty() -> OCNoOp { 198 | return OCNoOp() 199 | } 200 | 201 | /*--------- 运算符 --------*/ 202 | 203 | public func expr() -> OCAST { 204 | var node = term() 205 | 206 | while [.operation(.plus), .operation(.minus)].contains(currentTk) { 207 | let tk = currentTk 208 | eat(currentTk) 209 | if tk == .operation(.plus) { 210 | node = OCBinOp(left: node, operation: .plus, right: term()) 211 | } else if tk == .operation(.minus) { 212 | node = OCBinOp(left: node, operation: .minus, right: term()) 213 | } 214 | } 215 | return node 216 | } 217 | 218 | 219 | // 语法解析中对数字的处理 220 | private func term() -> OCAST { 221 | var node = factor() 222 | 223 | while [.operation(.mult), .operation(.intDiv)].contains(currentTk) { 224 | let tk = currentTk 225 | eat(currentTk) 226 | if tk == .operation(.mult) { 227 | node = OCBinOp(left: node, operation: .mult, right: factor()) 228 | } else if tk == .operation(.intDiv) { 229 | node = OCBinOp(left: node, operation: .intDiv, right: factor()) 230 | } 231 | } 232 | return node 233 | } 234 | 235 | private func factor() -> OCAST { 236 | let tk = currentTk 237 | switch tk { 238 | case .operation(.plus): 239 | eat(.operation(.plus)) 240 | return OCUnaryOperation(operation: .plus, operand: factor()) 241 | case .operation(.minus): 242 | eat(.operation(.minus)) 243 | return OCUnaryOperation(operation: .minus, operand: factor()) 244 | case let .constant(.integer(result)): 245 | eat(.constant(.integer(result))) 246 | return OCNumber.integer(result) 247 | case let .constant(.float(result)): 248 | eat(.constant(.float(result))) 249 | return OCNumber.float(result) 250 | case .paren(.left): 251 | eat(.paren(.left)) 252 | let result = expr() 253 | eat(.paren(.right)) 254 | return result 255 | default: 256 | return variable() 257 | } 258 | } 259 | 260 | private func eat(_ token: OCToken) { 261 | if currentTk == token { 262 | tkIndex += 1 263 | } else { 264 | fatalError("Error: eat wrong") 265 | } 266 | } 267 | 268 | 269 | 270 | } 271 | -------------------------------------------------------------------------------- /Sources/Core/OC/OCToken.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OCToken.swift 3 | // HTN 4 | // 5 | // Created by DaiMing on 2018/6/10. 6 | // 7 | 8 | import Foundation 9 | 10 | public enum OCValue { 11 | case none 12 | case number(OCNumber) 13 | case boolean(Bool) 14 | case string(String) 15 | } 16 | 17 | public enum OCConstant { 18 | case integer(Int) 19 | case float(Float) 20 | case boolean(Bool) 21 | case string(String) 22 | } 23 | 24 | public enum OCOperation { 25 | case plus 26 | case minus 27 | case mult 28 | case intDiv 29 | } 30 | 31 | public enum OCDirection { 32 | case left 33 | case right 34 | } 35 | 36 | public enum OCToken { 37 | case eof 38 | case whiteSpaceAndNewLine 39 | case comments(String) // 注释 // 和 /**/ 40 | case constant(OCConstant) // int float string bool 41 | case operation(OCOperation) // + - * / 42 | case paren(OCDirection) // ( ) 43 | case brace(OCDirection) // { } 44 | case asterisk // * 45 | case interface // @interface 46 | case end // @end 47 | case implementation // @implementation 48 | case id(String) // string 49 | case semi // ; 50 | case assign // = 51 | case `return` 52 | case comma // , 53 | case property // @property 54 | } 55 | 56 | extension OCConstant: Equatable { 57 | public static func == (lhs: OCConstant, rhs: OCConstant) -> Bool { 58 | switch (lhs, rhs) { 59 | case let (.integer(left), .integer(right)): 60 | return left == right 61 | case let (.float(left), .float(right)): 62 | return left == right 63 | case let (.boolean(left), .boolean(right)): 64 | return left == right 65 | case let (.string(left), .string(right)): 66 | return left == right 67 | default: 68 | return false 69 | } 70 | } 71 | } 72 | 73 | extension OCOperation: Equatable { 74 | public static func == (lhs: OCOperation, rhs: OCOperation) -> Bool { 75 | switch (lhs, rhs) { 76 | case (.plus, .plus): 77 | return true 78 | case (.minus, .minus): 79 | return true 80 | case (.mult, .mult): 81 | return true 82 | case (.intDiv, .intDiv): 83 | return true 84 | default: 85 | return false 86 | } 87 | } 88 | } 89 | 90 | extension OCDirection: Equatable { 91 | public static func == (lhs: OCDirection, rhs: OCDirection) -> Bool { 92 | switch (lhs, rhs) { 93 | case (.left, .left): 94 | return true 95 | case (.right, .right): 96 | return true 97 | default: 98 | return false 99 | } 100 | } 101 | } 102 | 103 | extension OCToken: Equatable { 104 | public static func == (lhs: OCToken, rhs: OCToken) -> Bool { 105 | switch (lhs, rhs) { 106 | case let (.constant(left), .constant(right)): 107 | return left == right 108 | case let (.operation(left), .operation(right)): 109 | return left == right 110 | case (.eof, .eof): 111 | return true 112 | case (.whiteSpaceAndNewLine, .whiteSpaceAndNewLine): 113 | return true 114 | case let (.comments(left), .comments(right)): 115 | return left == right 116 | case let (.paren(left), .paren(right)): 117 | return left == right 118 | case let (.brace(left), .brace(right)): 119 | return left == right 120 | case (.asterisk, .asterisk): 121 | return true 122 | case (.interface, .interface): 123 | return true 124 | case (.end, .end): 125 | return true 126 | case (.implementation, .implementation): 127 | return true 128 | case let (.id(left), .id(right)): 129 | return left == right 130 | case (.semi, .semi): 131 | return true 132 | case (.assign, .assign): 133 | return true 134 | case (.return, .return): 135 | return true 136 | case (.comma, .comma): 137 | return true 138 | case (.property, .property): 139 | return true 140 | default: 141 | return false 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /Sources/Core/OC/StaticAnalyzer/OCStaticAnalyzer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OCStaticAnalyzer.swift 3 | // HTN 4 | // 5 | // Created by DaiMing on 2018/7/4. 6 | // 7 | 8 | import Foundation 9 | 10 | public class OCStaticAnalyzer: OCVisitor { 11 | private var currentScope: OCSymbolTable? 12 | private var scopes: [String: OCSymbolTable] = [:] 13 | // private var symbolTable = OCSymbolTable(name: "global") 14 | 15 | public init() { 16 | 17 | } 18 | 19 | public func analyze(node: OCAST) -> [String: OCSymbolTable] { 20 | visit(node: node) 21 | return scopes 22 | } 23 | 24 | func visit(program: OCProgram) { 25 | let globalScope = OCSymbolTable(name: "global", level: 1, enclosingScope: nil) 26 | scopes[globalScope.name] = globalScope 27 | currentScope = globalScope 28 | visit(interface: program.interface) 29 | visit(implementation: program.implementation) 30 | currentScope = nil 31 | } 32 | 33 | func visit(variableDeclaration: OCVariableDeclaration) { 34 | guard let scope = currentScope else { 35 | fatalError("Error: out of a scope") 36 | } 37 | 38 | guard scope.lookup(variableDeclaration.variable.name, currentScopeOnly: true) == nil else { 39 | fatalError("Error: Doplicate identifier") 40 | } 41 | 42 | guard let symbolType = scope.lookup(variableDeclaration.type) else { 43 | fatalError("Error: type not found") 44 | } 45 | 46 | scope.define(OCVariableSymbol(name: variableDeclaration.variable.name, type: symbolType)) 47 | visit(node: variableDeclaration.variable) 48 | visit(node: variableDeclaration.right) 49 | } 50 | 51 | func visit(method: OCMethod) { 52 | let scope = OCSymbolTable(name: method.methodName, level: (currentScope?.level ?? 0) + 1, enclosingScope: currentScope) 53 | scopes[scope.name] = scope 54 | currentScope = scope 55 | 56 | for statement in method.statements { 57 | visit(node: statement) 58 | } 59 | 60 | currentScope = currentScope?.enclosingScope 61 | } 62 | 63 | func visit(propertyDeclaration: OCPropertyDeclaration) { 64 | guard let scope = currentScope else { 65 | fatalError("Error: out of a scope") 66 | } 67 | guard scope.lookup(propertyDeclaration.name) == nil else { 68 | fatalError("Error: duplicate identifier \(propertyDeclaration.name) found") 69 | } 70 | 71 | guard let symbolType = scope.lookup(propertyDeclaration.type) else { 72 | fatalError("Error: \(propertyDeclaration.type) type not found") 73 | } 74 | 75 | scope.define(OCVariableSymbol(name: propertyDeclaration.name, type: symbolType)) 76 | } 77 | 78 | func visit(variable: OCVar) { 79 | guard let scope = currentScope else { 80 | fatalError("Error: cannot access") 81 | } 82 | guard scope.lookup(variable.name) != nil else { 83 | fatalError("Error: \(variable.name) variable not found") 84 | } 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /Sources/Core/OC/StaticAnalyzer/OCSymbol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OCSymbol.swift 3 | // HTN 4 | // 5 | // Created by DaiMing on 2018/7/10. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol OCSymbol { 11 | var name : String { get } 12 | } 13 | 14 | public enum OCBuiltInTypeSymbol: OCSymbol { 15 | case integer 16 | case float 17 | case boolean 18 | case string 19 | 20 | var name: String { 21 | switch self { 22 | case .integer: 23 | return "NSUInteger" 24 | case .float: 25 | return "CGFloat" 26 | case .boolean: 27 | return "BOOL" 28 | case .string: 29 | return "NSString" 30 | } 31 | } 32 | } 33 | 34 | class OCVariableSymbol: OCSymbol { 35 | let name: String 36 | let type: OCSymbol 37 | 38 | init(name: String, type: OCSymbol) { 39 | self.name = name 40 | self.type = type 41 | } 42 | } 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /Sources/Core/OC/StaticAnalyzer/OCSymbolTable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OCSymbolTable.swift 3 | // HTN 4 | // 5 | // Created by DaiMing on 2018/7/10. 6 | // 7 | 8 | import Foundation 9 | 10 | public class OCSymbolTable { 11 | var symbols: [String: OCSymbol] = [:] 12 | 13 | let name: String 14 | let level: Int 15 | let enclosingScope: OCSymbolTable? 16 | 17 | init(name: String, level: Int, enclosingScope: OCSymbolTable?) { 18 | self.name = name 19 | self.level = level 20 | self.enclosingScope = enclosingScope 21 | 22 | defineBuiltInTypes() 23 | } 24 | 25 | private func defineBuiltInTypes() { 26 | define(OCBuiltInTypeSymbol.integer) 27 | define(OCBuiltInTypeSymbol.float) 28 | define(OCBuiltInTypeSymbol.boolean) 29 | define(OCBuiltInTypeSymbol.string) 30 | } 31 | 32 | func define(_ symbol: OCSymbol) { 33 | symbols[symbol.name] = symbol 34 | } 35 | 36 | func lookup(_ name: String, currentScopeOnly: Bool = false) -> OCSymbol? { 37 | if let symbol = symbols[name] { 38 | return symbol 39 | } 40 | if currentScopeOnly { 41 | return nil 42 | } 43 | return enclosingScope?.lookup(name) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Sources/Core/OC/StaticAnalyzer/OCVisitor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OCVisitor.swift 3 | // HTN 4 | // 5 | // Created by DaiMing on 2018/6/11. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol OCVisitor: class { 11 | func visit(node: OCAST) 12 | func visit(program: OCProgram) 13 | func visit(interface: OCInterface) 14 | func visit(propertyDeclaration: OCPropertyDeclaration) 15 | func visit(propertyAttribute: OCPropertyAttribute) 16 | func visit(implementation: OCImplementation) 17 | func visit(method: OCMethod) 18 | func visit(compoundStatement: OCCompoundStatement) 19 | func visit(identifier: OCIdentifier) 20 | func visit(assign: OCAssign) 21 | func visit(variable: OCVar) 22 | func visit(number: OCNumber) 23 | func visit(unaryOperation: OCUnaryOperation) 24 | func visit(binOp: OCBinOp) 25 | func visit(noOp: OCNoOp) 26 | func visit(variableDeclaration: OCVariableDeclaration) 27 | } 28 | 29 | extension OCVisitor { 30 | func visit(node: OCAST) { 31 | switch node { 32 | case let program as OCProgram: 33 | visit(program: program) 34 | case let interface as OCInterface: 35 | visit(interface: interface) 36 | case let propertyDeclaration as OCPropertyDeclaration: 37 | visit(propertyDeclaration: propertyDeclaration) 38 | case let propertyAttribute as OCPropertyAttribute: 39 | visit(propertyAttribute: propertyAttribute) 40 | case let implementation as OCImplementation: 41 | visit(implementation: implementation) 42 | case let method as OCMethod: 43 | visit(method: method) 44 | case let compoundStatement as OCCompoundStatement: 45 | visit(compoundStatement: compoundStatement) 46 | case let identifier as OCIdentifier: 47 | visit(identifier: identifier) 48 | case let assign as OCAssign: 49 | visit(assign: assign) 50 | case let variable as OCVar: 51 | visit(variable: variable) 52 | case let number as OCNumber: 53 | visit(number: number) 54 | case let unaryOperation as OCUnaryOperation: 55 | visit(unaryOperation: unaryOperation) 56 | case let binOp as OCBinOp: 57 | visit(binOp: binOp) 58 | case let noOp as OCNoOp: 59 | visit(noOp: noOp) 60 | case let variableDeclaration as OCVariableDeclaration: 61 | visit(variableDeclaration: variableDeclaration) 62 | default: 63 | fatalError("Error: Visitor type error") 64 | } 65 | } 66 | 67 | func visit(program: OCProgram) { 68 | visit(interface: program.interface) 69 | visit(implementation: program.implementation) 70 | } 71 | 72 | func visit(interface: OCInterface) { 73 | for property in interface.propertyList { 74 | visit(propertyDeclaration: property) 75 | } 76 | } 77 | 78 | func visit(propertyDeclaration: OCPropertyDeclaration) { 79 | for propertyAttribute in propertyDeclaration.propertyAttributesList { 80 | visit(propertyAttribute: propertyAttribute) 81 | } 82 | } 83 | 84 | func visit(propertyAttribute: OCPropertyAttribute) { 85 | 86 | } 87 | 88 | func visit(implementation: OCImplementation) { 89 | for method in implementation.methodList { 90 | visit(method: method) 91 | } 92 | } 93 | 94 | func visit(method: OCMethod) { 95 | for statement in method.statements { 96 | visit(node: statement) 97 | } 98 | } 99 | 100 | func visit(compoundStatement: OCCompoundStatement) { 101 | 102 | } 103 | 104 | func visit(identifier: OCIdentifier) { 105 | 106 | } 107 | 108 | func visit(assign: OCAssign) { 109 | visit(node: assign.left) 110 | visit(node: assign.right) 111 | } 112 | 113 | func visit(variable: OCVar) { 114 | 115 | } 116 | 117 | func visit(variableDeclaration: OCVariableDeclaration) { 118 | visit(node: variableDeclaration.variable) 119 | visit(node: variableDeclaration.right) 120 | } 121 | 122 | /*--------- 运算符 --------*/ 123 | 124 | func visit(number _: OCNumber) { 125 | 126 | } 127 | 128 | func visit(unaryOperation: OCUnaryOperation) { 129 | visit(node: unaryOperation.operand) 130 | } 131 | 132 | func visit(binOp: OCBinOp) { 133 | visit(node: binOp.left) 134 | visit(node: binOp.right) 135 | } 136 | func visit(noOp: OCNoOp) { 137 | 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /Sources/H5Editor/H5EditorDatasource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // H5EditorData.swift 3 | // HTNSwift 4 | // 5 | // Created by DaiMing on 2018/3/21. 6 | // Copyright © 2018年 Starming. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct H5EditorDatasource { 12 | func dataFromJson(id:String) -> H5Editor { 13 | let jsonString = """ 14 | { 15 | "errno": 0, 16 | "msg": "ok", 17 | "data": 18 | { 19 | "_id": "5aa72bf823e2380c378563ea", 20 | "clazz": "project", 21 | "userId": "daiming", 22 | "name": "start", 23 | "title": "start", 24 | "pages": [ 25 | { 26 | "id": "cdjy4sqej0", 27 | "clazz": "page", 28 | "name": "页面-1", 29 | "bgColor": "rgba(255,255,255,1)", 30 | "bgImage": "", 31 | "bgImagePosition": "center-center", 32 | "bgImageRepeat": "none", 33 | "widgets": [ 34 | { 35 | "id": "1tfjoi6ktr", 36 | "name": "图片-1", 37 | "type": "Image", 38 | "location": "page", 39 | "align": "free", 40 | "alignLeftMargin": 0, 41 | "alignRightMargin": 0, 42 | "alignTopMargin": 0, 43 | "alignBottomMargin": 0, 44 | "width": 278, 45 | "height": 273, 46 | "top": 240, 47 | "left": 35, 48 | "rotate": 0, 49 | "bgColor": "rgba(255,255,255,0)", 50 | "bgImage": "", 51 | "bgImagePosition": "center-center", 52 | "bgImageRepeat": "none", 53 | "borderRadius": 0, 54 | "visible": true, 55 | "data": 56 | { 57 | "url": "https://static.didialift.com/pinche/gift/resource/bd02101d8e198f34a1999f0c12e451cc-favorite.png", 58 | "usePicSet": false, 59 | "size": 60 | { 61 | "height": 496, 62 | "width": 505 63 | } 64 | }, 65 | "animations": [], 66 | "triggers": [], 67 | "isSelected": true, 68 | "layout": "flow", 69 | "pageWidth": 375, 70 | "pageHeight": 603, 71 | "opacity": 100, 72 | "hasBorder": false, 73 | "borderStyle": "solid", 74 | "borderWidth": 1, 75 | "borderColor": "rgba(0, 0, 0, 1)", 76 | "borderDirections": [ 77 | "top", 78 | "right", 79 | "bottom", 80 | "left" 81 | ], 82 | "condition": 83 | {}, 84 | "variableMap": 85 | {}, 86 | "needAttach": false, 87 | "attach": "", 88 | "locked": false, 89 | "readonly": false, 90 | "layers": [], 91 | "hasLayers": false, 92 | "defaultLayerCount": 0, 93 | "maxLayerCount": 0, 94 | "padding": "0 0 0 0" 95 | }, 96 | { 97 | "id": "emlku5dlde", 98 | "name": "单行文本-1", 99 | "type": "NormalText", 100 | "location": "page", 101 | "align": "free", 102 | "alignLeftMargin": 0, 103 | "alignRightMargin": 0, 104 | "alignTopMargin": 0, 105 | "alignBottomMargin": 0, 106 | "width": 375, 107 | "height": 38, 108 | "top": 205, 109 | "left": 0, 110 | "rotate": 0, 111 | "bgColor": "rgba(255,255,255,0)", 112 | "bgImage": "", 113 | "bgImagePosition": "center-center", 114 | "bgImageRepeat": "none", 115 | "borderRadius": 0, 116 | "visible": true, 117 | "data": 118 | { 119 | "content": "就是单行的", 120 | "color": "rgba(22.95,142.8,234.6,1)", 121 | "fontSize": 32, 122 | "fontFamily": "PingFangSC", 123 | "fontWeight": "Medium", 124 | "verticalAlign": "middle", 125 | "horizontalAlign": "center", 126 | "lineHeight": 60, 127 | "letterSpacing": 0 128 | }, 129 | "animations": [], 130 | "triggers": [], 131 | "isSelected": true, 132 | "layout": "flow", 133 | "pageWidth": 375, 134 | "pageHeight": 603, 135 | "opacity": 100, 136 | "hasBorder": false, 137 | "borderStyle": "solid", 138 | "borderWidth": 1, 139 | "borderColor": "rgba(0, 0, 0, 1)", 140 | "borderDirections": [ 141 | "top", 142 | "right", 143 | "bottom", 144 | "left" 145 | ], 146 | "condition": 147 | {}, 148 | "variableMap": 149 | {}, 150 | "needAttach": false, 151 | "attach": "", 152 | "locked": false, 153 | "readonly": false, 154 | "layers": [], 155 | "hasLayers": false, 156 | "defaultLayerCount": 0, 157 | "maxLayerCount": 0, 158 | "padding": "0 0 0 0" 159 | }, 160 | { 161 | "id": "xgrtr3x785", 162 | "name": "文本-3", 163 | "type": "RichText", 164 | "location": "page", 165 | "align": "free", 166 | "alignLeftMargin": 0, 167 | "alignRightMargin": 0, 168 | "alignTopMargin": 0, 169 | "alignBottomMargin": 0, 170 | "width": 375, 171 | "height": 48, 172 | "top": 0, 173 | "left": 0, 174 | "rotate": 0, 175 | "bgColor": "rgba(255,255,255,0)", 176 | "bgImage": "", 177 | "bgImagePosition": "center-center", 178 | "bgImageRepeat": "none", 179 | "borderRadius": 0, 180 | "visible": true, 181 | "data": 182 | { 183 | "content": "

流式1

" 184 | }, 185 | "animations": [], 186 | "triggers": [], 187 | "isSelected": false, 188 | "layout": "flow", 189 | "pageWidth": 375, 190 | "pageHeight": 603, 191 | "opacity": 100, 192 | "hasBorder": false, 193 | "borderStyle": "solid", 194 | "borderWidth": 1, 195 | "borderColor": "rgba(0, 0, 0, 1)", 196 | "borderDirections": [ 197 | "top", 198 | "right", 199 | "bottom", 200 | "left" 201 | ], 202 | "condition": 203 | {}, 204 | "variableMap": 205 | {}, 206 | "needAttach": false, 207 | "attach": "", 208 | "locked": false, 209 | "readonly": false, 210 | "layers": [], 211 | "hasLayers": false, 212 | "defaultLayerCount": 0, 213 | "maxLayerCount": 0, 214 | "padding": "8 16 8 16" 215 | }, 216 | { 217 | "id": "4fw4rfejxv", 218 | "name": "文本-4", 219 | "type": "RichText", 220 | "location": "page", 221 | "align": "free", 222 | "alignLeftMargin": 0, 223 | "alignRightMargin": 0, 224 | "alignTopMargin": 0, 225 | "alignBottomMargin": 0, 226 | "width": 375, 227 | "height": 48, 228 | "top": 0, 229 | "left": 0, 230 | "rotate": 0, 231 | "bgColor": "rgba(255,255,255,0)", 232 | "bgImage": "", 233 | "bgImagePosition": "center-center", 234 | "bgImageRepeat": "none", 235 | "borderRadius": 0, 236 | "visible": true, 237 | "data": 238 | { 239 | "content": "

流式2

" 240 | }, 241 | "animations": [], 242 | "triggers": [], 243 | "isSelected": false, 244 | "layout": "flow", 245 | "pageWidth": 375, 246 | "pageHeight": 603, 247 | "opacity": 100, 248 | "hasBorder": false, 249 | "borderStyle": "solid", 250 | "borderWidth": 1, 251 | "borderColor": "rgba(0, 0, 0, 1)", 252 | "borderDirections": [ 253 | "top", 254 | "right", 255 | "bottom", 256 | "left" 257 | ], 258 | "condition": 259 | {}, 260 | "variableMap": 261 | {}, 262 | "needAttach": false, 263 | "attach": "", 264 | "locked": false, 265 | "readonly": false, 266 | "layers": [], 267 | "hasLayers": false, 268 | "defaultLayerCount": 0, 269 | "maxLayerCount": 0, 270 | "padding": "8 16 8 16" 271 | }, 272 | { 273 | "id": "167r8ukvkj", 274 | "name": "文本-2", 275 | "type": "RichText", 276 | "location": "page", 277 | "align": "free", 278 | "alignLeftMargin": 0, 279 | "alignRightMargin": 0, 280 | "alignTopMargin": 0, 281 | "alignBottomMargin": 0, 282 | "width": 375, 283 | "height": 48, 284 | "top": 140, 285 | "left": 40, 286 | "rotate": 0, 287 | "bgColor": "rgba(255,255,255,0)", 288 | "bgImage": "", 289 | "bgImagePosition": "center-center", 290 | "bgImageRepeat": "none", 291 | "borderRadius": 0, 292 | "visible": true, 293 | "data": 294 | { 295 | "content": "

普通2

" 296 | }, 297 | "animations": [], 298 | "triggers": [], 299 | "isSelected": false, 300 | "layout": "flow", 301 | "pageWidth": 375, 302 | "pageHeight": 603, 303 | "opacity": 100, 304 | "hasBorder": false, 305 | "borderStyle": "solid", 306 | "borderWidth": 1, 307 | "borderColor": "rgba(0, 0, 0, 1)", 308 | "borderDirections": [ 309 | "top", 310 | "right", 311 | "bottom", 312 | "left" 313 | ], 314 | "condition": 315 | {}, 316 | "variableMap": 317 | {}, 318 | "needAttach": false, 319 | "attach": "", 320 | "locked": false, 321 | "readonly": false, 322 | "layers": [], 323 | "hasLayers": false, 324 | "defaultLayerCount": 0, 325 | "maxLayerCount": 0, 326 | "padding": "8 16 8 16" 327 | }, 328 | { 329 | "id": "llfxu51uie", 330 | "name": "文本-1", 331 | "type": "RichText", 332 | "location": "page", 333 | "align": "free", 334 | "alignLeftMargin": 0, 335 | "alignRightMargin": 0, 336 | "alignTopMargin": 0, 337 | "alignBottomMargin": 0, 338 | "width": 375, 339 | "height": 48, 340 | "top": 65, 341 | "left": 95, 342 | "rotate": 0, 343 | "bgColor": "rgba(163,120,95,0)", 344 | "bgImage": "", 345 | "bgImagePosition": "center-center", 346 | "bgImageRepeat": "none", 347 | "borderRadius": 0, 348 | "visible": true, 349 | "data": 350 | { 351 | "content": "

普通1

" 352 | }, 353 | "animations": [], 354 | "triggers": [], 355 | "isSelected": false, 356 | "layout": "normal", 357 | "pageWidth": 375, 358 | "pageHeight": 603, 359 | "opacity": 100, 360 | "hasBorder": false, 361 | "borderStyle": "solid", 362 | "borderWidth": 1, 363 | "borderColor": "rgba(0, 0, 0, 1)", 364 | "borderDirections": [ 365 | "top", 366 | "right", 367 | "bottom", 368 | "left" 369 | ], 370 | "condition": 371 | {}, 372 | "variableMap": 373 | {}, 374 | "needAttach": false, 375 | "attach": "", 376 | "locked": false, 377 | "readonly": false, 378 | "layers": [], 379 | "hasLayers": false, 380 | "defaultLayerCount": 0, 381 | "maxLayerCount": 0, 382 | "padding": "8 16 8 16" 383 | }], 384 | "isSelected": true, 385 | "width": 375, 386 | "height": 603, 387 | "type": "scrollscreen", 388 | "triggers": [], 389 | "variableMap": 390 | {}, 391 | "isStandard": false 392 | }], 393 | "createTime": "2018-03-13T01:40:08.916Z", 394 | "lastModifyTime": "2018-03-13T01:40:10.972Z", 395 | "publishTime": "2018-03-26T07:49:09.141Z", 396 | "layout": "normal", 397 | "carouselDirection": "vertical", 398 | "dataUrl": "", 399 | "descUrl": "", 400 | "descriptions": [], 401 | "useData": false, 402 | "triggers": [], 403 | "variableMap": 404 | {}, 405 | "type": "combine", 406 | "lastModifiedDate": "2018-03-26T07:49:08.576Z", 407 | "config": 408 | {}, 409 | "editable": true, 410 | "logId": "5ab8a5f41843679c813b768a" 411 | } 412 | } 413 | """ 414 | let jsonStringClear = jsonString.replacingOccurrences(of: "\n", with: "") 415 | let jsonData = jsonStringClear.data(using: .utf8)! 416 | 417 | let decoder = JSONDecoder() 418 | let jsonModel = try! decoder.decode(H5Editor.self, from: jsonData) 419 | return jsonModel 420 | } 421 | 422 | } 423 | -------------------------------------------------------------------------------- /Sources/H5Editor/H5EditorStruct.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // HTNSwift 4 | // 5 | // Created by DaiMing on 2018/3/21. 6 | // Copyright © 2018年 Starming. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct H5Editor : Codable { 12 | var errno: Int? 13 | var msg: String? 14 | var data: Data? 15 | 16 | struct Data: Codable { 17 | var _id: String? 18 | //var clazz: String? 19 | //var userId: String? 20 | var name: String? 21 | var title: String? 22 | var pages: [Page]? 23 | 24 | struct Page: Codable { 25 | var id: String? 26 | //var clazz: String? 27 | var name: String? 28 | var bgColor: String? 29 | var bgImage: String? 30 | var bgImagePosition: String? 31 | var bgImageRepeat: String? 32 | var widgets: [Widget]? 33 | var isSelected: Bool? 34 | var width: Float? 35 | var height: Float? 36 | var type: String? 37 | var triggers: [Trigger]? 38 | var variableMap: VariableMap? 39 | var isStandard: Bool? 40 | var panelType: String? 41 | var createAt: String? 42 | var themeType: String? 43 | 44 | struct Widget: Codable { 45 | var id: String? 46 | var name: String? 47 | var type: String? //类型 48 | var location: String? //相对定位的位置 page 49 | var align: String? //排列方式 free 50 | var alignLeftMargin: Float? //边距 51 | var alignRightMargin: Float? 52 | var alignTopMargin: Float? 53 | var alignBottomMargin: Float? 54 | var width: Float? //宽 55 | var height: Float? //高 56 | var top: Float? //顶部 57 | var left: Float? //居左多少 58 | var rotate: Float? //旋转 59 | var bgColor: String? //背景颜色 rgba(255,255,255,0) 60 | var bgImage: String? //背景图片 61 | var bgImagePosition: String? //背景图片位置 center-center 62 | var bgImageRepeat: String? //背景图片是否平铺 none 63 | var borderRadius: Float? //圆角 0 64 | var visible: Bool? //是否可视 true 65 | var data: WidgetData? //数据 66 | var animations: [Animation]? //动画 67 | var triggers: [Trigger]? //触发器 68 | var isSelected: Bool? //是否选择 false 69 | var layout: String? //布局 flow 70 | var pageWidth: Float? //所属页面宽度 71 | var pageHeight: Float? //所属页面高度 72 | var opacity: Float? //不透明度 100 73 | var hasBorder: Bool? //是否有边框 false 74 | var borderStyle: String? //边框样式 solid 75 | var borderWidth: Float? //边框宽度 1 76 | var borderColor: String? //边框颜色 77 | var borderDirections: [String]? //边框包含那些边['top','right','bottom','left'] 78 | var condition: Condition? //组件条件 79 | var variableMap: VariableMap? //组件属性的映射变量表 80 | var needAttach: Bool? //是否吸附其它组件 false 81 | var attach: String? //吸附组件 '' 82 | var locked: Bool? //锁定位置 83 | var readonly: Bool? //是否只读 84 | var layers: [Layer]? // 85 | var hasLayers: Bool? 86 | var defaultLayerCount: Float? 87 | var maxLayerCount: Float? 88 | var padding: String? //内边距 89 | var children: [Widget]? 90 | struct WidgetData: Codable { 91 | //label 92 | var content: String? 93 | var text: String? 94 | var color: String? 95 | var fontSize: Float? 96 | var fontFamily: String? 97 | var fontWeight: String? 98 | var verticalAlign: String? 99 | var horizontalAlign: String? 100 | var lineHeight: Float? 101 | var letterSpacing: Float? 102 | //image 103 | var url: String? 104 | var usePicSet: Bool? 105 | var size: Size? 106 | 107 | struct Size: Codable { 108 | var height: Float 109 | var width: Float 110 | } 111 | } 112 | struct Animation: Codable { 113 | 114 | } 115 | struct Condition: Codable { 116 | 117 | } 118 | struct Layer: Codable { 119 | 120 | } 121 | } 122 | 123 | } 124 | var createTime: String? 125 | var lastModifyTime: String? 126 | var publishTime: String? 127 | var layout: String? 128 | var carouselDirection: String? 129 | var dataUrl: String? 130 | var descUrl: String? 131 | var descriptions: [Description]? 132 | var useData: Bool? 133 | var triggers: [Trigger]? 134 | var variableMap: VariableMap? 135 | var type: String? 136 | var lastModifiedDate: String? 137 | var config: Config? 138 | var editable: Bool? 139 | var logId: String? 140 | 141 | struct Description: Codable { 142 | 143 | } 144 | //处理比如点击等事件 145 | struct Trigger: Codable { 146 | var id: String? 147 | var clazz: String? 148 | var type: String? 149 | var event: String? 150 | var data: TriggerData? 151 | var variableMap: VariableMap? 152 | 153 | struct TriggerData: Codable { 154 | var url: String? 155 | var parameter: Bool? 156 | } 157 | struct VariableMap: Codable { 158 | 159 | } 160 | } 161 | struct VariableMap: Codable { 162 | 163 | } 164 | struct Config: Codable { 165 | 166 | } 167 | 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /Sources/H5Editor/H5EditorToFrame.swift: -------------------------------------------------------------------------------- 1 | // 2 | // H5EditorToFrame.swift 3 | // HTNSwift 4 | // 5 | // Created by DaiMing on 2018/3/21. 6 | // Copyright © 2018年 Starming. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class H5EditorToFrame { 12 | public var m:M //多语言的支持 13 | public init(_ m:M) { 14 | self.m = m 15 | } 16 | 17 | public func convert(_ h5editor:H5Editor) -> (String, String) { 18 | // m.pageId = h5editor.data?.pages![0].id ?? "" 19 | m.pageId = h5editor.data?._id ?? "" 20 | //全部 widget 21 | let page = h5editor.data?.pages![0]; 22 | let allWidgets = page?.widgets ?? []; 23 | //流式布局 widget,逆序添加H5Editor的widgets 24 | let flowWidgets = allWidgets.filter{ $0.layout == "flow" }.reversed() 25 | //普通布局 widget,逆序添加H5Editor的widgets 26 | let normalWidgets = allWidgets.filter{ $0.layout == "normal" }.reversed() 27 | 28 | var wgPropertyStr = "" 29 | var wgInitStr = "" 30 | var wgGetterStr = "" 31 | var wgLayoutStr = "" 32 | //处理scrollView 33 | let pageStruct = pageStructConvertToScrollView(page: page!) 34 | wgPropertyStr += pageStruct.propertyStr 35 | wgInitStr += pageStruct.initStr 36 | wgGetterStr += pageStruct.getterStr 37 | //先处理和添加流式布局,再处理普通布局 38 | //流式布局 39 | var lastWidget : H5Editor.Data.Page.Widget 40 | for (index, widget) in flowWidgets.enumerated() { 41 | //更新标识 42 | lastWidget = widget 43 | //对 flow 的所有 widget 的处理 44 | let wd = widgetStructConvertToStr(widget: widget) 45 | m.id = widget.id ?? "" 46 | wgPropertyStr += wd.propertyStr 47 | wgInitStr += wd.initStr 48 | wgGetterStr += wd.getterStr 49 | wgLayoutStr += wd.layoutStr 50 | 51 | var fl = HTNMt.Flowly() 52 | fl.id = m.id 53 | fl.lastId = m.validIdStr(id: lastWidget.id ?? "") 54 | fl.isFirst = index == 0 55 | fl.viewPt = wd.viewPt 56 | 57 | wgInitStr += m.flowViewLayout(fl: fl) 58 | } 59 | 60 | //对于 normal 的处理 61 | for widget in normalWidgets { 62 | let wd = widgetStructConvertToStr(widget: widget) 63 | m.id = widget.id ?? "" 64 | wgPropertyStr += wd.propertyStr 65 | wgInitStr += wd.initStr 66 | wgGetterStr += wd.getterStr 67 | wgLayoutStr += wd.layoutStr 68 | } 69 | 70 | //最终对文件的拼装 71 | var imp = HTNMt.ImpFile() 72 | imp.properties = wgPropertyStr 73 | imp.initContent = wgInitStr 74 | imp.getters = wgGetterStr 75 | imp.layouts = wgLayoutStr 76 | 77 | let nativeMStr = m.impFile(impf: imp) 78 | let nativeHStr = m.interfaceFile(intf: HTNMt.InterfaceFile()) 79 | // print(nativeHStr) 80 | 81 | //生成文件测试 82 | // let hFilePath = "/Users/didi/Documents/Demo/HomePageTest/HomePageTest/\(m.pageId).h" 83 | // let mFilePath = "/Users/didi/Documents/Demo/HomePageTest/HomePageTest/\(m.pageId).m" 84 | // try! nativeHStr.write(toFile: hFilePath, atomically: true, encoding: String.Encoding.utf8) 85 | // try! nativeMStr.write(toFile: mFilePath, atomically: true, encoding: String.Encoding.utf8) 86 | 87 | return (nativeHStr, nativeMStr) 88 | } 89 | 90 | struct WidgetStr { 91 | let propertyStr: String 92 | let initStr: String 93 | let getterStr: String 94 | } 95 | 96 | fileprivate func pageStructConvertToScrollView(page: H5Editor.Data.Page) -> HTNMt.ViewStrStruct { 97 | //h5editor 结构体和 htn 多语言结构体的转换 98 | var vp = HTNMt.ViewPt() 99 | vp.id = m.pageId 100 | vp.viewType = .scrollView 101 | vp.width = page.width ?? 0 102 | vp.height = page.height ?? 0 103 | vp.bgColor = page.bgColor ?? "" 104 | 105 | let pageStruct = m.viewPtToStrStruct(vpt: vp) 106 | return pageStruct 107 | } 108 | 109 | fileprivate func widgetStructConvertToStr(widget:H5Editor.Data.Page.Widget) -> HTNMt.ViewStrStruct { 110 | var uiType = HTNMt.ViewType.label 111 | switch widget.type { 112 | case .some("RichText"),.some("NormalText"): 113 | uiType = .label 114 | case .some("Image"): 115 | uiType = .image 116 | case .some("Button"): 117 | uiType = .button 118 | default: 119 | uiType = .label 120 | } 121 | var layoutType = HTNMt.LayoutType.normal 122 | if widget.layout == "flow" { 123 | layoutType = .flow 124 | } 125 | //h5editor 结构体和 htn 多语言结构体的转换 126 | var vp = HTNMt.ViewPt() 127 | vp.id = m.validIdStr(id: widget.id ?? "") 128 | vp.viewType = uiType 129 | vp.layoutType = layoutType 130 | vp.width = widget.width ?? 0 131 | vp.height = widget.height ?? 0 132 | vp.top = widget.top ?? 0 133 | vp.left = widget.left ?? 0 134 | vp.bgColor = widget.bgColor ?? "" 135 | vp.radius = widget.borderRadius ?? 0 136 | vp.borderColor = widget.borderColor ?? "" 137 | vp.borderWidth = widget.borderWidth ?? 0 138 | vp.hasBorder = widget.hasBorder ?? false 139 | 140 | if uiType == .button { 141 | vp.text = widget.data?.text ?? "" 142 | }else{ 143 | vp.text = widget.data?.content ?? "" 144 | } 145 | vp.fontSize = widget.data?.fontSize ?? 32 146 | vp.textColor = widget.data?.color ?? "" 147 | vp.imageUrl = widget.data?.url ?? "" 148 | vp.isNormal = widget.layout == "normal" 149 | 150 | //padding 的处理 151 | if (widget.padding?.count)! > 0 { 152 | let paddingArr = widget.padding?.split(separator: " ") 153 | if paddingArr?.count == 4 { 154 | vp.padding = HTNMt.Padding(top: Float(paddingArr![0])!, left: Float(paddingArr![1])!, bottom: Float(paddingArr![2])!, right: Float(paddingArr![3])!) 155 | } 156 | } 157 | 158 | //横向和纵向 159 | var vAlign = HTNMt.VerticalAlign.padding 160 | switch widget.data?.verticalAlign { 161 | case "middle"?: 162 | vAlign = .middle 163 | case "top"?: 164 | vAlign = .top 165 | case "bottom"?: 166 | vAlign = .bottom 167 | default: 168 | vAlign = .padding 169 | } 170 | vp.verticalAlign = vAlign 171 | 172 | var hAlign = HTNMt.HorizontalAlign.padding 173 | switch widget.data?.horizontalAlign { 174 | case "center"?: 175 | hAlign = .center 176 | case "left"?: 177 | hAlign = .left 178 | case "right"?: 179 | hAlign = .right 180 | default: 181 | hAlign = .padding 182 | } 183 | vp.horizontalAlign = hAlign 184 | 185 | //处理 trigger 186 | if let triggers = widget.triggers { 187 | if triggers.count > 0 { 188 | for trigger in triggers { 189 | //跳转属性 190 | if trigger.type == "Redirect", let url = trigger.data?.url { 191 | vp.redirectUrl = url 192 | } 193 | } 194 | } 195 | } 196 | 197 | let reStruct = m.viewPtToStrStruct(vpt: vp) 198 | 199 | return reStruct 200 | } 201 | 202 | // fileprivate func validIdStr(w:H5Editor.Data.Page.Widget) -> String { 203 | // return "h\(w.id)" 204 | // } 205 | } 206 | -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ $# -eq 0 ]; then 4 | echo 'Usage: ./package.sh version' 5 | exit 1 6 | fi 7 | 8 | tag_name=$1 9 | 10 | git push --delete origin $tag_name 11 | git tag -d $tag_name 12 | 13 | git tag -a $tag_name -m"-" 14 | git push origin $tag_name 15 | -------------------------------------------------------------------------------- /htnbluemap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ming1016/HTN/40179dc3e576cc5773930c4bc1304420a48d37c0/htnbluemap.png --------------------------------------------------------------------------------