@@ -11,6 +11,7 @@ const unicode = std.unicode;
11
11
const meta = std .meta ;
12
12
const lossyCast = math .lossyCast ;
13
13
const expectFmt = std .testing .expectFmt ;
14
+ const testing = std .testing ;
14
15
15
16
pub const default_max_depth = 3 ;
16
17
@@ -319,7 +320,6 @@ pub const Specifier = union(enum) {
319
320
/// Allows to implement formatters compatible with std.fmt without replicating
320
321
/// the standard library behavior.
321
322
pub const Parser = struct {
322
- pos : usize = 0 ,
323
323
iter : std.unicode.Utf8Iterator ,
324
324
325
325
// Returns a decimal number or null if the current character is not a
@@ -345,23 +345,26 @@ pub const Parser = struct {
345
345
// Returns a substring of the input starting from the current position
346
346
// and ending where `ch` is found or until the end if not found
347
347
pub fn until (self : * @This (), ch : u21 ) []const u8 {
348
- var result : [] const u8 = &[ _ ] u8 {} ;
348
+ const start = self . iter . i ;
349
349
while (self .peek (0 )) | code_point | {
350
350
if (code_point == ch )
351
351
break ;
352
- result = result ++ ( self .iter .nextCodepointSlice () orelse &[ _ ] u8 {} );
352
+ _ = self .iter .nextCodepoint ( );
353
353
}
354
- return result ;
354
+ return self . iter . bytes [ start .. self . iter . i ] ;
355
355
}
356
356
357
- // Returns one character, if available
357
+ // Returns the character pointed to by the iterator if available, or
358
+ // null otherwise
358
359
pub fn char (self : * @This ()) ? u21 {
359
360
if (self .iter .nextCodepoint ()) | code_point | {
360
361
return code_point ;
361
362
}
362
363
return null ;
363
364
}
364
365
366
+ // Returns true if the iterator points to an existing character and
367
+ // false otherwise
365
368
pub fn maybe (self : * @This (), val : u21 ) bool {
366
369
if (self .peek (0 ) == val ) {
367
370
_ = self .iter .nextCodepoint ();
@@ -2779,3 +2782,239 @@ test hex {
2779
2782
try std .testing .expectEqualStrings ("[00efcdab78563412]" , s );
2780
2783
}
2781
2784
}
2785
+
2786
+ test "parser until" {
2787
+ { // return substring till ':'
2788
+ var parser : Parser = .{
2789
+ .iter = .{ .bytes = "abc:1234" , .i = 0 },
2790
+ };
2791
+ try testing .expectEqualStrings ("abc" , parser .until (':' ));
2792
+ }
2793
+
2794
+ { // return the entire string - `ch` not found
2795
+ var parser : Parser = .{
2796
+ .iter = .{ .bytes = "abc1234" , .i = 0 },
2797
+ };
2798
+ try testing .expectEqualStrings ("abc1234" , parser .until (':' ));
2799
+ }
2800
+
2801
+ { // substring is empty - `ch` is the only character
2802
+ var parser : Parser = .{
2803
+ .iter = .{ .bytes = ":" , .i = 0 },
2804
+ };
2805
+ try testing .expectEqualStrings ("" , parser .until (':' ));
2806
+ }
2807
+
2808
+ { // empty string and `ch` not found
2809
+ var parser : Parser = .{
2810
+ .iter = .{ .bytes = "" , .i = 0 },
2811
+ };
2812
+ try testing .expectEqualStrings ("" , parser .until (':' ));
2813
+ }
2814
+
2815
+ { // substring starts at index 2 and goes upto `ch`
2816
+ var parser : Parser = .{
2817
+ .iter = .{ .bytes = "abc:1234" , .i = 2 },
2818
+ };
2819
+ try testing .expectEqualStrings ("c" , parser .until (':' ));
2820
+ }
2821
+
2822
+ { // substring starts at index 4 and goes upto the end - `ch` not found
2823
+ var parser : Parser = .{
2824
+ .iter = .{ .bytes = "abc1234" , .i = 4 },
2825
+ };
2826
+ try testing .expectEqualStrings ("234" , parser .until (':' ));
2827
+ }
2828
+ }
2829
+
2830
+ test "parser peek" {
2831
+ { // start iteration from the first index
2832
+ var parser : Parser = .{
2833
+ .iter = .{ .bytes = "hello world" , .i = 0 },
2834
+ };
2835
+
2836
+ try testing .expectEqual ('h' , parser .peek (0 ));
2837
+ try testing .expectEqual ('e' , parser .peek (1 ));
2838
+ try testing .expectEqual (' ' , parser .peek (5 ));
2839
+ try testing .expectEqual ('d' , parser .peek (10 ));
2840
+ try testing .expectEqual (null , parser .peek (11 ));
2841
+ }
2842
+
2843
+ { // start iteration from the second last index
2844
+ var parser : Parser = .{
2845
+ .iter = .{ .bytes = "hello world!" , .i = 10 },
2846
+ };
2847
+
2848
+ try testing .expectEqual ('d' , parser .peek (0 ));
2849
+ try testing .expectEqual ('!' , parser .peek (1 ));
2850
+ try testing .expectEqual (null , parser .peek (5 ));
2851
+ }
2852
+
2853
+ { // start iteration beyond the length of the string
2854
+ var parser : Parser = .{
2855
+ .iter = .{ .bytes = "hello" , .i = 5 },
2856
+ };
2857
+
2858
+ try testing .expectEqual (null , parser .peek (0 ));
2859
+ try testing .expectEqual (null , parser .peek (1 ));
2860
+ }
2861
+
2862
+ { // empty string
2863
+ var parser : Parser = .{
2864
+ .iter = .{ .bytes = "" , .i = 0 },
2865
+ };
2866
+
2867
+ try testing .expectEqual (null , parser .peek (0 ));
2868
+ try testing .expectEqual (null , parser .peek (2 ));
2869
+ }
2870
+ }
2871
+
2872
+ test "parser char" {
2873
+ // character exists - iterator at 0
2874
+ var parser : Parser = .{ .iter = .{ .bytes = "~~hello" , .i = 0 } };
2875
+ try testing .expectEqual ('~' , parser .char ());
2876
+
2877
+ // character exists - iterator in the middle
2878
+ parser = .{ .iter = .{ .bytes = "~~hello" , .i = 3 } };
2879
+ try testing .expectEqual ('e' , parser .char ());
2880
+
2881
+ // character exists - iterator at the end
2882
+ parser = .{ .iter = .{ .bytes = "~~hello" , .i = 6 } };
2883
+ try testing .expectEqual ('o' , parser .char ());
2884
+
2885
+ // character doesn't exist - iterator beyond the length of the string
2886
+ parser = .{ .iter = .{ .bytes = "~~hello" , .i = 7 } };
2887
+ try testing .expectEqual (null , parser .char ());
2888
+ }
2889
+
2890
+ test "parser maybe" {
2891
+ // character exists - iterator at 0
2892
+ var parser : Parser = .{ .iter = .{ .bytes = "hello world" , .i = 0 } };
2893
+ try testing .expect (parser .maybe ('h' ));
2894
+
2895
+ // character exists - iterator at space
2896
+ parser = .{ .iter = .{ .bytes = "hello world" , .i = 5 } };
2897
+ try testing .expect (parser .maybe (' ' ));
2898
+
2899
+ // character exists - iterator at the end
2900
+ parser = .{ .iter = .{ .bytes = "hello world" , .i = 10 } };
2901
+ try testing .expect (parser .maybe ('d' ));
2902
+
2903
+ // character doesn't exist - iterator beyond the length of the string
2904
+ parser = .{ .iter = .{ .bytes = "hello world" , .i = 11 } };
2905
+ try testing .expect (! parser .maybe ('e' ));
2906
+ }
2907
+
2908
+ test "parser number" {
2909
+ // input is a single digit natural number - iterator at 0
2910
+ var parser : Parser = .{ .iter = .{ .bytes = "7" , .i = 0 } };
2911
+ try testing .expect (7 == parser .number ());
2912
+
2913
+ // input is a two digit natural number - iterator at 1
2914
+ parser = .{ .iter = .{ .bytes = "29" , .i = 1 } };
2915
+ try testing .expect (9 == parser .number ());
2916
+
2917
+ // input is a two digit natural number - iterator beyond the length of the string
2918
+ parser = .{ .iter = .{ .bytes = "32" , .i = 2 } };
2919
+ try testing .expectEqual (null , parser .number ());
2920
+
2921
+ // input is an integer
2922
+ parser = .{ .iter = .{ .bytes = "0" , .i = 0 } };
2923
+ try testing .expect (0 == parser .number ());
2924
+
2925
+ // input is a negative integer
2926
+ parser = .{ .iter = .{ .bytes = "-2" , .i = 0 } };
2927
+ try testing .expectEqual (null , parser .number ());
2928
+
2929
+ // input is a string
2930
+ parser = .{ .iter = .{ .bytes = "no_number" , .i = 2 } };
2931
+ try testing .expectEqual (null , parser .number ());
2932
+
2933
+ // input is a single character string
2934
+ parser = .{ .iter = .{ .bytes = "n" , .i = 0 } };
2935
+ try testing .expectEqual (null , parser .number ());
2936
+
2937
+ // input is an empty string
2938
+ parser = .{ .iter = .{ .bytes = "" , .i = 0 } };
2939
+ try testing .expectEqual (null , parser .number ());
2940
+ }
2941
+
2942
+ test "parser specifier" {
2943
+ { // input string is a digit; iterator at 0
2944
+ const expected : Specifier = Specifier { .number = 1 };
2945
+ var parser : Parser = .{ .iter = .{ .bytes = "1" , .i = 0 } };
2946
+
2947
+ const result = try parser .specifier ();
2948
+ try testing .expect (expected .number == result .number );
2949
+ }
2950
+
2951
+ { // input string is a two digit number; iterator at 0
2952
+ const digit : Specifier = Specifier { .number = 42 };
2953
+ var parser : Parser = .{ .iter = .{ .bytes = "42" , .i = 0 } };
2954
+
2955
+ const result = try parser .specifier ();
2956
+ try testing .expect (digit .number == result .number );
2957
+ }
2958
+
2959
+ { // input string is a two digit number digit; iterator at 1
2960
+ const digit : Specifier = Specifier { .number = 8 };
2961
+ var parser : Parser = .{ .iter = .{ .bytes = "28" , .i = 1 } };
2962
+
2963
+ const result = try parser .specifier ();
2964
+ try testing .expect (digit .number == result .number );
2965
+ }
2966
+
2967
+ { // input string is a two digit number with square brackets; iterator at 0
2968
+ const digit : Specifier = Specifier { .named = "15" };
2969
+ var parser : Parser = .{ .iter = .{ .bytes = "[15]" , .i = 0 } };
2970
+
2971
+ const result = try parser .specifier ();
2972
+ try testing .expectEqualStrings (digit .named , result .named );
2973
+ }
2974
+
2975
+ { // input string is not a number and contains square brackets; iterator at 0
2976
+ const digit : Specifier = Specifier { .named = "hello" };
2977
+ var parser : Parser = .{ .iter = .{ .bytes = "[hello]" , .i = 0 } };
2978
+
2979
+ const result = try parser .specifier ();
2980
+ try testing .expectEqualStrings (digit .named , result .named );
2981
+ }
2982
+
2983
+ { // input string is not a number and doesn't contain closing square bracket; iterator at 0
2984
+ var parser : Parser = .{ .iter = .{ .bytes = "[hello" , .i = 0 } };
2985
+
2986
+ const result = parser .specifier ();
2987
+ try testing .expectError (@field (anyerror , "Expected closing ]" ), result );
2988
+ }
2989
+
2990
+ { // input string is not a number and doesn't contain closing square bracket; iterator at 2
2991
+ var parser : Parser = .{ .iter = .{ .bytes = "[[[[hello" , .i = 2 } };
2992
+
2993
+ const result = parser .specifier ();
2994
+ try testing .expectError (@field (anyerror , "Expected closing ]" ), result );
2995
+ }
2996
+
2997
+ { // input string is not a number and contains unbalanced square brackets; iterator at 0
2998
+ const digit : Specifier = Specifier { .named = "[[hello" };
2999
+ var parser : Parser = .{ .iter = .{ .bytes = "[[[hello]" , .i = 0 } };
3000
+
3001
+ const result = try parser .specifier ();
3002
+ try testing .expectEqualStrings (digit .named , result .named );
3003
+ }
3004
+
3005
+ { // input string is not a number and contains unbalanced square brackets; iterator at 1
3006
+ const digit : Specifier = Specifier { .named = "[[hello" };
3007
+ var parser : Parser = .{ .iter = .{ .bytes = "[[[[hello]]]]]" , .i = 1 } };
3008
+
3009
+ const result = try parser .specifier ();
3010
+ try testing .expectEqualStrings (digit .named , result .named );
3011
+ }
3012
+
3013
+ { // input string is neither a digit nor a named argument
3014
+ const char : Specifier = Specifier { .none = {} };
3015
+ var parser : Parser = .{ .iter = .{ .bytes = "hello" , .i = 0 } };
3016
+
3017
+ const result = try parser .specifier ();
3018
+ try testing .expectEqual (char .none , result .none );
3019
+ }
3020
+ }
0 commit comments