diff --git a/.gitignore b/.gitignore
index cd0982a..0babe19 100644
--- a/.gitignore
+++ b/.gitignore
@@ -542,5 +542,3 @@ FodyWeavers.xsd
# End of https://www.toptal.com/developers/gitignore/api/rider,visualbasic,visualstudio
# Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option)
-
-*.glyph
diff --git a/GlobalUsing.cs b/GlobalUsing.cs
index 4d883b0..20fb182 100644
--- a/GlobalUsing.cs
+++ b/GlobalUsing.cs
@@ -1 +1,5 @@
global using Glyph;
+global using CliFx;
+global using CliFx.Attributes;
+global using CliFx.Infrastructure;
+global using System.IO;
diff --git a/Glyph.csproj b/Glyph.csproj
index 49c8395..b0278aa 100644
--- a/Glyph.csproj
+++ b/Glyph.csproj
@@ -4,11 +4,13 @@
Exe
net8.0
enable
+ glyph
enable
0.1.100
+
diff --git a/Program.cs b/Program.cs
index 7dcf62f..c55e416 100644
--- a/Program.cs
+++ b/Program.cs
@@ -1,10 +1,4 @@
-string[] code = new[]
-{
- "# Skip this line",
- "set x 5",
- "mul x 2",
- "say x",
- "end"
-};
-
-Runner.Interpret(code);
+await new CliApplicationBuilder()
+ .AddCommand()
+ .Build()
+ .RunAsync();
diff --git a/README.md b/README.md
index 78bdcdb..879cdd7 100644
--- a/README.md
+++ b/README.md
@@ -44,6 +44,36 @@ verb subject object [modifier]
| `mod` | modulo | `mod x 5` |
| `end` | stop execution | `end` |
-## 📜 License
+## 🧩 Tech Stack
+
+- .NET 8.0
+- Dynamic Expresso for interpreter
+
+## 🗓️ Update Cycle
+
+| Type | Frequency | Notes |
+| ------------ | ---------------- | ---------------------------------------- |
+| Minor Update | Every 3–6 months | Small enhancements, non-breaking changes |
+| Patch Update | As needed | Bug fixes, security updates |
+| Major Update | As needed | Framework upgrades, major refactors |
+
+- Reserve months: June (Mid-Year Chill) & December (End-Year Freeze)
+
+## 🛡️ Status
+
+- [x] Active Support
+- [ ] Limited Support (Security patches only)
+- [ ] Maintenance Mode (Dependency-only updates)
+- [ ] Archived (No active work planned)
+
+## 🎮 Relaxation Practices
+
+- 20% creative/recovery space built into development
+- Mandatory cooldowns after major launches (minimum 1 week)
+- Crisis Mode Activates if:
+ - Critical vulnerabilities
+ - Framework-breaking issues
+
+## 🗒️ License
I license this project under the GPL v3 license - see [LICENSE](LICENSE) for details.
diff --git a/RunCommand.cs b/RunCommand.cs
new file mode 100644
index 0000000..509b854
--- /dev/null
+++ b/RunCommand.cs
@@ -0,0 +1,51 @@
+namespace Glyph;
+
+[Command("run", Description = "Run a Glyph program.")]
+public class RunCommand : ICommand
+{
+ [CommandParameter(0, Name = "file", Description = "Path to the Glyph source file.")]
+ public string FilePath { get; set; }
+
+ [CommandOption("input", 'i', Description = "Path to optional input file.")]
+ public string InputPath { get; set; }
+
+ [CommandOption("binary", 'b', Description = "Run binary file")]
+ public bool Binary { get; set; }
+
+ [CommandOption("verbose", 'v', Description = "Enable verbose output.")]
+ public bool Verbose { get; set; }
+
+ public async ValueTask ExecuteAsync(IConsole console)
+ {
+ var lines = new string[] { };
+
+ if (!System.IO.File.Exists(FilePath))
+ {
+ await console.Error.WriteLineAsync($"Error: File not found: {FilePath}");
+ return;
+ }
+
+ if (Binary)
+ {
+ // TODO: Implement binary file execution
+ await console.Error.WriteLineAsync($"Error: Binary file execution not implemented");
+ return;
+ }
+
+ lines = await System.IO.File.ReadAllLinesAsync(FilePath);
+
+ if (!string.IsNullOrEmpty(InputPath))
+ {
+ // You could inject input to the interpreter here
+ string input = await File.ReadAllTextAsync(InputPath);
+ // TODO: Attach to interpreter input stream
+ }
+
+ if (Verbose)
+ {
+ await console.Output.WriteLineAsync($"Executing: {FilePath}");
+ }
+
+ Runner.Interpret(lines);
+ }
+}
diff --git a/Runner.cs b/Runner.cs
index c88085f..4443316 100644
--- a/Runner.cs
+++ b/Runner.cs
@@ -8,8 +8,8 @@ public static class Runner
{
var env = new Dictionary();
var labels = new Dictionary();
- var callStack = new Stack();
- var interpreter = new Interpreter();
+ var stack = new Stack();
+ var exp = new Interpreter();
int pc = 0;
// First pass: record labels
@@ -40,8 +40,8 @@ public static class Runner
object Eval(string expr)
{
foreach (var (k, v) in env)
- interpreter.SetVariable(k, v);
- return interpreter.Eval(expr);
+ exp.SetVariable(k, v);
+ return exp.Eval(expr);
}
switch (cmd)
@@ -101,7 +101,7 @@ public static class Runner
case "call":
if (arg1 != null && labels.TryGetValue(arg1, out var fnStart))
{
- callStack.Push(pc);
+ stack.Push(pc);
pc = fnStart + 1;
if (arg2 != null)
env["x"] = Eval(arg2);
@@ -109,8 +109,8 @@ public static class Runner
break;
case "ret":
- if (callStack.Count > 0)
- pc = callStack.Pop();
+ if (stack.Count > 0)
+ pc = stack.Pop();
else
return;
break;
diff --git a/ScriptHelper.cs b/ScriptHelper.cs
new file mode 100644
index 0000000..6b5f956
--- /dev/null
+++ b/ScriptHelper.cs
@@ -0,0 +1,18 @@
+namespace Glyph;
+
+public static class ScriptHelper
+{
+ ///
+ /// Encodes a string to Base64.
+ ///
+ /// The string to encode.
+ /// The Base64 encoded string.
+ // https://arcanecode.com/2007/03/21/encoding-strings-to-base64-in-c/
+ static public string EncodeTo64(string toEncode)
+ {
+ byte[] toEncodeAsBytes = System.Text.ASCIIEncoding.ASCII.GetBytes(toEncode);
+ string returnValue = System.Convert.ToBase64String(toEncodeAsBytes);
+ return returnValue;
+
+ }
+}
diff --git a/sample.glif b/sample.glif
new file mode 100644
index 0000000..3146d31
--- /dev/null
+++ b/sample.glif
@@ -0,0 +1,4 @@
+set x 5
+mul x 2
+say x
+end