github.com/aretext/aretext@v1.3.0/syntax/parser/combinators_test.go (about)

     1  package parser
     2  
     3  import (
     4  	"math"
     5  	"testing"
     6  
     7  	"github.com/stretchr/testify/assert"
     8  	"github.com/stretchr/testify/require"
     9  
    10  	"github.com/aretext/aretext/text"
    11  )
    12  
    13  func TestMaybeBefore(t *testing.T) {
    14  	// Parse consecutive numerals as numbers.
    15  	firstParseFunc := func(iter TrackingRuneIter, state State) Result {
    16  		var n uint64
    17  		for {
    18  			r, err := iter.NextRune()
    19  			if err != nil || r < '0' || r > '9' {
    20  				break
    21  			}
    22  			n++
    23  		}
    24  		return Result{
    25  			NumConsumed: n,
    26  			ComputedTokens: []ComputedToken{
    27  				{
    28  					Length: n,
    29  					Role:   TokenRoleNumber,
    30  				},
    31  			},
    32  			NextState: state,
    33  		}
    34  	}
    35  
    36  	// Parse alpha characters as keywords.
    37  	secondParseFunc := func(iter TrackingRuneIter, state State) Result {
    38  		var n uint64
    39  		for {
    40  			r, err := iter.NextRune()
    41  			if err != nil || r < 'A' || r > 'z' {
    42  				break
    43  			}
    44  			n++
    45  		}
    46  		return Result{
    47  			NumConsumed: n,
    48  			ComputedTokens: []ComputedToken{
    49  				{
    50  					Length: n,
    51  					Role:   TokenRoleKeyword,
    52  				},
    53  			},
    54  			NextState: state,
    55  		}
    56  	}
    57  
    58  	// Alpha characters, optionally prefixed with spaces.
    59  	combinedParseFunc := Func(firstParseFunc).MaybeBefore(Func(secondParseFunc))
    60  
    61  	testCases := []struct {
    62  		name     string
    63  		text     string
    64  		expected []Token
    65  	}{
    66  		{
    67  			name: "only second parse func",
    68  			text: "abc",
    69  			expected: []Token{
    70  				{StartPos: 0, EndPos: 3, Role: TokenRoleKeyword},
    71  			},
    72  		},
    73  		{
    74  			name: "first and second parse func",
    75  			text: "1234abc",
    76  			expected: []Token{
    77  				{StartPos: 0, EndPos: 4, Role: TokenRoleNumber},
    78  				{StartPos: 4, EndPos: 7, Role: TokenRoleKeyword},
    79  			},
    80  		},
    81  		{
    82  			name:     "only first parse func",
    83  			text:     "1234",
    84  			expected: nil,
    85  		},
    86  	}
    87  
    88  	for _, tc := range testCases {
    89  		t.Run(tc.name, func(t *testing.T) {
    90  			tree, err := text.NewTreeFromString(tc.text)
    91  			require.NoError(t, err)
    92  
    93  			p := New(combinedParseFunc)
    94  			p.ParseAll(tree)
    95  			tokens := p.TokensIntersectingRange(0, math.MaxUint64)
    96  			assert.Equal(t, tc.expected, tokens)
    97  		})
    98  	}
    99  
   100  }
   101  
   102  func TestThenCombinatorShiftTokens(t *testing.T) {
   103  	// Parse up to ":" as a keyword.
   104  	firstParseFunc := func(iter TrackingRuneIter, state State) Result {
   105  		var n uint64
   106  		for {
   107  			r, err := iter.NextRune()
   108  			if err != nil || r == ':' {
   109  				break
   110  			}
   111  			n++
   112  		}
   113  		return Result{
   114  			NumConsumed: n,
   115  			NextState:   state,
   116  			ComputedTokens: []ComputedToken{
   117  				{
   118  					Length: n,
   119  					Role:   TokenRoleKeyword,
   120  				},
   121  			},
   122  		}
   123  	}
   124  
   125  	// Parse rest of the string as a number.
   126  	secondParseFunc := func(iter TrackingRuneIter, state State) Result {
   127  		var n uint64
   128  		for {
   129  			_, err := iter.NextRune()
   130  			if err != nil {
   131  				break
   132  			}
   133  			n++
   134  		}
   135  		return Result{
   136  			NumConsumed: n,
   137  			NextState:   state,
   138  			ComputedTokens: []ComputedToken{
   139  				{
   140  					Length: n,
   141  					Role:   TokenRoleNumber,
   142  				},
   143  			},
   144  		}
   145  	}
   146  
   147  	tree, err := text.NewTreeFromString("abc:123")
   148  	require.NoError(t, err)
   149  
   150  	combinedParseFunc := Func(firstParseFunc).Then(Func(secondParseFunc))
   151  	p := New(combinedParseFunc)
   152  	p.ParseAll(tree)
   153  	tokens := p.TokensIntersectingRange(0, math.MaxUint64)
   154  	expectedTokens := []Token{
   155  		{StartPos: 0, EndPos: 3, Role: TokenRoleKeyword},
   156  		{StartPos: 3, EndPos: 7, Role: TokenRoleNumber},
   157  	}
   158  	assert.Equal(t, expectedTokens, tokens)
   159  }
   160  
   161  func TestMapWithInput(t *testing.T) {
   162  	// Parse func that consumes up to three runes in the input without producing any tokens.
   163  	parseFunc := func(iter TrackingRuneIter, state State) Result {
   164  		var n uint64
   165  		for n < 3 {
   166  			_, err := iter.NextRune()
   167  			if err != nil {
   168  				break
   169  			}
   170  			n++
   171  		}
   172  		return Result{
   173  			NumConsumed: n,
   174  			NextState:   state,
   175  		}
   176  	}
   177  
   178  	// MapFn that produces a single token with length of runes read from the iterator.
   179  	// This validates that the iterator returns only runes consumed by the parse func.
   180  	mapFn := func(result Result, iter TrackingRuneIter, state State) Result {
   181  		var n uint64
   182  		for {
   183  			_, err := iter.NextRune()
   184  			if err != nil {
   185  				break
   186  			}
   187  			n++
   188  		}
   189  
   190  		result.ComputedTokens = append(result.ComputedTokens, ComputedToken{
   191  			Offset: 0,
   192  			Length: n,
   193  			Role:   TokenRoleNumber,
   194  		})
   195  		return result
   196  	}
   197  
   198  	tree, err := text.NewTreeFromString("abc123")
   199  	require.NoError(t, err)
   200  
   201  	combinedParseFunc := Func(parseFunc).MapWithInput(mapFn)
   202  	p := New(combinedParseFunc)
   203  	p.ParseAll(tree)
   204  	tokens := p.TokensIntersectingRange(0, math.MaxUint64)
   205  	expectedTokens := []Token{
   206  		{StartPos: 0, EndPos: 3, Role: TokenRoleNumber},
   207  		{StartPos: 3, EndPos: 6, Role: TokenRoleNumber},
   208  	}
   209  	assert.Equal(t, expectedTokens, tokens)
   210  }