Skip to content

Commit 091f124

Browse files
authored
Merge pull request #546 from b3b00/feature/fluent-builder-errors
Feature/fluent builder errors
2 parents db954d6 + bef291e commit 091f124

File tree

8 files changed

+206
-27
lines changed

8 files changed

+206
-27
lines changed

src/sly/buildresult/BuildResult.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ namespace sly.buildresult
77
{
88
public class BuildResult<R>
99
{
10-
public BuildResult() : this(default)
10+
public BuildResult() : this(default(R))
1111
{ }
1212

1313
public BuildResult(R result)
@@ -16,6 +16,11 @@ public BuildResult(R result)
1616
Errors = new List<InitializationError>();
1717
}
1818

19+
public BuildResult(List<InitializationError> errors)
20+
{
21+
Errors = errors;
22+
}
23+
1924
public List<InitializationError> Errors { get; }
2025

2126
public R Result { get; set; }

src/sly/lexer/fluent/FluentLexerBuilder.cs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ public BuildResult<ILexer<IN>> Build(string lang)
315315
KeyWordIgnoreCase = _keyWordIgnoreCase
316316
};
317317

318-
BuildResult<ILexer<IN>> r = new BuildResult<ILexer<IN>>();
318+
BuildResult<ILexer<IN>> lexerResult = new BuildResult<ILexer<IN>>();
319319

320320
Func<KeyValuePair<IN, (List<LexemeAttribute> lexemes, List<LexemeLabelAttribute> labels)>, (List<string> modes,
321321
bool isModePopper, string pushTarget)> modesGetter =
@@ -336,11 +336,13 @@ public BuildResult<ILexer<IN>> Build(string lang)
336336
}
337337
return (modes, isModePopper, pushMode);
338338
};
339-
340-
341-
342-
var lexerResult = LexerBuilder.BuildLexer(r, _extensionBuilder, lang, _lexerPostProcessor, _explicitTokens, _lexemesDefinitionsAndLabels, lexerConfig, _comments, modesGetter, () => _callbacks);
343-
339+
340+
if (lexerResult.IsOk)
341+
{
342+
lexerResult = LexerBuilder.BuildLexer(lexerResult, _extensionBuilder, lang, _lexerPostProcessor,
343+
_explicitTokens, _lexemesDefinitionsAndLabels, lexerConfig, _comments, modesGetter, () => _callbacks);
344+
}
345+
344346
return lexerResult;
345347
}
346348

src/sly/parser/fluent/FluentEBNFParserBuilder.cs

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using System.Linq;
44
using sly.buildresult;
5+
using sly.i18n;
56
using sly.lexer.fluent;
67
using sly.parser.fluent;
78
using sly.parser.generator.visitor;
@@ -38,11 +39,17 @@ public class FluentEBNFParserBuilder<IN, OUT> : IFluentEbnfRuleBuilder<IN, OUT>
3839
private Rule<IN,OUT> _currentRule;
3940

4041
private ParserConfiguration<IN, OUT> _configuration = null;
42+
43+
private string I18N { get; set; } = "en";
44+
45+
4146
public static IFluentEbnfParserBuilder<IN,OUT> NewBuilder(object parserInstance, string rootRule, string i18N = "en")
4247
{
4348
return new FluentEBNFParserBuilder<IN,OUT>(i18N, parserInstance, rootRule);
4449
}
4550

51+
52+
4653
public static IFluentEbnfParserBuilder<IN,OUT> NewBuilder(string rootRule, string i18N = "en")
4754
{
4855
return NewBuilder(null, rootRule, i18N);
@@ -58,7 +65,7 @@ private FluentEBNFParserBuilder(string i18N, object parserInstance, string rootR
5865
_grammarParser = grammar.Result;
5966
}
6067

61-
public ISyntaxParser<IN, OUT> BuildSyntaxParser(BuildResult<ParserConfiguration<IN, OUT>> result)
68+
public BuildResult<ISyntaxParser<IN, OUT>> BuildSyntaxParser(BuildResult<ParserConfiguration<IN, OUT>> result)
6269
{
6370
// build configuration
6471
_configuration = new ParserConfiguration<IN, OUT>();
@@ -79,21 +86,41 @@ public ISyntaxParser<IN, OUT> BuildSyntaxParser(BuildResult<ParserConfiguration<
7986
_operationsByPrecedence, out var expressionResult);
8087
if (!ok)
8188
{
82-
return null;
89+
return new BuildResult<ISyntaxParser<IN, OUT>>(result.Errors);
8390
}
8491
}
8592

86-
var b = new EBNFParserBuilder<IN,OUT>("en");
93+
var b = new EBNFParserBuilder<IN,OUT>(I18N);
94+
b.SetEmptyNonTerminals(_configuration);
95+
// check left recursion.
96+
var (foundRecursion, recursions) = LeftRecursionChecker<IN, OUT>.CheckLeftRecursion(_configuration);
97+
if (foundRecursion)
98+
{
99+
var recs = string.Join("\n", recursions.Select<List<string>, string>(x => string.Join(" > ", x)));
100+
result.AddError(new ParserInitializationError(ErrorLevel.FATAL,
101+
i18n.I18N.Instance.GetText(I18N, I18NMessage.LeftRecursion, recs),
102+
ErrorCodes.PARSER_LEFT_RECURSIVE));
103+
return new BuildResult<ISyntaxParser<IN, OUT>>(result.Errors);
104+
}
87105
var syntaxParser = b.BuildSyntaxParser(_configuration, ParserType.EBNF_LL_RECURSIVE_DESCENT, _rootRule);
106+
var checkResult =b.CheckParser(_configuration);
107+
if (!checkResult.IsOk)
108+
{
109+
result.AddErrors(checkResult.Errors);
110+
var checkedParser = new BuildResult<ISyntaxParser<IN, OUT>>();
111+
checkedParser.AddErrors(checkResult.Errors);
112+
return checkedParser;
113+
}
114+
88115
// initialize starting tokens
89116
syntaxParser.Init(_configuration,_rootRule);
90-
return syntaxParser;
117+
return new BuildResult<ISyntaxParser<IN, OUT>>(syntaxParser);
91118
}
92119

93120
public BuildResult<Parser<IN, OUT>> BuildParser()
94121
{
95122
var buildResult = new BuildResult<ParserConfiguration<IN, OUT>>();
96-
var syntaxParser = BuildSyntaxParser(buildResult);
123+
var syntaxParserResult = BuildSyntaxParser(buildResult);
97124
if (buildResult.IsError)
98125
{
99126
var result = new BuildResult<Parser<IN, OUT>>();
@@ -112,7 +139,7 @@ public BuildResult<Parser<IN, OUT>> BuildParser()
112139

113140
if (lexer.IsOk)
114141
{
115-
Parser<IN, OUT> parser = new Parser<IN, OUT>(_i18N, syntaxParser, visitor);
142+
Parser<IN, OUT> parser = new Parser<IN, OUT>(_i18N, syntaxParserResult.Result, visitor);
116143
parser.Configuration = _configuration;
117144
parser.Lexer = lexer.Result;
118145
return new BuildResult<Parser<IN, OUT>>(parser);
@@ -122,6 +149,12 @@ public BuildResult<Parser<IN, OUT>> BuildParser()
122149
error.AddErrors(lexer.Errors);
123150
return error;
124151
}
152+
153+
public IFluentEbnfParserBuilder<IN, OUT> WithLang(string i18nLang)
154+
{
155+
I18N = i18nLang;
156+
return this;
157+
}
125158

126159
public IFluentEbnfParserBuilder<IN, OUT> WithLexerbuilder(IFluentLexerBuilder<IN> lexerBuilder)
127160
{

src/sly/parser/fluent/IFluentEbnfParserBuilder.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ public interface IFluentEbnfRuleBuilder<IN, OUT> : IFluentEbnfParserBuilder<IN,
88
{
99
public IFluentEbnfParserBuilder<IN, OUT> Named(string name);
1010

11+
public IFluentEbnfParserBuilder<IN, OUT> WithLang(string i18nLang);
12+
1113
public IFluentEbnfParserBuilder<IN, OUT> WithSubNodeNamed(params string[] subNodeNames);
1214
}
1315

@@ -44,7 +46,7 @@ public interface IFluentEbnfParserBuilder<IN,OUT> where IN : struct
4446

4547
IFluentEbnfRuleBuilder<IN,OUT> Postfix(string operation, int precedence, Func<object[], OUT> visitor);
4648

47-
public ISyntaxParser<IN, OUT> BuildSyntaxParser(BuildResult<ParserConfiguration<IN, OUT>> result);
49+
public BuildResult<ISyntaxParser<IN, OUT>> BuildSyntaxParser(BuildResult<ParserConfiguration<IN, OUT>> result);
4850

4951
public BuildResult<Parser<IN, OUT>> BuildParser();
5052

src/sly/parser/generator/EBNFParserBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ public override ISyntaxParser<IN, OUT> BuildSyntaxParser(ParserConfiguration<IN,
119119
return parser;
120120
}
121121

122-
private void SetEmptyNonTerminals(ParserConfiguration<IN, OUT> conf)
122+
public void SetEmptyNonTerminals(ParserConfiguration<IN, OUT> conf)
123123
{
124124
bool stillSetting = true;
125125
while (stillSetting)

src/sly/parser/generator/ParserBuilder.cs

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ public Rule<IN, OUT> BuildNonTerminal(Tuple<string, string> ntAndRule)
277277

278278
private BuildResult<Parser<IN, OUT>> CheckParser(BuildResult<Parser<IN, OUT>> result)
279279
{
280-
var checkers = new List<Func<BuildResult<Parser<IN, OUT>>,NonTerminal<IN, OUT>,BuildResult<Parser<IN, OUT>>>>
280+
var checkers = new List<Func<ParserConfiguration<IN,OUT>, BuildResult<Parser<IN, OUT>>,NonTerminal<IN, OUT>,BuildResult<Parser<IN, OUT>>>>
281281
{
282282
CheckUnreachable,
283283
CheckNotFound,
@@ -294,22 +294,42 @@ private BuildResult<Parser<IN, OUT>> CheckParser(BuildResult<Parser<IN, OUT>> re
294294
{
295295
if (checker != null)
296296
result.Result.Configuration.NonTerminals.Values.ToList<NonTerminal<IN, OUT>>()
297-
.ForEach(nt => result = checker(result, nt));
297+
.ForEach(nt => result = checker(result.Result.Configuration, result, nt));
298298
}
299299

300300
return result;
301301
}
302302

303-
private BuildResult<Parser<IN, OUT>> CheckUnreachable(BuildResult<Parser<IN, OUT>> result,
303+
public BuildResult<Parser<IN, OUT>> CheckParser(ParserConfiguration<IN, OUT> configuration)
304+
{
305+
var checkers = new List<Func<ParserConfiguration<IN,OUT>, BuildResult<Parser<IN, OUT>>,NonTerminal<IN, OUT>,BuildResult<Parser<IN, OUT>>>>
306+
{
307+
CheckUnreachable,
308+
CheckNotFound,
309+
CheckAlternates
310+
};
311+
var result = new BuildResult<Parser<IN, OUT>>();
312+
313+
foreach (var checker in checkers)
314+
{
315+
if (checker != null)
316+
configuration.NonTerminals.Values.ToList<NonTerminal<IN, OUT>>()
317+
.ForEach(nt => result = checker(configuration, result, nt));
318+
}
319+
320+
return result;
321+
}
322+
323+
private BuildResult<Parser<IN, OUT>> CheckUnreachable(ParserConfiguration<IN,OUT> configuration, BuildResult<Parser<IN, OUT>> result,
304324
NonTerminal<IN, OUT> nonTerminal)
305325
{
306-
var conf = result.Result.Configuration;
326+
307327
var found = false;
308-
if (nonTerminal.Name == conf.StartingRule)
328+
if (nonTerminal.Name == configuration.StartingRule)
309329
{
310330
return result;
311331
}
312-
foreach (var nt in result.Result.Configuration.NonTerminals.Values.ToList<NonTerminal<IN, OUT>>())
332+
foreach (var nt in configuration.NonTerminals.Values.ToList<NonTerminal<IN, OUT>>())
313333
if (nt.Name != nonTerminal.Name)
314334
{
315335
found = IsNonTerminalReferencing(nt, nonTerminal.Name);
@@ -395,17 +415,17 @@ private static bool IsNonTerminalReferencing(NonTerminal<IN, OUT> nonTerminal, s
395415
}
396416

397417

398-
private BuildResult<Parser<IN, OUT>> CheckNotFound(BuildResult<Parser<IN, OUT>> result,
418+
private BuildResult<Parser<IN, OUT>> CheckNotFound(ParserConfiguration<IN,OUT> configuration, BuildResult<Parser<IN, OUT>> result,
399419
NonTerminal<IN, OUT> nonTerminal)
400420
{
401-
var conf = result.Result.Configuration;
421+
402422
foreach (var rule in nonTerminal.Rules)
403423
{
404424
foreach (var clause in rule.Clauses)
405425
{
406426
{
407427
if (clause is NonTerminalClause<IN, OUT> ntClause &&
408-
!conf.NonTerminals.ContainsKey(ntClause.NonTerminalName))
428+
!configuration.NonTerminals.ContainsKey(ntClause.NonTerminalName))
409429
{
410430
result.AddError(new ParserInitializationError(ErrorLevel.ERROR,
411431
i18n.I18N.Instance.GetText(I18N, I18NMessage.ReferenceNotFound,
@@ -420,7 +440,7 @@ private BuildResult<Parser<IN, OUT>> CheckNotFound(BuildResult<Parser<IN, OUT>>
420440
return result;
421441
}
422442

423-
private BuildResult<Parser<IN, OUT>> CheckAlternates(BuildResult<Parser<IN, OUT>> result,
443+
private BuildResult<Parser<IN, OUT>> CheckAlternates(ParserConfiguration<IN,OUT> configuration, BuildResult<Parser<IN, OUT>> result,
424444
NonTerminal<IN, OUT> nonTerminal)
425445
{
426446
foreach (var rule in nonTerminal.Rules)
@@ -451,7 +471,7 @@ private BuildResult<Parser<IN, OUT>> CheckAlternates(BuildResult<Parser<IN, OUT>
451471
return result;
452472
}
453473

454-
private BuildResult<Parser<IN, OUT>> CheckVisitorsSignature(BuildResult<Parser<IN, OUT>> result,
474+
private BuildResult<Parser<IN, OUT>> CheckVisitorsSignature(ParserConfiguration<IN,OUT> configuration, BuildResult<Parser<IN, OUT>> result,
455475
NonTerminal<IN, OUT> nonTerminal)
456476
{
457477
foreach (var rule in nonTerminal.Rules)

0 commit comments

Comments
 (0)