浏览代码

Add return, set statement

xcbosa mbp16 2 年之前
父节点
当前提交
6929c3c46a

+ 92 - 3
TestApp/RootViewController.m

@@ -8,11 +8,16 @@
 #import "RootViewController.h"
 #import <XCTreeLang/XCTreeLang.h>
 
+char *kRootViewControllerKVOKey = "kRootViewControllerKVOKey";
+
 @interface RootViewController () <UITextViewDelegate, XCTLStreamDelegate>
 
 @property (nonatomic, strong) UITextView *sourceTextView;
 @property (nonatomic, strong) UITextView *terminalTextView;
 @property (nonatomic, strong) UIButton *runButton;
+@property (nonatomic, strong) UIView *previewViewControllerContext;
+@property (nonatomic, strong) UIViewController *previewViewController;
+@property (nonatomic, copy) NSArray<NSLayoutConstraint *> *previewConstraints;
 
 @end
 
@@ -22,18 +27,38 @@
     [self.view addSubview:self.sourceTextView];
     [self.view addSubview:self.terminalTextView];
     [self.view addSubview:self.runButton];
+    [self.view addSubview:self.previewViewControllerContext];
+    
+    [self.previewViewControllerContext.rightAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.rightAnchor].active = true;
+    [self.previewViewControllerContext.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor].active = true;
+    [self.previewViewControllerContext.bottomAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.bottomAnchor].active = true;
+    [self.previewViewControllerContext.widthAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.widthAnchor multiplier:0.3].active = true;
+    
     [self.sourceTextView.leftAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.leftAnchor].active = true;
     [self.sourceTextView.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor].active = true;
-    [self.sourceTextView.rightAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.rightAnchor].active = true;
+    [self.sourceTextView.rightAnchor constraintEqualToAnchor:self.previewViewControllerContext.leftAnchor].active = true;
+    [self.sourceTextView.heightAnchor constraintEqualToAnchor:self.terminalTextView.heightAnchor multiplier:2].active = true;
+    
     [self.terminalTextView.topAnchor constraintEqualToAnchor:self.sourceTextView.bottomAnchor].active = true;
     [self.terminalTextView.leftAnchor constraintEqualToAnchor:self.sourceTextView.leftAnchor].active = true;
     [self.terminalTextView.rightAnchor constraintEqualToAnchor:self.sourceTextView.rightAnchor].active = true;
     [self.terminalTextView.bottomAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.bottomAnchor].active = true;
-    [self.sourceTextView.heightAnchor constraintEqualToAnchor:self.terminalTextView.heightAnchor multiplier:2].active = true;
+    
     [self.runButton.rightAnchor constraintEqualToAnchor:self.sourceTextView.rightAnchor constant:-10].active = true;
     [self.runButton.bottomAnchor constraintEqualToAnchor:self.sourceTextView.bottomAnchor constant:-10].active = true;
     [self.runButton.widthAnchor constraintEqualToConstant:108].active = true;
     [self.runButton.heightAnchor constraintEqualToConstant:38].active = true;
+    
+    [self addObserver:self
+           forKeyPath:@"previewViewController"
+              options:NSKeyValueObservingOptionOld
+              context:kRootViewControllerKVOKey];
+    
+    [self addObserver:self
+           forKeyPath:@"previewViewController"
+              options:NSKeyValueObservingOptionNew
+              context:kRootViewControllerKVOKey];
+    
     [super viewDidLoad];
 }
 
@@ -59,6 +84,7 @@
 - (UITextView *)sourceTextView {
     if (!_sourceTextView) {
         _sourceTextView = [self makeTextView];
+        _sourceTextView.backgroundColor = UIColor.systemBackgroundColor;
         _sourceTextView.text = [NSUserDefaults.standardUserDefaults stringForKey:@"code"];
         _sourceTextView.delegate = self;
     }
@@ -68,7 +94,7 @@
 - (UITextView *)terminalTextView {
     if (!_terminalTextView) {
         _terminalTextView = [self makeTextView];
-        _terminalTextView.backgroundColor = UIColor.secondarySystemBackgroundColor;
+        _terminalTextView.backgroundColor = UIColor.tertiarySystemBackgroundColor;
         _terminalTextView.editable = false;
     }
     return _terminalTextView;
@@ -89,7 +115,19 @@
     return _runButton;
 }
 
+- (UIView *)previewViewControllerContext {
+    if (!_previewViewControllerContext) {
+        _previewViewControllerContext = [UIView new];
+        _previewViewControllerContext.translatesAutoresizingMaskIntoConstraints = false;
+        _previewViewControllerContext.backgroundColor = UIColor.secondarySystemBackgroundColor;
+    }
+    return _previewViewControllerContext;
+}
+
 - (void)runButtonDidTouchUp {
+    [self clearPreviewWithViewController:self.previewViewController];
+    _previewViewController = nil;
+    
     self.terminalTextView.text = @"";
     XCTLAST *program = [XCTLEngine.shared compileWithCode:self.sourceTextView.text];
     if (program == NULL) {
@@ -106,6 +144,51 @@
     }
 }
 
+- (void)updatePreview {
+    if (self.previewViewController) {
+        [self addChildViewController:self.previewViewController];
+        [self.previewViewControllerContext addSubview:self.previewViewController.view];
+        self.previewViewController.view.translatesAutoresizingMaskIntoConstraints = false;
+        self.previewConstraints = @[
+            [self.previewViewController.view.leftAnchor constraintEqualToAnchor:self.previewViewControllerContext.leftAnchor],
+            [self.previewViewController.view.topAnchor constraintEqualToAnchor:self.previewViewControllerContext.topAnchor],
+            [self.previewViewController.view.rightAnchor constraintEqualToAnchor:self.previewViewControllerContext.rightAnchor],
+            [self.previewViewController.view.bottomAnchor constraintEqualToAnchor:self.previewViewControllerContext.bottomAnchor]
+        ];
+        for (NSLayoutConstraint *constraint in self.previewConstraints) {
+            constraint.active = true;
+        }
+    }
+}
+
+- (void)clearPreviewWithViewController:(UIViewController *)viewController {
+    if (self.previewConstraints) {
+        [self.previewViewControllerContext removeConstraints:self.previewConstraints];
+        self.previewConstraints = nil;
+    }
+    if (viewController) {
+        [viewController.view removeFromSuperview];
+        [viewController removeFromParentViewController];
+    }
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
+    if (context == kRootViewControllerKVOKey) {
+        if ([@"previewViewController" isEqualToString:keyPath]) {
+            UIViewController *oldValue = change[NSKeyValueChangeOldKey];
+            if (oldValue) {
+                if ([oldValue isKindOfClass:UIViewController.class]) {
+                    [self clearPreviewWithViewController:oldValue];
+                }
+            } else {
+                [self updatePreview];
+            }
+        }
+    } else {
+        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
+    }
+}
+
 - (void)stream:(XCTLStream *)stream appendText:(NSString *)text {
     self.terminalTextView.text = [self.terminalTextView.text stringByAppendingString:text];
 }
@@ -114,4 +197,10 @@
     [NSUserDefaults.standardUserDefaults setValue:textView.text forKey:@"code"];
 }
 
+- (void)dealloc {
+    [self removeObserver:self
+              forKeyPath:@"previewViewController"
+                 context:kRootViewControllerKVOKey];
+}
+
 @end

+ 16 - 0
XCTreeLang.xcodeproj/project.pbxproj

@@ -48,6 +48,8 @@
 		7569167A2A283E78005FF14B /* XCTreeLang.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 7520C3DA2A283AFC0010E7F8 /* XCTreeLang.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
 		756916872A2851DF005FF14B /* XCTLStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 756916862A2851DF005FF14B /* XCTLStream.swift */; };
 		756916892A286A90005FF14B /* XCTLSetStatement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 756916882A286A90005FF14B /* XCTLSetStatement.swift */; };
+		7569168C2A2880CD005FF14B /* Invocation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7569168B2A2880CD005FF14B /* Invocation.swift */; };
+		7569168E2A2896FE005FF14B /* XCTLReturnStatement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7569168D2A2896FE005FF14B /* XCTLReturnStatement.swift */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -120,6 +122,8 @@
 		7520C4572A283CCC0010E7F8 /* TestApp.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = TestApp.entitlements; sourceTree = "<group>"; };
 		756916862A2851DF005FF14B /* XCTLStream.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCTLStream.swift; sourceTree = "<group>"; };
 		756916882A286A90005FF14B /* XCTLSetStatement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCTLSetStatement.swift; sourceTree = "<group>"; };
+		7569168B2A2880CD005FF14B /* Invocation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Invocation.swift; sourceTree = "<group>"; };
+		7569168D2A2896FE005FF14B /* XCTLReturnStatement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCTLReturnStatement.swift; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -163,6 +167,7 @@
 		7520C3DC2A283AFC0010E7F8 /* XCTreeLang */ = {
 			isa = PBXGroup;
 			children = (
+				7569168A2A2880C4005FF14B /* Utils */,
 				7520C3DD2A283AFC0010E7F8 /* XCTreeLang.h */,
 				7520C4172A283B1E0010E7F8 /* InitializerImpl */,
 				7520C3F32A283B1E0010E7F8 /* Lex */,
@@ -208,6 +213,7 @@
 				7520C4072A283B1E0010E7F8 /* XCTLLessthanStatement.swift */,
 				7520C40A2A283B1E0010E7F8 /* XCTLVariableRefStatement.swift */,
 				756916882A286A90005FF14B /* XCTLSetStatement.swift */,
+				7569168D2A2896FE005FF14B /* XCTLReturnStatement.swift */,
 			);
 			path = Statements;
 			sourceTree = "<group>";
@@ -264,6 +270,14 @@
 			name = Frameworks;
 			sourceTree = "<group>";
 		};
+		7569168A2A2880C4005FF14B /* Utils */ = {
+			isa = PBXGroup;
+			children = (
+				7569168B2A2880CD005FF14B /* Invocation.swift */,
+			);
+			path = Utils;
+			sourceTree = "<group>";
+		};
 /* End PBXGroup section */
 
 /* Begin PBXHeadersBuildPhase section */
@@ -378,6 +392,7 @@
 			buildActionMask = 2147483647;
 			files = (
 				7520C42B2A283B1E0010E7F8 /* XCTLElseStatement.swift in Sources */,
+				7569168C2A2880CD005FF14B /* Invocation.swift in Sources */,
 				7520C4212A283B1E0010E7F8 /* XCTLNextthanStatement.swift in Sources */,
 				7520C4342A283B1E0010E7F8 /* XCTLRuntimeAbstractContext.swift in Sources */,
 				7520C4352A283B1E0010E7F8 /* XCTLRuntimeContext.swift in Sources */,
@@ -404,6 +419,7 @@
 				7520C4202A283B1E0010E7F8 /* XCTLRootStatement.swift in Sources */,
 				7520C42A2A283B1E0010E7F8 /* XCTLMorethanStatement.swift in Sources */,
 				7520C4292A283B1E0010E7F8 /* XCTLEqualthanStatement.swift in Sources */,
+				7569168E2A2896FE005FF14B /* XCTLReturnStatement.swift in Sources */,
 				7520C4192A283B1E0010E7F8 /* XCTLCompileTimeError.swift in Sources */,
 				7520C4382A283B1E0010E7F8 /* XCTLEngine.swift in Sources */,
 				7520C4392A283B1E0010E7F8 /* XCTLViewController.swift in Sources */,

+ 1 - 0
XCTreeLang/InitializerImpl/XCTLViewController.swift

@@ -22,6 +22,7 @@ public class XCTLViewController: UIViewController, XCTLGenerateProtocol {
     }
     
     public override func viewDidLoad() {
+        self.view.backgroundColor = UIColor.systemCyan
         super.viewDidLoad()
         for it in self.children {
             self.view.addSubview(it.view)

+ 4 - 0
XCTreeLang/Lex/XCTLLexer.swift

@@ -78,6 +78,8 @@ internal class XCTLLexer {
                 return XCTLToken(type: .typeSet, rawValue: buffer)
             case "else":
                 return XCTLToken(type: .typeElse, rawValue: buffer)
+            case "return":
+                return XCTLToken(type: .typeReturn, rawValue: buffer)
             default:
                 return XCTLToken(type: .typeIdentifier, rawValue: buffer)
             }
@@ -139,6 +141,8 @@ internal class XCTLLexer {
             return XCTLToken(type: .typeXOR, rawValue: "\(char)")
         case "$":
             return XCTLToken(type: .typeValue, rawValue: "\(char)")
+        case ".":
+            return XCTLToken(type: .typePoint, rawValue: "\(char)")
         default:
             throw XCTLCompileTimeError.illegalCharacter(char: char)
         }

+ 6 - 0
XCTreeLang/Lex/XCTLTokenType.swift

@@ -44,4 +44,10 @@ internal enum XCTLTokenType: String {
     
     case typeEOF
     
+    /// .
+    case typePoint
+    
+    /// return
+    case typeReturn
+    
 }

+ 5 - 0
XCTreeLang/Runtime/XCTLRuntimeAbstractContext.swift

@@ -24,4 +24,9 @@ internal protocol XCTLRuntimeAbstractContext: AnyObject {
     
     func makeSubContext() -> XCTLRuntimeAbstractContext
     
+    func findConditionFrame() -> XCTLConditionParentStatementFrame?
+    func findListFrame() -> XCTLListStatementFrame?
+    func recordConditionFrame(_ frame: XCTLConditionParentStatementFrame?)
+    func recordListFrame(_ frame: XCTLListStatementFrame?)
+    
 }

+ 67 - 0
XCTreeLang/Runtime/XCTLRuntimeContext.swift

@@ -54,16 +54,60 @@ internal class XCTLRuntimeContext: XCTLRuntimeAbstractContext {
         self.setValue(XCTLRuntimeVariable(funcImpl: {
             for it in $0 {
                 self.stdout.append(text: it.toString())
+                self.stdout.append(text: " ")
             }
             return .void
         }), forName: "log")
         self.setValue(XCTLRuntimeVariable(funcImpl: {
             for it in $0 {
                 self.stdout.append(text: it.toString())
+                self.stdout.append(text: " ")
             }
             self.stdout.append(text: "\n")
             return .void
         }), forName: "logn")
+        self.setValue(XCTLRuntimeVariable(funcImpl: {
+            var dest: Double = 0
+            for it in $0 {
+                if it.type == .typeNumber {
+                    dest += it.doubleValue
+                }
+            }
+            return XCTLRuntimeVariable(type: .typeNumber, rawValue: dest.description)
+        }), forName: "add")
+        self.setValue(XCTLRuntimeVariable(funcImpl: {
+            var list = $0
+            list = list.filter({ $0.type == .typeNumber })
+            if list.isEmpty { return XCTLRuntimeVariable(type: .typeNumber, rawValue: "0") }
+            var dest: Double = list.removeFirst().doubleValue
+            for it in list {
+                if it.type == .typeNumber {
+                    dest -= it.doubleValue
+                }
+            }
+            return XCTLRuntimeVariable(type: .typeNumber, rawValue: dest.description)
+        }), forName: "minus")
+        self.setValue(XCTLRuntimeVariable(funcImpl: {
+            var dest: Double = 0
+            for it in $0 {
+                if it.type == .typeNumber {
+                    dest *= it.doubleValue
+                }
+            }
+            return XCTLRuntimeVariable(type: .typeNumber, rawValue: dest.description)
+        }), forName: "mult")
+        self.setValue(XCTLRuntimeVariable(funcImpl: {
+            var list = $0
+            list = list.filter({ $0.type == .typeNumber })
+            if list.isEmpty { return XCTLRuntimeVariable(type: .typeNumber, rawValue: "0") }
+            var dest: Double = list.removeFirst().doubleValue
+            for it in list {
+                if it.type == .typeNumber {
+                    dest /= it.doubleValue
+                }
+            }
+            return XCTLRuntimeVariable(type: .typeNumber, rawValue: dest.description)
+        }), forName: "div")
         self.setValue(XCTLRuntimeVariable(type: .typeNumber, rawValue: "\(Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "-1")"), forName: "appBundleVersion")
         for it in paragraphMembers {
             self.setValue(XCTLRuntimeVariable(funcImplStmt: it.value), forName: it.key)
@@ -96,6 +140,10 @@ internal class XCTLRuntimeContext: XCTLRuntimeAbstractContext {
             self.values[name] = object
             return object
         }
+        if let klass: AnyObject = NSClassFromString(name),
+           let klass = klass as? NSObject {
+            return XCTLRuntimeVariable(rawObject: klass)
+        }
         return .void
     }
     
@@ -136,4 +184,23 @@ internal class XCTLRuntimeContext: XCTLRuntimeAbstractContext {
         return XCTLRuntimeSubContext(parent: self)
     }
     
+    private var conditionFrame: XCTLConditionParentStatementFrame?
+    private var listFrame: XCTLListStatementFrame?
+    
+    func findConditionFrame() -> XCTLConditionParentStatementFrame? {
+        return self.conditionFrame
+    }
+    
+    func findListFrame() -> XCTLListStatementFrame? {
+        return self.listFrame
+    }
+    
+    func recordListFrame(_ frame: XCTLListStatementFrame?) {
+        self.listFrame = frame
+    }
+    
+    func recordConditionFrame(_ frame: XCTLConditionParentStatementFrame?) {
+        self.conditionFrame = frame
+    }
+    
 }

+ 25 - 0
XCTreeLang/Runtime/XCTLRuntimeSubContext.swift

@@ -88,4 +88,29 @@ internal class XCTLRuntimeSubContext: XCTLRuntimeAbstractContext {
         return XCTLRuntimeSubContext(parent: self)
     }
     
+    private var conditionFrame: XCTLConditionParentStatementFrame?
+    private var listFrame: XCTLListStatementFrame?
+    
+    func findConditionFrame() -> XCTLConditionParentStatementFrame? {
+        if let conditionFrame = self.conditionFrame {
+            return self.conditionFrame
+        }
+        return self.parent.findConditionFrame()
+    }
+    
+    func findListFrame() -> XCTLListStatementFrame? {
+        if let listFrame = self.listFrame {
+            return self.listFrame
+        }
+        return self.parent.findListFrame()
+    }
+    
+    func recordListFrame(_ frame: XCTLListStatementFrame?) {
+        self.listFrame = frame
+    }
+    
+    func recordConditionFrame(_ frame: XCTLConditionParentStatementFrame?) {
+        self.conditionFrame = frame
+    }
+    
 }

+ 3 - 0
XCTreeLang/Runtime/XCTLRuntimeVariable.swift

@@ -14,6 +14,9 @@ public class XCTLRuntimeVariable {
     public var rawObject: NSObject?
     public var rawFunction: (([XCTLRuntimeVariable]) -> XCTLRuntimeVariable)?
     
+//    public var leftValue: XCTLRuntimeVariable?
+//    public var leftValueMemberName: String?
+    
     internal weak var rawFunctionStatement: XCTLStatement?
     
     public func toString() -> String {

+ 1 - 0
XCTreeLang/Statements/VirtualStatement/XCTLListStatement.swift

@@ -53,6 +53,7 @@ internal class XCTLListStatement: XCTLStatement, XCTLListStatementProtocol {
     }
     
     func evaluate(inContext context: XCTLRuntimeAbstractContext) throws -> XCTLRuntimeVariable {
+        let context = context.makeSubContext()
         var lastValue = XCTLRuntimeVariable.void
         for it in statements {
             lastValue = try it.evaluate(inContext: context)

+ 13 - 18
XCTreeLang/Statements/XCTLLazyEqualStatement.swift

@@ -13,15 +13,13 @@ internal class XCTLLazyEqualStatement: XCTLStatement {
     
     var holdingObject: XCTLRuntimeVariable { .void }
     
-    var thisMemberName: String = ""
+    var leftStatement = XCTLVariableRefStatement()
     var equalToStatement: XCTLStatement = XCTLImmediateStatement()
     
     weak var parent: XCTLStatement?
     
     func matchSelfStatement(lex: XCTLLexer) throws -> XCTLStatement? {
-        if try lex.next().type == .typeXOR,
-            try lex.next().type == .typeIdentifier,
-            try lex.next().type == .typeEqual {
+        if try lex.next().type == .typePoint {
             return XCTLLazyEqualStatement()
         }
         return nil
@@ -29,12 +27,17 @@ internal class XCTLLazyEqualStatement: XCTLStatement {
     
     func parseStatement(fromLexerToSelf lex: XCTLLexer, fromParent: XCTLStatement?) throws {
         self.parent = fromParent
-        try lex.next()
-        let leftToken = try lex.next()
-        if leftToken.type != .typeIdentifier {
-            throw XCTLCompileTimeError.unexpectTokenInStatement(expect: XCTLTokenType.typeIdentifier.rawValue, butGot: leftToken.type.rawValue)
+        guard let parent = self.parent as? XCTLInitStatement else {
+            throw XCTLCompileTimeError.unexpectParentStatementType(expect: "value", butGot: "got lazy nothing")
         }
-        self.thisMemberName = leftToken.rawValue
+        try lex.next()
+        
+        let leftStmtWithOutParent = XCTLVariableRefStatement()
+        try leftStmtWithOutParent.parseStatement(fromLexerToSelf: lex, fromParent: fromParent)
+        self.leftStatement.parent = self
+        self.leftStatement.nextVariableRefStmt = leftStmtWithOutParent
+        self.leftStatement.variableName = parent.defineName
+        
         let equalToken = try lex.next()
         if equalToken.type != .typeEqual {
             throw XCTLCompileTimeError.unexpectTokenInStatement(expect: XCTLTokenType.typeEqual.rawValue, butGot: equalToken.type.rawValue)
@@ -43,16 +46,8 @@ internal class XCTLLazyEqualStatement: XCTLStatement {
     }
     
     func evaluate(inContext context: XCTLRuntimeAbstractContext) throws -> XCTLRuntimeVariable {
-        guard let parent = self.parent as? XCTLInitStatement,
-              let operationObject = parent.generatedObject else {
-            throw XCTLCompileTimeError.unexpectParentStatementType(expect: "value", butGot: "got lazy nothing")
-        }
         let rightValue = try equalToStatement.evaluate(inContext: context)
-        if operationObject.type != .typeObject {
-            throw XCTLRuntimeError.unexpectedVariableType(expect: XCTLRuntimeVariableType.typeObject.rawValue, butGot: operationObject.type.rawValue)
-        }
-        operationObject.objectValue.setValue(rightValue.nativeValue, forKey: self.thisMemberName)
-        return .void
+        return try self.leftStatement.evaluateBack(rightValue, inContext: context)
     }
     
 }

+ 1 - 0
XCTreeLang/Statements/XCTLParagraphStatement.swift

@@ -45,6 +45,7 @@ internal class XCTLParagraphStatement: XCTLStatement, XCTLLateExecuteStatement {
             }
             try lex.next()
         }
+        self.runStatements.parent = self
         try self.runStatements.parseStatement(fromLexerToSelf: lex, fromParent: self)
         if lex.paragraphTable[self.paragraphName] != nil {
             throw XCTLCompileTimeError.tooMuchParagraphDefinitionForName(name: self.paragraphName)

+ 56 - 0
XCTreeLang/Statements/XCTLReturnStatement.swift

@@ -0,0 +1,56 @@
+//
+//  XCTLReturnStatement.swift
+//  XCTreeLang
+//
+//  Created by 邢铖 on 2023/6/1.
+//
+
+import Foundation
+
+internal class XCTLReturnStatement: XCTLStatement {
+    
+    var type: XCTLStatementType { .typeReturn }
+    
+    var holdingObject: XCTLRuntimeVariable { .void }
+    
+    var returnValueStatement: XCTLStatement!
+    
+    weak var parent: XCTLStatement?
+    
+    weak var parentParagraph: XCTLParagraphStatement!
+    
+    func matchSelfStatement(lex: XCTLLexer) throws -> XCTLStatement? {
+        return try lex.peek().type == .typeReturn ? XCTLReturnStatement() : nil
+    }
+    
+    func parseStatement(fromLexerToSelf lex: XCTLLexer, fromParent: XCTLStatement?) throws {
+        self.parent = fromParent
+        
+        var parentStmt: XCTLStatement? = self.parent
+        while let stmt = parentStmt {
+            if let listStmt = stmt as? XCTLParagraphStatement {
+                self.parentParagraph = listStmt
+                break
+            }
+            parentStmt = stmt.parent
+        }
+        
+        if parentParagraph == nil {
+            throw XCTLCompileTimeError.unexpectParentStatementType(expect: XCTLStatementType.typeParagraph.rawValue, butGot: self.parent?.type.rawValue ?? "none")
+        }
+        
+        try lex.next()
+        
+        self.returnValueStatement = try self.parseNextStatement(forLexer: lex, prototypes: XCTLExpressionPrototypes)
+    }
+    
+    func evaluate(inContext context: XCTLRuntimeAbstractContext) throws -> XCTLRuntimeVariable {
+        let value = try self.returnValueStatement.evaluate(inContext: context)
+        self.parentParagraph.runStatements.breakListEvaluate = true
+        self.parentParagraph.runStatements.listResultValue = value
+        self.parentParagraph.runStatements.conditionParent?.doElse = false
+        return value
+    }
+    
+}
+

+ 3 - 8
XCTreeLang/Statements/XCTLSetStatement.swift

@@ -13,7 +13,7 @@ internal class XCTLSetStatement: XCTLStatement {
     
     var holdingObject: XCTLRuntimeVariable { .void }
     
-    var variableName: String = ""
+    var leftStatement: XCTLBackableStatement & XCTLStatement = XCTLVariableRefStatement()
     
     var setToStatement: XCTLStatement!
     
@@ -27,11 +27,7 @@ internal class XCTLSetStatement: XCTLStatement {
         self.parent = fromParent
         try lex.next()
         
-        let variableNameToken = try lex.next()
-        if variableNameToken.type != .typeIdentifier {
-            throw XCTLCompileTimeError.unexpectTokenInStatement(expect: XCTLTokenType.typeIdentifier.rawValue, butGot: variableNameToken.type.rawValue)
-        }
-        self.variableName = variableNameToken.rawValue
+        try self.leftStatement.parseStatement(fromLexerToSelf: lex, fromParent: self)
         
         let equalToken = try lex.next()
         if equalToken.type != .typeEqual {
@@ -43,8 +39,7 @@ internal class XCTLSetStatement: XCTLStatement {
     
     func evaluate(inContext context: XCTLRuntimeAbstractContext) throws -> XCTLRuntimeVariable {
         let value = try self.setToStatement.evaluate(inContext: context)
-        context.setValue(value, forName: self.variableName)
-        return value
+        return try self.leftStatement.evaluateBack(value, inContext: context)
     }
     
 }

+ 40 - 2
XCTreeLang/Statements/XCTLStatement.swift

@@ -52,6 +52,42 @@ internal protocol XCTLLateExecuteStatement: AnyObject {
     
 }
 
+internal protocol XCTLBackableStatement: AnyObject {
+    
+    func evaluateBack(_ valueToBack: XCTLRuntimeVariable, inContext context: XCTLRuntimeAbstractContext) throws -> XCTLRuntimeVariable
+    
+}
+
+internal class XCTLConditionParentStatementFrame {
+    var doElse: Bool
+    var doNext: Bool
+    
+    internal init() {
+        self.doElse = true
+        self.doNext = false
+    }
+    
+    internal init(doElse: Bool, doNext: Bool) {
+        self.doElse = doElse
+        self.doNext = doNext
+    }
+}
+
+internal class XCTLListStatementFrame {
+    var breakListEvaluate: Bool
+    var listResultValue: XCTLRuntimeVariable
+    
+    internal init() {
+        self.breakListEvaluate = false
+        self.listResultValue = .void
+    }
+    
+    internal init(breakListEvaluate: Bool, listResultValue: XCTLRuntimeVariable) {
+        self.breakListEvaluate = breakListEvaluate
+        self.listResultValue = listResultValue
+    }
+}
+
 internal var XCTLStatementPrototypes: [XCTLStatement] = [
     XCTLImportStatement(),
     XCTLExportStatement(),
@@ -67,14 +103,16 @@ internal var XCTLStatementPrototypes: [XCTLStatement] = [
     XCTLNextthanStatement(),
     XCTLElseStatement(),
     XCTLParagraphStatement(),
-    XCTLSetStatement()
+    XCTLSetStatement(),
+    XCTLReturnStatement()
 ]
 
 internal var XCTLExpressionPrototypes: [XCTLStatement] = [
     XCTLFunctionCallStatement(),
     XCTLImmediateStatement(),
     XCTLVariableRefStatement(),
-    XCTLSetStatement()
+    XCTLSetStatement(),
+    XCTLReturnStatement()
 ]
 
 internal func XCTLStatementParseNextStatement(forLexer lex: XCTLLexer,

+ 1 - 0
XCTreeLang/Statements/XCTLStatementType.swift

@@ -39,5 +39,6 @@ internal enum XCTLStatementType: String {
     case typeParagraph
     
     case typeSet
+    case typeReturn
     
 }

+ 72 - 4
XCTreeLang/Statements/XCTLVariableRefStatement.swift

@@ -7,7 +7,7 @@
 
 import Foundation
 
-internal class XCTLVariableRefStatement: XCTLStatement {
+internal class XCTLVariableRefStatement: XCTLStatement, XCTLBackableStatement {
     
     var type: XCTLStatementType { .typeVariableRef }
     
@@ -15,6 +15,8 @@ internal class XCTLVariableRefStatement: XCTLStatement {
     
     var variableName: String = ""
     
+    var nextVariableRefStmt: XCTLVariableRefStatement?
+    
     weak var parent: XCTLStatement?
     
     func matchSelfStatement(lex: XCTLLexer) throws -> XCTLStatement? {
@@ -29,13 +31,79 @@ internal class XCTLVariableRefStatement: XCTLStatement {
 //        try lex.next()
         let value = try lex.next()
         self.variableName = value.rawValue
+        
+        let point = try lex.peek()
+        if point.type == .typePoint {
+            try lex.next()
+            self.nextVariableRefStmt = XCTLVariableRefStatement()
+            try self.nextVariableRefStmt?.parseStatement(fromLexerToSelf: lex, fromParent: fromParent)
+        }
     }
     
     func evaluate(inContext context: XCTLRuntimeAbstractContext) throws -> XCTLRuntimeVariable {
-        if let value = context.value(forName: variableName) {
-            return value
+        guard let value = context.value(forName: variableName) else {
+            throw XCTLRuntimeError.undefinedVariable(variableName: variableName)
+        }
+        var nextMemberStmt = self.nextVariableRefStmt
+        var currentValue = value
+        var refName = self.variableName
+        while let nextStmt = nextMemberStmt {
+            let memberName = nextStmt.variableName
+            refName.append(".")
+            refName.append(memberName)
+            if currentValue.type != .typeObject {
+                throw XCTLRuntimeError.unknownMemberForVariable(memberName: memberName, variableName: refName)
+            }
+            let rawObject = currentValue.objectValue
+            guard let obj = rawObject.value(forKey: memberName),
+                  let obj = obj as? NSObject else {
+                throw XCTLRuntimeError.unknownMemberForVariable(memberName: memberName, variableName: refName)
+            }
+            let newValue = XCTLRuntimeVariable(rawObject: obj)
+//            newValue.leftValue = currentValue
+//            newValue.leftValueMemberName = memberName
+            currentValue = newValue
+            nextMemberStmt = nextStmt.nextVariableRefStmt
+        }
+        return currentValue
+    }
+    
+    func evaluateBack(_ valueToBack: XCTLRuntimeVariable, inContext context: XCTLRuntimeAbstractContext) throws -> XCTLRuntimeVariable {
+        guard let value = context.value(forName: variableName) else {
+            throw XCTLRuntimeError.undefinedVariable(variableName: variableName)
+        }
+        var nextMemberStmt = self.nextVariableRefStmt
+        var currentValue = value
+        var refName = self.variableName
+        
+        if nextMemberStmt == nil {
+            context.setValue(valueToBack, forName: self.variableName)
+        }
+        while let nextStmt = nextMemberStmt {
+            let memberName = nextStmt.variableName
+            refName.append(".")
+            refName.append(memberName)
+            if currentValue.type != .typeObject {
+                throw XCTLRuntimeError.unknownMemberForVariable(memberName: memberName, variableName: refName)
+            }
+            let rawObject = currentValue.objectValue
+            
+            if nextStmt.nextVariableRefStmt == nil {
+                rawObject.setValue(valueToBack.nativeValue, forKey: memberName)
+                break
+            }
+            
+            guard let obj = rawObject.value(forKey: memberName),
+                  let obj = obj as? NSObject else {
+                throw XCTLRuntimeError.unknownMemberForVariable(memberName: memberName, variableName: refName)
+            }
+            let newValue = XCTLRuntimeVariable(rawObject: obj)
+//            newValue.leftValue = currentValue
+//            newValue.leftValueMemberName = memberName
+            currentValue = newValue
+            nextMemberStmt = nextStmt.nextVariableRefStmt
         }
-        throw XCTLRuntimeError.undefinedVariable(variableName: variableName)
+        return valueToBack
     }
     
 }

+ 214 - 0
XCTreeLang/Utils/Invocation.swift

@@ -0,0 +1,214 @@
+//
+//  Dynamic
+//  Created by Mhd Hejazi on 4/15/20.
+//  Copyright © 2020 Samabox. All rights reserved.
+//
+
+import Foundation
+
+class Invocation: NSObject {
+
+    private let target: NSObject
+    private let selector: Selector
+
+    var invocation: NSObject?
+
+    var numberOfArguments: Int = 0
+    var returnLength: Int = 0
+    var returnType: UnsafePointer<CChar>?
+    var returnTypeString: String? {
+        guard let returnType = returnType else { return nil }
+        return String(cString: returnType)
+    }
+    var returnsObject: Bool {
+        /// `@` is the type encoding for an object
+        returnTypeString == "@"
+    }
+    var returnsAny: Bool {
+        /// `v` is the type encoding for Void
+        returnTypeString != "v"
+    }
+    lazy var returnedObject: AnyObject? = {
+        returnedObjectValue()
+    }()
+    private(set) var isInvoked: Bool = false
+
+    init(target: NSObject, selector: Selector) throws {
+        self.target = target
+        self.selector = selector
+        super.init()
+
+        try initialize()
+    }
+
+    private func initialize() throws {
+        /// `NSMethodSignature *methodSignature = [target methodSignatureForSelector: selector]`
+        let methodSignature: NSObject
+        do {
+            let selector = NSSelectorFromString("methodSignatureForSelector:")
+            let signature = (@convention(c)(NSObject, Selector, Selector) -> Any).self
+            let method = unsafeBitCast(target.method(for: selector), to: signature)
+            guard let result = method(target, selector, self.selector) as? NSObject else {
+                let error = InvocationError.unrecognizedSelector(type(of: target), self.selector)
+                throw error
+            }
+            methodSignature = result
+        }
+
+        /// `numberOfArguments = methodSignature.numberOfArguments`
+        self.numberOfArguments = methodSignature.value(forKeyPath: "numberOfArguments") as? Int ?? 0
+
+        /// `methodReturnLength = methodSignature.methodReturnLength`
+        self.returnLength = methodSignature.value(forKeyPath: "methodReturnLength") as? Int ?? 0
+
+        /// `methodReturnType = methodSignature.methodReturnType`
+        let methodReturnType: UnsafePointer<CChar>
+        do {
+            let selector = NSSelectorFromString("methodReturnType")
+            let signature = (@convention(c)(NSObject, Selector) -> UnsafePointer<CChar>).self
+            let method = unsafeBitCast(methodSignature.method(for: selector), to: signature)
+            methodReturnType = method(methodSignature, selector)
+        }
+        self.returnType = methodReturnType
+
+        /// `NSInvocation *invocation = [NSInvocation invocationWithMethodSignature: methodSignature]`
+        let invocation: NSObject
+        do {
+            let NSInvocation = NSClassFromString("NSInvocation") as AnyObject
+            let selector = NSSelectorFromString("invocationWithMethodSignature:")
+            let signature = (@convention(c)(AnyObject, Selector, AnyObject) -> AnyObject).self
+            let method = unsafeBitCast(NSInvocation.method(for: selector), to: signature)
+            guard let result = method(NSInvocation, selector, methodSignature) as? NSObject else {
+                let error = InvocationError.unrecognizedSelector(type(of: target), self.selector)
+                throw error
+            }
+            invocation = result
+        }
+        self.invocation = invocation
+
+        /// `invocation.selector = selector`
+        do {
+            let selector = NSSelectorFromString("setSelector:")
+            let signature = (@convention(c)(NSObject, Selector, Selector) -> Void).self
+            let method = unsafeBitCast(invocation.method(for: selector), to: signature)
+            method(invocation, selector, self.selector)
+        }
+
+        /// `[invocation retainArguments]`
+        do {
+            let selector = NSSelectorFromString("retainArguments")
+            let signature = (@convention(c)(NSObject, Selector) -> Void).self
+            let method = unsafeBitCast(invocation.method(for: selector), to: signature)
+            method(invocation, selector)
+        }
+    }
+
+    func setArgument(_ argument: Any?, at index: NSInteger) {
+        guard let invocation = invocation else { return }
+
+        /// `[invocation setArgument:&argument atIndex:i + 2]`
+        let selector = NSSelectorFromString("setArgument:atIndex:")
+        let signature = (@convention(c)(NSObject, Selector, UnsafeRawPointer, Int) -> Void).self
+        let method = unsafeBitCast(invocation.method(for: selector), to: signature)
+
+        if let valueArgument = argument as? NSValue {
+            /// Get the type byte size
+            let typeSize = UnsafeMutablePointer<Int>.allocate(capacity: 1)
+            defer { typeSize.deallocate() }
+            NSGetSizeAndAlignment(valueArgument.objCType, typeSize, nil)
+
+            /// Get the actual value
+            let buffer = UnsafeMutablePointer<Int8>.allocate(capacity: typeSize.pointee)
+            defer { buffer.deallocate() }
+            valueArgument.getValue(buffer)
+
+            method(invocation, selector, buffer, index)
+        } else {
+            withUnsafePointer(to: argument) { pointer in
+                method(invocation, selector, pointer, index)
+            }
+        }
+    }
+
+    func invoke() {
+        guard let invocation = invocation, !isInvoked else { return }
+
+        isInvoked = true
+
+        /// `[invocation invokeWithTarget: target]`
+        do {
+            let selector = NSSelectorFromString("invokeWithTarget:")
+            let signature = (@convention(c)(NSObject, Selector, AnyObject) -> Void).self
+            let method = unsafeBitCast(invocation.method(for: selector), to: signature)
+            method(invocation, selector, target)
+        }
+    }
+
+    func getReturnValue<T>(result: inout T) {
+        guard let invocation = invocation else { return }
+
+        /// `[invocation getReturnValue: returnValue]`
+        do {
+            let selector = NSSelectorFromString("getReturnValue:")
+            let signature = (@convention(c)(NSObject, Selector, UnsafeMutableRawPointer) -> Void).self
+            let method = unsafeBitCast(invocation.method(for: selector), to: signature)
+            withUnsafeMutablePointer(to: &result) { pointer in
+                method(invocation, selector, pointer)
+            }
+        }
+    }
+
+    private func returnedObjectValue() -> AnyObject? {
+        guard returnsObject, returnLength > 0 else {
+            return nil
+        }
+
+        var result: AnyObject?
+
+        getReturnValue(result: &result)
+
+        guard let object = result else {
+            return nil
+        }
+
+        /// Take the ownership of the initialized objects to ensure they're deallocated properly.
+        if isRetainingMethod() {
+            return Unmanaged.passRetained(object).takeRetainedValue()
+        }
+
+        /// `NSInvocation.getReturnValue()` doesn't give us the ownership of the returned object, but the compiler
+        /// tries to release this object anyway. So, we are retaining it to balance with the compiler's release.
+        return Unmanaged.passRetained(object).takeUnretainedValue()
+    }
+
+    private func isRetainingMethod() -> Bool {
+        /// Refer to: https://bit.ly/308okXm
+        let selector = NSStringFromSelector(self.selector)
+        return selector == "alloc" ||
+            selector.hasPrefix("new") ||
+            selector.hasPrefix("copy") ||
+            selector.hasPrefix("mutableCopy")
+    }
+}
+
+public enum InvocationError: CustomNSError {
+    case unrecognizedSelector(_ classType: AnyClass, _ selector: Selector)
+
+    public static var errorDomain: String { String(describing: Invocation.self) }
+
+    public var errorCode: Int {
+        switch self {
+        case .unrecognizedSelector:
+            return 404
+        }
+    }
+
+    public var errorUserInfo: [String: Any] {
+        var message: String
+        switch self {
+        case .unrecognizedSelector(let classType, let selector):
+            message = "'\(String(describing: classType))' doesn't recognize selector '\(selector)'"
+        }
+        return [NSLocalizedDescriptionKey: message]
+    }
+}