public static string Compiled(ObjectMacro asset) { var output = string.Empty; var isNamespaceNull = string.IsNullOrEmpty(asset.@namespace); var usingStatements = new List <string>(); var inherits = asset.baseType; var implements = asset.interfaces; var typeName = Patcher.TypeName(asset); var variables = asset.variables; var scope = asset.scope; var modifier = asset.classModifier; var @namespace = asset.@namespace; var name = asset.name; var methods = asset.methods; foreach (Method method in methods) { var units = method.graph.units; foreach (IUnit unit in units) { var statements = unit.CodeGenerator().usingStatements; foreach (string statement in statements) { AssemblyBuilder.UsingStatement(statement, usingStatements); } } } AssemblyBuilder.UsingStatements(variables, usingStatements); AssemblyBuilder.UsingStatement(inherits.type, usingStatements); string typeDeclaration = ClassBuilder.Declaration( isNamespaceNull ? 0 : 1, scope, modifier, typeName, inherits.type.CSharpName(false) == "object" ? string.Empty : inherits.type.CSharpName(false), implements, string.Empty, string.Empty, variables, string.Empty, string.Empty, methods, false ); output += AssemblyBuilder.GenerateUsingStatements(usingStatements); if (!isNamespaceNull) { output += AssemblyBuilder.Namespace(typeDeclaration, @namespace); return(output); } output += typeDeclaration; return(output); }
public static string CompiledEnum(ObjectMacro asset) { var output = string.Empty; var @namespace = asset.@namespace; var name = asset.name; var scope = asset.scope; var items = asset.enumValues; var isNamespaceNull = string.IsNullOrEmpty(@namespace); var typeName = Patcher.TypeName(asset); var typeDeclaration = EnumDeclaration( isNamespaceNull ? 0 : 1, scope, typeName, string.Empty, items, false ); if (!isNamespaceNull) { output += AssemblyBuilder.Namespace(typeDeclaration, @namespace); return(output); } output += typeDeclaration; return(output); }
public static void SetCompiledData(ObjectMacro macro) { macro.last_BaseType = macro.baseType; macro.last_ClassModifier = macro.classModifier; macro.last_ConstructorCount = macro.constructors.Count; macro.last_InterfaceCount = macro.interfaces.Count; macro.last_Kind = macro.kind; macro.last_MethodCount = macro.methods.Count; }
public static string Define(int indent, ObjectMacro liveObject, bool isLive) { return(Declaration(indent, liveObject.scope, liveObject.name, liveObject.baseType.type.CSharpName(), liveObject.interfaces, string.Empty, string.Empty, liveObject.variables, string.Empty, string.Empty, liveObject.methods, isLive )); }
public static string Proxy(ObjectMacro asset) { return(string.Empty); }
public static string Proxy(ObjectMacro asset) { var output = string.Empty; var @namespace = asset.@namespace; var isNamespaceNull = string.IsNullOrEmpty(@namespace); var usingStatements = new List <string>(); var inherits = asset.baseType; var implements = asset.interfaces.ToListPooled(); var typeName = Patcher.TypeName(asset); var variables = asset.variables; var scope = asset.scope; var modifier = asset.classModifier; var name = asset.name; var methods = asset.methods; AssemblyBuilder.UsingStatement("System", usingStatements); AssemblyBuilder.UsingStatement("System.Linq", usingStatements); AssemblyBuilder.UsingStatement("System.Collections.Generic", usingStatements); AssemblyBuilder.UsingStatement("UnityEngine", usingStatements); AssemblyBuilder.UsingStatement("Lasm.UAlive", usingStatements); AssemblyBuilder.UsingStatement("Ludiq", usingStatements); AssemblyBuilder.UsingStatements(variables, usingStatements); AssemblyBuilder.UsingStatement(inherits.type, usingStatements); implements.Add(new Interface() { type = typeof(ILiveObject) }); //var dynamicVariables = CodeBuilder.Indent(isNamespaceNull ? 1 : 2) + "[Serialize]" + "\n"; //dynamicVariables += CodeBuilder.Indent(isNamespaceNull ? 1 : 2) + "protected Dictionary<string, object> variables = new Dictionary<string, object>();"; var objectMacro = CodeBuilder.Indent(isNamespaceNull ? 1 : 2) + "protected ObjectMacro macro;" + "\n"; var methodMacros = string.Empty; foreach (Method method in methods) { methodMacros += CodeBuilder.Indent(isNamespaceNull ? 1 : 2) + "protected Method " + method.name + "_Method;" + "\n"; } string typeDeclaration = ClassBuilder.Declaration( isNamespaceNull ? 0 : 1, scope, modifier, typeName, inherits.type.CSharpName(false) == "object" ? string.Empty : inherits.type.CSharpName(false), implements, string.Empty, string.Empty, variables, objectMacro + methodMacros, string.Empty, string.Empty, methods, true ); output += AssemblyBuilder.GenerateUsingStatements(usingStatements); output += "\n"; output += "#if UNITY_EDITOR"; output += "\n"; output += "using UnityEditor;"; output += "\n"; output += "#endif"; output += "\n \n"; if (!isNamespaceNull) { output += AssemblyBuilder.Namespace(typeDeclaration, @namespace); return(output); } output += typeDeclaration; return(output); }
public static string TypeName(ObjectMacro asset) { return(Patcher.LegalVariableName((string.IsNullOrEmpty(asset.name) ? CodeBuilder.GetExtensionlessFileName(CodeBuilder.GetFileName(asset)).Replace(" ", string.Empty) : asset.name).Replace(" ", string.Empty), false)); }
public Root parse(string filename, string[] code) { logger.Debug($"Parsing {filename}"); if (logger.IsTraceEnable) { foreach (var line in code) { logger.Trace(line); } } var startTime = DateTime.Now.Ticks; var ast = new Root(); // Track some state variables for this parsing round. string topic = "random"; // Default topic = random int lineno = 0; bool inComment = false; // In a multi-line comment bool inObject = false; // In an object string objectName = null; // Name of the current object string objectLanguage = null; // Programming language of the object ICollection <string> objectBuffer = null; // Buffer for the current object string onTrigger = null; // Trigger we're on //Trigger currentTriggerObject = null; // Trigger we're on string previous = null; // The a %Previous trigger // File scoped parser options. IDictionary <string, string> local_options = new Dictionary <string, string>(); local_options.AddOrUpdate("concat", "none"); //local_options.Add("concat", "space"); //local_options.Add("concat", "newline"); // The given "code" is an array of lines, so jump right in. for (int i = 0; i < code.Length; i++) { lineno++; // Increment the line counter. string line = code[i] ?? ""; logger.Debug("Original Line: " + line); // Trim the line of whitespaces. line = Util.Strip(line); if (line.Length == 0) { continue; //Skip blank line } // Are we inside an object? if (inObject) { if (line.StartsWith("<object") || line.StartsWith("< object")) { // End of the object. Did we have a handler? if (!string.IsNullOrWhiteSpace(objectName)) { var macro = new ObjectMacro { Code = objectBuffer, Language = objectLanguage, Name = objectName, }; ast.addObject(macro); } objectName = null; objectLanguage = null; objectBuffer = null; inObject = false; } else { // Collect the code. objectBuffer.Add(line); } continue; } // Look for comments. if (line.StartsWith("/*")) { // Beginning a multi-line comment. if (line.IndexOf("*/") > -1) { // It ends on the same line. continue; } inComment = true; } else if (line.StartsWith("//")) { // A single line comment. continue; } else if (line.IndexOf("*/") > -1) { // End a multi-line comment. inComment = false; continue; } if (inComment) //else if? { continue; } // Skip any blank lines and weird lines. if (line.Length < 2) { logger.Warn($"Weird single-character line '#" + line + "' found (in topic #" + topic + ")"); continue; } // Separate the command from the rest of the line. string cmd = line.Substring(0, 1); line = Util.Strip(line.Substring(1)); logger.Debug("\tCmd: " + cmd); // Ignore in-line comments if there's a space before and after the "//". if (line.IndexOf(" // ") > -1) { string[] split = line.Split(new[] { " // " }, StringSplitOptions.None); line = split[0]; //remove space between comment and code line = line.TrimEnd(' '); } // In the event of a +Trigger, if we are force-lowercasing it, then do so // In the event of a +Trigger, if we are force-lowercasing it, then do so // now before the syntax check. if (forceCase && cmd == CMD_TRIGGER) { line = line.ToLower(); } //Run a syntax check on this line try { checkSyntax(cmd, line); } catch (ParserException pex) { if (strict) { throw pex; } else { logger.Warn($"Syntax logger.error(: {pex.Message} at {filename} line {lineno} near {cmd} {line}"); } } // Reset the %Previous if this is a new +Trigger. if (cmd.Equals(CMD_TRIGGER)) { previous = ""; } // Do a look-ahead to see ^Continue and %Previous. if (cmd != CMD_CONTINUE) { for (int j = (i + 1); j < code.Length; j++) { // Peek ahead. string peek = Util.Strip(code[j]); // Skip blank. if (peek.Length == 0) //peek.Length < 2? { continue; } // Get the command. string peekCmd = peek.Substring(0, 1); peek = Util.Strip(peek.Substring(1)); // Only continue if the lookahead line has any data. if (peek.Length > 0) { // The lookahead command has to be a % or a ^ if (!peekCmd.Equals(CMD_CONTINUE) && !peekCmd.Equals(CMD_PREVIOUS)) { break; } // If the current command is a +, see if the following is a %. if (cmd.Equals(CMD_TRIGGER)) { if (peekCmd.Equals(CMD_PREVIOUS)) { // It has a %Previous! previous = peek; break; } else { previous = ""; } } // If the current command is a ! and the next command(s) are // ^, we'll tack each extension on as a "line break". if (cmd.Equals(CMD_DEFINE)) { if (peekCmd.Equals(CMD_CONTINUE)) { line += "<crlf>" + peek; } } // If the current command is not a ^ and the line after is // not a %, but the line after IS a ^, then tack it onto the // end of the current line. if (!cmd.Equals(CMD_CONTINUE) && !cmd.Equals(CMD_PREVIOUS) && !cmd.Equals(CMD_DEFINE)) { if (peekCmd.Equals(CMD_CONTINUE)) { // Concatenation character? ConcatMode concatMode = null; if (local_options.ContainsKey("concat")) { concatMode = ConcatMode.FromName(local_options["concat"]); } if (concatMode == null) { concatMode = concat ?? Config.DEFAULT_CONCAT; } line += concatMode.ConcatChar + peek; } else { break; //?warn } } } } } // Start handling command types. //TODO: change to switch-case if (cmd.Equals(CMD_DEFINE)) { logger.Debug("\t! DEFINE"); //string[] halves = line.split("\\s*=\\s*", 2); string[] halves = new Regex("\\s*=\\s*").Split(line, 2); //string[] left = whatis[0].split("\\s+", 2); string[] left = new Regex("\\s+").Split(halves[0], 2); string value = ""; string kind = ""; //global, var, sub, ... string name = ""; bool delete = false; if (halves.Length == 2) { value = halves[1].Trim(); } if (left.Length >= 1) { kind = left[0]; if (left.Length >= 2) { left = Util.CopyOfRange(left, 1, left.Length); //name = left[1].Trim().ToLower(); name = Util.Join(left, " ").Trim(); } } // Remove line breaks unless this is an array. if (!kind.Equals("array")) { value = value.Replace("<crlf>", ""); } // Version is the only type that doesn't have a var. if (kind.Equals("version")) { logger.Debug("\tUsing RiveScript version " + value); // Convert the value into a double, catch exceptions. double version = 0; try { version = double.Parse(value ?? "", new NumberFormatInfo { CurrencyDecimalSeparator = "." }); } catch (FormatException) { logger.Warn("RiveScript version \"" + value + "\" not a valid floating number", filename, lineno); continue; } if (version > RS_VERSION) { throw new ParserException($"We can't parse RiveScript v {value} documents at {filename} line {lineno}. Only support {RS_VERSION}."); } continue; } else { // All the other types require a variable and value. if (name.Equals("")) { logger.Warn("Missing a " + kind + " variable name", filename, lineno); continue; } if (value.Equals("")) { logger.Warn("Missing a " + kind + " value", filename, lineno); continue; } if (value.Equals(Constants.UNDEF_TAG)) { // Deleting its value. delete = true; } } // Handle the variable set types. //TODO: change to switch-case if (kind.Equals("local")) { // Local file scoped parser options logger.Debug("\tSet local parser option " + name + " = " + value); local_options.AddOrUpdate(name, value); } else if (kind.Equals("global")) { // Is it a special global? (debug or depth or etc). logger.Debug("\tSet global " + name + " = " + value); ast.begin.addGlobals(name, value); } else if (kind.Equals("var")) { // Set a bot variable. logger.Debug("\tSet bot variable " + name + " = " + value); ast.begin.addVar(name, value); } else if (kind.Equals("array")) { // Set an array. logger.Debug("\tSet array " + name); // Deleting it? if (delete) { ast.begin.removeArray(name); continue; } // Did the array have multiple lines? //string[] parts = value.split("<crlf>"); //WARN: string[] parts = value.Split("<crlf>"); ICollection <string> items = new List <string>(); for (int a = 0; a < parts.Length; a++) { // Split at pipes or spaces? string[] pieces; if (parts[a].IndexOf("|") > -1) { //pieces = parts[a].split("\\|"); pieces = new Regex("\\|").Split(parts[a]); } else { pieces = new Regex("\\s+").Split(parts[a]); } // Add the pieces to the final array. for (int b = 0; b < pieces.Length; b++) { items.Add(pieces[b]); } } // Store this array. ast.begin.addArray(name, items); } else if (kind.Equals("sub")) { // Set a substitution. logger.Debug("\tSubstitution " + name + " => " + value); ast.begin.addSub(name, value); } else if (kind.Equals("person")) { // Set a person substitution. logger.Debug("\tPerson substitution " + name + " => " + value); ast.begin.addPerson(name, value); } else { logger.Warn("Unknown definition type \"" + kind + "\"", filename, lineno); continue; } } else if (cmd.Equals(CMD_LABEL)) { // > LABEL logger.Debug("\t> LABEL"); //string[] label = line.split("\\s+"); string[] label = line.SplitRegex("\\s+"); string type = ""; string name = ""; if (label.Length >= 1) { type = label[0].Trim().ToLower(); } if (label.Length >= 2) { name = label[1].Trim(); } // Handle the label types. if (type.Equals("begin")) { // The BEGIN statement. logger.Debug("\tFound the BEGIN Statement."); // A BEGIN is just a special topic. type = "topic"; name = "__begin__"; } if (type.Equals("topic")) { if (forceCase) { name = name.ToLower(); } // Starting a new topic. logger.Debug("\tSet topic to " + name); onTrigger = ""; //currentTriggerObject = null; topic = name; // Does this topic include or inherit another one? if (label.Length >= 3) { int mode_includes = 1; int mode_inherits = 2; int mode = 0; for (int a = 2; a < label.Length; a++) { if (label[a].ToLowerInvariant().Equals("includes")) { mode = mode_includes; } else if (label[a].ToLowerInvariant().Equals("inherits")) { mode = mode_inherits; } else if (mode > 0) { // This topic is either inherited or included. if (mode == mode_includes) { topicManager.topic(topic).includes(label[a]); } else if (mode == mode_inherits) { topicManager.topic(topic).inherits(label[a]); } } } } } if (type.Equals("object")) { // If a field was provided, it should be the programming language. string language = ""; if (label.Length >= 3) { language = label[2].ToLower(); } // Only try to parse a language we support. onTrigger = ""; if (language.Length == 0) { logger.Warn("Trying to parse unknown programming language (assuming it's CSharp)", filename, lineno); language = Constants.CSharpHandlerName; // Assume it's JavaScript } //INFO: to remove? //if (!handlers.ContainsKey(language)) //{ // // We don't have a handler for this language. // logger.debug("We can't handle " + language + " object code!"); // continue; //} // Start collecting its code! objectName = name; objectLanguage = language; objectBuffer = new List <string>(); inObject = true; } } else if (cmd.Equals(CMD_ENDLABEL)) { // < ENDLABEL logger.Debug("\t< ENDLABEL"); string type = line.Trim().ToLower(); if (type.Equals("begin") || type.Equals("topic")) { logger.Debug("\t\tEnd topic label."); topic = "random"; } else if (type.Equals("object")) { logger.Debug("\t\tEnd object label."); inObject = false; } else { logger.Warn("Unknown end topic type \"" + type + "\"", filename, lineno); } } else if (cmd.Equals(CMD_TRIGGER)) { // + TRIGGER logger.Debug("\t+ TRIGGER pattern: " + line); //currentTriggerObject = new Trigger(line); //if (previous.Length > 0) //{ // onTrigger = line + "{previous}" + previous; // currentTriggerObject.setPrevious(true); // topicManager.topic(topic).addPrevious(line, previous); //} //else //{ // onTrigger = line; //} //topicManager.topic(topic).addTrigger(currentTriggerObject); //TODO onld stuff to see if (previous.Length > 0) { // This trigger had a %Previous. To prevent conflict, tag the // trigger with the "that" text. onTrigger = line + "{previous}" + previous; topicManager.topic(topic).trigger(line).setPrevious(true); topicManager.topic(topic).addPrevious(line, previous); } else { // Set the current trigger to this. onTrigger = line; } } else if (cmd.Equals(CMD_REPLY)) { // - REPLY logger.Debug("\t- REPLY: " + line); // This can't come before a trigger! if (onTrigger.Length == 0) { logger.Warn("Reply found before trigger", filename, lineno); continue; } // Warn if we also saw a hard redirect. if (topicManager.topic(topic).trigger(onTrigger).hasRedirect()) { logger.Warn("You can't mix @Redirects with -Replies", filename, lineno); } // Add the reply to the trigger topicManager.topic(topic).trigger(onTrigger).addReply(line); } else if (cmd.Equals(CMD_PREVIOUS)) { // % PREVIOUS // This was handled above. continue; } else if (cmd.Equals(CMD_CONTINUE)) { // ^ CONTINUE // This was handled above. continue; } else if (cmd.Equals(CMD_REDIRECT)) { // @ REDIRECT logger.Debug("\t@ REDIRECT: " + line); // This can't come before a trigger! if (onTrigger.Length == 0) { logger.Warn("Redirect found before trigger", filename, lineno); continue; } // Add the redirect to the trigger. // TODO: this extends RiveScript, not compat w/ Perl yet topicManager.topic(topic).trigger(onTrigger).addRedirect(line); } else if (cmd.Equals(CMD_CONDITION)) { // * CONDITION logger.Debug("\t* CONDITION: " + line); // This can't come before a trigger! if (onTrigger.Length == 0) { logger.Warn("Redirect found before trigger", filename, lineno); continue; } // Add the condition to the trigger. topicManager.topic(topic).trigger(onTrigger).addCondition(line); } else { logger.Warn("Unrecognized command \"" + cmd + "\"", filename, lineno); } } //becouse we use topicmanager to manage topis, we have to fill ast topics foreach (var item in topicManager.listTopics()) { ast.addTopic(item.Value); } if (logger.IsDebugEnable) { logger.Debug($"Parsing {filename} completed in {DateTime.Now.Ticks - startTime} ms"); } return(ast); }