using System.Globalization; using Smoll.Ex2; using Smoll.Ex4.Commands; namespace Smoll.Ex4 { class ScriptComponent : Component { string script; ScriptComponent(string filename) { script = filename; } static Dictionary? globalCommands; Dictionary localCommands; public override void Draw(Layer.DrawMode drawMode) { ICommand command; if(localCommands.TryGetValue("Draw", out command)) { List stack = new List(); command.Perform(stack); } } public override void OnAttached() { localCommands = Compile(owner.layers.First().attachedEngine.GetModule().GetScript(script)); ICommand command; if(localCommands.TryGetValue("OnAttached", out command)) { List stack = new List(); command.Perform(stack); } } public override void Update(float deltaTimeSeconds) { ICommand command; if(localCommands.TryGetValue("Update", out command)) { List stack = new List(); stack.Add(new Number(deltaTimeSeconds)); command.Perform(stack); } } static void EnsureCommandsInitialized() { if(globalCommands != null) return; globalCommands = new Dictionary { { "+", new Commands.AddNumberCommand() }, { "print", new Commands.PrintCommand() } }; } struct SString { public bool isLiteral; public string value; public string ToString() { if(isLiteral) { return "<" + value + ">"; } else { return "["+value+"]"; } } } static List Tokenize(string str) { var l = new List(); string current = ""; bool inString = false; TextElementEnumerator graphemeEnum = StringInfo.GetTextElementEnumerator(str.Normalize()); while (graphemeEnum.MoveNext()) { string grapheme = graphemeEnum.GetTextElement(); if(inString) { if(grapheme == "\"") { bool valid = graphemeEnum.MoveNext(); if(!valid) { SString s; s.value = current; s.isLiteral = true; l.Add(s); break; } grapheme = graphemeEnum.GetTextElement(); if(grapheme == "\"") current += "\""; else if(grapheme.Trim() == string.Empty) { SString s; s.value = current; s.isLiteral = true; l.Add(s); current = ""; inString = false; } else if(grapheme == "/") { bool valid2 = graphemeEnum.MoveNext(); if(!valid2) { throw new Exception("Found a / for a new line definition that didn't end"); } grapheme = graphemeEnum.GetTextElement(); if(grapheme == "\"") current += "\n"; } else { throw new Exception("Found \" of a literal followed by things"); } } else { current += grapheme; } } else if(grapheme.Trim() == string.Empty) { if(current != string.Empty) { SString s; s.value = current; s.isLiteral = false; l.Add(s); current = ""; inString = false; } } else { if(grapheme == "\"" && current == string.Empty) { inString = true; } else if(grapheme == "\"" && current != string.Empty) { throw new Exception("Found \" in atom"); } else { current += grapheme; } } } if(inString) { throw new Exception("Found unterminated string"); } if(current != string.Empty) { SString s; s.value = current; s.isLiteral = false; l.Add(s); } return l; } static public Dictionary Compile(string code) { EnsureCommandsInitialized(); Dictionary localCommands = new Dictionary(); var lines = code.Split("\n", StringSplitOptions.RemoveEmptyEntries); foreach (var line in lines) { var executable = new List(); var fullLine = Tokenize(line); if(fullLine.Count < 2) { throw new Exception("Functions need to start with a name and a --"); } var name = fullLine.First().value; if(fullLine.First().isLiteral) { throw new Exception("Literals cannot be function names"); } if(fullLine[1].isLiteral || fullLine[1].value != "--") { throw new Exception("Function body needs to start with --"); } foreach(var elem in fullLine.Skip(2)) { if(elem.isLiteral) { executable.Add(new Commands.PushStringCommand(elem.value)); continue; } { double attempt; if(Double.TryParse(elem.value, out attempt)) { executable.Add(new Commands.PushNumberCommand(attempt)); continue; } } { ICommand command; if(!localCommands.TryGetValue(elem.value, out command)) { if(!globalCommands.TryGetValue(elem.value, out command)) { executable.Add(new Commands.PushAtomCommand(elem.value)); continue; } } executable.Add(command); } } localCommands.Add(name, new CompositeCommand(executable)); } return localCommands; } } }