add: constant parser and variable parser.

This commit is contained in:
jackfiled 2024-08-16 17:07:53 +08:00
parent bdcc59a2ab
commit ed36546b30
10 changed files with 244 additions and 24 deletions

View File

@ -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,
@ -166,7 +150,7 @@ public sealed class GrammarParser : GrammarParserBuilder
from _ in Operator(":=") from _ in Operator(":=")
from expression in ExpressionParser() from expression in ExpressionParser()
select new AssignNode(variable, expression) select new AssignNode(variable, expression)
); );
} }
public static IParser<LexicalToken, BlockNode> CompoundStatementParser() public static IParser<LexicalToken, BlockNode> CompoundStatementParser()
@ -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()

View File

@ -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")
);
}
} }

View 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;
}

View 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;
}

View File

@ -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;
} }

View File

@ -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

View 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;
}

View 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;
}

View 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);
}
}

View File

@ -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);
}
} }