Skip to content

Commit

Permalink
Improving type resolution
Browse files Browse the repository at this point in the history
  • Loading branch information
cdiggins committed Dec 1, 2024
1 parent 49ca163 commit 8bad027
Show file tree
Hide file tree
Showing 19 changed files with 568 additions and 223 deletions.
113 changes: 49 additions & 64 deletions Plato.CSharpWriter/Analzyer.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Ara3D.Utils;
Expand Down Expand Up @@ -53,13 +52,43 @@ public StringBuilder WriteReifiedTypes(StringBuilder sb)
return sb;
}

public static List<FunctionDef> ViableFunctions(FunctionCall fc, FunctionGroupRefSymbol fgr)
public StringBuilder AnalyzeBody(FunctionAnalysis fa, FunctionCall fc)
{
// Get all the functions that have the same number of arguments as the function call, and that aren't abstract.
return fgr.Def.Functions.Where(f1 => !f1.OwnerType.IsConcept() && f1.Parameters.Count == fc.Args.Count).ToList();
var sb = new StringBuilder();
var r = new FunctionCallResolver(Compilation, fa, fc);
var f = fc.Function;
if (f is FunctionGroupRefSymbol fgr)
{
var fcr = new FunctionCallResolver(Compilation, fa, fc);
var funcs = fcr.ViableFunctions;
var isAmb = funcs.Count > 1;
var isAmbStr = isAmb ? "AMBIGUOUS" : "";
sb.AppendLine($" # functions: {funcs.Count} {isAmbStr}");
foreach (var f1 in funcs)
{
sb.AppendLine($" - {f1.FunctionDef.GetSignature()} => {f1.FunctionDef.Body}");
}
}
else if (f is TypeRefSymbol trs)
{
sb.AppendLine($" _NEW_ {trs}({fc.Args.JoinStringsWithComma()});");
}
else
{
sb.AppendLine($" NOT A FUNCTION GROUP");
}

for (var j = 0; j < fc.Args.Count; ++j)
{
var arg = fc.Args[j];
var argType = r.ArgTypes[j];
sb.AppendLine($" _ARG_ {j}: {arg} => {argType}");
}

return sb;
}
public StringBuilder OutputFunctionAnalysis(StringBuilder sb2)

public StringBuilder OutputFunctionAnalysis(StringBuilder sb)
{
var i = 0;

Expand All @@ -71,75 +100,31 @@ public StringBuilder OutputFunctionAnalysis(StringBuilder sb2)
.GroupBy(fa => fa.OwnerType)
.ToList();

sb2.AppendLine($"Found {functionGroups.Count} function groups");
sb.AppendLine($"Found {functionGroups.Count} function groups");

foreach (var g in functionGroups)
{
sb2.AppendLine();
sb2.AppendLine($"Group {g.Key}");
sb2.AppendLine();
sb.AppendLine();
sb.AppendLine($"Group {g.Key}");
sb.AppendLine();

foreach (var fa in g)
{
var sb = new StringBuilder();

var isAmb = false;

var typeSig = $"({string.Join(", ", fa.ParameterTypes)}) => {fa.DeclaredReturnType}";
var inliner = new FunctionInliner(fa);
if (inliner.InlinedBody == null)
continue;
sb.AppendLine($"{i++}. {fa.Def.Name}");
sb.AppendLine($" Type: {typeSig}");
sb.AppendLine($" Sig: {fa.Signature}");
sb.AppendLine($" Body: {fa.Def.Body}");

var expr = fa.Def.Body;

if (expr is FunctionCall fc)
{
var r = new FunctionCallResolver(Compilation, fa, fc);
var f = fc.Function;
if (f is FunctionGroupRefSymbol fgr)
{
var fcr = new FunctionCallResolver(Compilation, fa, fc);
var funcs = ViableFunctions(fc, fgr);
isAmb = funcs.Count > 1;
var isAmbStr = isAmb ? "AMBIGUOUS" : "";
sb.AppendLine($" # functions: {funcs.Count} {isAmbStr}");
foreach (var f1 in funcs)
{
sb.AppendLine($" - {f1.GetSignature()} => {f1.Body}");
}
}
else if (f is TypeRefSymbol trs)
{
sb.AppendLine($" _NEW_ {trs}({fc.Args.JoinStringsWithComma()});");
}
else
{
sb.AppendLine($" NOT A FUNCTION GROUP");
}

for (var j = 0; j < fc.Args.Count; ++j)
{
var arg = fc.Args[j];
var argType = r.ArgTypes[j];
sb.AppendLine($" _ARG_ {j}: {arg} => {argType}");
}
}
else
{
sb.AppendLine($" NOT FUNCTION CALL");
}

// Only do this for the ambiguous functions
if (isAmb)
sb2.AppendLine(sb.ToString());

//var typeSig = $"({string.Join(", ", fa.ParameterTypes)}) => {fa.DeclaredReturnType}";
//sb.AppendLine($" Type : {typeSig}");
sb.AppendLine($" Signature : {fa.Signature}");
sb.AppendLine($" Body : {fa.Def.Body}");
sb.AppendLine($" Inline : {inliner.InlinedBody}");
//sb.AppendLine($" Has {fa.TypeParameterToTypeLookup.Count} Type parameters ");
//sb.AppendLine($" Has type parameter in return: {fa.DeclaredReturnType.HasTypeVariable()}");
}
}

return sb2;
return sb;
}

public void OutputFunctionCallAnalysis(StringBuilder sb, FunctionCallAnalysis fca)
Expand Down
149 changes: 149 additions & 0 deletions Plato.CSharpWriter/FunctionInliner.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Plato.Compiler;
using Plato.Compiler.Symbols;
using Plato.Compiler.Types;

namespace Plato.CSharpWriter
{
public class FunctionInliner
{
public Compilation Compilation => Context.Compilation;
public readonly FunctionAnalysis Context;
public readonly List<string> FailedInlines = new List<string>();
public readonly TypedExpression InlinedBody;
public readonly IType BooleanType;

public FunctionInliner(FunctionAnalysis fa)
{
BooleanType = fa.ToSimpleType("Boolean");
Context = fa;
var body = fa.Def.Body as Expression;
InlinedBody = body == null
? null
: InlineExpression(
body,
new ParameterReplacements(null, null, null),
fa.DeclaredReturnType);
}

public TypedExpression InlineExpression(Expression expr, ParameterReplacements replacements, IType usage = null)
{
var type = Compilation.GetExpressionType(expr);
var r = new TypedExpression(expr, type, usage);

switch (expr)
{
case ArrayLiteral arrayLiteral:
return new TypedExpression(
new ArrayLiteral(
arrayLiteral.Expressions.Select(x => InlineExpression(x, replacements)).ToArray()),
type, usage);

case Assignment assignment:
return r;

case ConditionalExpression conditionalExpression:
return new TypedExpression(
new ConditionalExpression(
InlineExpression(conditionalExpression.Condition, replacements, BooleanType),
InlineExpression(conditionalExpression.IfTrue, replacements, null),
// TODO: the second branch might need to be cast to the first (or vice versa)
InlineExpression(conditionalExpression.IfFalse, replacements, null)),
type,
usage);

case FunctionCall functionCall:
return InlineCall(functionCall, replacements, usage);

case FunctionGroupRefSymbol functionGroupRefSymbol:
return r;

case Lambda lambda:
return r;

case Literal literal:
return r;

case ParameterRefSymbol parameterRefSymbol:
// TODO: if a parameter is used in multiple places, we want to replace this with a variable.
return replacements.Replace(parameterRefSymbol.Def) ?? r;

case VariableRefSymbol variableRefSymbol:
// TODO: in some cases, it might make more sense to inline the variable definition as well.
// It depends on how often it is used.
return r;

case Parenthesized parenthesized:
return r;

case TypeRefSymbol typeRef:
return r;

default:
throw new ArgumentOutOfRangeException(nameof(expr));
}
}

public TypedExpression InlineCall(FunctionCall fc, ParameterReplacements replacements, IType usage)
{
var determined = Compilation.GetExpressionType(fc);
var args = fc.Args.Select(a => InlineExpression(a, replacements)).ToList();
var r = new TypedExpression(
new FunctionCall(fc.Function, fc.HasArgList, args.Cast<Expression>().ToArray()), determined, usage);

if (fc.Function is TypeRefSymbol trs)
{
if (!trs.Name.StartsWith("Tuple"))
throw new Exception($"Expected type refs used as function calls to be ony Tuples not {trs.Name}");

// TODO: resolve tuples into actual constructors of the type.
}
else if (fc.Function is FunctionGroupRefSymbol fg)
{
var fcr = new FunctionCallResolver(Compilation, Context, fc);
var funcs = fcr.ViableFunctions;
if (funcs.Count > 1)
{
FailedInlines.Add($"Too many functions for {fc}");
return r.With(fcr);
}

if (funcs.Count == 0)
{
FailedInlines.Add($"No valid function found for {fc}");
return r.With(fcr);
}

// TODO: inject and expand type casts where appropriate.

var f = funcs[0];
var fd = f.FunctionDef;
var fa = Compilation.GetProcessedFunctionAnalysis(fd);

var args2 = new List<TypedExpression>();
for (var i = 0; i < fc.Args.Count; ++i)
{
var parameterType = fa.ParameterTypes[i];
var arg = InlineExpression(fc.Args[i], replacements, parameterType);
args2.Add(arg);
}

// TODO: there is going to be some additional type information that we should leverage as well.

if (fd.Body is Expression body)
return InlineExpression(body, replacements.AddRange(fd.Parameters, args2)).With(fcr);

FailedInlines.Add($"Can't inline function with non-expression body");
return r.With(fcr);
}
else
{
// TODO: what if we find it in the parameter replacements?
}

return r;
}
}
}
37 changes: 37 additions & 0 deletions Plato.CSharpWriter/ParameterReplacements.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System.Collections.Generic;
using System.Linq;
using Plato.Compiler.Symbols;

namespace Plato.CSharpWriter
{
public class ParameterReplacements
{
public readonly ParameterReplacements Previous;
public readonly ParameterDef Parameter;
public readonly TypedExpression Expression;

public ParameterReplacements(ParameterDef pd, TypedExpression expr, ParameterReplacements previous)
{
Previous = previous;
Parameter = pd;
Expression = expr;
}

public ParameterReplacements Add(ParameterDef pd, TypedExpression expr)
=> new ParameterReplacements(pd, expr, this);

public ParameterReplacements AddRange(IEnumerable<ParameterDef> pds, IEnumerable<TypedExpression> exprs)
=> pds.Zip(exprs, (x, y) => (pd: x, expr: y))
.Aggregate(this,
(current, pair) => current.Add(pair.pd, pair.expr));

public TypedExpression Replace(ParameterDef pd)
{
if (pd.Equals(Parameter))
return Expression;
if (Previous != null)
return Previous.Replace(pd);
return null;
}
}
}
5 changes: 1 addition & 4 deletions Plato.CSharpWriter/SymbolWriterCSharp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1100,10 +1100,7 @@ public SymbolWriterCSharp Write(Expression expr, string type = null)

case RefSymbol refSymbol:
return Write(refSymbol.Name);

case Argument argument:
return Write(argument.Expression);


case Assignment assignment:
return Write(assignment.LValue)
.Write(" = ")
Expand Down
44 changes: 44 additions & 0 deletions Plato.CSharpWriter/TypedExpression.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using Plato.Compiler.Symbols;
using Plato.Compiler.Types;

namespace Plato.CSharpWriter
{
public class TypedExpression : Expression
{
public IType DeterminedType;
public IType UsageType;
public Expression Expression;
public FunctionCallResolver Resolver;

public TypedExpression(Expression expr, IType determined, IType usage)
{
DeterminedType = determined;
UsageType = usage;
Expression = expr;
}

public override IEnumerable<Symbol> GetChildSymbols()
{
yield return Expression;
}

public override string Name => $"TypedExpression:{Expression.Name}";

public override string ToString()
{
var fid = Resolver?.ViableFunctions.Count;
return $"{Expression}#{fid}";// :{DeterminedType}:{UsageType}";
}

public override Symbol Rewrite(Func<Symbol, Symbol> f)
=> throw new NotImplementedException();

public TypedExpression With(FunctionCallResolver fcr)
{
Resolver = fcr;
return this;
}
}
}
Loading

0 comments on commit 8bad027

Please sign in to comment.