|
|
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;
|
|
}
|
|
}
|
|
}
|