add: constant parser and variable parser.
This commit is contained in:
parent
bdcc59a2ab
commit
ed36546b30
|
@ -10,21 +10,6 @@ public sealed class GrammarParser : GrammarParserBuilder
|
||||||
{
|
{
|
||||||
public static IParser<LexicalToken, SyntaxNodeBase> FactorParser()
|
public static IParser<LexicalToken, SyntaxNodeBase> FactorParser()
|
||||||
{
|
{
|
||||||
// factor -> true | false | num
|
|
||||||
IParser<LexicalToken, SyntaxNodeBase> trueParser = from _ in Keyword("true")
|
|
||||||
select new BooleanValueNode(true);
|
|
||||||
|
|
||||||
IParser<LexicalToken, SyntaxNodeBase> falseParser = from _ in Keyword("false")
|
|
||||||
select new BooleanValueNode(false);
|
|
||||||
|
|
||||||
IParser<LexicalToken, SyntaxNodeBase> integerParser =
|
|
||||||
from token in Satisfy<LexicalToken>(x => x.TokenType == LexicalTokenType.ConstInteger)
|
|
||||||
select new IntegerValueNode(int.Parse(token.LiteralValue));
|
|
||||||
|
|
||||||
IParser<LexicalToken, SyntaxNodeBase> floatParser =
|
|
||||||
from token in Satisfy<LexicalToken>(x => x.TokenType == LexicalTokenType.ConstFloat)
|
|
||||||
select new FloatValueNode(double.Parse(token.LiteralValue));
|
|
||||||
|
|
||||||
// factor -> - factor | + factor
|
// factor -> - factor | + factor
|
||||||
IParser<LexicalToken, SyntaxNodeBase> minusParser =
|
IParser<LexicalToken, SyntaxNodeBase> minusParser =
|
||||||
from _ in Operator("-")
|
from _ in Operator("-")
|
||||||
|
@ -48,10 +33,9 @@ public sealed class GrammarParser : GrammarParserBuilder
|
||||||
select node;
|
select node;
|
||||||
|
|
||||||
return Choice(
|
return Choice(
|
||||||
trueParser,
|
TrueParser(),
|
||||||
falseParser,
|
FalseParser(),
|
||||||
integerParser,
|
NumberParser(),
|
||||||
floatParser,
|
|
||||||
minusParser,
|
minusParser,
|
||||||
plusParser,
|
plusParser,
|
||||||
notParser,
|
notParser,
|
||||||
|
@ -177,10 +161,66 @@ public sealed class GrammarParser : GrammarParserBuilder
|
||||||
select new BlockNode(statements);
|
select new BlockNode(statements);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IParser<LexicalToken, SyntaxNodeBase> ConstValueParser()
|
||||||
|
{
|
||||||
|
return Choice(
|
||||||
|
from _ in Operator("-")
|
||||||
|
from num in NumberParser()
|
||||||
|
select new UnaryOperatorNode(UnaryOperatorType.Minus, num),
|
||||||
|
from _ in Operator("+")
|
||||||
|
from num in NumberParser()
|
||||||
|
select new UnaryOperatorNode(UnaryOperatorType.Plus, num),
|
||||||
|
NumberParser(),
|
||||||
|
CharParser(),
|
||||||
|
TrueParser(),
|
||||||
|
FalseParser()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IParser<LexicalToken, SyntaxNodeBase> ConstDeclarationParser()
|
||||||
|
{
|
||||||
|
return from identifier in Satisfy<LexicalToken>(token =>
|
||||||
|
token.TokenType == LexicalTokenType.Identifier)
|
||||||
|
from _ in Operator("=")
|
||||||
|
from node in ConstValueParser()
|
||||||
|
select new ConstantNode(identifier, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IParser<LexicalToken, SyntaxNodeBase> ConstDeclarationsParser()
|
||||||
|
{
|
||||||
|
return (from _ in Keyword("const")
|
||||||
|
from tokens in ConstDeclarationParser().SeparatedOrEndBy1(Delimiter(";"))
|
||||||
|
select new BlockNode(tokens)).Try(new BlockNode([]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IParser<LexicalToken, SyntaxNodeBase> TypeParser()
|
||||||
|
{
|
||||||
|
return from token in BasicTypeParser()
|
||||||
|
select new TypeNode(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IParser<LexicalToken, VariableDeclarationNode> VariableDeclarationParser()
|
||||||
|
{
|
||||||
|
return from tokens in Satisfy<LexicalToken>(
|
||||||
|
token => token.TokenType == LexicalTokenType.Identifier).SeparatedBy1(Delimiter(","))
|
||||||
|
from _1 in Delimiter(":")
|
||||||
|
from type in TypeParser()
|
||||||
|
select new VariableDeclarationNode(tokens, type.Convert<TypeNode>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IParser<LexicalToken, BlockNode> VariableDeclarationsParser()
|
||||||
|
{
|
||||||
|
return (from _ in Keyword("var")
|
||||||
|
from nodes in VariableDeclarationParser().SeparatedOrEndBy1(Delimiter(";"))
|
||||||
|
select new BlockNode(nodes)).Try(new BlockNode([]));
|
||||||
|
}
|
||||||
|
|
||||||
public static IParser<LexicalToken, ProgramBody> ProgramBodyParser()
|
public static IParser<LexicalToken, ProgramBody> ProgramBodyParser()
|
||||||
{
|
{
|
||||||
return from block in CompoundStatementParser()
|
return from constant in ConstDeclarationsParser()
|
||||||
select new ProgramBody(block);
|
from variables in VariableDeclarationsParser()
|
||||||
|
from block in CompoundStatementParser()
|
||||||
|
select new ProgramBody(constant.Convert<BlockNode>(), variables, block);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IParser<LexicalToken, ProgramHead> ProgramHeadParser()
|
public static IParser<LexicalToken, ProgramHead> ProgramHeadParser()
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
using CanonSharp.Combinator.Abstractions;
|
using CanonSharp.Combinator.Abstractions;
|
||||||
|
using CanonSharp.Combinator.Extensions;
|
||||||
using CanonSharp.Pascal.Scanner;
|
using CanonSharp.Pascal.Scanner;
|
||||||
|
using CanonSharp.Pascal.SyntaxTree;
|
||||||
using static CanonSharp.Combinator.ParserBuilder;
|
using static CanonSharp.Combinator.ParserBuilder;
|
||||||
|
|
||||||
namespace CanonSharp.Pascal.Parser;
|
namespace CanonSharp.Pascal.Parser;
|
||||||
|
@ -14,4 +16,42 @@ public abstract class GrammarParserBuilder
|
||||||
|
|
||||||
protected static IParser<LexicalToken, LexicalToken> Delimiter(string value)
|
protected static IParser<LexicalToken, LexicalToken> Delimiter(string value)
|
||||||
=> Satisfy<LexicalToken>(token => token.TokenType == LexicalTokenType.Delimiter && token.LiteralValue == value);
|
=> Satisfy<LexicalToken>(token => token.TokenType == LexicalTokenType.Delimiter && token.LiteralValue == value);
|
||||||
|
|
||||||
|
protected static IParser<LexicalToken, BooleanValueNode> TrueParser()
|
||||||
|
{
|
||||||
|
return from _ in Keyword("true")
|
||||||
|
select new BooleanValueNode(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static IParser<LexicalToken, BooleanValueNode> FalseParser()
|
||||||
|
{
|
||||||
|
return from _ in Keyword("false")
|
||||||
|
select new BooleanValueNode(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static IParser<LexicalToken, SyntaxNodeBase> NumberParser()
|
||||||
|
{
|
||||||
|
return Choice<LexicalToken, SyntaxNodeBase>(
|
||||||
|
from token in Satisfy<LexicalToken>(x => x.TokenType == LexicalTokenType.ConstInteger)
|
||||||
|
select new IntegerValueNode(int.Parse(token.LiteralValue)),
|
||||||
|
from token in Satisfy<LexicalToken>(x => x.TokenType == LexicalTokenType.ConstFloat)
|
||||||
|
select new FloatValueNode(double.Parse(token.LiteralValue))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static IParser<LexicalToken, CharValueNode> CharParser()
|
||||||
|
{
|
||||||
|
return Satisfy<LexicalToken>(token => token.TokenType == LexicalTokenType.Character)
|
||||||
|
.Map(x => new CharValueNode(char.Parse(x.LiteralValue)));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static IParser<LexicalToken, LexicalToken> BasicTypeParser()
|
||||||
|
{
|
||||||
|
return Choice(
|
||||||
|
Keyword("integer"),
|
||||||
|
Keyword("real"),
|
||||||
|
Keyword("boolean"),
|
||||||
|
Keyword("char")
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
8
CanonSharp.Pascal/SyntaxTree/CharValueNode.cs
Normal file
8
CanonSharp.Pascal/SyntaxTree/CharValueNode.cs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
namespace CanonSharp.Pascal.SyntaxTree;
|
||||||
|
|
||||||
|
public sealed class CharValueNode(char value) : SyntaxNodeBase
|
||||||
|
{
|
||||||
|
public override SyntaxNodeType NodeType => SyntaxNodeType.CharValue;
|
||||||
|
|
||||||
|
public char Value => value;
|
||||||
|
}
|
12
CanonSharp.Pascal/SyntaxTree/ConstantNode.cs
Normal file
12
CanonSharp.Pascal/SyntaxTree/ConstantNode.cs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
using CanonSharp.Pascal.Scanner;
|
||||||
|
|
||||||
|
namespace CanonSharp.Pascal.SyntaxTree;
|
||||||
|
|
||||||
|
public sealed class ConstantNode(LexicalToken identifier, SyntaxNodeBase value) : SyntaxNodeBase
|
||||||
|
{
|
||||||
|
public override SyntaxNodeType NodeType => SyntaxNodeType.Constant;
|
||||||
|
|
||||||
|
public LexicalToken Identifier = identifier;
|
||||||
|
|
||||||
|
public SyntaxNodeBase Value = value;
|
||||||
|
}
|
|
@ -1,8 +1,12 @@
|
||||||
namespace CanonSharp.Pascal.SyntaxTree;
|
namespace CanonSharp.Pascal.SyntaxTree;
|
||||||
|
|
||||||
public sealed class ProgramBody(BlockNode block) : SyntaxNodeBase
|
public sealed class ProgramBody(BlockNode constantDeclarations, BlockNode variableDeclarations, BlockNode mainBlock) : SyntaxNodeBase
|
||||||
{
|
{
|
||||||
public override SyntaxNodeType NodeType => SyntaxNodeType.ProgramBody;
|
public override SyntaxNodeType NodeType => SyntaxNodeType.ProgramBody;
|
||||||
|
|
||||||
public BlockNode MainBlock => block;
|
public BlockNode ConstantDeclarations => constantDeclarations;
|
||||||
|
|
||||||
|
public BlockNode VariableDeclarations => variableDeclarations;
|
||||||
|
|
||||||
|
public BlockNode MainBlock => mainBlock;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,9 +7,13 @@ public enum SyntaxNodeType
|
||||||
IntegerValue,
|
IntegerValue,
|
||||||
FloatValue,
|
FloatValue,
|
||||||
BooleanValue,
|
BooleanValue,
|
||||||
|
CharValue,
|
||||||
Variable,
|
Variable,
|
||||||
|
Type,
|
||||||
Assign,
|
Assign,
|
||||||
Block,
|
Block,
|
||||||
|
Constant,
|
||||||
|
VariableDeclaration,
|
||||||
ProgramBody,
|
ProgramBody,
|
||||||
ProgramHead,
|
ProgramHead,
|
||||||
Program
|
Program
|
||||||
|
|
10
CanonSharp.Pascal/SyntaxTree/TypeNode.cs
Normal file
10
CanonSharp.Pascal/SyntaxTree/TypeNode.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
using CanonSharp.Pascal.Scanner;
|
||||||
|
|
||||||
|
namespace CanonSharp.Pascal.SyntaxTree;
|
||||||
|
|
||||||
|
public sealed class TypeNode(LexicalToken typeToken) : SyntaxNodeBase
|
||||||
|
{
|
||||||
|
public override SyntaxNodeType NodeType => SyntaxNodeType.Type;
|
||||||
|
|
||||||
|
public LexicalToken TypeToken => typeToken;
|
||||||
|
}
|
12
CanonSharp.Pascal/SyntaxTree/VariableDeclarationNode.cs
Normal file
12
CanonSharp.Pascal/SyntaxTree/VariableDeclarationNode.cs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
using CanonSharp.Pascal.Scanner;
|
||||||
|
|
||||||
|
namespace CanonSharp.Pascal.SyntaxTree;
|
||||||
|
|
||||||
|
public sealed class VariableDeclarationNode(IEnumerable<LexicalToken> identifiers, TypeNode typeNode) : SyntaxNodeBase
|
||||||
|
{
|
||||||
|
public override SyntaxNodeType NodeType => SyntaxNodeType.VariableDeclaration;
|
||||||
|
|
||||||
|
public IList<LexicalToken> Identifiers => identifiers.ToList();
|
||||||
|
|
||||||
|
public TypeNode TypeNode => typeNode;
|
||||||
|
}
|
62
CanonSharp.Tests/ParserTests/ConstDeclarationTests.cs
Normal file
62
CanonSharp.Tests/ParserTests/ConstDeclarationTests.cs
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
using CanonSharp.Pascal.Parser;
|
||||||
|
using CanonSharp.Pascal.SyntaxTree;
|
||||||
|
using CanonSharp.Tests.Utils;
|
||||||
|
|
||||||
|
namespace CanonSharp.Tests.ParserTests;
|
||||||
|
|
||||||
|
public class ConstDeclarationTests : GrammarParserTestBase
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void BooleanConstValueTest()
|
||||||
|
{
|
||||||
|
BooleanValueNode node = RunParser<BooleanValueNode>(GrammarParser.ConstValueParser(), "true");
|
||||||
|
Assert.True(node.Value);
|
||||||
|
|
||||||
|
node = RunParser<BooleanValueNode>(GrammarParser.ConstValueParser(), "false");
|
||||||
|
Assert.False(node.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CharConstValueTest()
|
||||||
|
{
|
||||||
|
CharValueNode node = RunParser<CharValueNode>(GrammarParser.ConstValueParser(), "'a'");
|
||||||
|
Assert.Equal('a', node.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void NumberConstValueTest()
|
||||||
|
{
|
||||||
|
IntegerValueNode integerValueNode = RunParser<IntegerValueNode>(GrammarParser.ConstValueParser(), "250");
|
||||||
|
Assert.Equal(250, integerValueNode.Value);
|
||||||
|
|
||||||
|
FloatValueNode floatValueNode = RunParser<FloatValueNode>(GrammarParser.ConstValueParser(), "100.5");
|
||||||
|
Assert.Equal(100.5, floatValueNode.Value);
|
||||||
|
|
||||||
|
UnaryOperatorNode node = RunParser<UnaryOperatorNode>(GrammarParser.ConstValueParser(), "+250");
|
||||||
|
Assert.Equal(UnaryOperatorType.Plus, node.OperatorType);
|
||||||
|
node = RunParser<UnaryOperatorNode>(GrammarParser.ConstValueParser(), "-100.5");
|
||||||
|
Assert.Equal(UnaryOperatorType.Minus, node.OperatorType);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ConstDeclarationTest()
|
||||||
|
{
|
||||||
|
ConstantNode node = RunParser<ConstantNode>(GrammarParser.ConstDeclarationParser(), "a = true");
|
||||||
|
|
||||||
|
Assert.Equal("a", node.Identifier.LiteralValue);
|
||||||
|
Assert.True(node.Value.Convert<BooleanValueNode>().Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("", 0)]
|
||||||
|
[InlineData("const INFINITE = 100;", 1)]
|
||||||
|
[InlineData("const a = true; b = false", 2)]
|
||||||
|
[InlineData("const code1 = 100.4;code2 = 'a'", 2)]
|
||||||
|
public void ConstDeclarationsCountTest(string input, int count)
|
||||||
|
{
|
||||||
|
BlockNode blockNode = RunParser<BlockNode>(GrammarParser.ConstDeclarationsParser(), input);
|
||||||
|
List<ConstantNode> constantNodes = blockNode.Statements.Select(node => node.Convert<ConstantNode>()).ToList();
|
||||||
|
|
||||||
|
Assert.Equal(count, constantNodes.Count);
|
||||||
|
}
|
||||||
|
}
|
|
@ -38,4 +38,32 @@ public class ProgramParserTests : GrammarParserTestBase
|
||||||
Assert.Equal(1, binaryOperatorNode.Left.Convert<IntegerValueNode>().Value);
|
Assert.Equal(1, binaryOperatorNode.Left.Convert<IntegerValueNode>().Value);
|
||||||
Assert.Equal(1, binaryOperatorNode.Right.Convert<IntegerValueNode>().Value);
|
Assert.Equal(1, binaryOperatorNode.Right.Convert<IntegerValueNode>().Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ProgramParseTest3()
|
||||||
|
{
|
||||||
|
Program program = ProgramParse("""
|
||||||
|
program main;
|
||||||
|
const a = 1; b = true;
|
||||||
|
begin
|
||||||
|
temp := 1 + 1;
|
||||||
|
end.
|
||||||
|
""");
|
||||||
|
|
||||||
|
Assert.Equal("main", program.Head.ProgramName.LiteralValue);
|
||||||
|
|
||||||
|
List<ConstantNode> constantNodes = program.Body.ConstantDeclarations.Statements
|
||||||
|
.Select(x => x.Convert<ConstantNode>()).ToList();
|
||||||
|
Assert.Equal(2, constantNodes.Count);
|
||||||
|
Assert.Equal("a", constantNodes[0].Identifier.LiteralValue);
|
||||||
|
Assert.Equal("b", constantNodes[1].Identifier.LiteralValue);
|
||||||
|
|
||||||
|
AssignNode assignNode = program.Body.MainBlock.Statements[0].Convert<AssignNode>();
|
||||||
|
Assert.Equal("temp", assignNode.Variable.IdentifierName);
|
||||||
|
|
||||||
|
BinaryOperatorNode binaryOperatorNode = assignNode.Expression.Convert<BinaryOperatorNode>();
|
||||||
|
Assert.Equal(BinaryOperatorType.Add, binaryOperatorNode.OperatorType);
|
||||||
|
Assert.Equal(1, binaryOperatorNode.Left.Convert<IntegerValueNode>().Value);
|
||||||
|
Assert.Equal(1, binaryOperatorNode.Right.Convert<IntegerValueNode>().Value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user