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.

192 lines
7.2 KiB

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