Implement function calls

This commit is contained in:
Alexander Luzgarev 2020-07-16 12:29:41 +02:00
parent 5e9111a085
commit f6de2931ea
9 changed files with 155 additions and 50 deletions

View File

@ -18,23 +18,24 @@ namespace Parser.Binding
var binder = new Binder();
var boundRoot = binder.BindRoot(syntaxTree.NullRoot);
var statements = ((BoundBlockStatement)boundRoot.File.Body).Statements;
var functionsBuilder = ImmutableDictionary.CreateBuilder<FunctionSymbol, BoundBlockStatement>();
var functionsBuilder = ImmutableDictionary.CreateBuilder<FunctionSymbol, LoweredFunction>();
var globalStatements = statements.Where(s => s.Kind != BoundNodeKind.FunctionDeclaration).ToArray();
var mainFunction = (FunctionSymbol?)null;
var scriptFunction = (FunctionSymbol?)null;
if (globalStatements.Length > 0)
{
// we have to gather all bound expression statements into a "script" function.
scriptFunction = new FunctionSymbol(
name: "%script",
parameters: ImmutableArray<ParameterSymbol>.Empty,
declaration: null);
scriptFunction = new FunctionSymbol("%script");
var body = Block(globalStatements[0].Syntax, globalStatements);
var loweredBody = Lowerer.Lower(body);
functionsBuilder.Add(scriptFunction, loweredBody);
}
else
{
var declaration = new BoundFunctionDeclaration(
syntax: globalStatements[0].Syntax,
name: "%script",
inputDescription: ImmutableArray<ParameterSymbol>.Empty,
outputDescription: ImmutableArray<ParameterSymbol>.Empty,
body: body);
var loweredFunction = LowerFunction(declaration);
functionsBuilder.Add(scriptFunction, loweredFunction);
}
var functions = statements.OfType<BoundFunctionDeclaration>().ToArray();
@ -42,11 +43,9 @@ namespace Parser.Binding
foreach (var function in functions)
{
var functionSymbol = new FunctionSymbol(
name: function.Name,
parameters: function.InputDescription,
declaration: (FunctionDeclarationSyntaxNode)function.Syntax);
var loweredBody = Lowerer.Lower(function.Body);
functionsBuilder.Add(functionSymbol, loweredBody);
name: function.Name);
var loweredFunction = LowerFunction(function);
functionsBuilder.Add(functionSymbol, loweredFunction);
if (first && globalStatements.Length == 0)
{
// the first function in a file will become "main".
@ -62,6 +61,17 @@ namespace Parser.Binding
functionsBuilder.ToImmutable());
}
private static LoweredFunction LowerFunction(BoundFunctionDeclaration declaration)
{
var loweredBody = Lowerer.Lower(declaration.Body);
return new LoweredFunction(
declaration: declaration,
name: declaration.Name,
inputDescription: declaration.InputDescription,
outputDescription: declaration.OutputDescription,
body: loweredBody);
}
private BoundRoot BindRoot(RootSyntaxNode node)
{
var boundFile = BindFile(node.File);

View File

@ -9,7 +9,7 @@ namespace Parser.Binding
ImmutableArray<Diagnostic> diagnostics,
FunctionSymbol? mainFunction,
FunctionSymbol? scriptFunction,
ImmutableDictionary<FunctionSymbol, BoundBlockStatement> functions)
ImmutableDictionary<FunctionSymbol, LoweredFunction> functions)
{
Diagnostics = diagnostics;
MainFunction = mainFunction;
@ -32,6 +32,6 @@ namespace Parser.Binding
/// <summary>
/// So-called "local" functions.
/// </summary>
public ImmutableDictionary<FunctionSymbol, BoundBlockStatement> Functions { get; }
public ImmutableDictionary<FunctionSymbol, LoweredFunction> Functions { get; }
}
}

View File

@ -156,6 +156,21 @@ namespace Parser.Binding
public ImmutableArray<ParameterSymbol> InputDescription { get; }
public ImmutableArray<ParameterSymbol> OutputDescription { get; }
public BoundStatement Body { get; }
public BoundFunctionDeclaration WithBody(BoundStatement body)
{
if (body == Body)
{
return this;
}
return new BoundFunctionDeclaration(
Syntax,
Name,
InputDescription,
OutputDescription,
body);
}
}
public class BoundGotoStatement : BoundStatement

View File

@ -1,23 +1,13 @@
using System.Collections.Immutable;
namespace Parser.Binding
namespace Parser.Binding
{
public class FunctionSymbol
{
public FunctionSymbol(
string name,
ImmutableArray<ParameterSymbol> parameters,
FunctionDeclarationSyntaxNode? declaration)
string name)
{
Name = name;
Parameters = parameters;
Declaration = declaration;
}
public string Name { get; }
public ImmutableArray<ParameterSymbol> Parameters { get; }
public FunctionDeclarationSyntaxNode? Declaration { get; }
}
}

View File

@ -0,0 +1,26 @@
using System.Collections.Immutable;
namespace Parser.Binding
{
public class LoweredFunction {
public LoweredFunction(
BoundFunctionDeclaration declaration,
string name,
ImmutableArray<ParameterSymbol> inputDescription,
ImmutableArray<ParameterSymbol> outputDescription,
BoundBlockStatement body)
{
Declaration = declaration;
Name = name;
InputDescription = inputDescription;
OutputDescription = outputDescription;
Body = body;
}
public BoundFunctionDeclaration Declaration { get; }
public string Name { get; }
public ImmutableArray<ParameterSymbol> InputDescription { get; }
public ImmutableArray<ParameterSymbol> OutputDescription { get; }
public BoundBlockStatement Body { get; }
}
}

View File

@ -5,6 +5,7 @@ using Parser.Objects;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
namespace Parser
{
@ -15,6 +16,7 @@ namespace Parser
private readonly DiagnosticsBag _diagnostics = new DiagnosticsBag();
private bool _inRepl = false;
private readonly Stack<EvaluationScope> _scopeStack = new Stack<EvaluationScope>();
private readonly Dictionary<FunctionSymbol, LoweredFunction> _functions = new Dictionary<FunctionSymbol, LoweredFunction>();
public Evaluator(BoundProgram program, CompilationContext context, bool inRepl)
{
@ -23,28 +25,34 @@ namespace Parser
var outerScope = new EvaluationScope();
_scopeStack.Push(outerScope);
_inRepl = inRepl;
foreach (var pair in program.Functions)
{
_functions[pair.Key] = pair.Value;
}
}
internal EvaluationResult Evaluate()
{
if (_program.MainFunction is { } mainFunction)
if (_program.MainFunction is { } mainFunctionSymbol)
{
if (mainFunction.Parameters.Length > 0)
var mainFunction = _program.Functions[mainFunctionSymbol];
if (mainFunction.InputDescription.Length > 0)
{
_diagnostics.ReportNotEnoughInputs(
new TextSpan(mainFunction.Declaration.Position, mainFunction.Declaration.Position + mainFunction.Declaration.FullWidth),
new TextSpan(mainFunction.Body.Syntax.Position, mainFunction.Body.Syntax.Position + mainFunction.Body.Syntax.FullWidth),
mainFunction.Name);
return new EvaluationResult(null, _diagnostics.ToImmutableArray());
}
else
{
var result = EvaluateBlockStatement(_program.Functions[mainFunction]);
var result = EvaluateBlockStatement(mainFunction.Body);
return new EvaluationResult(result, _diagnostics.ToImmutableArray());
}
}
else if (_program.ScriptFunction is { } scriptFunction)
else if (_program.ScriptFunction is { } scriptFunctionSymbol)
{
var result = EvaluateBlockStatement(_program.Functions[scriptFunction]);
var scriptFunction = _program.Functions[scriptFunctionSymbol];
var result = EvaluateBlockStatement(scriptFunction.Body);
return new EvaluationResult(result, _diagnostics.ToImmutableArray());
}
else
@ -304,8 +312,56 @@ namespace Parser
}
else
{
throw new NotImplementedException("Functions are not supported.");
var resolvedFunction = ResolveFunction(function);
if (resolvedFunction is null)
{
_diagnostics.ReportFunctionNotFound(
new TextSpan(
node.Name.Syntax.Position,
node.Name.Syntax.Position + node.Name.Syntax.FullWidth),
function.Name);
return null;
}
else
{
// bring arguments into context
var newScope = new EvaluationScope();
var counter = 0;
foreach (var expectedArgument in resolvedFunction.InputDescription)
{
if (counter >= arguments.Count)
{
break;
}
newScope.Variables.Add(expectedArgument.Name, arguments[counter]);
counter++;
}
if (counter < arguments.Count)
{
_diagnostics.ReportTooManyInputs(
new TextSpan(
node.Arguments[counter].Syntax.Position,
node.Arguments[counter].Syntax.Position + node.Arguments[counter].Syntax.FullWidth),
function.Name);
return null;
}
_scopeStack.Push(newScope);
var result = EvaluateBlockStatement(resolvedFunction.Body);
_scopeStack.Pop();
return result;
}
}
}
private LoweredFunction? ResolveFunction(UnresolvedFunctionSymbol functionSymbol)
{
var maybeKey = _functions.Keys.FirstOrDefault(k => k.Name == functionSymbol.Name);
return maybeKey switch
{
{ } key => _functions[key],
_ => null,
};
}
private MObject? EvaluateDisp(List<MObject> arguments)
@ -319,11 +375,11 @@ namespace Parser
return arguments[0];
}
private FunctionSymbol GetFunctionSymbol(BoundExpression functionName)
private UnresolvedFunctionSymbol GetFunctionSymbol(BoundExpression functionName)
{
if (functionName.Kind == BoundNodeKind.IdentifierNameExpression)
{
return new FunctionSymbol(((BoundIdentifierNameExpression)functionName).Name);
return new UnresolvedFunctionSymbol(((BoundIdentifierNameExpression)functionName).Name);
}
throw new NotImplementedException($"Unknown function symbol '{functionName.Syntax.Text}'.");

View File

@ -92,5 +92,15 @@ namespace Parser.Internal
{
Report(span, $"Variable '{variableName}' not found.");
}
internal void ReportFunctionNotFound(TextSpan span, string functionName)
{
Report(span, $"Function '{functionName}' not found.");
}
internal void ReportTooManyInputs(TextSpan span, string functionName)
{
Report(span, $"Too many inputs in the call to '{functionName}'.");
}
}
}

View File

@ -1,10 +1,10 @@
namespace Parser
{
internal class FunctionSymbol
internal class UnresolvedFunctionSymbol
{
public string Name { get; }
public FunctionSymbol(string name)
public UnresolvedFunctionSymbol(string name)
{
Name = name;
}

View File

@ -1,13 +1,11 @@
x = 2;
y = 3;
disp(x + y * y);
disp('Hello world!');
f(x);
f(x);
x = 2 * 3;
if x > 5
y = 'Greater than 5';
elseif x > 0
y = 10;
function f(x)
disp('X was');
disp(x);
x = x + 1;
disp('X is')
disp(x);
end
disp(y);