using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using Immutable; using static Global; public class Evaluator { public static string EvaluateWrap(Ast.AstNode source) => Evaluate(source, new Dictionary { { "true", Ast.Val.Bool(true) }, { "false", Ast.Val.Bool(false) } }.ToImmutableDictionary()).Match( Int: i => i.ToString(), String: s => s, Bool: b => b ? "true" : "false"); public static Ast.Val Evaluate(Ast.AstNode source, ImmutableDictionary env) // => Log(source.Str(), () => source.Match( Operator: o => o.Item1.semantics.Match( // The wrapper around the whole program: Program: () => { if (o.Item2.Count() != 3) { throw new RuntimeErrorException("The Program wrapper should contain two parts: StartOfInput, prog and EndOfInput"); } // TODO: check that the last token is indeed Program return Evaluate(o.Item2.ElementAt(1), env); }, EnvLookup: () => o.Item2 .Single() .ElseThrow( new RuntimeErrorException("EnvLookup should contain a single lexeme")) .AsTerminal .ElseThrow( new RuntimeErrorException("EnvLookup's contents should be a lexeme")) .lexeme .Pipe(x => env[x]), And: () => { if (o.Item2.Count() != 3) { throw new RuntimeErrorException("The And operator should contain three parts"); } // TODO: check that the last token is indeed Program var a = Evaluate(o.Item2.ElementAt(0), env); var b = Evaluate(o.Item2.ElementAt(2), env); return Ast.Val.Bool( a.AsBool.ElseThrow(new RuntimeErrorException("type error: and requires two bools")) && b.AsBool.ElseThrow(new RuntimeErrorException("type error: and requires two bools")) ); }, LiteralInt: () => o.Item2 .Single() .ElseThrow( new RuntimeErrorException("LiteralInt should contain a single lexeme")) .AsTerminal .ElseThrow( new RuntimeErrorException("LiteralInt's contents should be a lexeme")) .lexeme .Pipe(x => Ast.Val.Int(Int32.Parse(x))), LiteralString: () => { if (o.Item2.Count() != 3) { throw new RuntimeErrorException("LiteralString should contain three lexemes: OpenString, String and CloseString"); } // TODO: check that the open & close are indeed that return o.Item2.ElementAt(1) .AsTerminal.ElseThrow( new RuntimeErrorException("LiteralInt's contents should be a lexeme")) .lexeme .Pipe(x => Ast.Val.String(x)); }, Unsupported: () => throw new RuntimeErrorException($"Unsupported opeartor {o}, sorry.")), Terminal: t => Ast.Val.String(t.lexeme)/*TODO*/); } // Note: for typeclass resolution, ask that functions have their parameters and return types annotated. This annotation is added to the values at run-time, which allows to dispatch based on the annotation rather than on the actual value.