From b3434237cdf42769a90ecc7f30a8144d970502aa Mon Sep 17 00:00:00 2001 From: FrederikJA Date: Mon, 26 Jun 2023 17:08:26 +0200 Subject: [PATCH 1/3] Scoping rules with temporary union --- SudoScript.Core/Generator.cs | 52 ++++- SudoScript.Core/Plugins.cs | 286 ++++++++++++++-------------- SudoScript.Core/SymbolTable.cs | 185 +++++++++++++++++- SudoScript.Core/Visitors/Visitor.cs | 159 ---------------- 4 files changed, 369 insertions(+), 313 deletions(-) delete mode 100644 SudoScript.Core/Visitors/Visitor.cs diff --git a/SudoScript.Core/Generator.cs b/SudoScript.Core/Generator.cs index 66d667d..01d7cb8 100644 --- a/SudoScript.Core/Generator.cs +++ b/SudoScript.Core/Generator.cs @@ -7,10 +7,10 @@ public static class Generator { static Generator() { - Plugins.AddUnitFunction("Union", Union); + // Plugins.AddUnitFunction("Union", Union); } - private static IEnumerable Union(SymbolTable table, object[] args) + private static IEnumerable Union(object[] args) { //Creates a new instance of unit class. Unit unit = new Unit(); @@ -24,7 +24,7 @@ private static IEnumerable Union(SymbolTable table, object[] args) } else { - throw new Exception("Union only accepts Cells as arguments."); + throw new ArgumentException(); } } yield return unit; @@ -48,9 +48,10 @@ public static Board GenerateBoard(ProgramNode node, SymbolTable symbolTable) // Takes UnitNode, SymbolTable as inputs and a new function from the Plugin class. private static void AddUnitFunction(UnitNode node, SymbolTable symbolTable) - { + { + SymbolTable.ArgType[] args = ArgTypeFromParameterNodes(node.Parameters); //register the new Unit function. - Plugins.AddUnitFunction(node.NameToken!.Match, (symbolTable, args) => + symbolTable.AddUnitFunction(node.NameToken!.Match, (args) => { //Create a new SymbolTable SymbolTable table = new SymbolTable(symbolTable); @@ -73,11 +74,33 @@ private static void AddUnitFunction(UnitNode node, SymbolTable symbolTable) } else { - throw new Exception($"Invalid parameter type."); + throw new ArgumentException(); } } return GetUnitFromStatements(node, symbolTable); - }); + }, args); + } + + private static SymbolTable.ArgType[] ArgTypeFromParameterNodes(IReadOnlyList parameters) + { + SymbolTable.ArgType[] args = new SymbolTable.ArgType[parameters.Count]; + for (int i = 0; i < parameters.Count; i++) + { + if (parameters[i] is ParameterCellNode) + { + args[i] = SymbolTable.ArgType.Cell; + } + else if (parameters[i] is ParameterIdentifierNode) + { + args[i] = SymbolTable.ArgType.Digit; + } + else + { + throw new ArgumentOutOfRangeException(); + } + } + + return args; } private static IEnumerable GetUnitFromStatements(UnitNode node, SymbolTable symbolTable) @@ -136,7 +159,7 @@ private static IEnumerable GetRules(RulesNode node, SymbolTable symbolTab foreach(List arguments in argumentCombinations) { - yield return Plugins.CreateRule(child.Name.Match, arguments.ToArray()); + yield return symbolTable.GetRules(child.Name.Match, arguments.ToArray()); } } } @@ -222,8 +245,17 @@ private static IEnumerable GetCellsAndUnitsFromFunction(FunctionCallNode n List> argumentCombinations = ArgumentToArgumentCombinations(node.Arguments, symbolTable); foreach (List arguments in argumentCombinations) { - //Use Plugin to create a unit from the funtion call and arguments. - IEnumerable units = Plugins.CreateUnit(node.Name.Match, symbolTable, arguments.ToArray()); + //Use SymbolTable to create a unit from the funtion call and arguments. + IEnumerable units; + if (node.Name.Match == "Union") // TODO: Properly support array arguments. + { + units = Union(arguments.ToArray()); + } + else + { + units = symbolTable.GetUnits(node.Name.Match, arguments.ToArray()); + } + foreach (Unit unit in units) { //Add all cells in the unit to the symbolTable. diff --git a/SudoScript.Core/Plugins.cs b/SudoScript.Core/Plugins.cs index c1775d2..1942536 100644 --- a/SudoScript.Core/Plugins.cs +++ b/SudoScript.Core/Plugins.cs @@ -1,143 +1,143 @@ -using System.Diagnostics; -using System.Reflection; -using SudoScript.Core.Data; - -namespace SudoScript.Core; - -public static class Plugins -{ - public delegate IEnumerable UnitFunction(SymbolTable symbolTable, object[] arguments); - - private static readonly Dictionary> _rules = new Dictionary>(); - // This exists to not create unnessary objects in Plugins.CreateRule. - private static readonly Dictionary> _ruleFunctions = new Dictionary>(); - private static readonly SymbolTable _emptyTable = new SymbolTable(); - - private static readonly Dictionary> _units = new Dictionary>(); - // TODO: Take a list of UnitFunction here and loop over them to find the correct overload. - private static readonly Dictionary> _unitFunctions = new Dictionary>(); - - static Plugins() - { - string[] files = Directory.GetFiles("./", "*.dll"); - foreach (string file in files) - { - AssemblyName name = AssemblyName.GetAssemblyName(file); - Assembly assembly; - try - { - assembly = Assembly.Load(name); - } - catch (FileNotFoundException) - { - assembly = Assembly.LoadFrom(file); - } - - foreach (TypeInfo type in assembly.DefinedTypes) - { - if (type.ImplementedInterfaces.Contains(typeof(IRule))) - { - if (_rules.TryGetValue(type.Name, out List? functions)) - { - Debug.WriteLine($"Warning, multiple definitions of {type.Name}"); - } - else - { - functions = new List(); - _rules.Add(type.Name, functions); - } - functions.Add(type); - } - if (type.IsSubclassOf(typeof(Unit))) - { - if (_units.TryGetValue(type.Name, out List? functions)) - { - Debug.WriteLine($"Warning, multiple definitions of {type.Name}"); - } - else - { - functions = new List(); - _units.Add(type.Name, functions); - } - functions.Add(type); - } - } - } - } - - public static IRule CreateRule(string name, params object[] args) - { - return Create(_rules, _ruleFunctions, _emptyTable, name, args).First(); - } - public static IEnumerable CreateUnit(string name, SymbolTable symbolTable, params object[] args) - { - return Create(_units, _unitFunctions, symbolTable, name, args); - } - - public static void AddUnitFunction(string name, UnitFunction unitFunction) - { - if (!_unitFunctions.ContainsKey(name) && !_units.ContainsKey(name)) - { - _unitFunctions.Add(name, unitFunction); - } - else - { - throw new Exception($"Unit function {name} already defined"); - } - } - - private static IEnumerable Create( - IReadOnlyDictionary> typesByName, - IReadOnlyDictionary> functions, - SymbolTable symbolTable, string name, params object[] args) - { - bool foundUnits = false; - - if (typesByName.TryGetValue(name, out List? types)) - { - foreach (TypeInfo type in types) - { - T? obj = default(T); - try - { - obj = (T?)Activator.CreateInstance(type, args); - } - catch - { - // Ignore. - } - if (obj is not null) - { - yield return obj; - foundUnits = true; - } - } - } - - if (foundUnits) - { - yield break; - } - - if (functions.TryGetValue(name, out UnitFunction? unitFunction)) - { - foreach (T obj in unitFunction.Invoke(symbolTable, args)) - { - yield return obj; - foundUnits = true; - } - } - - if (foundUnits) - { - yield break; - } - - IEnumerable argStrings = args.Select(x => - x is int ? "Digit" : x is CellReference ? "Cell" : "None"); - string argList = string.Join(" ", argStrings); - string functionCall = name + " " + argList; - // TODO: Add fuzzy search to search fo closest matching function name. - throw new Exception($"Found no matching function interface for function call '{functionCall}'"); - } -} +// using System.Diagnostics; +// using System.Reflection; +// using SudoScript.Core.Data; +// +// namespace SudoScript.Core; +// +// public static class Plugins +// { +// public delegate IEnumerable UnitFunction(SymbolTable symbolTable, object[] arguments); +// +// private static readonly Dictionary> _rules = new Dictionary>(); +// // This exists to not create unnessary objects in Plugins.CreateRule. +// private static readonly Dictionary> _ruleFunctions = new Dictionary>(); +// private static readonly SymbolTable _emptyTable = new SymbolTable(); +// +// private static readonly Dictionary> _units = new Dictionary>(); +// // TODO: Take a list of UnitFunction here and loop over them to find the correct overload. +// private static readonly Dictionary> _unitFunctions = new Dictionary>(); +// +// static Plugins() +// { +// string[] files = Directory.GetFiles("./", "*.dll"); +// foreach (string file in files) +// { +// AssemblyName name = AssemblyName.GetAssemblyName(file); +// Assembly assembly; +// try +// { +// assembly = Assembly.Load(name); +// } +// catch (FileNotFoundException) +// { +// assembly = Assembly.LoadFrom(file); +// } +// +// foreach (TypeInfo type in assembly.DefinedTypes) +// { +// if (type.ImplementedInterfaces.Contains(typeof(IRule))) +// { +// if (_rules.TryGetValue(type.Name, out List? functions)) +// { +// Debug.WriteLine($"Warning, multiple definitions of {type.Name}"); +// } +// else +// { +// functions = new List(); +// _rules.Add(type.Name, functions); +// } +// functions.Add(type); +// } +// if (type.IsSubclassOf(typeof(Unit))) +// { +// if (_units.TryGetValue(type.Name, out List? functions)) +// { +// Debug.WriteLine($"Warning, multiple definitions of {type.Name}"); +// } +// else +// { +// functions = new List(); +// _units.Add(type.Name, functions); +// } +// functions.Add(type); +// } +// } +// } +// } +// +// public static IRule CreateRule(string name, params object[] args) +// { +// return Create(_rules, _ruleFunctions, _emptyTable, name, args).First(); +// } +// public static IEnumerable CreateUnit(string name, SymbolTable symbolTable, params object[] args) +// { +// return Create(_units, _unitFunctions, symbolTable, name, args); +// } +// +// public static void AddUnitFunction(string name, UnitFunction unitFunction) +// { +// if (!_unitFunctions.ContainsKey(name) && !_units.ContainsKey(name)) +// { +// _unitFunctions.Add(name, unitFunction); +// } +// else +// { +// throw new Exception($"Unit function {name} already defined"); +// } +// } +// +// private static IEnumerable Create( +// IReadOnlyDictionary> typesByName, +// IReadOnlyDictionary> functions, +// SymbolTable symbolTable, string name, params object[] args) +// { +// bool foundUnits = false; +// +// if (typesByName.TryGetValue(name, out List? types)) +// { +// foreach (TypeInfo type in types) +// { +// T? obj = default(T); +// try +// { +// obj = (T?)Activator.CreateInstance(type, args); +// } +// catch +// { +// // Ignore. +// } +// if (obj is not null) +// { +// yield return obj; +// foundUnits = true; +// } +// } +// } +// +// if (foundUnits) +// { +// yield break; +// } +// +// if (functions.TryGetValue(name, out UnitFunction? unitFunction)) +// { +// foreach (T obj in unitFunction.Invoke(symbolTable, args)) +// { +// yield return obj; +// foundUnits = true; +// } +// } +// +// if (foundUnits) +// { +// yield break; +// } +// +// IEnumerable argStrings = args.Select(x => +// x is int ? "Digit" : x is CellReference ? "Cell" : "None"); +// string argList = string.Join(" ", argStrings); +// string functionCall = name + " " + argList; +// // TODO: Add fuzzy search to search fo closest matching function name. +// throw new Exception($"Found no matching function interface for function call '{functionCall}'"); +// } +// } diff --git a/SudoScript.Core/SymbolTable.cs b/SudoScript.Core/SymbolTable.cs index 8915463..b3b0784 100644 --- a/SudoScript.Core/SymbolTable.cs +++ b/SudoScript.Core/SymbolTable.cs @@ -1,22 +1,83 @@ +using System.Diagnostics; +using System.Reflection; using SudoScript.Core.Data; namespace SudoScript.Core; -public sealed class SymbolTable +public sealed class SymbolTable { + public enum ArgType + { + None, + Cell, + Digit, + } + + public record Function(ArgType[] Args); + + public record UnitFunction(Func> Delegate, ArgType[] Args) : Function(Args); + public record RuleFunction(Func Delegate, ArgType[] Args) : Function(Args); + private readonly Dictionary _digitTable; private readonly Dictionary _cellTable; + private readonly Dictionary> _rules; + private readonly Dictionary> _units; public SymbolTable() { _digitTable = new Dictionary(); _cellTable = new Dictionary(); + _rules = new Dictionary>(); + _units = new Dictionary>(); + + string[] files = Directory.GetFiles("./", "*.dll"); + foreach (string file in files) + { + AssemblyName name = AssemblyName.GetAssemblyName(file); + Assembly assembly; + try + { + assembly = Assembly.Load(name); + } + catch (FileNotFoundException) + { + assembly = Assembly.LoadFrom(file); + } + + foreach (TypeInfo type in assembly.DefinedTypes) + { + if (type.ImplementedInterfaces.Contains(typeof(IRule))) + { + foreach ((Func func, ArgType[] args) in GetConstructors(type)) + { + AddRulesFunction(type.Name, new Func(func), args); + } + } + if (type.IsSubclassOf(typeof(Unit))) + { + foreach ((Func func, ArgType[] args) in GetConstructors(type)) + { + AddUnitFunction(type.Name, new Func(func), args); + } + } + + if (type.ImplementedInterfaces.Contains(typeof(IEnumerable))) + { + foreach ((Func func, ArgType[] args) in GetConstructors(type)) + { + AddUnitFunction(type.Name, new Func>(func), args); + } + } + } + } } public SymbolTable(SymbolTable table) { _digitTable = table._digitTable.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + _units = table._units.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); _cellTable = table._cellTable; + _rules = table._rules; } public void AddDigit(string identifier, int value) @@ -45,9 +106,131 @@ public void AddCell(CellReference reference, Cell cell) _cellTable[reference] = cell; } } + + public void AddUnitFunction(string name, Func func, ArgType[] args) + { + if (!_units.TryGetValue(name, out List? functions)) + { + functions = new List(); + _units.Add(name, functions); + } + + if (functions.Any(f => f.Args.SequenceEqual(args))) + { + throw new Exception(name + ' ' + string.Join(' ', args) + " is defined two times."); + } + + IEnumerable EnumUnitFunc(object[] obj) + { + yield return func(obj); + } + functions.Add(new UnitFunction(EnumUnitFunc, args)); + } + + public void AddUnitFunction(string name, Func> func, ArgType[] args) + { + if (!_units.TryGetValue(name, out List? functions)) + { + functions = new List(); + _units.Add(name, functions); + } + + if (functions.Any(f => f.Args.SequenceEqual(args))) + { + throw new Exception(name + ' ' + string.Join(' ', args) + " is defined two times."); + } + + functions.Add(new UnitFunction(func, args)); + } + + private void AddRulesFunction(string name, Func func, ArgType[] args) + { + if (!_rules.TryGetValue(name, out List? functions)) + { + functions = new List(); + _rules.Add(name, functions); + } + + if (functions.Any(f => f.Args.SequenceEqual(args))) + { + throw new Exception(name + ' ' + string.Join(' ', args) + " is defined two times."); + } + + functions.Add(new RuleFunction(func, args)); + } public Cell[] GetCells() { return _cellTable.Values.ToArray(); } + + public IEnumerable GetUnits(string name, object[] args) + { + if (!_units.TryGetValue(name, out List? functions)) + { + throw new Exception($"No unit functions found with name {name}"); + } + + IEnumerable argTypes = GetArgTypes(args.Select(o => o.GetType())); + UnitFunction? function = functions.Find(f => f.Args.SequenceEqual(argTypes)); + if (function is null) + { + throw new Exception("Function not found"); + } + + return function.Delegate.Invoke(args); + } + + public IRule GetRules(string name, object[] args) + { + if (!_rules.TryGetValue(name, out List? functions)) + { + throw new Exception($"No unit functions found with name {name}"); + } + + IEnumerable argTypes = GetArgTypes(args.Select(o => o.GetType())); + RuleFunction? function = functions.Find(f => f.Args.SequenceEqual(argTypes)); + if (function is null) + { + throw new Exception("Function not found"); + } + + return function.Delegate.Invoke(args); + } + + private static IEnumerable<(Func, ArgType[])> GetConstructors(TypeInfo type) + { + foreach (ConstructorInfo constructor in type.GetConstructors()) + { + IEnumerable paramTypes = constructor.GetParameters().Select(p => p.ParameterType); + Func func = constructor.Invoke; + ArgType[] args = GetArgTypes(paramTypes).ToArray(); + + // Skip constructors with non-valid args. + if (args.Any(a => a == ArgType.None)) + { + continue; + } + yield return (func, args); + } + } + + private static IEnumerable GetArgTypes(IEnumerable types) + { + foreach (Type type in types) + { + if (type == typeof(CellReference)) + { + yield return ArgType.Cell; + } + else if (type == typeof(int)) + { + yield return ArgType.Digit; + } + else + { + yield return ArgType.None; + } + } + } } \ No newline at end of file diff --git a/SudoScript.Core/Visitors/Visitor.cs b/SudoScript.Core/Visitors/Visitor.cs deleted file mode 100644 index 2c6e4fe..0000000 --- a/SudoScript.Core/Visitors/Visitor.cs +++ /dev/null @@ -1,159 +0,0 @@ -using SudoScript.Core.Ast; - -namespace SudoScript.Core.Visitors; - -public interface IVisitor -{ - public void Visit(IAstNode node); - public void Visit(ProgramNode node); - public void Visit(UnitNode node); - public void Visit(ArgumentNode node); - public void Visit(ParameterCellNode node); - public void Visit(ParameterIdentifierNode node); - public void Visit(UnitStatementNode node); - public void Visit(FunctionCallNode node); - public void Visit(CellNode node); - public void Visit(ExpressionNode node); - public void Visit(RangeNode node); - public void Visit(BinaryNode node); - public void Visit(UnaryNode node); - public void Visit(IdentifierNode node); - public void Visit(RulesNode node); - public void Visit(GivensNode node); - public void Visit(GivensStatementNode node); -} - -public abstract class VisitorBase : IVisitor -{ - public virtual void Visit(IAstNode node) - { - switch (node) - { - case ProgramNode programNode: - Visit(programNode); - break; - case UnitNode unitNode: - Visit(unitNode); - Visit((UnitStatementNode)unitNode); - break; - case ParameterCellNode parameterCellNode: - Visit(parameterCellNode); - Visit((ParameterNode)parameterCellNode); - break; - case ParameterIdentifierNode parameterIdentifierNode: - Visit(parameterIdentifierNode); - Visit((ParameterNode)parameterIdentifierNode); - break; - case FunctionCallNode functionCallNode: - Visit(functionCallNode); - Visit((UnitStatementNode)functionCallNode); - break; - case CellNode cellNode: - Visit(cellNode); - Visit((ArgumentNode)cellNode); - break; - case RangeNode rangeNode: - Visit(rangeNode); - Visit((ArgumentNode)rangeNode); - Visit((ExpressionNode)rangeNode); - break; - case BinaryNode binaryNode: - Visit(binaryNode); - Visit((ArgumentNode)binaryNode); - Visit((ExpressionNode)binaryNode); - break; - case UnaryNode unaryNode: - Visit(unaryNode); - Visit((ArgumentNode)unaryNode); - Visit((ExpressionNode)unaryNode); - break; - case IdentifierNode identifierNode: - Visit(identifierNode); - Visit((ArgumentNode)identifierNode); - Visit((ExpressionNode)identifierNode); - break; - case RulesNode rulesNode: - Visit(rulesNode); - Visit((UnitStatementNode)rulesNode); - break; - case GivensNode givensNode: - Visit(givensNode); - Visit((UnitStatementNode)givensNode); - break; - case GivensStatementNode givensStatementNode: - Visit(givensStatementNode); - break; - default: - break; - } - - foreach (IAstNode children in node.Children()) - { - Visit(children); - } - } - - public virtual void Visit(ProgramNode node) - { - } - - public virtual void Visit(UnitNode node) - { - } - - public virtual void Visit(ArgumentNode node) - { - } - - public virtual void Visit(ParameterCellNode node) - { - } - - public virtual void Visit(ParameterIdentifierNode node) - { - } - - public virtual void Visit(UnitStatementNode node) - { - } - - public virtual void Visit(FunctionCallNode node) - { - } - - public virtual void Visit(CellNode node) - { - } - - public virtual void Visit(ExpressionNode node) - { - } - - public virtual void Visit(RangeNode node) - { - } - - public virtual void Visit(BinaryNode node) - { - } - - public virtual void Visit(UnaryNode node) - { - } - - public virtual void Visit(IdentifierNode node) - { - } - - public virtual void Visit(RulesNode node) - { - } - - public virtual void Visit(GivensNode node) - { - } - - public virtual void Visit(GivensStatementNode node) - { - } -} From fa4eb86b99ece7b0a9965aabccaa942669c337f6 Mon Sep 17 00:00:00 2001 From: FrederikJA Date: Mon, 26 Jun 2023 18:55:53 +0200 Subject: [PATCH 2/3] Fix build pipeline --- SudoScript.Core.Test/PluginTests.cs | 16 ++-- SudoScript.Core/Plugins.cs | 143 ---------------------------- SudoScript.Core/SymbolTable.cs | 13 +-- 3 files changed, 15 insertions(+), 157 deletions(-) delete mode 100644 SudoScript.Core/Plugins.cs diff --git a/SudoScript.Core.Test/PluginTests.cs b/SudoScript.Core.Test/PluginTests.cs index baf16fa..82f99c8 100644 --- a/SudoScript.Core.Test/PluginTests.cs +++ b/SudoScript.Core.Test/PluginTests.cs @@ -75,14 +75,14 @@ internal sealed class PluginTests [Test] public void CreateEmptyRuleTest() { - IRule rule = Plugins.CreateRule("TestRule1"); + IRule rule = new SymbolTable().GetRules("TestRule1"); Assert.IsInstanceOf(rule); } [Test] public void CreateRuleWithCellAndDigitTest() { - IRule rule = Plugins.CreateRule("TestRule2", new CellReference(1, 2), 3); + IRule rule = new SymbolTable().GetRules("TestRule2", new CellReference(1, 2), 3); Assert.IsInstanceOf(rule); TestRule2 rule2 = (TestRule2)rule; Assert.That(rule2.Cell.X, Is.EqualTo(1)); @@ -93,7 +93,7 @@ public void CreateRuleWithCellAndDigitTest() [Test] public void CreateRuleWithDigitAndCellTest() { - IRule rule = Plugins.CreateRule("TestRule2", 3, new CellReference(1, 2)); + IRule rule = new SymbolTable().GetRules("TestRule2", 3, new CellReference(1, 2)); Assert.IsInstanceOf(rule); TestRule2 rule2 = (TestRule2)rule; Assert.That(rule2.Cell.X, Is.EqualTo(1)); @@ -104,20 +104,20 @@ public void CreateRuleWithDigitAndCellTest() [Test] public void InvalidOperatorsCreateRule() { - Assert.Throws(() => Plugins.CreateRule("TestRule2", 1, 2)); + Assert.Throws(() => new SymbolTable().GetRules("TestRule2", 1, 2)); } [Test] public void CreateEmptyUnitTest() { - Unit rule = Plugins.CreateUnit("TestUnit1", new SymbolTable()).First(); + Unit rule = new SymbolTable().GetUnits("TestUnit1").First(); Assert.IsInstanceOf(rule); } [Test] public void CreateUnitWithCellAndDigitTest() { - Unit unit = Plugins.CreateUnit("TestUnit2", new SymbolTable(), new CellReference(1, 2), 3).First(); + Unit unit = new SymbolTable().GetUnits("TestUnit2", new CellReference(1, 2), 3).First(); Assert.IsInstanceOf(unit); TestUnit2 unit2 = (TestUnit2)unit; Assert.That(unit2.Cell.X, Is.EqualTo(1)); @@ -128,7 +128,7 @@ public void CreateUnitWithCellAndDigitTest() [Test] public void CreateUnitWithDigitAndCellTest() { - Unit unit = Plugins.CreateUnit("TestUnit2", new SymbolTable(), 3, new CellReference(1, 2)).First(); + Unit unit = new SymbolTable().GetUnits("TestUnit2", 3, new CellReference(1, 2)).First(); Assert.IsInstanceOf(unit); TestUnit2 unit2 = (TestUnit2)unit; Assert.That(unit2.Cell.X, Is.EqualTo(1)); @@ -139,6 +139,6 @@ public void CreateUnitWithDigitAndCellTest() [Test] public void InvalidOperatorsCreateUnit() { - Assert.Throws(() => Plugins.CreateRule("TestUnit2", 1, 2)); + Assert.Throws(() => new SymbolTable().GetUnits("TestUnit2", 1, 2)); } } diff --git a/SudoScript.Core/Plugins.cs b/SudoScript.Core/Plugins.cs deleted file mode 100644 index 1942536..0000000 --- a/SudoScript.Core/Plugins.cs +++ /dev/null @@ -1,143 +0,0 @@ -// using System.Diagnostics; -// using System.Reflection; -// using SudoScript.Core.Data; -// -// namespace SudoScript.Core; -// -// public static class Plugins -// { -// public delegate IEnumerable UnitFunction(SymbolTable symbolTable, object[] arguments); -// -// private static readonly Dictionary> _rules = new Dictionary>(); -// // This exists to not create unnessary objects in Plugins.CreateRule. -// private static readonly Dictionary> _ruleFunctions = new Dictionary>(); -// private static readonly SymbolTable _emptyTable = new SymbolTable(); -// -// private static readonly Dictionary> _units = new Dictionary>(); -// // TODO: Take a list of UnitFunction here and loop over them to find the correct overload. -// private static readonly Dictionary> _unitFunctions = new Dictionary>(); -// -// static Plugins() -// { -// string[] files = Directory.GetFiles("./", "*.dll"); -// foreach (string file in files) -// { -// AssemblyName name = AssemblyName.GetAssemblyName(file); -// Assembly assembly; -// try -// { -// assembly = Assembly.Load(name); -// } -// catch (FileNotFoundException) -// { -// assembly = Assembly.LoadFrom(file); -// } -// -// foreach (TypeInfo type in assembly.DefinedTypes) -// { -// if (type.ImplementedInterfaces.Contains(typeof(IRule))) -// { -// if (_rules.TryGetValue(type.Name, out List? functions)) -// { -// Debug.WriteLine($"Warning, multiple definitions of {type.Name}"); -// } -// else -// { -// functions = new List(); -// _rules.Add(type.Name, functions); -// } -// functions.Add(type); -// } -// if (type.IsSubclassOf(typeof(Unit))) -// { -// if (_units.TryGetValue(type.Name, out List? functions)) -// { -// Debug.WriteLine($"Warning, multiple definitions of {type.Name}"); -// } -// else -// { -// functions = new List(); -// _units.Add(type.Name, functions); -// } -// functions.Add(type); -// } -// } -// } -// } -// -// public static IRule CreateRule(string name, params object[] args) -// { -// return Create(_rules, _ruleFunctions, _emptyTable, name, args).First(); -// } -// public static IEnumerable CreateUnit(string name, SymbolTable symbolTable, params object[] args) -// { -// return Create(_units, _unitFunctions, symbolTable, name, args); -// } -// -// public static void AddUnitFunction(string name, UnitFunction unitFunction) -// { -// if (!_unitFunctions.ContainsKey(name) && !_units.ContainsKey(name)) -// { -// _unitFunctions.Add(name, unitFunction); -// } -// else -// { -// throw new Exception($"Unit function {name} already defined"); -// } -// } -// -// private static IEnumerable Create( -// IReadOnlyDictionary> typesByName, -// IReadOnlyDictionary> functions, -// SymbolTable symbolTable, string name, params object[] args) -// { -// bool foundUnits = false; -// -// if (typesByName.TryGetValue(name, out List? types)) -// { -// foreach (TypeInfo type in types) -// { -// T? obj = default(T); -// try -// { -// obj = (T?)Activator.CreateInstance(type, args); -// } -// catch -// { -// // Ignore. -// } -// if (obj is not null) -// { -// yield return obj; -// foundUnits = true; -// } -// } -// } -// -// if (foundUnits) -// { -// yield break; -// } -// -// if (functions.TryGetValue(name, out UnitFunction? unitFunction)) -// { -// foreach (T obj in unitFunction.Invoke(symbolTable, args)) -// { -// yield return obj; -// foundUnits = true; -// } -// } -// -// if (foundUnits) -// { -// yield break; -// } -// -// IEnumerable argStrings = args.Select(x => -// x is int ? "Digit" : x is CellReference ? "Cell" : "None"); -// string argList = string.Join(" ", argStrings); -// string functionCall = name + " " + argList; -// // TODO: Add fuzzy search to search fo closest matching function name. -// throw new Exception($"Found no matching function interface for function call '{functionCall}'"); -// } -// } diff --git a/SudoScript.Core/SymbolTable.cs b/SudoScript.Core/SymbolTable.cs index b3b0784..8fd5233 100644 --- a/SudoScript.Core/SymbolTable.cs +++ b/SudoScript.Core/SymbolTable.cs @@ -29,7 +29,7 @@ public SymbolTable() _cellTable = new Dictionary(); _rules = new Dictionary>(); _units = new Dictionary>(); - + string[] files = Directory.GetFiles("./", "*.dll"); foreach (string file in files) { @@ -50,14 +50,15 @@ public SymbolTable() { foreach ((Func func, ArgType[] args) in GetConstructors(type)) { - AddRulesFunction(type.Name, new Func(func), args); + AddRulesFunction(type.Name, o => (IRule)func(o), args); } + } if (type.IsSubclassOf(typeof(Unit))) { foreach ((Func func, ArgType[] args) in GetConstructors(type)) { - AddUnitFunction(type.Name, new Func(func), args); + AddUnitFunction(type.Name, o => (Unit)func(o), args); } } @@ -65,7 +66,7 @@ public SymbolTable() { foreach ((Func func, ArgType[] args) in GetConstructors(type)) { - AddUnitFunction(type.Name, new Func>(func), args); + AddUnitFunction(type.Name, o => (Unit)func(o), args); } } } @@ -164,7 +165,7 @@ public Cell[] GetCells() return _cellTable.Values.ToArray(); } - public IEnumerable GetUnits(string name, object[] args) + public IEnumerable GetUnits(string name, params object[] args) { if (!_units.TryGetValue(name, out List? functions)) { @@ -181,7 +182,7 @@ public IEnumerable GetUnits(string name, object[] args) return function.Delegate.Invoke(args); } - public IRule GetRules(string name, object[] args) + public IRule GetRules(string name, params object[] args) { if (!_rules.TryGetValue(name, out List? functions)) { From 39d08f3a4aa35935c911f29b5b1c08ca3d989f42 Mon Sep 17 00:00:00 2001 From: FrederikJA Date: Tue, 27 Jun 2023 12:49:07 +0200 Subject: [PATCH 3/3] SymbolTable rework and fix some parsing to do with identifiers --- SudoScript.Core/Generator.cs | 4 ++-- SudoScript.Core/Parser.cs | 41 +++++++++++++++++++++++++++------- SudoScript.Core/SymbolTable.cs | 7 +++++- SudoScript/CliApplication.cs | 22 ++++++++++++------ 4 files changed, 56 insertions(+), 18 deletions(-) diff --git a/SudoScript.Core/Generator.cs b/SudoScript.Core/Generator.cs index 01d7cb8..d2a6883 100644 --- a/SudoScript.Core/Generator.cs +++ b/SudoScript.Core/Generator.cs @@ -77,7 +77,7 @@ private static void AddUnitFunction(UnitNode node, SymbolTable symbolTable) throw new ArgumentException(); } } - return GetUnitFromStatements(node, symbolTable); + return GetUnitFromStatements(node, table); }, args); } @@ -384,7 +384,7 @@ private static List CalculateUnaryNode(UnaryNode node, SymbolTable symbolTa private static List IdentifierRetriever(IdentifierNode node, SymbolTable symbolTable) { - throw new NotImplementedException(); + return new List() { symbolTable.GetDigit(node.NameToken.Match) }; } //Method takes in two lists of T2 and a function for two arguments to return T1 value. diff --git a/SudoScript.Core/Parser.cs b/SudoScript.Core/Parser.cs index 1e1c6cc..737ac5a 100644 --- a/SudoScript.Core/Parser.cs +++ b/SudoScript.Core/Parser.cs @@ -241,7 +241,7 @@ private static List ParseArguments(TokenStream stream) } // If more arguments follow, parse them recursively. - if (stream.Peek(false, out Token? identifier) && (identifier.Type == TokenType.Identifier || identifier.Type == TokenType.LeftParenthesis)) + if (stream.Peek(false, out Token? identifier) && (identifier.Type == TokenType.Identifier || identifier.Type == TokenType.LeftParenthesis || identifier.Type == TokenType.Number)) { arguments.AddRange(ParseArguments(stream)); } @@ -299,12 +299,37 @@ private static ArgumentNode ParseElement(TokenStream stream) private static ExpressionNode ParseExpression(TokenStream stream) { - if(stream.Expect(TokenType.Number, out Token? value)) // - { // Temporary Expression Solution - return new ValueNode(value); // - } // + stream.Peek(true, out Token? token); - throw new NotImplementedException(); + // Parses Element depending on the type of token. + switch (token?.Type) { + case TokenType.Identifier: + if (stream.Expect(TokenType.Identifier, out Token? identifierToken)) + { + return new IdentifierNode(identifierToken); + } + + break; + // Minus, plus and number are all parsed as valuenodes + case TokenType.Minus: + case TokenType.Plus: + case TokenType.Number: + if(stream.Expect(TokenType.Number, out Token? valueToken)) + { + return new ValueNode(valueToken); + } + + throw new NullReferenceException(); + // Left- and rightbrackets indicate start of a range. + case TokenType.LeftBracket: + case TokenType.RightBracket: + return ParseRange(stream); + // Leftparenthesis indicates start of expression wrapped in parentheses. + case TokenType.LeftParenthesis: + return ParseExpression(stream); + } + + throw new Exception("Argument not identified"); } private static ExpressionNode ParseExpression(IEnumerable tokens) @@ -509,7 +534,7 @@ private static CellNode ParseCell(TokenStream stream) // Parses LeftParenthesis. if(stream.Expect(TokenType.LeftParenthesis, out Token? startToken)) { - if (stream.Peek(true, out Token? identifier) && identifier.Type != TokenType.Number) + if (stream.Peek(true, out Token? identifier) && identifier.Type != TokenType.Number && identifier.Type != TokenType.Identifier) { throw new Exception("Expected identifier"); } @@ -523,7 +548,7 @@ private static CellNode ParseCell(TokenStream stream) throw new Exception(", expected"); } - if (stream.Peek(true, out identifier) && identifier.Type != TokenType.Number) + if (stream.Peek(true, out identifier) && identifier.Type != TokenType.Number && identifier.Type != TokenType.Identifier) { throw new Exception("Expected identifier"); } diff --git a/SudoScript.Core/SymbolTable.cs b/SudoScript.Core/SymbolTable.cs index 8fd5233..c9220fc 100644 --- a/SudoScript.Core/SymbolTable.cs +++ b/SudoScript.Core/SymbolTable.cs @@ -76,7 +76,7 @@ public SymbolTable() public SymbolTable(SymbolTable table) { _digitTable = table._digitTable.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); - _units = table._units.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + _units = table._units.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.ToList()); _cellTable = table._cellTable; _rules = table._rules; } @@ -165,6 +165,11 @@ public Cell[] GetCells() return _cellTable.Values.ToArray(); } + public int GetDigit(string identifier) + { + return _digitTable[identifier]; + } + public IEnumerable GetUnits(string name, params object[] args) { if (!_units.TryGetValue(name, out List? functions)) diff --git a/SudoScript/CliApplication.cs b/SudoScript/CliApplication.cs index 061216f..554e646 100644 --- a/SudoScript/CliApplication.cs +++ b/SudoScript/CliApplication.cs @@ -44,13 +44,21 @@ private void Load(string path) Console.SetCursorPosition(0, Console.WindowHeight - 1); Console.CursorVisible = false; Console.Write("[F1 Eliminate candidates] [F2 Solve board]"); - - using StreamReader reader = new StreamReader(path); - ProgramNode programNode = Parser.ParseProgram(reader); - _board = Generator.GetBoardFromAST(programNode); - _boardRenderer = new BoardRenderer(_board); - _cellInfoRenderer = new CellInfoRenderer(_board, Console.WindowWidth / 2); - SelectedCell = (_board.MinX, _board.MinY); + + try + { + using StreamReader reader = new StreamReader(path); + ProgramNode programNode = Parser.ParseProgram(reader); + _board = Generator.GetBoardFromAST(programNode); + _boardRenderer = new BoardRenderer(_board); + _cellInfoRenderer = new CellInfoRenderer(_board, Console.WindowWidth / 2); + SelectedCell = (_board.MinX, _board.MinY); + } + catch (Exception ex) + { + Console.Clear(); + Console.WriteLine(ex.ToString(), ex.StackTrace); + } } public CellReference SelectedCell