Implement script function
This commit is contained in:
parent
7f0889f0d4
commit
2f04f86af3
@ -17,11 +17,49 @@ namespace Parser.Binding
|
||||
{
|
||||
var binder = new Binder();
|
||||
var boundRoot = binder.BindRoot(syntaxTree.NullRoot);
|
||||
var loweredStatement = Lowerer.Lower(boundRoot.File.Body);
|
||||
var newRoot = Root(
|
||||
boundRoot.Syntax,
|
||||
File(boundRoot.File.Syntax, loweredStatement));
|
||||
return new BoundProgram(newRoot, binder._diagnostics.ToImmutableArray());
|
||||
var statements = ((BoundBlockStatement)boundRoot.File.Body).Statements;
|
||||
var functionsBuilder = ImmutableDictionary.CreateBuilder<FunctionSymbol, BoundBlockStatement>();
|
||||
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);
|
||||
var body = Block(globalStatements[0].Syntax, globalStatements);
|
||||
var loweredBody = Lowerer.Lower(body);
|
||||
functionsBuilder.Add(scriptFunction, loweredBody);
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
|
||||
var functions = statements.OfType<BoundFunctionDeclaration>().ToArray();
|
||||
var first = true;
|
||||
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);
|
||||
if (first && globalStatements.Length == 0)
|
||||
{
|
||||
// the first function in a file will become "main".
|
||||
first = false;
|
||||
mainFunction = functionSymbol;
|
||||
}
|
||||
}
|
||||
|
||||
return new BoundProgram(
|
||||
binder._diagnostics.ToImmutableArray(),
|
||||
mainFunction,
|
||||
scriptFunction,
|
||||
functionsBuilder.ToImmutable());
|
||||
}
|
||||
|
||||
private BoundRoot BindRoot(RootSyntaxNode node)
|
||||
@ -148,7 +186,58 @@ namespace Parser.Binding
|
||||
|
||||
private BoundFunctionDeclaration BindFunctionDeclaration(FunctionDeclarationSyntaxNode node)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
var inputDescription = BindInputDescription(node.InputDescription);
|
||||
var outputDescription = BindOutputDescription(node.OutputDescription);
|
||||
var body = BindStatement(node.Body);
|
||||
return new BoundFunctionDeclaration(node, node.Name.Text, inputDescription, outputDescription, body);
|
||||
}
|
||||
|
||||
private ImmutableArray<ParameterSymbol> BindOutputDescription(FunctionOutputDescriptionSyntaxNode? node)
|
||||
{
|
||||
if (node is null)
|
||||
{
|
||||
return ImmutableArray<ParameterSymbol>.Empty;
|
||||
}
|
||||
var outputs = node.OutputList.Where(p => p.IsNode).Select(p => p.AsNode()!);
|
||||
var builder = ImmutableArray.CreateBuilder<ParameterSymbol>();
|
||||
foreach (var output in outputs)
|
||||
{
|
||||
if (output.Kind != TokenKind.IdentifierNameExpression)
|
||||
{
|
||||
throw new Exception($"Invalid function output kind {output.Kind}.");
|
||||
}
|
||||
|
||||
builder.Add(BindParameterSymbol((IdentifierNameExpressionSyntaxNode)output));
|
||||
}
|
||||
|
||||
return builder.ToImmutable();
|
||||
}
|
||||
|
||||
private ImmutableArray<ParameterSymbol> BindInputDescription(FunctionInputDescriptionSyntaxNode? node)
|
||||
{
|
||||
if (node is null)
|
||||
{
|
||||
return ImmutableArray<ParameterSymbol>.Empty;
|
||||
}
|
||||
|
||||
var parameters = node.ParameterList.Where(p => p.IsNode).Select(p => p.AsNode()!);
|
||||
var builder = ImmutableArray.CreateBuilder<ParameterSymbol>();
|
||||
foreach (var parameter in parameters)
|
||||
{
|
||||
if (parameter.Kind != TokenKind.IdentifierNameExpression)
|
||||
{
|
||||
throw new Exception($"Invalid function parameter kind {parameter.Kind}.");
|
||||
}
|
||||
|
||||
builder.Add(BindParameterSymbol((IdentifierNameExpressionSyntaxNode)parameter));
|
||||
}
|
||||
|
||||
return builder.ToImmutable();
|
||||
}
|
||||
|
||||
private ParameterSymbol BindParameterSymbol(IdentifierNameExpressionSyntaxNode parameter)
|
||||
{
|
||||
return new ParameterSymbol(parameter.Text);
|
||||
}
|
||||
|
||||
private BoundForStatement BindForStatement(ForStatementSyntaxNode node)
|
||||
|
@ -5,16 +5,33 @@ namespace Parser.Binding
|
||||
{
|
||||
public class BoundProgram
|
||||
{
|
||||
public BoundProgram(BoundRoot nullRoot, ImmutableArray<Diagnostic> diagnostics)
|
||||
public BoundProgram(
|
||||
ImmutableArray<Diagnostic> diagnostics,
|
||||
FunctionSymbol? mainFunction,
|
||||
FunctionSymbol? scriptFunction,
|
||||
ImmutableDictionary<FunctionSymbol, BoundBlockStatement> functions)
|
||||
{
|
||||
NullRoot = nullRoot;
|
||||
Diagnostics = diagnostics;
|
||||
MainFunction = mainFunction;
|
||||
ScriptFunction = scriptFunction;
|
||||
Functions = functions;
|
||||
}
|
||||
|
||||
public ImmutableArray<Diagnostic> Diagnostics { get; }
|
||||
|
||||
public BoundRoot NullRoot { get; }
|
||||
/// <summary>
|
||||
/// A "main" function (first in a file without any global statements).
|
||||
/// </summary>
|
||||
public FunctionSymbol? MainFunction { get; }
|
||||
|
||||
public BoundFile Root => NullRoot.File;
|
||||
/// <summary>
|
||||
/// A "script" function (generated from all global statements in a file if there are any).
|
||||
/// </summary>
|
||||
public FunctionSymbol? ScriptFunction { get; }
|
||||
|
||||
/// <summary>
|
||||
/// So-called "local" functions.
|
||||
/// </summary>
|
||||
public ImmutableDictionary<FunctionSymbol, BoundBlockStatement> Functions { get; }
|
||||
}
|
||||
}
|
||||
|
@ -141,12 +141,21 @@ namespace Parser.Binding
|
||||
|
||||
public class BoundFunctionDeclaration : BoundStatement
|
||||
{
|
||||
public BoundFunctionDeclaration(SyntaxNode syntax)
|
||||
public BoundFunctionDeclaration(SyntaxNode syntax, string name, ImmutableArray<ParameterSymbol> inputDescription, ImmutableArray<ParameterSymbol> outputDescription, BoundStatement body)
|
||||
: base(syntax)
|
||||
{
|
||||
Name = name;
|
||||
InputDescription = inputDescription;
|
||||
OutputDescription = outputDescription;
|
||||
Body = body;
|
||||
}
|
||||
|
||||
public override BoundNodeKind Kind => BoundNodeKind.FunctionDeclaration;
|
||||
|
||||
public string Name { get; }
|
||||
public ImmutableArray<ParameterSymbol> InputDescription { get; }
|
||||
public ImmutableArray<ParameterSymbol> OutputDescription { get; }
|
||||
public BoundStatement Body { get; }
|
||||
}
|
||||
|
||||
public class BoundGotoStatement : BoundStatement
|
||||
|
23
Parser/Binding/FunctionSymbol.cs
Normal file
23
Parser/Binding/FunctionSymbol.cs
Normal file
@ -0,0 +1,23 @@
|
||||
using System.Collections.Immutable;
|
||||
|
||||
namespace Parser.Binding
|
||||
{
|
||||
public class FunctionSymbol
|
||||
{
|
||||
public FunctionSymbol(
|
||||
string name,
|
||||
ImmutableArray<ParameterSymbol> parameters,
|
||||
FunctionDeclarationSyntaxNode? declaration)
|
||||
{
|
||||
Name = name;
|
||||
Parameters = parameters;
|
||||
Declaration = declaration;
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
|
||||
public ImmutableArray<ParameterSymbol> Parameters { get; }
|
||||
|
||||
public FunctionDeclarationSyntaxNode? Declaration { get; }
|
||||
}
|
||||
}
|
12
Parser/Binding/ParameterSymbol.cs
Normal file
12
Parser/Binding/ParameterSymbol.cs
Normal file
@ -0,0 +1,12 @@
|
||||
namespace Parser.Binding
|
||||
{
|
||||
public class ParameterSymbol
|
||||
{
|
||||
public ParameterSymbol(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
}
|
||||
}
|
@ -26,9 +26,31 @@ namespace Parser
|
||||
|
||||
internal EvaluationResult Evaluate()
|
||||
{
|
||||
var result = EvaluateFile(_program.Root);
|
||||
if (_program.MainFunction is { } mainFunction)
|
||||
{
|
||||
if (mainFunction.Parameters.Length > 0)
|
||||
{
|
||||
_diagnostics.ReportNotEnoughInputs(
|
||||
new TextSpan(mainFunction.Declaration.Position, mainFunction.Declaration.Position + mainFunction.Declaration.FullWidth),
|
||||
mainFunction.Name);
|
||||
return new EvaluationResult(null, _diagnostics.ToImmutableArray());
|
||||
}
|
||||
else
|
||||
{
|
||||
var result = EvaluateBlockStatement(_program.Functions[mainFunction]);
|
||||
return new EvaluationResult(result, _diagnostics.ToImmutableArray());
|
||||
}
|
||||
}
|
||||
else if (_program.ScriptFunction is { } scriptFunction)
|
||||
{
|
||||
var result = EvaluateBlockStatement(_program.Functions[scriptFunction]);
|
||||
return new EvaluationResult(result, _diagnostics.ToImmutableArray());
|
||||
}
|
||||
else
|
||||
{
|
||||
return new EvaluationResult(null, _diagnostics.ToImmutableArray());
|
||||
}
|
||||
}
|
||||
|
||||
private MObject? EvaluateFile(BoundFile root)
|
||||
{
|
||||
|
@ -38,6 +38,11 @@ namespace Parser.Internal
|
||||
Report(span, "Unexpected end of file.");
|
||||
}
|
||||
|
||||
internal void ReportNotEnoughInputs(TextSpan span, string functionName)
|
||||
{
|
||||
Report(span, $"Not enough inputs for function '{functionName}'.");
|
||||
}
|
||||
|
||||
internal void ReportUnexpectedCharacterWhileParsingNumber(TextSpan span, char c)
|
||||
{
|
||||
Report(span, $"Unexpected character '{c}' while parsing a number.");
|
||||
|
@ -175,7 +175,7 @@ namespace Parser.Internal
|
||||
if (CurrentToken.Kind == TokenKind.TildeToken)
|
||||
{
|
||||
var notToken = EatToken();
|
||||
builder.Add(notToken);
|
||||
builder.Add(Factory.IdentifierNameExpressionSyntax(notToken));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user