// ---------------------- // JSON ENCODING SCHEME // ---------------------- // // Glue: "<>", "G<", "G>" // // ControlCommand: "ev", "out", "/ev", "du" "pop", "->->", "~ret", "str", "/str", "nop", // "choiceCnt", "turns", "visit", "seq", "thread", "done", "end" // // NativeFunction: "+", "-", "/", "*", "%" "~", "==", ">", "<", ">=", "<=", "!=", "!"... etc // // Void: "void" // // Value: "^string value", "^^string value beginning with ^" // 5, 5.2 // {"^->": "path.target"} // {"^var": "varname", "ci": 0} // // Container: [...] // [..., // { // "subContainerName": ..., // "#f": 5, // flags // "#n": "containerOwnName" // only if not redundant // } // ] // // Divert: {"->": "path.target", "c": true } // {"->": "path.target", "var": true} // {"f()": "path.func"} // {"->t->": "path.tunnel"} // {"x()": "externalFuncName", "exArgs": 5} // // Var Assign: {"VAR=": "varName", "re": true} // reassignment // {"temp=": "varName"} // // Var ref: {"VAR?": "varName"} // {"CNT?": "stitch name"} // // ChoicePoint: {"*": pathString, // "flg": 18 } // // Choice: Nothing too clever, it's only used in the save state, // there's not likely to be many of them. // // Tag: {"#": "the tag text"} public static Runtime.Object JTokenToRuntimeObject(object token) { if (token is int || token is float) { return(Value.Create(token)); } if (token is string) { string str = (string)token; // String value char firstChar = str[0]; if (firstChar == '^') { return(new StringValue(str.Substring(1))); } else if (firstChar == '\n' && str.Length == 1) { return(new StringValue("\n")); } // Glue if (str == "<>") { return(new Runtime.Glue()); } // Control commands (would looking up in a hash set be faster?) for (int i = 0; i < _controlCommandNames.Length; ++i) { string cmdName = _controlCommandNames [i]; if (str == cmdName) { return(new Runtime.ControlCommand((ControlCommand.CommandType)i)); } } // Native functions // "^" conflicts with the way to identify strings, so now // we know it's not a string, we can convert back to the proper // symbol for the operator. if (str == "L^") { str = "^"; } if (NativeFunctionCall.CallExistsWithName(str)) { return(NativeFunctionCall.CallWithName(str)); } // Pop if (str == "->->") { return(Runtime.ControlCommand.PopTunnel()); } else if (str == "~ret") { return(Runtime.ControlCommand.PopFunction()); } // Void if (str == "void") { return(new Runtime.Void()); } } if (token is Dictionary <string, object> ) { var obj = (Dictionary <string, object>)token; object propValue; // Divert target value to path if (obj.TryGetValue("^->", out propValue)) { return(new DivertTargetValue(new Path((string)propValue))); } // VariablePointerValue if (obj.TryGetValue("^var", out propValue)) { var varPtr = new VariablePointerValue((string)propValue); if (obj.TryGetValue("ci", out propValue)) { varPtr.contextIndex = (int)propValue; } return(varPtr); } // Divert bool isDivert = false; bool pushesToStack = false; PushPopType divPushType = PushPopType.Function; bool external = false; if (obj.TryGetValue("->", out propValue)) { isDivert = true; } else if (obj.TryGetValue("f()", out propValue)) { isDivert = true; pushesToStack = true; divPushType = PushPopType.Function; } else if (obj.TryGetValue("->t->", out propValue)) { isDivert = true; pushesToStack = true; divPushType = PushPopType.Tunnel; } else if (obj.TryGetValue("x()", out propValue)) { isDivert = true; external = true; pushesToStack = false; divPushType = PushPopType.Function; } if (isDivert) { var divert = new Divert(); divert.pushesToStack = pushesToStack; divert.stackPushType = divPushType; divert.isExternal = external; string target = propValue.ToString(); if (obj.TryGetValue("var", out propValue)) { divert.variableDivertName = target; } else { divert.targetPathString = target; } divert.isConditional = obj.TryGetValue("c", out propValue); if (external) { if (obj.TryGetValue("exArgs", out propValue)) { divert.externalArgs = (int)propValue; } } return(divert); } // Choice if (obj.TryGetValue("*", out propValue)) { var choice = new ChoicePoint(); choice.pathStringOnChoice = propValue.ToString(); if (obj.TryGetValue("flg", out propValue)) { choice.flags = (int)propValue; } return(choice); } // Variable reference if (obj.TryGetValue("VAR?", out propValue)) { return(new VariableReference(propValue.ToString())); } else if (obj.TryGetValue("CNT?", out propValue)) { var readCountVarRef = new VariableReference(); readCountVarRef.pathStringForCount = propValue.ToString(); return(readCountVarRef); } // Variable assignment bool isVarAss = false; bool isGlobalVar = false; if (obj.TryGetValue("VAR=", out propValue)) { isVarAss = true; isGlobalVar = true; } else if (obj.TryGetValue("temp=", out propValue)) { isVarAss = true; isGlobalVar = false; } if (isVarAss) { var varName = propValue.ToString(); var isNewDecl = !obj.TryGetValue("re", out propValue); var varAss = new VariableAssignment(varName, isNewDecl); varAss.isGlobal = isGlobalVar; return(varAss); } // Tag if (obj.TryGetValue("#", out propValue)) { return(new Runtime.Tag((string)propValue)); } // List value if (obj.TryGetValue("list", out propValue)) { var listContent = (Dictionary <string, object>)propValue; var rawList = new InkList(); if (obj.TryGetValue("origins", out propValue)) { var namesAsObjs = (List <object>)propValue; rawList.SetInitialOriginNames(namesAsObjs.Cast <string>().ToList()); } foreach (var nameToVal in listContent) { var item = new InkListItem(nameToVal.Key); var val = (int)nameToVal.Value; rawList.Add(item, val); } return(new ListValue(rawList)); } // Used when serialising save state only if (obj ["originalChoicePath"] != null) { return(JObjectToChoice(obj)); } } // Array is always a Runtime.Container if (token is List <object> ) { return(JArrayToContainer((List <object>)token)); } if (token == null) { return(null); } throw new System.Exception("Failed to convert token to runtime object: " + token); }
// ---------------------- // JSON ENCODING SCHEME // ---------------------- // // Glue: "<>", "G<", "G>" // // ControlCommand: "ev", "out", "/ev", "du" "pop", "->->", "~ret", "str", "/str", "nop", // "choiceCnt", "turns", "visit", "seq", "thread", "done", "end" // // NativeFunction: "+", "-", "/", "*", "%" "~", "==", ">", "<", ">=", "<=", "!=", "!"... etc // // Void: "void" // // Value: "^string value", "^^string value beginning with ^" // 5, 5.2 // {"^->": "path.target"} // {"^var": "varname", "ci": 0} // // Container: [...] // [..., // { // "subContainerName": ..., // "#f": 5, // flags // "#n": "containerOwnName" // only if not redundant // } // ] // // Divert: {"->": "path.target", "c": true } // {"->": "path.target", "var": true} // {"f()": "path.func"} // {"->t->": "path.tunnel"} // {"x()": "externalFuncName", "exArgs": 5} // // Var Assign: {"VAR=": "varName", "re": true} // reassignment // {"temp=": "varName"} // // Var ref: {"VAR?": "varName"} // {"CNT?": "stitch name"} // // ChoicePoint: {"*": pathString, // "flg": 18 } // // Choice: Nothing too clever, it's only used in the save state, // there's not likely to be many of them. public static Runtime.Object JTokenToRuntimeObject(object token) { if (token is int || token is float) { return(Value.Create(token)); } if (token is string) { string str = (string)token; // String value char firstChar = str[0]; if (firstChar == '^') { return(new StringValue(str.Substring(1))); } else if (firstChar == '\n' && str.Length == 1) { return(new StringValue("\n")); } // Glue if (str == "<>") { return(new Runtime.Glue(GlueType.Bidirectional)); } else if (str == "G<") { return(new Runtime.Glue(GlueType.Left)); } else if (str == "G>") { return(new Runtime.Glue(GlueType.Right)); } // Control commands (would looking up in a hash set be faster?) for (int i = 0; i < _controlCommandNames.Length; ++i) { string cmdName = _controlCommandNames [i]; if (str == cmdName) { return(new Runtime.ControlCommand((ControlCommand.CommandType)i)); } } // Native functions if (NativeFunctionCall.CallExistsWithName(str)) { return(NativeFunctionCall.CallWithName(str)); } // Pop if (str == "->->") { return(Runtime.ControlCommand.PopTunnel()); } else if (str == "~ret") { return(Runtime.ControlCommand.PopFunction()); } // Void if (str == "void") { return(new Runtime.Void()); } } if (token is Dictionary <string, object> ) { var obj = (Dictionary <string, object>)token; object propValue; // Divert target value to path if (obj.TryGetValue("^->", out propValue)) { return(new DivertTargetValue(new Path((string)propValue))); } // VariablePointerValue if (obj.TryGetValue("^var", out propValue)) { var varPtr = new VariablePointerValue((string)propValue); if (obj.TryGetValue("ci", out propValue)) { varPtr.contextIndex = (int)propValue; } return(varPtr); } // Divert bool isDivert = false; bool pushesToStack = false; PushPopType divPushType = PushPopType.Function; bool external = false; if (obj.TryGetValue("->", out propValue)) { isDivert = true; } else if (obj.TryGetValue("f()", out propValue)) { isDivert = true; pushesToStack = true; divPushType = PushPopType.Function; } else if (obj.TryGetValue("->t->", out propValue)) { isDivert = true; pushesToStack = true; divPushType = PushPopType.Tunnel; } else if (obj.TryGetValue("x()", out propValue)) { isDivert = true; external = true; pushesToStack = false; divPushType = PushPopType.Function; } if (isDivert) { var divert = new Divert(); divert.pushesToStack = pushesToStack; divert.stackPushType = divPushType; divert.isExternal = external; string target = propValue.ToString(); if (obj.TryGetValue("var", out propValue)) { divert.variableDivertName = target; } else { divert.targetPathString = target; } divert.isConditional = obj.TryGetValue("c", out propValue); if (external) { if (obj.TryGetValue("exArgs", out propValue)) { divert.externalArgs = (int)propValue; } } return(divert); } // Choice if (obj.TryGetValue("*", out propValue)) { var choice = new ChoicePoint(); choice.pathStringOnChoice = propValue.ToString(); if (obj.TryGetValue("flg", out propValue)) { choice.flags = (int)propValue; } return(choice); } // Variable reference if (obj.TryGetValue("VAR?", out propValue)) { return(new VariableReference(propValue.ToString())); } else if (obj.TryGetValue("CNT?", out propValue)) { var readCountVarRef = new VariableReference(); readCountVarRef.pathStringForCount = propValue.ToString(); return(readCountVarRef); } // Variable assignment bool isVarAss = false; bool isGlobalVar = false; if (obj.TryGetValue("VAR=", out propValue)) { isVarAss = true; isGlobalVar = true; } else if (obj.TryGetValue("temp=", out propValue)) { isVarAss = true; isGlobalVar = false; } if (isVarAss) { var varName = propValue.ToString(); var isNewDecl = !obj.TryGetValue("re", out propValue); var varAss = new VariableAssignment(varName, isNewDecl); varAss.isGlobal = isGlobalVar; return(varAss); } if (obj ["originalChoicePath"] != null) { return(JObjectToChoice(obj)); } } // Array is always a Runtime.Container if (token is List <object> ) { return(JArrayToContainer((List <object>)token)); } if (token == null) { return(null); } throw new System.Exception("Failed to convert token to runtime object: " + token); }