Browse Source

Added a primitive scripting system

master
Ludovic 'Archivist' Lagouardette 1 month ago
parent
commit
ac9d4b15d1
4 changed files with 342 additions and 0 deletions
  1. +11
    -0
      Expansion2/RaylibAssetManagerModule.cs
  2. +85
    -0
      Expansion4/Commands/ICommand.cs
  3. +54
    -0
      Expansion4/Commands/IScriptable.cs
  4. +192
    -0
      Expansion4/ScriptComponent.cs

+ 11
- 0
Expansion2/RaylibAssetManagerModule.cs View File

@ -8,12 +8,14 @@ namespace Smoll.Ex2
Dictionary<string, Image> images;
Dictionary<string, Texture2D> textures;
Dictionary<string, Sound> sounds;
Dictionary<string, string> scripts;
public RaylibAssetManagerModule(string path = "./assets") {
this.path = path;
images = new Dictionary<string, Image>();
textures = new Dictionary<string, Texture2D>();
sounds = new Dictionary<string, Sound>();
scripts = new Dictionary<string, string>();
}
public Image GetImage(string name) {
@ -43,5 +45,14 @@ namespace Smoll.Ex2
}
return sound;
}
public string GetScript(string name) {
string script;
if(! scripts.TryGetValue(name, out script)) {
script = File.ReadAllText(path+"/"+name);
scripts.Add(name, script);
}
return script;
}
}
}

+ 85
- 0
Expansion4/Commands/ICommand.cs View File

@ -0,0 +1,85 @@
namespace Smoll.Ex4.Commands {
interface ICommand{
void Perform(List<IScriptable> stack);
}
class PushNumberCommand : ICommand
{
public double self;
public PushNumberCommand(double value) {
self = value;
}
public void Perform(List<IScriptable> stack)
{
stack.Add(new Number(self));;
}
}
class PushStringCommand : ICommand
{
public string self;
public PushStringCommand(string value) {
self = value;
}
public void Perform(List<IScriptable> stack)
{
stack.Add(new Commands.String(self));;
}
}
class PushAtomCommand : ICommand
{
public string self;
public PushAtomCommand(string value) {
self = value;
}
public void Perform(List<IScriptable> stack)
{
stack.Add(new Commands.Atom(self));;
}
}
class AddNumberCommand : ICommand
{
public void Perform(List<IScriptable> stack)
{
if(! stack.TakeLast(2).All(x => x is Ex4.Commands.Number)) {
throw new Exception("Bad arguments to add as numbers");
}
var ret = stack.TakeLast(2).Cast<Ex4.Commands.Number>().Select(x => x.value).Sum();
stack.RemoveAt(stack.Count - 1);
stack.RemoveAt(stack.Count - 1);
stack.Add(new Number(ret));
}
}
class PrintCommand : ICommand
{
public void Perform(List<IScriptable> stack)
{
var ret = stack.Last();
stack.RemoveAt(stack.Count - 1);
System.Console.Out.Write(ret.ToString());
}
}
class CompositeCommand : ICommand
{
List<ICommand> executable;
public CompositeCommand(List<ICommand> executable) {
this.executable = executable;
}
public void Perform(List<IScriptable> stack)
{
foreach (var item in executable)
{
item.Perform(stack);
}
}
}
}

+ 54
- 0
Expansion4/Commands/IScriptable.cs View File

@ -0,0 +1,54 @@
namespace Smoll.Ex4.Commands {
interface IScriptable {
}
class Number : IScriptable {
public double value;
public Number(double value) {
this.value = value;
}
public override string ToString()
{
return value.ToString();
}
}
class Complex : IScriptable {
public Complex value;
public override string ToString()
{
return value.ToString();
}
}
class Atom : IScriptable {
public string value;
public Atom(string value) {
this.value = value;
}
public override string ToString()
{
return "[[" + value.ToString() + "]]";
}
}
class String : IScriptable {
public string value;
public String(string value) {
this.value = value;
}
public override string ToString()
{
return value;
}
}
}

+ 192
- 0
Expansion4/ScriptComponent.cs View File

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

Loading…
Cancel
Save