Implement function calls
This commit is contained in:
parent
5e9111a085
commit
f6de2931ea
@ -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);
|
||||
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
26
Parser/Binding/LoweredFunction.cs
Normal file
26
Parser/Binding/LoweredFunction.cs
Normal 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; }
|
||||
}
|
||||
}
|
@ -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,10 +312,58 @@ 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)
|
||||
{
|
||||
if (arguments.Count != 1)
|
||||
@ -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}'.");
|
||||
|
@ -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}'.");
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
end
|
||||
|
||||
disp(y);
|
||||
function f(x)
|
||||
disp('X was');
|
||||
disp(x);
|
||||
x = x + 1;
|
||||
disp('X is')
|
||||
disp(x);
|
||||
end
|
Loading…
x
Reference in New Issue
Block a user