add: Subprogram declarations and procedure call support.
This commit is contained in:
parent
c44b720e09
commit
5acae7d489
|
@ -32,6 +32,13 @@ public sealed class GrammarParser : GrammarParserBuilder
|
|||
from _2 in Delimiter(")")
|
||||
select node;
|
||||
|
||||
IParser<LexicalToken, SyntaxNodeBase> procedureCallParser =
|
||||
from identifier in IdentifierParser()
|
||||
from _ in Delimiter("(")
|
||||
from expressions in ExpressionParser().SeparatedBy(Delimiter(","))
|
||||
from _1 in Delimiter(")")
|
||||
select new ProcedureCallNode(identifier, expressions);
|
||||
|
||||
return Choice(
|
||||
TrueParser(),
|
||||
FalseParser(),
|
||||
|
@ -39,6 +46,7 @@ public sealed class GrammarParser : GrammarParserBuilder
|
|||
minusParser,
|
||||
plusParser,
|
||||
notParser,
|
||||
procedureCallParser,
|
||||
VariableParser(),
|
||||
parenthesisParser
|
||||
);
|
||||
|
@ -189,6 +197,19 @@ public sealed class GrammarParser : GrammarParserBuilder
|
|||
select new WhileNode(condition, statement);
|
||||
}
|
||||
|
||||
public static IParser<LexicalToken, ProcedureCallNode> ProcedureCallParser()
|
||||
{
|
||||
return Choice(
|
||||
from identifier in IdentifierParser()
|
||||
from _ in Delimiter("(")
|
||||
from expressions in ExpressionParser().SeparatedBy(Delimiter(","))
|
||||
from _1 in Delimiter(")")
|
||||
select new ProcedureCallNode(identifier, expressions),
|
||||
from identifier in IdentifierParser()
|
||||
select new ProcedureCallNode(identifier, [])
|
||||
);
|
||||
}
|
||||
|
||||
public static IParser<LexicalToken, SyntaxNodeBase> StatementParser()
|
||||
{
|
||||
return Choice<LexicalToken, SyntaxNodeBase>(
|
||||
|
@ -196,6 +217,7 @@ public sealed class GrammarParser : GrammarParserBuilder
|
|||
from _ in Operator(":=")
|
||||
from expression in ExpressionParser()
|
||||
select new AssignNode(variable, expression),
|
||||
ProcedureCallParser(),
|
||||
IfParser(),
|
||||
ForParser(),
|
||||
WhileParser(),
|
||||
|
@ -236,7 +258,7 @@ public sealed class GrammarParser : GrammarParserBuilder
|
|||
select new ConstantNode(identifier, node);
|
||||
}
|
||||
|
||||
public static IParser<LexicalToken, SyntaxNodeBase> ConstDeclarationsParser()
|
||||
public static IParser<LexicalToken, BlockNode> ConstDeclarationsParser()
|
||||
{
|
||||
return (from _ in Keyword("const")
|
||||
from tokens in ConstDeclarationParser().SeparatedOrEndBy1(Delimiter(";"))
|
||||
|
@ -283,12 +305,73 @@ public sealed class GrammarParser : GrammarParserBuilder
|
|||
select new BlockNode(nodes)).Try(new BlockNode([]));
|
||||
}
|
||||
|
||||
public static IParser<LexicalToken, ProgramBody> ProgramBodyParser()
|
||||
public static IParser<LexicalToken, IEnumerable<Parameter>> ParameterParser()
|
||||
{
|
||||
return Choice(
|
||||
from _ in Keyword("var")
|
||||
from tokens in IdentifierParser().SeparatedBy1(Delimiter(","))
|
||||
from _1 in Delimiter(":")
|
||||
from typeToken in TypeParser()
|
||||
select tokens.Select(x => new Parameter(true, x, typeToken)),
|
||||
from tokens in IdentifierParser().SeparatedBy1(Delimiter(","))
|
||||
from _ in Delimiter(":")
|
||||
from typeToken in TypeParser()
|
||||
select tokens.Select(x => new Parameter(false, x, typeToken))
|
||||
);
|
||||
}
|
||||
|
||||
public static IParser<LexicalToken, IEnumerable<Parameter>> FormalParameterParser()
|
||||
{
|
||||
return (from _ in Delimiter("(")
|
||||
from parameters in ParameterParser().SeparatedBy(Delimiter(";"))
|
||||
from _1 in Delimiter(")")
|
||||
select parameters.Aggregate(new List<Parameter>(), (result, array) =>
|
||||
{
|
||||
result.AddRange(array);
|
||||
return result;
|
||||
})).Try([]);
|
||||
}
|
||||
|
||||
public static IParser<LexicalToken, SubprogramHead> SubprogramHeadParser()
|
||||
{
|
||||
return Choice(
|
||||
from _ in Keyword("procedure")
|
||||
from identifier in IdentifierParser()
|
||||
from parameters in FormalParameterParser()
|
||||
select new SubprogramHead(identifier, parameters),
|
||||
from _ in Keyword("function")
|
||||
from identifier in IdentifierParser()
|
||||
from parameters in FormalParameterParser()
|
||||
from _1 in Delimiter(":")
|
||||
from typeToken in TypeParser()
|
||||
select new SubprogramHead(identifier, parameters, typeToken)
|
||||
);
|
||||
}
|
||||
|
||||
public static IParser<LexicalToken, SubprogramBody> SubprogramBodyParser()
|
||||
{
|
||||
return from constant in ConstDeclarationsParser()
|
||||
from variables in VariableDeclarationsParser()
|
||||
from block in CompoundStatementParser()
|
||||
select new ProgramBody(constant.Convert<BlockNode>(), variables, block);
|
||||
select new SubprogramBody(constant, variables, block);
|
||||
}
|
||||
|
||||
public static IParser<LexicalToken, Subprogram> SubprogramParser()
|
||||
{
|
||||
return from head in SubprogramHeadParser()
|
||||
from _ in Delimiter(";")
|
||||
from body in SubprogramBodyParser()
|
||||
select new Subprogram(head, body);
|
||||
}
|
||||
|
||||
public static IParser<LexicalToken, ProgramBody> ProgramBodyParser()
|
||||
{
|
||||
return from constant in ConstDeclarationsParser()
|
||||
from variables in VariableDeclarationsParser()
|
||||
from subprograms in SubprogramParser().SeparatedOrEndBy(Delimiter(";"))
|
||||
.Map(x => new BlockNode(x))
|
||||
from block in CompoundStatementParser()
|
||||
select new ProgramBody(constant, variables, subprograms, block);
|
||||
}
|
||||
|
||||
public static IParser<LexicalToken, ProgramHead> ProgramHeadParser()
|
||||
|
|
14
CanonSharp.Pascal/SyntaxTree/Parameter.cs
Normal file
14
CanonSharp.Pascal/SyntaxTree/Parameter.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using CanonSharp.Pascal.Scanner;
|
||||
|
||||
namespace CanonSharp.Pascal.SyntaxTree;
|
||||
|
||||
public sealed class Parameter(bool isReference, LexicalToken identifier, SyntaxNodeBase typeNode) : SyntaxNodeBase
|
||||
{
|
||||
public override SyntaxNodeType NodeType => SyntaxNodeType.Parameter;
|
||||
|
||||
public bool IsReference => isReference;
|
||||
|
||||
public LexicalToken Identifier => identifier;
|
||||
|
||||
public SyntaxNodeBase TypeNode => typeNode;
|
||||
}
|
18
CanonSharp.Pascal/SyntaxTree/ProcedureCallNode.cs
Normal file
18
CanonSharp.Pascal/SyntaxTree/ProcedureCallNode.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
using CanonSharp.Pascal.Scanner;
|
||||
|
||||
namespace CanonSharp.Pascal.SyntaxTree;
|
||||
|
||||
public sealed class ProcedureCallNode : SyntaxNodeBase
|
||||
{
|
||||
public ProcedureCallNode(LexicalToken identifier, IEnumerable<SyntaxNodeBase> parameters)
|
||||
{
|
||||
Identifier = identifier;
|
||||
Parameters.AddRange(parameters);
|
||||
}
|
||||
|
||||
public override SyntaxNodeType NodeType => SyntaxNodeType.ProcedureCall;
|
||||
|
||||
public LexicalToken Identifier { get; }
|
||||
|
||||
public List<SyntaxNodeBase> Parameters { get; } = [];
|
||||
}
|
|
@ -1,6 +1,9 @@
|
|||
namespace CanonSharp.Pascal.SyntaxTree;
|
||||
|
||||
public sealed class ProgramBody(BlockNode constantDeclarations, BlockNode variableDeclarations, BlockNode mainBlock) : SyntaxNodeBase
|
||||
public sealed class ProgramBody(BlockNode constantDeclarations, BlockNode variableDeclarations,
|
||||
BlockNode subprograms,
|
||||
BlockNode mainBlock)
|
||||
: SyntaxNodeBase
|
||||
{
|
||||
public override SyntaxNodeType NodeType => SyntaxNodeType.ProgramBody;
|
||||
|
||||
|
@ -8,5 +11,7 @@ public sealed class ProgramBody(BlockNode constantDeclarations, BlockNode variab
|
|||
|
||||
public BlockNode VariableDeclarations => variableDeclarations;
|
||||
|
||||
public BlockNode Subprograms => subprograms;
|
||||
|
||||
public BlockNode MainBlock => mainBlock;
|
||||
}
|
||||
|
|
10
CanonSharp.Pascal/SyntaxTree/Subprogram.cs
Normal file
10
CanonSharp.Pascal/SyntaxTree/Subprogram.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace CanonSharp.Pascal.SyntaxTree;
|
||||
|
||||
public sealed class Subprogram(SubprogramHead subprogramHead, SubprogramBody subprogramBody) : SyntaxNodeBase
|
||||
{
|
||||
public override SyntaxNodeType NodeType => SyntaxNodeType.SubProgram;
|
||||
|
||||
public SubprogramHead Head => subprogramHead;
|
||||
|
||||
public SubprogramBody Body => subprogramBody;
|
||||
}
|
12
CanonSharp.Pascal/SyntaxTree/SubprogramBody.cs
Normal file
12
CanonSharp.Pascal/SyntaxTree/SubprogramBody.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
namespace CanonSharp.Pascal.SyntaxTree;
|
||||
|
||||
public sealed class SubprogramBody(BlockNode constDeclarations, BlockNode variableDeclarations, BlockNode mainBlock) : SyntaxNodeBase
|
||||
{
|
||||
public override SyntaxNodeType NodeType => SyntaxNodeType.SubprogramBody;
|
||||
|
||||
public BlockNode ConstDeclarations => constDeclarations;
|
||||
|
||||
public BlockNode VariableDeclarations => variableDeclarations;
|
||||
|
||||
public BlockNode MainBlock => mainBlock;
|
||||
}
|
28
CanonSharp.Pascal/SyntaxTree/SubprogramHead.cs
Normal file
28
CanonSharp.Pascal/SyntaxTree/SubprogramHead.cs
Normal file
|
@ -0,0 +1,28 @@
|
|||
using CanonSharp.Pascal.Scanner;
|
||||
|
||||
namespace CanonSharp.Pascal.SyntaxTree;
|
||||
|
||||
public sealed class SubprogramHead : SyntaxNodeBase
|
||||
{
|
||||
public override SyntaxNodeType NodeType => SyntaxNodeType.SubprogramHead;
|
||||
|
||||
public LexicalToken Identifier { get; }
|
||||
|
||||
public List<Parameter> Parameters { get; } = [];
|
||||
|
||||
public SyntaxNodeBase? TypeToken { get; }
|
||||
|
||||
public SubprogramHead(LexicalToken identifier, IEnumerable<Parameter> parameters)
|
||||
{
|
||||
Identifier = identifier;
|
||||
Parameters.AddRange(parameters);
|
||||
TypeToken = null;
|
||||
}
|
||||
|
||||
public SubprogramHead(LexicalToken identifier, IEnumerable<Parameter> parameters, SyntaxNodeBase typeToken) : this(
|
||||
identifier, parameters)
|
||||
{
|
||||
TypeToken = typeToken;
|
||||
}
|
||||
|
||||
}
|
|
@ -17,6 +17,11 @@ public enum SyntaxNodeType
|
|||
If,
|
||||
While,
|
||||
For,
|
||||
ProcedureCall,
|
||||
Parameter,
|
||||
SubprogramHead,
|
||||
SubprogramBody,
|
||||
SubProgram,
|
||||
ProgramBody,
|
||||
ProgramHead,
|
||||
Program
|
||||
|
|
|
@ -82,6 +82,23 @@ public sealed class ExpressionParserTests : GrammarParserTestBase
|
|||
Assert.Equal(1, binaryOperatorNode.Right.Convert<IntegerValueNode>().Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProcedureCallTest()
|
||||
{
|
||||
ProcedureCallNode node = RunParser<ProcedureCallNode>(GrammarParser.FactorParser(), "test(1)");
|
||||
Assert.Equal("test", node.Identifier.LiteralValue);
|
||||
Assert.Single(node.Parameters);
|
||||
Assert.Equal(1, node.Parameters[0].Convert<IntegerValueNode>().Value);
|
||||
|
||||
node = RunParser<ProcedureCallNode>(GrammarParser.FactorParser(), "test()");
|
||||
Assert.Equal("test", node.Identifier.LiteralValue);
|
||||
Assert.Empty(node.Parameters);
|
||||
|
||||
VariableNode variableNode = RunParser<VariableNode>(GrammarParser.FactorParser(), "test");
|
||||
Assert.Equal("test", variableNode.Identifier.LiteralValue);
|
||||
Assert.Empty(variableNode.Indexers);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SingleTermTest()
|
||||
{
|
||||
|
|
|
@ -153,6 +153,75 @@ public class ProgramParserTests : GrammarParserTestBase
|
|||
i := i + 1;
|
||||
end.
|
||||
""")]
|
||||
[InlineData("""
|
||||
program main;
|
||||
procedure test;
|
||||
begin
|
||||
end;
|
||||
begin
|
||||
end.
|
||||
""")]
|
||||
[InlineData("""
|
||||
program main;
|
||||
function test(a, b : integer) : integer;
|
||||
begin
|
||||
test := 1;
|
||||
end;
|
||||
begin
|
||||
end.
|
||||
""")]
|
||||
[InlineData("""
|
||||
program main;
|
||||
var i : integer;
|
||||
begin
|
||||
for i := 1 to 100 do
|
||||
begin
|
||||
doSomething(i);
|
||||
end;
|
||||
end.
|
||||
""")]
|
||||
[InlineData("""
|
||||
program main;
|
||||
begin
|
||||
if 1 = 2 then
|
||||
begin
|
||||
test1;
|
||||
test2 := a;
|
||||
end
|
||||
else
|
||||
begin
|
||||
doSomething;
|
||||
end;
|
||||
end.
|
||||
""")]
|
||||
[InlineData("""
|
||||
program main;
|
||||
var a : integer;
|
||||
function test : integer;
|
||||
begin
|
||||
end;
|
||||
begin
|
||||
test;
|
||||
a := test;
|
||||
test();
|
||||
a := test();
|
||||
end.
|
||||
""")]
|
||||
[InlineData("""
|
||||
program main;
|
||||
procedure test();
|
||||
begin
|
||||
end;
|
||||
begin
|
||||
test();
|
||||
end.
|
||||
""")]
|
||||
[InlineData("""
|
||||
program main;
|
||||
begin
|
||||
test
|
||||
end.
|
||||
""")]
|
||||
public void ProgramParseTest(string input)
|
||||
{
|
||||
ProgramParse(input);
|
||||
|
|
|
@ -83,6 +83,46 @@ public class StatementParserTests : GrammarParserTestBase
|
|||
Assert.Equal("a", assignNode.Variable.Identifier.LiteralValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProcedureCallTest1()
|
||||
{
|
||||
ProcedureCallNode node = RunParser<ProcedureCallNode>(GrammarParser.ProcedureCallParser(), "test()");
|
||||
Assert.Equal("test", node.Identifier.LiteralValue);
|
||||
Assert.Empty(node.Parameters);
|
||||
|
||||
node = RunParser<ProcedureCallNode>(GrammarParser.ProcedureCallParser(), "test");
|
||||
Assert.Equal("test", node.Identifier.LiteralValue);
|
||||
Assert.Empty(node.Parameters);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProcedureCallTest2()
|
||||
{
|
||||
ProcedureCallNode node = RunParser<ProcedureCallNode>(GrammarParser.ProcedureCallParser(),
|
||||
"test(1 + 1, true)");
|
||||
|
||||
Assert.Equal(2, node.Parameters.Count);
|
||||
|
||||
BinaryOperatorNode firstParameter = node.Parameters[0].Convert<BinaryOperatorNode>();
|
||||
Assert.Equal(BinaryOperatorType.Add, firstParameter.OperatorType);
|
||||
|
||||
BooleanValueNode secondParameter = node.Parameters[1].Convert<BooleanValueNode>();
|
||||
Assert.True(secondParameter.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProcedureCallTest3()
|
||||
{
|
||||
ProcedureCallNode node =
|
||||
RunParser<ProcedureCallNode>(GrammarParser.ProcedureCallParser(), "test(1 * + 1, test2[0])");
|
||||
|
||||
Assert.Equal(2, node.Parameters.Count);
|
||||
|
||||
VariableNode secondParameter = node.Parameters[1].Convert<VariableNode>();
|
||||
Assert.Equal("test2", secondParameter.Identifier.LiteralValue);
|
||||
Assert.Single(secondParameter.Indexers);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("""
|
||||
begin
|
||||
|
|
83
CanonSharp.Tests/ParserTests/SubprogramParserTests.cs
Normal file
83
CanonSharp.Tests/ParserTests/SubprogramParserTests.cs
Normal file
|
@ -0,0 +1,83 @@
|
|||
using CanonSharp.Pascal.Parser;
|
||||
using CanonSharp.Pascal.SyntaxTree;
|
||||
using CanonSharp.Tests.Utils;
|
||||
|
||||
namespace CanonSharp.Tests.ParserTests;
|
||||
|
||||
public class SubprogramParserTests : GrammarParserTestBase
|
||||
{
|
||||
[Fact]
|
||||
public void ParameterTest1()
|
||||
{
|
||||
List<Parameter> parameters = RunParser<Parameter>(GrammarParser.ParameterParser(), "a ,b : integer");
|
||||
|
||||
Assert.Equal(2, parameters.Count);
|
||||
Assert.All(parameters, p =>
|
||||
{
|
||||
Assert.False(p.IsReference);
|
||||
Assert.Equal("integer", p.TypeNode.Convert<TypeNode>().TypeToken.LiteralValue);
|
||||
});
|
||||
|
||||
parameters = RunParser<Parameter>(GrammarParser.ParameterParser(), "var a, b,c: real");
|
||||
Assert.Equal(3, parameters.Count);
|
||||
Assert.All(parameters, p =>
|
||||
{
|
||||
Assert.True(p.IsReference);
|
||||
Assert.Equal("real", p.TypeNode.Convert<TypeNode>().TypeToken.LiteralValue);
|
||||
});
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("(var a, b : integer; c, d : real)", 4)]
|
||||
[InlineData("(a : boolean; var c, d, e : char)", 4)]
|
||||
[InlineData("(a : integer)", 1)]
|
||||
[InlineData("(var b ,c : real)", 2)]
|
||||
public void FormalParameterTest(string input, int count)
|
||||
{
|
||||
List<Parameter> parameters = RunParser<Parameter>(GrammarParser.FormalParameterParser(), input);
|
||||
Assert.Equal(count, parameters.Count);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("procedure test", 0)]
|
||||
[InlineData("procedure test()", 0)]
|
||||
[InlineData("procedure test(a : integer; var b : real)", 2)]
|
||||
[InlineData("procedure test(a,b, c : real; var e, f : boolean)", 5)]
|
||||
[InlineData("function test : integer", 0)]
|
||||
[InlineData("function test() : real", 0)]
|
||||
[InlineData("function test(a : integer; var b : real) : boolean", 2)]
|
||||
[InlineData("function test(a,b, c : real; var e, f : boolean) : char", 5)]
|
||||
public void SubprogramHeadTest(string input, int count)
|
||||
{
|
||||
SubprogramHead head = RunParser<SubprogramHead>(GrammarParser.SubprogramHeadParser(), input);
|
||||
Assert.Equal(count, head.Parameters.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SubprogramHeadTest1()
|
||||
{
|
||||
SubprogramHead head =
|
||||
RunParser<SubprogramHead>(GrammarParser.SubprogramHeadParser(), "function test(a : integer) : real");
|
||||
|
||||
Assert.Equal("test", head.Identifier.LiteralValue);
|
||||
Assert.Single(head.Parameters);
|
||||
Assert.NotNull(head.TypeToken);
|
||||
Assert.Equal("real", head.TypeToken.Convert<TypeNode>().TypeToken.LiteralValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SubprogramBodyTest1()
|
||||
{
|
||||
SubprogramBody body = RunParser<SubprogramBody>(GrammarParser.SubprogramBodyParser(), """
|
||||
const a = 3;
|
||||
var c, d : integer; e, f :real;
|
||||
begin
|
||||
c := a + d;
|
||||
end
|
||||
""");
|
||||
|
||||
Assert.Single(body.ConstDeclarations.Statements);
|
||||
Assert.Equal(2, body.VariableDeclarations.Statements.Count);
|
||||
Assert.Equal(1, body.MainBlock.Statements.Count);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user