Implement function calls
This commit is contained in:
parent
5e9111a085
commit
f6de2931ea
@ -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);
|
||||||
|
@ -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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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;
|
||||||
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}'.");
|
||||||
|
@ -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}'.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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;
|
||||||
}
|
}
|
@ -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')
|
||||||
end
|
disp(x);
|
||||||
|
end
|
||||||
disp(y);
|
|
Loading…
x
Reference in New Issue
Block a user