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

View File

@ -9,7 +9,7 @@ namespace Parser.Binding
ImmutableArray<Diagnostic> diagnostics, ImmutableArray<Diagnostic> diagnostics,
FunctionSymbol? mainFunction, FunctionSymbol? mainFunction,
FunctionSymbol? scriptFunction, FunctionSymbol? scriptFunction,
ImmutableDictionary<FunctionSymbol, BoundBlockStatement> functions) ImmutableDictionary<FunctionSymbol, LoweredFunction> functions)
{ {
Diagnostics = diagnostics; Diagnostics = diagnostics;
MainFunction = mainFunction; MainFunction = mainFunction;
@ -32,6 +32,6 @@ namespace Parser.Binding
/// <summary> /// <summary>
/// So-called "local" functions. /// So-called "local" functions.
/// </summary> /// </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> InputDescription { get; }
public ImmutableArray<ParameterSymbol> OutputDescription { get; } public ImmutableArray<ParameterSymbol> OutputDescription { get; }
public BoundStatement Body { 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 public class BoundGotoStatement : BoundStatement

View File

@ -1,23 +1,13 @@
using System.Collections.Immutable; namespace Parser.Binding
namespace Parser.Binding
{ {
public class FunctionSymbol public class FunctionSymbol
{ {
public FunctionSymbol( public FunctionSymbol(
string name, string name)
ImmutableArray<ParameterSymbol> parameters,
FunctionDeclarationSyntaxNode? declaration)
{ {
Name = name; Name = name;
Parameters = parameters;
Declaration = declaration;
} }
public string Name { get; } 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Linq;
namespace Parser namespace Parser
{ {
@ -15,6 +16,7 @@ namespace Parser
private readonly DiagnosticsBag _diagnostics = new DiagnosticsBag(); private readonly DiagnosticsBag _diagnostics = new DiagnosticsBag();
private bool _inRepl = false; private bool _inRepl = false;
private readonly Stack<EvaluationScope> _scopeStack = new Stack<EvaluationScope>(); 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) public Evaluator(BoundProgram program, CompilationContext context, bool inRepl)
{ {
@ -23,28 +25,34 @@ namespace Parser
var outerScope = new EvaluationScope(); var outerScope = new EvaluationScope();
_scopeStack.Push(outerScope); _scopeStack.Push(outerScope);
_inRepl = inRepl; _inRepl = inRepl;
foreach (var pair in program.Functions)
{
_functions[pair.Key] = pair.Value;
}
} }
internal EvaluationResult Evaluate() 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( _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); mainFunction.Name);
return new EvaluationResult(null, _diagnostics.ToImmutableArray()); return new EvaluationResult(null, _diagnostics.ToImmutableArray());
} }
else else
{ {
var result = EvaluateBlockStatement(_program.Functions[mainFunction]); var result = EvaluateBlockStatement(mainFunction.Body);
return new EvaluationResult(result, _diagnostics.ToImmutableArray()); 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()); return new EvaluationResult(result, _diagnostics.ToImmutableArray());
} }
else else
@ -304,10 +312,58 @@ namespace Parser
} }
else 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) private MObject? EvaluateDisp(List<MObject> arguments)
{ {
if (arguments.Count != 1) if (arguments.Count != 1)
@ -319,11 +375,11 @@ namespace Parser
return arguments[0]; return arguments[0];
} }
private FunctionSymbol GetFunctionSymbol(BoundExpression functionName) private UnresolvedFunctionSymbol GetFunctionSymbol(BoundExpression functionName)
{ {
if (functionName.Kind == BoundNodeKind.IdentifierNameExpression) 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}'."); throw new NotImplementedException($"Unknown function symbol '{functionName.Syntax.Text}'.");

View File

@ -92,5 +92,15 @@ namespace Parser.Internal
{ {
Report(span, $"Variable '{variableName}' not found."); 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 namespace Parser
{ {
internal class FunctionSymbol internal class UnresolvedFunctionSymbol
{ {
public string Name { get; } public string Name { get; }
public FunctionSymbol(string name) public UnresolvedFunctionSymbol(string name)
{ {
Name = name; Name = name;
} }

View File

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