A Smoll game engine
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

191 lines
7.2 KiB

преди 8 месеца
  1. using System.Globalization;
  2. using Smoll.Ex2;
  3. using Smoll.Ex4.Commands;
  4. namespace Smoll.Ex4 {
  5. class ScriptComponent : Component {
  6. string script;
  7. ScriptComponent(string filename) {
  8. script = filename;
  9. }
  10. static Dictionary<string, Commands.ICommand>? globalCommands;
  11. Dictionary<string, Commands.ICommand> localCommands;
  12. public override void Draw(Layer.DrawMode drawMode)
  13. {
  14. ICommand command;
  15. if(localCommands.TryGetValue("Draw", out command)) {
  16. List<IScriptable> stack = new List<IScriptable>();
  17. command.Perform(stack);
  18. }
  19. }
  20. public override void OnAttached()
  21. {
  22. localCommands = Compile(owner.layers.First().attachedEngine.GetModule<RaylibAssetManagerModule>().GetScript(script));
  23. ICommand command;
  24. if(localCommands.TryGetValue("OnAttached", out command)) {
  25. List<IScriptable> stack = new List<IScriptable>();
  26. command.Perform(stack);
  27. }
  28. }
  29. public override void Update(float deltaTimeSeconds)
  30. {
  31. ICommand command;
  32. if(localCommands.TryGetValue("Update", out command)) {
  33. List<IScriptable> stack = new List<IScriptable>();
  34. stack.Add(new Number(deltaTimeSeconds));
  35. command.Perform(stack);
  36. }
  37. }
  38. static void EnsureCommandsInitialized() {
  39. if(globalCommands != null) return;
  40. globalCommands = new Dictionary<string, Commands.ICommand>
  41. {
  42. { "+", new Commands.AddNumberCommand() },
  43. { "print", new Commands.PrintCommand() }
  44. };
  45. }
  46. struct SString {
  47. public bool isLiteral;
  48. public string value;
  49. public string ToString() {
  50. if(isLiteral) {
  51. return "<" + value + ">";
  52. } else {
  53. return "["+value+"]";
  54. }
  55. }
  56. }
  57. static List<SString> Tokenize(string str) {
  58. var l = new List<SString>();
  59. string current = "";
  60. bool inString = false;
  61. TextElementEnumerator graphemeEnum = StringInfo.GetTextElementEnumerator(str.Normalize());
  62. while (graphemeEnum.MoveNext())
  63. {
  64. string grapheme = graphemeEnum.GetTextElement();
  65. if(inString) {
  66. if(grapheme == "\"") {
  67. bool valid = graphemeEnum.MoveNext();
  68. if(!valid) {
  69. SString s;
  70. s.value = current;
  71. s.isLiteral = true;
  72. l.Add(s);
  73. break;
  74. }
  75. grapheme = graphemeEnum.GetTextElement();
  76. if(grapheme == "\"") current += "\"";
  77. else if(grapheme.Trim() == string.Empty) {
  78. SString s;
  79. s.value = current;
  80. s.isLiteral = true;
  81. l.Add(s);
  82. current = "";
  83. inString = false;
  84. } else if(grapheme == "/") {
  85. bool valid2 = graphemeEnum.MoveNext();
  86. if(!valid2) {
  87. throw new Exception("Found a / for a new line definition that didn't end");
  88. }
  89. grapheme = graphemeEnum.GetTextElement();
  90. if(grapheme == "\"") current += "\n";
  91. } else {
  92. throw new Exception("Found \" of a literal followed by things");
  93. }
  94. } else {
  95. current += grapheme;
  96. }
  97. } else if(grapheme.Trim() == string.Empty) {
  98. if(current != string.Empty) {
  99. SString s;
  100. s.value = current;
  101. s.isLiteral = false;
  102. l.Add(s);
  103. current = "";
  104. inString = false;
  105. }
  106. } else {
  107. if(grapheme == "\"" && current == string.Empty) {
  108. inString = true;
  109. } else if(grapheme == "\"" && current != string.Empty) {
  110. throw new Exception("Found \" in atom");
  111. } else {
  112. current += grapheme;
  113. }
  114. }
  115. }
  116. if(inString) {
  117. throw new Exception("Found unterminated string");
  118. }
  119. if(current != string.Empty) {
  120. SString s;
  121. s.value = current;
  122. s.isLiteral = false;
  123. l.Add(s);
  124. }
  125. return l;
  126. }
  127. static public Dictionary<string, Commands.ICommand> Compile(string code) {
  128. EnsureCommandsInitialized();
  129. Dictionary<string, Commands.ICommand> localCommands = new Dictionary<string, Commands.ICommand>();
  130. var lines = code.Split("\n", StringSplitOptions.RemoveEmptyEntries);
  131. foreach (var line in lines)
  132. {
  133. var executable = new List<ICommand>();
  134. var fullLine = Tokenize(line);
  135. if(fullLine.Count < 2) {
  136. throw new Exception("Functions need to start with a name and a --");
  137. }
  138. var name = fullLine.First().value;
  139. if(fullLine.First().isLiteral) {
  140. throw new Exception("Literals cannot be function names");
  141. }
  142. if(fullLine[1].isLiteral || fullLine[1].value != "--") {
  143. throw new Exception("Function body needs to start with --");
  144. }
  145. foreach(var elem in fullLine.Skip(2)) {
  146. if(elem.isLiteral) {
  147. executable.Add(new Commands.PushStringCommand(elem.value));
  148. continue;
  149. }
  150. {
  151. double attempt;
  152. if(Double.TryParse(elem.value, out attempt)) {
  153. executable.Add(new Commands.PushNumberCommand(attempt));
  154. continue;
  155. }
  156. }
  157. {
  158. ICommand command;
  159. if(!localCommands.TryGetValue(elem.value, out command)) {
  160. if(!globalCommands.TryGetValue(elem.value, out command)) {
  161. executable.Add(new Commands.PushAtomCommand(elem.value));
  162. continue;
  163. }
  164. }
  165. executable.Add(command);
  166. }
  167. }
  168. localCommands.Add(name, new CompositeCommand(executable));
  169. }
  170. return localCommands;
  171. }
  172. }
  173. }