Implement script function
This commit is contained in:
parent
7f0889f0d4
commit
2f04f86af3
@ -17,11 +17,49 @@ namespace Parser.Binding
|
|||||||
{
|
{
|
||||||
var binder = new Binder();
|
var binder = new Binder();
|
||||||
var boundRoot = binder.BindRoot(syntaxTree.NullRoot);
|
var boundRoot = binder.BindRoot(syntaxTree.NullRoot);
|
||||||
var loweredStatement = Lowerer.Lower(boundRoot.File.Body);
|
var statements = ((BoundBlockStatement)boundRoot.File.Body).Statements;
|
||||||
var newRoot = Root(
|
var functionsBuilder = ImmutableDictionary.CreateBuilder<FunctionSymbol, BoundBlockStatement>();
|
||||||
boundRoot.Syntax,
|
var globalStatements = statements.Where(s => s.Kind != BoundNodeKind.FunctionDeclaration).ToArray();
|
||||||
File(boundRoot.File.Syntax, loweredStatement));
|
var mainFunction = (FunctionSymbol?)null;
|
||||||
return new BoundProgram(newRoot, binder._diagnostics.ToImmutableArray());
|
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)
|
private BoundRoot BindRoot(RootSyntaxNode node)
|
||||||
@ -148,7 +186,58 @@ namespace Parser.Binding
|
|||||||
|
|
||||||
private BoundFunctionDeclaration BindFunctionDeclaration(FunctionDeclarationSyntaxNode node)
|
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)
|
private BoundForStatement BindForStatement(ForStatementSyntaxNode node)
|
||||||
|
@ -5,16 +5,33 @@ namespace Parser.Binding
|
|||||||
{
|
{
|
||||||
public class BoundProgram
|
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;
|
Diagnostics = diagnostics;
|
||||||
|
MainFunction = mainFunction;
|
||||||
|
ScriptFunction = scriptFunction;
|
||||||
|
Functions = functions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImmutableArray<Diagnostic> Diagnostics { get; }
|
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 class BoundFunctionDeclaration : BoundStatement
|
||||||
{
|
{
|
||||||
public BoundFunctionDeclaration(SyntaxNode syntax)
|
public BoundFunctionDeclaration(SyntaxNode syntax, string name, ImmutableArray<ParameterSymbol> inputDescription, ImmutableArray<ParameterSymbol> outputDescription, BoundStatement body)
|
||||||
: base(syntax)
|
: base(syntax)
|
||||||
{
|
{
|
||||||
|
Name = name;
|
||||||
|
InputDescription = inputDescription;
|
||||||
|
OutputDescription = outputDescription;
|
||||||
|
Body = body;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override BoundNodeKind Kind => BoundNodeKind.FunctionDeclaration;
|
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
|
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,8 +26,30 @@ namespace Parser
|
|||||||
|
|
||||||
internal EvaluationResult Evaluate()
|
internal EvaluationResult Evaluate()
|
||||||
{
|
{
|
||||||
var result = EvaluateFile(_program.Root);
|
if (_program.MainFunction is { } mainFunction)
|
||||||
return new EvaluationResult(result, _diagnostics.ToImmutableArray());
|
{
|
||||||
|
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)
|
private MObject? EvaluateFile(BoundFile root)
|
||||||
|
@ -38,6 +38,11 @@ namespace Parser.Internal
|
|||||||
Report(span, "Unexpected end of file.");
|
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)
|
internal void ReportUnexpectedCharacterWhileParsingNumber(TextSpan span, char c)
|
||||||
{
|
{
|
||||||
Report(span, $"Unexpected character '{c}' while parsing a number.");
|
Report(span, $"Unexpected character '{c}' while parsing a number.");
|
||||||
|
@ -175,7 +175,7 @@ namespace Parser.Internal
|
|||||||
if (CurrentToken.Kind == TokenKind.TildeToken)
|
if (CurrentToken.Kind == TokenKind.TildeToken)
|
||||||
{
|
{
|
||||||
var notToken = EatToken();
|
var notToken = EatToken();
|
||||||
builder.Add(notToken);
|
builder.Add(Factory.IdentifierNameExpressionSyntax(notToken));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user