Skip to content

Commit

Permalink
Fix whitespace skipping (#43)
Browse files Browse the repository at this point in the history
  • Loading branch information
sebastienros authored May 3, 2021
1 parent 5497d56 commit 74363cb
Show file tree
Hide file tree
Showing 15 changed files with 260 additions and 274 deletions.
26 changes: 26 additions & 0 deletions docs/parsers.md
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,32 @@ Result:
0 // failure
```

### SkipWhiteSpace

Matches a parser after any blank spaces. This parser respects the `Scanner` options related to multi-line grammars.


```c#
Parser<T> SkipWhiteSpace<T>(Parser<T> parser)
```

Usage:

```c#
var parser = SkipWhiteSpace(Literals.Text("abc"));
parser.Parse("abc");
parser.Parse(" abc");
```

Result:

```
"abc"
"abc"
```

> Note: This parser is used by all Terms (e.g., Terms.Text) to skip blank spaces before a Literal.
### Deferred

Creates a parser that can be references before it is actually defined. This is used when there is a cyclic dependency between parsers.
Expand Down
8 changes: 6 additions & 2 deletions src/Parlot/Cursor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,17 @@ internal bool AdvanceOnce()
/// <summary>
/// Moves the cursor to the specific position
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ResetPosition(in TextPosition position)
{
if (position.Offset == _offset)
if (position.Offset != _offset)
{
return;
ResetPositionNotInlined(position);
}
}

private void ResetPositionNotInlined(in TextPosition position)
{
_offset = position.Offset;
_line = position.Line;
_column = position.Column;
Expand Down
112 changes: 39 additions & 73 deletions src/Parlot/Fluent/Between.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,86 +10,39 @@ public sealed class Between<A, T, B> : Parser<T>, ICompilable
private readonly Parser<A> _before;
private readonly Parser<B> _after;

private readonly bool _beforeIsChar;
private readonly char _beforeChar;
private readonly bool _beforeSkipWhiteSpace;

private readonly bool _afterIsChar;
private readonly char _afterChar;
private readonly bool _afterSkipWhiteSpace;

public Between(Parser<A> before, Parser<T> parser, Parser<B> after)
{
_before = before ?? throw new ArgumentNullException(nameof(before));
_parser = parser ?? throw new ArgumentNullException(nameof(parser));
_after = after ?? throw new ArgumentNullException(nameof(after));

if (before is CharLiteral literal1)
{
_beforeIsChar = true;
_beforeChar = literal1.Char;
_beforeSkipWhiteSpace = literal1.SkipWhiteSpace;
}

if (after is CharLiteral literal2)
{
_afterIsChar = true;
_afterChar = literal2.Char;
_afterSkipWhiteSpace = literal2.SkipWhiteSpace;
}
}

public override bool Parse(ParseContext context, ref ParseResult<T> result)
{
context.EnterParser(this);

if (_beforeIsChar)
{
if (_beforeSkipWhiteSpace)
{
context.SkipWhiteSpace();
}

if (!context.Scanner.ReadChar(_beforeChar))
{
return false;
}
}
else
{
var parsedA = new ParseResult<A>();
var start = context.Scanner.Cursor.Position;

if (!_before.Parse(context, ref parsedA))
{
return false;
}
var parsedA = new ParseResult<A>();

if (!_before.Parse(context, ref parsedA))
{
// Don't reset position since _before should do it
return false;
}

if (!_parser.Parse(context, ref result))
{
context.Scanner.Cursor.ResetPosition(start);
return false;
}

if (_afterIsChar)
{
if (_afterSkipWhiteSpace)
{
context.SkipWhiteSpace();
}

if (!context.Scanner.ReadChar(_afterChar))
{
return false;
}
}
else
{
var parsedB = new ParseResult<B>();
var parsedB = new ParseResult<B>();

if (!_after.Parse(context, ref parsedB))
{
return false;
}
if (!_after.Parse(context, ref parsedB))
{
context.Scanner.Cursor.ResetPosition(start);
return false;
}

return true;
Expand All @@ -102,28 +55,37 @@ public CompilationResult Compile(CompilationContext context)
var success = context.DeclareSuccessVariable(result, false);
var value = context.DeclareValueVariable(result, Expression.Default(typeof(T)));

// start = context.Scanner.Cursor.Position;
//
// before instructions
//
//
// if (before.Success)
// {
// parser instructions
//
// if (parser.Success)
// {
// after instructions
//
// if (after.Success)
// {
// success = true;
// value = parser.Value;
// }
// }
// parser instructions
//
// if (parser.Success)
// {
// after instructions
//
// if (after.Success)
// {
// success = true;
// value = parser.Value;
// }
// }
//
// if (!success)
// {
// resetPosition(start);
// }
// }

var beforeCompileResult = _before.Build(context);
var parserCompileResult = _parser.Build(context);
var afterCompileResult = _after.Build(context);

var start = context.DeclarePositionVariable(result);

var block = Expression.Block(
beforeCompileResult.Variables,
Expression.Block(beforeCompileResult.Body),
Expand All @@ -147,6 +109,10 @@ public CompilationResult Compile(CompilationContext context)
)
)
)
),
Expression.IfThen(
Expression.Not(success),
context.ResetPosition(start)
)
)
)
Expand Down
21 changes: 1 addition & 20 deletions src/Parlot/Fluent/CharLiteral.cs
Original file line number Diff line number Diff line change
@@ -1,30 +1,21 @@
using Parlot.Compilation;
using System;
using System.Linq.Expressions;

namespace Parlot.Fluent
{
public sealed class CharLiteral : Parser<char>, ICompilable
{
public CharLiteral(char c, bool skipWhiteSpace = true)
public CharLiteral(char c)
{
Char = c;
SkipWhiteSpace = skipWhiteSpace;
}

public char Char { get; }

public bool SkipWhiteSpace { get; }

public override bool Parse(ParseContext context, ref ParseResult<char> result)
{
context.EnterParser(this);

if (SkipWhiteSpace)
{
context.SkipWhiteSpace();
}

var start = context.Scanner.Cursor.Offset;

if (context.Scanner.ReadChar(Char))
Expand All @@ -43,16 +34,6 @@ public CompilationResult Compile(CompilationContext context)
var success = context.DeclareSuccessVariable(result, false);
var value = context.DeclareValueVariable(result, Expression.Default(typeof(char)));

//if (_skipWhiteSpace)
//{
// context.SkipWhiteSpace();
//}

if (SkipWhiteSpace)
{
result.Body.Add(context.ParserSkipWhiteSpace());
}

// if (context.Scanner.ReadChar(Char))
// {
// success = true;
Expand Down
37 changes: 5 additions & 32 deletions src/Parlot/Fluent/DecimalLiteral.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,18 @@ namespace Parlot.Fluent
public sealed class DecimalLiteral : Parser<decimal>, ICompilable
{
private readonly NumberOptions _numberOptions;
private readonly bool _skipWhiteSpace;

public DecimalLiteral(NumberOptions numberOptions = NumberOptions.Default, bool skipWhiteSpace = true)
public DecimalLiteral(NumberOptions numberOptions = NumberOptions.Default)
{
_numberOptions = numberOptions;
_skipWhiteSpace = skipWhiteSpace;
}

public override bool Parse(ParseContext context, ref ParseResult<decimal> result)
{
context.EnterParser(this);

var reset = context.Scanner.Cursor.Position;

if (_skipWhiteSpace)
{
context.SkipWhiteSpace();
}

var start = context.Scanner.Cursor.Offset;
var start = reset.Offset;

if ((_numberOptions & NumberOptions.AllowSign) == NumberOptions.AllowSign)
{
Expand All @@ -42,7 +34,7 @@ public override bool Parse(ParseContext context, ref ParseResult<decimal> result
{
var end = context.Scanner.Cursor.Offset;
#if NETSTANDARD2_0
var sourceToParse = context.Scanner.Buffer.Substring(start, end -start);
var sourceToParse = context.Scanner.Buffer.Substring(start, end - start);
#else
var sourceToParse = context.Scanner.Buffer.AsSpan(start, end - start);
#endif
Expand All @@ -66,31 +58,12 @@ public CompilationResult Compile(CompilationContext context)
var success = context.DeclareSuccessVariable(result, false);
var value = context.DeclareValueVariable(result, Expression.Default(typeof(decimal)));

// TODO: if !_skiptWhiteSpace and !NumberOptions.AllowSign then we don't need to store the reset position
// since the ReadDecimal method will do it at the correct location.

//
// var start = context.Scanner.Cursor.Offset;
// var reset = context.Scanner.Cursor.Position;
//
// if (_skipWhiteSpace)
// {
// context.SkipWhiteSpace();
// }

var start = context.DeclareOffsetVariable(result);
var reset = context.DeclarePositionVariable(result);

if (_skipWhiteSpace)
{
result.Body.Add(context.ParserSkipWhiteSpace());
}

// var start = context.Scanner.Cursor.Offset;

var start = Expression.Variable(typeof(int), $"start{context.NextNumber}");
result.Variables.Add(start);

result.Body.Add(Expression.Assign(start, context.Offset()));

if ((_numberOptions & NumberOptions.AllowSign) == NumberOptions.AllowSign)
{
// if (!context.Scanner.ReadChar('-'))
Expand Down
19 changes: 1 addition & 18 deletions src/Parlot/Fluent/Identifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,17 @@ public sealed class Identifier : Parser<TextSpan>, ICompilable
{
private readonly Func<char, bool> _extraStart;
private readonly Func<char, bool> _extraPart;
private readonly bool _skipWhiteSpace;

public Identifier(Func<char, bool> extraStart = null, Func<char, bool> extraPart = null, bool skipWhiteSpace = true)
public Identifier(Func<char, bool> extraStart = null, Func<char, bool> extraPart = null)
{
_extraStart = extraStart;
_extraPart = extraPart;
_skipWhiteSpace = skipWhiteSpace;
}

public override bool Parse(ParseContext context, ref ParseResult<TextSpan> result)
{
context.EnterParser(this);

if (_skipWhiteSpace)
{
context.SkipWhiteSpace();
}

var first = context.Scanner.Cursor.Current;

if (Character.IsIdentifierStart(first) || _extraStart != null && _extraStart(first))
Expand Down Expand Up @@ -57,16 +50,6 @@ public CompilationResult Compile(CompilationContext context)
var success = context.DeclareSuccessVariable(result, false);
var value = context.DeclareValueVariable(result, Expression.Default(typeof(TextSpan)));

//if (_skipWhiteSpace)
//{
// context.SkipWhiteSpace();
//}

if (_skipWhiteSpace)
{
result.Body.Add(context.ParserSkipWhiteSpace());
}

// var first = context.Scanner.Cursor.Current;

var first = Expression.Parameter(typeof(char), $"first{context.NextNumber}");
Expand Down
Loading

0 comments on commit 74363cb

Please sign in to comment.