Skip to content

Commit

Permalink
refactor ErgoFacade to create consistent environments
Browse files Browse the repository at this point in the history
  • Loading branch information
gkevin-kappas committed Aug 17, 2024
1 parent 85b4cb4 commit 19d425b
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 97 deletions.
96 changes: 67 additions & 29 deletions Ergo/Facade/ErgoFacade.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,30 @@ public readonly struct ErgoFacade
.AddLibrariesByReflection(typeof(Library).Assembly)
.AddCommandsByReflection(typeof(Save).Assembly)
.AddParsersByReflection(typeof(DictParser).Assembly)
.AfterKnowledgeBaseCompile(kb =>
{
kb.Trim();
})
;

private readonly ImmutableHashSet<Func<Library>> _libraries = ImmutableHashSet<Func<Library>>.Empty;
private readonly ImmutableHashSet<ShellCommand> _commands = ImmutableHashSet<ShellCommand>.Empty;
private readonly ImmutableDictionary<Type, IAbstractTermParser> _parsers = ImmutableDictionary<Type, IAbstractTermParser>.Empty;

public readonly Func<InterpreterScope, InterpreterScope> ConfigureStdlibScopeHandler = s => s;
public readonly Func<ErgoInterpreter, InterpreterScope, InterpreterScope> ConfigureInterpreterScopeHandler = (i, s) => s;
public readonly Action<KnowledgeBase> BeforeKbCompiledHandler = k => { };
public readonly Action<KnowledgeBase> AfterKbCompiledHandler;

public readonly bool TrimKnowledgeBase;

public readonly Maybe<TextReader> Input = default;
public readonly Maybe<TextWriter> Output = default;
public readonly Maybe<TextWriter> Error = default;
public readonly Maybe<IAsyncInputReader> InputReader = default;
public readonly InterpreterFlags InterpreterFlags = InterpreterFlags.Default;
public readonly CompilerFlags CompilerFlags = CompilerFlags.Default;
public readonly DecimalType DecimalType = DecimalType.CliDecimal;

public ErgoFacade() { }

Expand All @@ -47,31 +62,65 @@ private ErgoFacade(
Maybe<TextReader> inStream,
Maybe<TextWriter> outStream,
Maybe<TextWriter> errStream,
Maybe<IAsyncInputReader> inReader
Maybe<IAsyncInputReader> inReader,
Func<InterpreterScope, InterpreterScope> configureStdlibScope,
Func<ErgoInterpreter, InterpreterScope, InterpreterScope> configureInterpreterScope,
Action<KnowledgeBase> beforeKbCompiled,
Action<KnowledgeBase> afterKbCompiled,
InterpreterFlags interpreterFlags,
CompilerFlags compilerFlags,
DecimalType decimalType,
bool trimKnowledgeBase
)
{
_libraries = libs;
_commands = commands;
_parsers = parsers;
ConfigureStdlibScopeHandler = configureStdlibScope;
ConfigureInterpreterScopeHandler = configureInterpreterScope;
BeforeKbCompiledHandler = beforeKbCompiled;
AfterKbCompiledHandler = afterKbCompiled;
InterpreterFlags = interpreterFlags;
CompilerFlags = compilerFlags;
DecimalType = decimalType;
TrimKnowledgeBase = trimKnowledgeBase;
Input = inStream;
Output = outStream;
Error = errStream;
InputReader = inReader;
AfterKbCompiledHandler = kb =>
{
if (trimKnowledgeBase)
kb.Trim();
};
}

public ErgoFacade AddLibrary(Func<Library> lib)
=> new(_libraries.Add(lib), _commands, _parsers, Input, Output, Error, InputReader);
=> new(_libraries.Add(lib), _commands, _parsers, Input, Output, Error, InputReader, ConfigureStdlibScopeHandler, ConfigureInterpreterScopeHandler, BeforeKbCompiledHandler, AfterKbCompiledHandler, InterpreterFlags, CompilerFlags, DecimalType, TrimKnowledgeBase);
public ErgoFacade AddCommand(ShellCommand command)
=> new(_libraries, _commands.Add(command), _parsers, Input, Output, Error, InputReader);
=> new(_libraries, _commands.Add(command), _parsers, Input, Output, Error, InputReader, ConfigureStdlibScopeHandler, ConfigureInterpreterScopeHandler, BeforeKbCompiledHandler, AfterKbCompiledHandler, InterpreterFlags, CompilerFlags, DecimalType, TrimKnowledgeBase);
public ErgoFacade RemoveCommand(ShellCommand command)
=> new(_libraries, _commands.Remove(command), _parsers, Input, Output, Error, InputReader);
=> new(_libraries, _commands.Remove(command), _parsers, Input, Output, Error, InputReader, ConfigureStdlibScopeHandler, ConfigureInterpreterScopeHandler, BeforeKbCompiledHandler, AfterKbCompiledHandler, InterpreterFlags, CompilerFlags, DecimalType, TrimKnowledgeBase);
public ErgoFacade AddAbstractParser<A>(IAbstractTermParser<A> parser) where A : AbstractTerm
=> new(_libraries, _commands, _parsers.SetItem(typeof(A), parser), Input, Output, Error, InputReader);
=> new(_libraries, _commands, _parsers.SetItem(typeof(A), parser), Input, Output, Error, InputReader, ConfigureStdlibScopeHandler, ConfigureInterpreterScopeHandler, BeforeKbCompiledHandler, AfterKbCompiledHandler, InterpreterFlags, CompilerFlags, DecimalType, TrimKnowledgeBase);
public ErgoFacade RemoveAbstractParser<A>() where A : AbstractTerm
=> new(_libraries, _commands, _parsers.Remove(typeof(A)), Input, Output, Error, InputReader);
public ErgoFacade SetInput(TextReader input, Maybe<IAsyncInputReader> reader = default) => new(_libraries, _commands, _parsers, input, Output, Error, reader);
public ErgoFacade SetOutput(TextWriter output) => new(_libraries, _commands, _parsers, Input, output, Error, InputReader);
public ErgoFacade SetError(TextWriter err) => new(_libraries, _commands, _parsers, Input, Output, err, InputReader);
=> new(_libraries, _commands, _parsers.Remove(typeof(A)), Input, Output, Error, InputReader, ConfigureStdlibScopeHandler, ConfigureInterpreterScopeHandler, BeforeKbCompiledHandler, AfterKbCompiledHandler, InterpreterFlags, CompilerFlags, DecimalType, TrimKnowledgeBase);
public ErgoFacade SetInput(TextReader input, Maybe<IAsyncInputReader> reader = default) => new(_libraries, _commands, _parsers, input, Output, Error, reader, ConfigureStdlibScopeHandler, ConfigureInterpreterScopeHandler, BeforeKbCompiledHandler, AfterKbCompiledHandler, InterpreterFlags, CompilerFlags, DecimalType, TrimKnowledgeBase);
public ErgoFacade SetOutput(TextWriter output) => new(_libraries, _commands, _parsers, Input, output, Error, InputReader, ConfigureStdlibScopeHandler, ConfigureInterpreterScopeHandler, BeforeKbCompiledHandler, AfterKbCompiledHandler, InterpreterFlags, CompilerFlags, DecimalType, TrimKnowledgeBase);
public ErgoFacade SetError(TextWriter err) => new(_libraries, _commands, _parsers, Input, Output, err, InputReader, ConfigureStdlibScopeHandler, ConfigureInterpreterScopeHandler, BeforeKbCompiledHandler, AfterKbCompiledHandler, InterpreterFlags, CompilerFlags, DecimalType, TrimKnowledgeBase);
public ErgoFacade ConfigureStdlibScope(Func<InterpreterScope, InterpreterScope> f) => new(_libraries, _commands, _parsers, Input, Output, Error, InputReader, f, ConfigureInterpreterScopeHandler, BeforeKbCompiledHandler, AfterKbCompiledHandler, InterpreterFlags, CompilerFlags, DecimalType, TrimKnowledgeBase);
public ErgoFacade ConfigureInterpreterScope(Func<ErgoInterpreter, InterpreterScope, InterpreterScope> f) => new(_libraries, _commands, _parsers, Input, Output, Error, InputReader, ConfigureStdlibScopeHandler, f, BeforeKbCompiledHandler, AfterKbCompiledHandler, InterpreterFlags, CompilerFlags, DecimalType, TrimKnowledgeBase);
public ErgoFacade BeforeKnowledgeBaseCompile(Action<KnowledgeBase> a) => new(_libraries, _commands, _parsers, Input, Output, Error, InputReader, ConfigureStdlibScopeHandler, ConfigureInterpreterScopeHandler, a, AfterKbCompiledHandler, InterpreterFlags, CompilerFlags, DecimalType, TrimKnowledgeBase);
public ErgoFacade AfterKnowledgeBaseCompile(Action<KnowledgeBase> a) => new(_libraries, _commands, _parsers, Input, Output, Error, InputReader, ConfigureStdlibScopeHandler, ConfigureInterpreterScopeHandler, BeforeKbCompiledHandler, a, InterpreterFlags, CompilerFlags, DecimalType, TrimKnowledgeBase);
public ErgoFacade SetInterpreterFlags(InterpreterFlags flags)
=> new(_libraries, _commands, _parsers, Input, Output, Error, InputReader, ConfigureStdlibScopeHandler, ConfigureInterpreterScopeHandler, BeforeKbCompiledHandler, AfterKbCompiledHandler, flags, CompilerFlags, DecimalType, TrimKnowledgeBase);
public ErgoFacade SetCompilerFlags(CompilerFlags flags)
=> new(_libraries, _commands, _parsers, Input, Output, Error, InputReader, ConfigureStdlibScopeHandler, ConfigureInterpreterScopeHandler, BeforeKbCompiledHandler, AfterKbCompiledHandler, InterpreterFlags, flags, DecimalType, TrimKnowledgeBase);
public ErgoFacade SetDecimalType(DecimalType type)
=> new(_libraries, _commands, _parsers, Input, Output, Error, InputReader, ConfigureStdlibScopeHandler, ConfigureInterpreterScopeHandler, BeforeKbCompiledHandler, AfterKbCompiledHandler, InterpreterFlags, CompilerFlags, type, TrimKnowledgeBase);
public ErgoFacade SetTrimKnowledgeBase(bool trimKnowledgeBase)
=> new(_libraries, _commands, _parsers, Input, Output, Error, InputReader, ConfigureStdlibScopeHandler, ConfigureInterpreterScopeHandler, BeforeKbCompiledHandler, AfterKbCompiledHandler, InterpreterFlags, CompilerFlags, DecimalType, trimKnowledgeBase);

/// <summary>
/// Adds all libraries with a public parameterless constructor in the target assembly.
/// </summary>
Expand Down Expand Up @@ -158,26 +207,15 @@ private ErgoParser ConfigureParser(ErgoParser parser)
}

public ErgoParser BuildParser(ErgoStream stream, IEnumerable<Operator> operators = null)
=> ConfigureParser(new(this, new(this, stream, operators ?? Enumerable.Empty<Operator>())));
public ErgoInterpreter BuildInterpreter(InterpreterFlags flags = InterpreterFlags.Default)
=> ConfigureInterpreter(new(this, flags));
public ErgoVM BuildVM(KnowledgeBase kb, DecimalType decimalType = DecimalType.BigDecimal)
=> ConfigureVM(new(kb, decimalType));
public ErgoVM BuildVM(
InterpreterFlags interpreterFlags = InterpreterFlags.Default,
CompilerFlags vmFlags = CompilerFlags.Default,
DecimalType decimalType = DecimalType.BigDecimal,
Func<InterpreterScope, InterpreterScope> configureStdlibScope = null,
Func<ErgoInterpreter, InterpreterScope, InterpreterScope> configureScope = null,
Action<KnowledgeBase> beforeKbCompile = null,
Action<KnowledgeBase> afterKbCompile = null,
bool trimKb = false
) {
var interpreter = BuildInterpreter(interpreterFlags);
var scope = interpreter.CreateScope(configureStdlibScope);
if (configureScope != null)
scope = configureScope(interpreter, scope);
var kb = scope.BuildKnowledgeBase(vmFlags, beforeKbCompile, afterKbCompile, trimKb);
=> ConfigureParser(new(this, new(this, stream, operators ?? [])));
public ErgoInterpreter BuildInterpreter()
=> ConfigureInterpreter(new(this));
public ErgoVM BuildVM(KnowledgeBase kb)
=> ConfigureVM(new(kb));
public ErgoVM BuildVM() {
var interpreter = BuildInterpreter();
var scope = interpreter.CreateScope();
var kb = scope.BuildKnowledgeBase();
return BuildVM(kb);
}
public ErgoShell BuildShell(Func<LogLine, string> formatter = null, Encoding encoding = null)
Expand Down
14 changes: 6 additions & 8 deletions Ergo/Interpreter/ErgoInterpreter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ namespace Ergo.Interpreter;
public partial class ErgoInterpreter
{
public readonly ErgoFacade Facade;
public readonly InterpreterFlags Flags;
private readonly Dictionary<Atom, InterpreterScope> ModuleCache = new();
public InterpreterFlags Flags => Facade.InterpreterFlags;
private readonly Dictionary<Atom, InterpreterScope> ModuleCache = [];

protected readonly DiagnosticProbe Probe = new()
{
Expand All @@ -24,9 +24,8 @@ public partial class ErgoInterpreter
private readonly Dictionary<Atom, Library> _libraries = new();
public Maybe<Library> GetLibrary(Atom module) => _libraries.TryGetValue(module, out var lib) ? Maybe.Some(lib) : default;

internal ErgoInterpreter(ErgoFacade facade, InterpreterFlags flags = InterpreterFlags.Default)
internal ErgoInterpreter(ErgoFacade facade)
{
Flags = flags;
Facade = facade;

}
Expand Down Expand Up @@ -240,11 +239,10 @@ public virtual Maybe<Module> Load(ref InterpreterScope scope, Atom moduleName, E
return module;
}

public InterpreterScope CreateScope(Func<InterpreterScope, InterpreterScope> configureStdlibScope = null)
public InterpreterScope CreateScope()
{
configureStdlibScope ??= s => s;
var stdlibScope = new InterpreterScope(Facade, new Module(WellKnown.Modules.Stdlib, runtime: true));
stdlibScope = configureStdlibScope(stdlibScope);
stdlibScope = Facade.ConfigureStdlibScopeHandler(stdlibScope);
Load(ref stdlibScope, WellKnown.Modules.Stdlib);
var scope = stdlibScope
.WithRuntime(false)
Expand All @@ -254,6 +252,6 @@ public InterpreterScope CreateScope(Func<InterpreterScope, InterpreterScope> con
#if ERGO_INTERPRETER_DIAGNOSTICS
Console.WriteLine(Probe.GetDiagnostics());
#endif
return scope;
return Facade.ConfigureInterpreterScopeHandler(this, scope);
}
}
15 changes: 4 additions & 11 deletions Ergo/Interpreter/InterpreterScope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,7 @@ private InterpreterScope(
VisibleOperators = GetOperators().ToImmutableArray();
}

public KnowledgeBase BuildKnowledgeBase(
CompilerFlags vmFlags = CompilerFlags.Default,
Action<KnowledgeBase> beforeCompile = null,
Action<KnowledgeBase> afterCompile = null,
bool trim = false
) {
public KnowledgeBase BuildKnowledgeBase() {
var kb = new KnowledgeBase(this);
foreach (var builtIn in VisibleBuiltIns.Values)
{
Expand All @@ -120,11 +115,9 @@ public KnowledgeBase BuildKnowledgeBase(
kb.AssertZ(newPred);
}
}
beforeCompile?.Invoke(kb);
ForwardEventToLibraries(new KnowledgeBaseCreatedEvent(kb, vmFlags));
afterCompile?.Invoke(kb);
if (trim)
kb.Trim();
Facade.BeforeKbCompiledHandler(kb);
ForwardEventToLibraries(new KnowledgeBaseCreatedEvent(kb, Facade.CompilerFlags));
Facade.AfterKbCompiledHandler(kb);
return kb;
}

Expand Down
6 changes: 3 additions & 3 deletions Ergo/Runtime/ErgoVM.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,11 @@ public partial class ErgoVM
/// Register for the current continuation.
/// </summary>
internal Op @continue;
public ErgoVM(KnowledgeBase kb, DecimalType decimalType = DecimalType.CliDecimal)
public ErgoVM(KnowledgeBase kb)
{
args = new ITerm[MAX_ARGUMENTS];
KB = kb;
DecimalType = decimalType;
DecimalType = kb.Scope.Facade.DecimalType;
In = Console.In;
Out = Console.Out;
Err = Console.Error;
Expand Down Expand Up @@ -143,7 +143,7 @@ public Op Query
/// <summary>
/// Creates a new ErgoVM instance that shares the same knowledge base as the current one.
/// </summary>
public ErgoVM ScopedInstance() => new(KB, DecimalType)
public ErgoVM ScopedInstance() => new(KB)
{ In = In, Err = Err, Out = Out, /*args = [.. args], Arity = Arity*/ };
/// <summary>
/// Executes <see cref="Query"/> and backtracks until all solutions are computed. See also <see cref="Solutions"/> and <see cref="RunInteractive"/>.
Expand Down
7 changes: 3 additions & 4 deletions Ergo/Shell/Shell.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,8 @@ public ShellScope CreateScope(Maybe<InterpreterScope> interpreterScope, Func<She
.GetOrThrow(new InvalidOperationException())
.WithExceptionHandler(LoggingExceptionHandler)
.WithRuntime(true);
var vmFlags = CompilerFlags.Default;
var kb = scope.BuildKnowledgeBase(vmFlags);
return transformShell(new(scope, false, kb, vmFlags));
var kb = scope.BuildKnowledgeBase();
return transformShell(new(scope, false, kb, Facade.CompilerFlags));
}

internal ErgoShell(
Expand Down Expand Up @@ -127,7 +126,7 @@ public virtual void Load(ref ShellScope scope, string fileName)
var loaded = Interpreter.Load(ref interpreterScope, new Atom(fileName));
loaded.Do(some =>
{
var newKb = interpreterScope.BuildKnowledgeBase(copy.CompilerFlags);
var newKb = interpreterScope.BuildKnowledgeBase();
copy = copy
.WithInterpreterScope(interpreterScope)
.WithKnowledgeBase(newKb)
Expand Down
73 changes: 31 additions & 42 deletions XUnitTests/_Shared/SolverTestFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ namespace Tests;
public class CompilerTestFixture : ErgoTestFixture
{
protected override string TestsModuleName => "inlining";
protected override CompilerFlags CompilerFlags => base.CompilerFlags
| CompilerFlags.EnableInlining;
protected override bool TrimKnowledgeBase => false;

protected override ErgoFacade Facade => base.Facade
.SetTrimKnowledgeBase(false)
.SetCompilerFlags(base.Facade.CompilerFlags | CompilerFlags.EnableInlining);
}


public class ErgoTestFixture : IDisposable
{
public readonly ExceptionHandler NullExceptionHandler = default;
Expand All @@ -26,51 +28,38 @@ public class ErgoTestFixture : IDisposable
public readonly ErgoVM VM;

protected virtual string TestsModuleName => "tests";
protected virtual InterpreterFlags InterpreterFlags => InterpreterFlags.Default;
protected virtual CompilerFlags CompilerFlags => CompilerFlags.Default;
protected virtual DecimalType DecimalType => DecimalType.BigDecimal;
protected virtual bool TrimKnowledgeBase => true;

protected virtual ErgoFacade Facade { get; private set; }

public ErgoTestFixture()
{
var basePath = Directory.GetCurrentDirectory();
var testsPath = Path.Combine(basePath, @"..\..\..\ergo");

Interpreter = CreateInterpreter();
InterpreterScope = CreateScope(testsPath);
LoadTestsModule(ref InterpreterScope);
KnowledgeBase = InterpreterScope.BuildKnowledgeBase(CompilerFlags);
if(TrimKnowledgeBase)
KnowledgeBase.Trim();
VM = CreateVM();
}

protected virtual ErgoVM CreateVM()
{
return Interpreter.Facade.BuildVM(KnowledgeBase, DecimalType.BigDecimal);
}

protected virtual ErgoInterpreter CreateInterpreter()
{
return ErgoFacade.Standard
.BuildInterpreter(InterpreterFlags);
}

protected virtual InterpreterScope CreateScope(string testsPath)
{
return Interpreter.CreateScope(x => x
.WithExceptionHandler(ThrowingExceptionHandler)
.WithoutSearchDirectories()
.WithSearchDirectory(testsPath))
.WithRuntime(true);
}

protected virtual void LoadTestsModule(ref InterpreterScope scope)
{
var module = Interpreter
.Load(ref scope, new(TestsModuleName))
.GetOrThrow(new InvalidOperationException());
scope = scope.WithModule(scope.EntryModule.WithImport(module.Name));
Facade = ErgoFacade.Standard
.SetDecimalType(DecimalType.BigDecimal)
.ConfigureStdlibScope(scope =>
{
scope = scope
.WithExceptionHandler(ThrowingExceptionHandler)
.WithoutSearchDirectories()
.WithSearchDirectory(testsPath);
return ErgoFacade.Standard.ConfigureStdlibScopeHandler(scope);
})
.ConfigureInterpreterScope((interpreter, scope) =>
{
var module = interpreter
.Load(ref scope, new(TestsModuleName))
.GetOrThrow(new InvalidOperationException());
scope = scope.WithModule(scope.EntryModule.WithImport(module.Name));
scope = scope.WithRuntime(true);
return ErgoFacade.Standard.ConfigureInterpreterScopeHandler(interpreter, scope);
})
;
Interpreter = Facade.BuildInterpreter();
InterpreterScope = Interpreter.CreateScope();
KnowledgeBase = InterpreterScope.BuildKnowledgeBase();
VM = Facade.BuildVM();
}

~ErgoTestFixture()
Expand Down

0 comments on commit 19d425b

Please sign in to comment.