|
|
@ -0,0 +1,192 @@ |
|
|
|
|
|
|
|
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<string, Commands.ICommand>? globalCommands; |
|
|
|
Dictionary<string, Commands.ICommand> localCommands; |
|
|
|
|
|
|
|
public override void Draw(Layer.DrawMode drawMode) |
|
|
|
{ |
|
|
|
ICommand command; |
|
|
|
if(localCommands.TryGetValue("Draw", out command)) { |
|
|
|
List<IScriptable> stack = new List<IScriptable>(); |
|
|
|
command.Perform(stack); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public override void OnAttached() |
|
|
|
{ |
|
|
|
localCommands = Compile(owner.layers.First().attachedEngine.GetModule<RaylibAssetManagerModule>().GetScript(script)); |
|
|
|
|
|
|
|
ICommand command; |
|
|
|
if(localCommands.TryGetValue("OnAttached", out command)) { |
|
|
|
List<IScriptable> stack = new List<IScriptable>(); |
|
|
|
command.Perform(stack); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public override void Update(float deltaTimeSeconds) |
|
|
|
{ |
|
|
|
ICommand command; |
|
|
|
if(localCommands.TryGetValue("Update", out command)) { |
|
|
|
List<IScriptable> stack = new List<IScriptable>(); |
|
|
|
stack.Add(new Number(deltaTimeSeconds)); |
|
|
|
command.Perform(stack); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static void EnsureCommandsInitialized() { |
|
|
|
if(globalCommands != null) return; |
|
|
|
globalCommands = new Dictionary<string, Commands.ICommand> |
|
|
|
{ |
|
|
|
{ "+", 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<SString> Tokenize(string str) { |
|
|
|
var l = new List<SString>(); |
|
|
|
|
|
|
|
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<string, Commands.ICommand> Compile(string code) { |
|
|
|
EnsureCommandsInitialized(); |
|
|
|
Dictionary<string, Commands.ICommand> localCommands = new Dictionary<string, Commands.ICommand>(); |
|
|
|
var lines = code.Split("\n", StringSplitOptions.RemoveEmptyEntries); |
|
|
|
|
|
|
|
foreach (var line in lines) |
|
|
|
{ |
|
|
|
var executable = new List<ICommand>(); |
|
|
|
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; |
|
|
|
} |
|
|
|
} |
|
|
|
} |