Skip to content

Commit 5c441fb

Browse files
committed
Merge branch 'dev' of github.com:b3b00/csly into dev
* 'dev' of github.com:b3b00/csly: entrypoint rule for expression parser readme.md : add documentation for expression parser generator compilation fix expression parser generator remov unused project UT for expression parser generator expression parser WIP WIP upgrade to net core 2.0 gestion des regles de parser d'expression dans les parser BNF et EBNF passage à .net core 2.0 pratt parsing WIP WIP / does not compiler start dev
2 parents 9fb18cc + 564a4b3 commit 5c441fb

38 files changed

+1293
-170
lines changed
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
using Xunit;
2+
using sly.parser;
3+
using sly.parser.generator;
4+
using expressionparser;
5+
using simpleExpressionParser;
6+
using System.Collections.Generic;
7+
8+
namespace ParserTests
9+
{
10+
11+
public class ExpressionGeneratorTests
12+
{
13+
14+
private Parser<ExpressionToken,int> Parser;
15+
16+
private string StartingRule = "";
17+
public ExpressionGeneratorTests()
18+
{
19+
20+
}
21+
22+
23+
private void BuildParser()
24+
{
25+
StartingRule = $"{typeof(SimpleExpressionParser).Name}_expressions";
26+
SimpleExpressionParser parserInstance = new SimpleExpressionParser();
27+
ParserBuilder<ExpressionToken, int> builder = new ParserBuilder<ExpressionToken, int>();
28+
Parser = builder.BuildParser(parserInstance, ParserType.LL_RECURSIVE_DESCENT, StartingRule);
29+
}
30+
31+
[Fact]
32+
public void TestBuild()
33+
{
34+
BuildParser();
35+
Assert.Equal(5, Parser.Configuration.NonTerminals.Count);
36+
var nonterminals = new List<NonTerminal<ExpressionToken>>();
37+
foreach(var pair in Parser.Configuration.NonTerminals)
38+
{
39+
nonterminals.Add(pair.Value);
40+
}
41+
NonTerminal<ExpressionToken> nt = nonterminals[0];
42+
Assert.Equal(1, nt.Rules.Count);
43+
Assert.Equal("operand", nt.Name);
44+
nt = nonterminals[1];
45+
Assert.Equal(3, nt.Rules.Count);
46+
Assert.Contains("10", nt.Name);
47+
Assert.Contains("PLUS", nt.Name);
48+
Assert.Contains("MINUS", nt.Name);
49+
nt = nonterminals[2];
50+
Assert.Equal(3, nt.Rules.Count);
51+
Assert.Contains("50", nt.Name);
52+
Assert.Contains("TIMES", nt.Name);
53+
Assert.Contains("DIVIDE", nt.Name);
54+
nt = nonterminals[3];
55+
Assert.Equal(2, nt.Rules.Count);
56+
Assert.Contains("100", nt.Name);
57+
Assert.Contains("MINUS", nt.Name);
58+
nt = nonterminals[4];
59+
Assert.Equal(1, nt.Rules.Count);
60+
Assert.Equal(StartingRule, nt.Name);
61+
Assert.Equal(1, nt.Rules[0].Clauses.Count);
62+
}
63+
64+
65+
[Fact]
66+
public void TestSingleValue()
67+
{
68+
BuildParser();
69+
ParseResult<ExpressionToken, int> r = Parser.Parse("1",StartingRule);
70+
Assert.False(r.IsError);
71+
Assert.NotNull(r.Result);
72+
Assert.Equal(1, r.Result);
73+
}
74+
75+
[Fact]
76+
public void TestSingleNegativeValue()
77+
{
78+
BuildParser();
79+
ParseResult<ExpressionToken, int> r = Parser.Parse("-1", StartingRule);
80+
Assert.False(r.IsError);
81+
Assert.NotNull(r.Result);
82+
Assert.Equal(-1, r.Result);
83+
}
84+
85+
[Fact]
86+
public void TestTermPlus()
87+
{
88+
BuildParser();
89+
ParseResult<ExpressionToken, int> r = Parser.Parse("1 + 1", StartingRule);
90+
Assert.False(r.IsError);
91+
Assert.NotNull(r.Result);
92+
Assert.IsAssignableFrom(typeof(int), r.Result);
93+
Assert.Equal(2, (int)r.Result);
94+
}
95+
96+
[Fact]
97+
public void TestTermMinus()
98+
{
99+
BuildParser();
100+
ParseResult<ExpressionToken, int> r = Parser.Parse("1 - 1", StartingRule);
101+
Assert.False(r.IsError);
102+
Assert.NotNull(r.Result);
103+
Assert.Equal(0, r.Result);
104+
}
105+
106+
[Fact]
107+
public void TestFactorTimes()
108+
{
109+
BuildParser();
110+
ParseResult<ExpressionToken, int> r = Parser.Parse("2*2", StartingRule);
111+
Assert.False(r.IsError);
112+
Assert.NotNull(r.Result);
113+
Assert.IsAssignableFrom(typeof(int), r.Result);
114+
Assert.Equal(4, r.Result);
115+
}
116+
117+
[Fact]
118+
public void TestFactorDivide()
119+
{
120+
BuildParser();
121+
ParseResult<ExpressionToken, int> r = Parser.Parse("42/2", StartingRule);
122+
Assert.False(r.IsError);
123+
Assert.NotNull(r.Result);
124+
Assert.Equal(21, r.Result);
125+
}
126+
127+
[Fact]
128+
public void TestUnaryPrecedence()
129+
{
130+
BuildParser();
131+
ParseResult<ExpressionToken, int> r = Parser.Parse("-1 * 2", StartingRule);
132+
Assert.False(r.IsError);
133+
Assert.NotNull(r.Result);
134+
Assert.Equal(-2, r.Result);
135+
}
136+
137+
138+
[Fact]
139+
public void TestPrecedence()
140+
{
141+
BuildParser();
142+
ParseResult<ExpressionToken, int> r = Parser.Parse("-1 + 2 * 3", StartingRule);
143+
Assert.False(r.IsError);
144+
Assert.NotNull(r.Result);
145+
Assert.Equal(5, r.Result);
146+
}
147+
148+
149+
}
150+
}

ParserTests/ParserTests.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
3-
<TargetFrameworks>netcoreapp1.0;net45</TargetFrameworks>
3+
<TargetFrameworks>netcoreapp2.0;net45</TargetFrameworks>
44
<Authors>b3b00</Authors>
55
</PropertyGroup>
66
<ItemGroup>
@@ -13,6 +13,7 @@
1313
</ItemGroup>
1414
<ItemGroup>
1515
<ProjectReference Include="..\samples\expressionParser\expressionParser.csproj" />
16+
<ProjectReference Include="..\samples\SimpleExpressionParser\SimpleExpressionParser.csproj" />
1617
<ProjectReference Include="..\samples\while\while.csproj" />
1718
<ProjectReference Include="..\sly\sly.csproj" />
1819
<ProjectReference Include="..\samples\expressionParser\expressionParser.csproj" />

Pratt/ParseException.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using System;
2+
3+
namespace sly.pratt
4+
{
5+
6+
public class PrattParseException : Exception
7+
{
8+
public PrattParseException(String message) : base(message)
9+
{
10+
}
11+
}
12+
13+
}

Pratt/Parser.cs

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using sly.pratt.parselets;
4+
using sly.lexer;
5+
6+
7+
8+
namespace sly.pratt
9+
{
10+
11+
12+
public static class DictionaryExtensions
13+
{
14+
15+
public static V Get<K, V>(this Dictionary<K, V> dict, K key)
16+
{
17+
if (dict.ContainsKey(key))
18+
{
19+
return dict[key];
20+
}
21+
return default(V);
22+
}
23+
24+
}
25+
26+
public class Parser<IN, OUT> where IN : struct
27+
{
28+
29+
private int Position;
30+
31+
private List<Token<IN>> Tokens;
32+
//private List<Token<IN>> ReadTokens = new List<Token<IN>>();
33+
private Dictionary<IN, PrefixParselet<IN, OUT>> PrefixParselets =
34+
new Dictionary<IN, PrefixParselet<IN, OUT>>();
35+
private Dictionary<IN, InfixParselet<IN, OUT>> InfixParselets =
36+
new Dictionary<IN, InfixParselet<IN, OUT>>();
37+
38+
public Parser(List<Token<IN>> tokens, int startPosition)
39+
{
40+
Position = startPosition;
41+
Tokens = tokens;
42+
}
43+
44+
45+
46+
47+
public void infix(IN token, int precedence, BinaryExpressionBuilder<IN,OUT> builder, Associativity assoc = Associativity.Right)
48+
{
49+
InfixParselet<IN, OUT> infixparse = new InfixParselet<IN, OUT>(token, precedence, assoc, builder);
50+
InfixParselets[token] = infixparse;
51+
}
52+
53+
public void prefix(IN token, int precedence, UnaryExpressionBuilder<IN, OUT> builder)
54+
{
55+
PrefixParselet<IN, OUT> prefixparse = new PrefixParselet<IN, OUT>(token, precedence, builder);
56+
PrefixParselets[token] = prefixparse;
57+
}
58+
59+
public void register(IN token, PrefixParselet<IN, OUT> parselet)
60+
{
61+
PrefixParselets[token] = parselet;
62+
}
63+
64+
public void register(IN token, InfixParselet<IN, OUT> parselet)
65+
{
66+
InfixParselets[token] = parselet;
67+
}
68+
69+
public OUT parseExpression(int precedence)
70+
{
71+
Token<IN> token = lookAhead(0);
72+
PrefixParselet<IN, OUT> prefix = PrefixParselets.Get(token.TokenID);
73+
74+
OUT left = default(OUT);
75+
76+
if (prefix != null)
77+
{
78+
left = prefix.Parse(this, token);
79+
}
80+
81+
//OUT left = prefix.Parse(this, token);
82+
83+
while (precedence < getPrecedence())
84+
{
85+
token = consume();
86+
87+
InfixParselet<IN, OUT> infix = InfixParselets.Get(token.TokenID);
88+
left = infix.Parse(this, left, token);
89+
}
90+
91+
return left;
92+
}
93+
94+
public OUT parseExpression()
95+
{
96+
return parseExpression(0);
97+
}
98+
99+
public bool match(IN expected)
100+
{
101+
Token<IN> token = lookAhead(0);
102+
if (!token.TokenID.Equals(expected))
103+
{
104+
return false;
105+
}
106+
107+
return true;
108+
}
109+
110+
public Token<IN> consume(IN expected)
111+
{
112+
113+
Token<IN> token = Tokens[Position];
114+
if (!token.TokenID.Equals(expected))
115+
{
116+
throw new Exception("Expected token " + expected +
117+
" and found " + token.TokenID);
118+
}
119+
Position++;
120+
121+
return token;
122+
}
123+
124+
public Token<IN> consume()
125+
{
126+
// Make sure we've read the token.
127+
lookAhead(0);
128+
Token<IN> t = Tokens[Position];
129+
Position++;
130+
return t;
131+
}
132+
133+
private Token<IN> lookAhead(int distance)
134+
{
135+
// Read in as many as needed.
136+
//while (distance >= ReadTokens.Count)
137+
//{
138+
// ReadTokens.Add(Tokens[distance]);
139+
//}
140+
141+
// Get the queued token.
142+
return Tokens[Position+distance];
143+
144+
}
145+
146+
private int getPrecedence()
147+
{
148+
Token<IN> next = lookAhead(0);
149+
IN toktyp = next.TokenID;
150+
InfixParselet<IN,OUT> parser = InfixParselets.ContainsKey(toktyp) ? InfixParselets[toktyp] : null;
151+
if (parser != null) return parser.Precedence;
152+
153+
return 0;
154+
}
155+
156+
157+
}
158+
159+
}

Pratt/Pratt.csproj

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>netcoreapp2.0</TargetFramework>
6+
<RootNamespace>sly.pratt</RootNamespace>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<ProjectReference Include="..\samples\expressionParser\expressionParser.csproj" />
11+
<ProjectReference Include="..\sly\sly.csproj" />
12+
</ItemGroup>
13+
14+
</Project>

Pratt/Precedence.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
namespace sly.pratt.bantam {
2+
3+
/**
4+
* Defines the different precendence levels used by the infix parsers. These
5+
* determine how a series of infix expressions will be grouped. For example,
6+
* "a + b * c - d" will be parsed as "(a + (b * c)) - d" because "*" has higher
7+
* precedence than "+" and "-". Here, bigger numbers mean higher precedence.
8+
*/
9+
public class Precedence {
10+
// Ordered in increasing precedence.
11+
public static int ASSIGNMENT = 1;
12+
public static int CONDITIONAL = 2;
13+
public static int SUM = 3;
14+
public static int PRODUCT = 4;
15+
public static int EXPONENT = 5;
16+
public static int PREFIX = 6;
17+
public static int POSTFIX = 7;
18+
public static int CALL = 8;
19+
}
20+
}

0 commit comments

Comments
 (0)