Skip to content

Commit

Permalink
command line parser
Browse files Browse the repository at this point in the history
  • Loading branch information
sten-code committed Nov 2, 2023
1 parent 63afe52 commit 20cd47c
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 27 deletions.
3 changes: 0 additions & 3 deletions Http/Http.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using Stenguage.Json;
using Stenguage.Runtime;
using Stenguage.Runtime.Values;
using System.Net;

namespace Http
{
Expand All @@ -17,7 +16,6 @@ public static RuntimeResult get(Context ctx,
try
{
HttpResponseMessage response = client.GetAsync(url.Value).Result;
string responseBody = response.Content.ReadAsStringAsync().Result;
return res.Success(new ObjectValue(new Dictionary<string, RuntimeValue>
{
["StatusCode"] = new NumberValue((int)response.StatusCode),
Expand All @@ -42,7 +40,6 @@ public static RuntimeResult post(Context ctx,
{
Console.WriteLine(body.Properties.ToJson());
HttpResponseMessage response = client.PostAsync(url.Value, new StringContent(body.Properties.ToJson())).Result;
string responseBody = response.Content.ReadAsStringAsync().Result;
return res.Success(new ObjectValue(new Dictionary<string, RuntimeValue>
{
["StatusCode"] = new NumberValue((int)response.StatusCode),
Expand Down
132 changes: 132 additions & 0 deletions Stenguage/CommandLineParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
using System.Reflection;

namespace Stenguage
{
[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
sealed class ArgumentAttribute : Attribute
{
public string ShortName { get; }
public string LongName { get; }
public string Description { get; }
public bool IsRequired { get; }
public bool IsPositional { get; }

public ArgumentAttribute(string shortName, string longName, string description, bool isRequired = false, bool isPositional = false)
{
ShortName = shortName;
LongName = longName;
Description = description;
IsRequired = isRequired;
IsPositional = isPositional;
}
}

class ArgumentParser
{
public T Parse<T>(string[] args) where T : new()
{
T options = new T();
var propertyDictionary = GetArgumentProperties<T>();

for (int i = 0; i < args.Length; i++)
{
var arg = args[i];
if (arg.StartsWith("-"))
{
if (arg == "--help" || arg == "-h")
{
DisplayHelp(propertyDictionary);
return options;
}

string argName = arg.StartsWith("--") ? arg.Substring(2) : arg.Substring(1);
if (propertyDictionary.TryGetValue(argName, out PropertyInfo property))
{
if (property.PropertyType == typeof(bool))
{
property.SetValue(options, true);
}
else if (i + 1 < args.Length)
{
i++;
property.SetValue(options, Convert.ChangeType(args[i], property.PropertyType));
}
}
else
{
Console.WriteLine($"Unknown argument: {arg}");
Environment.Exit(1);
return options;
}
}
else
{
// Treat the argument as a positional argument
var positionalProperties = propertyDictionary.Values
.Where(prop => prop.GetCustomAttribute<ArgumentAttribute>().IsPositional)
.ToList();

if (positionalProperties.Count > 0)
{
PropertyInfo property = positionalProperties.First();
property.SetValue(options, Convert.ChangeType(arg, property.PropertyType));
}
else
{
Console.WriteLine($"Unknown positional argument: {arg}");
Environment.Exit(1);
return options;
}
}
}

// Check if required arguments are missing
var missingRequiredArguments = propertyDictionary.Values
.Where(prop => prop.GetCustomAttribute<ArgumentAttribute>().IsRequired && prop.GetValue(options) == null)
.ToList();

if (missingRequiredArguments.Count > 0)
{
Console.WriteLine("Required argument(s) missing:");
foreach (var missingArg in missingRequiredArguments)
{
var attribute = missingArg.GetCustomAttribute<ArgumentAttribute>();
Console.WriteLine($" -{attribute.ShortName} or --{attribute.LongName}: {attribute.Description}");
}
Environment.Exit(1);
}

return options;
}

private void DisplayHelp(Dictionary<string, PropertyInfo> propertyDictionary)
{
Console.WriteLine("Available Commands:");
foreach (var kvp in propertyDictionary)
{
var attribute = kvp.Value.GetCustomAttribute<ArgumentAttribute>();
Console.WriteLine($" -{attribute.ShortName}, --{attribute.LongName}: {attribute.Description}");
}
Console.WriteLine(" -h, --help: Show this help message");
}

private Dictionary<string, PropertyInfo> GetArgumentProperties<T>()
{
var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
var propertyDictionary = new Dictionary<string, PropertyInfo>();

foreach (var property in properties)
{
var attribute = property.GetCustomAttribute<ArgumentAttribute>();
if (attribute != null)
{
propertyDictionary[attribute.ShortName] = property;
propertyDictionary[attribute.LongName] = property;
}
}

return propertyDictionary;
}
}

}
48 changes: 24 additions & 24 deletions Stenguage/Program.cs
Original file line number Diff line number Diff line change
@@ -1,38 +1,38 @@
using Stenguage.Ast;
using Stenguage.Json;
using Stenguage.Runtime;
using Stenguage.Runtime.Values;

namespace Stenguage
{
class ProgramOptions
{
[Argument("v", "verbose", "Enable verbose mode", false)]
public bool Verbose { get; set; }

[Argument("i", "input", "Input file path", false, true)]
public string InputFile { get; set; }
}

public class Application
{
public static void LogResult(RuntimeValue result)
public static void Main(string[] args)
{
switch (result.Type)
ArgumentParser parser = new ArgumentParser();
ProgramOptions options = parser.Parse<ProgramOptions>(args);

if (options.Verbose)
{
case RuntimeValueType.Function:
Console.WriteLine($"<Function>");
break;
case RuntimeValueType.Object:
ObjectValue obj = (ObjectValue)result;
foreach (KeyValuePair<string, RuntimeValue> val in obj.Properties)
{
Console.Write(val.Key + " ");
LogResult(val.Value);
}
break;
default:
Console.WriteLine(result.ToString());
break;
Console.WriteLine("Verbose mode enabled");
}
}

public static void Main(string[] args)
{
if (args.Length != 0)
if (options.InputFile != null)
{
string code = File.ReadAllText(args[0]);
if (!File.Exists(options.InputFile))
{
Console.WriteLine($"The file \"{options.InputFile}\" doesn't exist.");
return;
}
string code = File.ReadAllText(options.InputFile);
ParseResult parseResult = new Parser(code).ProduceAST();
if (parseResult.ShouldReturn())
{
Expand Down Expand Up @@ -75,9 +75,9 @@ public static void Main(string[] args)
{
Console.WriteLine(result.Error);
}
else if (result.Value.Type != Runtime.Values.RuntimeValueType.Null)
else if (result.Value.Type != RuntimeValueType.Null)
{
LogResult(result.Value);
Console.WriteLine(result.Value);
}
}
}
Expand Down

0 comments on commit 20cd47c

Please sign in to comment.