1 /**
2 This module contains a example grammar rules to parse different kind of numbers literals.
3 */
4 module pegged.examples.numbers;
5 
6 import pegged.grammar;
7 
8 /// Numbers
9 mixin(grammar(`
10 Numbers:
11     Scientific <~ Floating ( ('e' / 'E' ) Integer )?
12     Floating   <~ Integer ('.' Unsigned )?
13     Unsigned   <~ [0-9]+
14     Integer    <~ Sign? Unsigned
15     Hexa       <~ [0-9a-fA-F]+
16     Binary     <~ "0b" [01] [01_]*
17     Sign       <- '-' / '+'
18 `));
19 
20 unittest
21 {
22     string[] testNumbers =
23     [
24         "0", "0.0", "0.01",
25         "-0", "+0", "-0.0", "+0.0",
26         "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
27         "0123456789",
28         "123", "123.0", "123.01", "-123", "-123.0", "-123.01",
29         "-123e+12", "-123e-12", "+123e+12", "+123e-12", "-123E+12", "-123E-12", "+123E+12", "+123E-12",
30         "123.456e+00", "123.456E+00", "123.456e-00", "123.456E-00",
31         "-123.456e+00", "-123.456E+00", "-123.456e-00", "-123.456E-00",
32         "+123.456e+00", "+123.456E+00", "+123.456e-00", "+123.456E-00"
33     ];
34 
35     foreach(number; testNumbers)
36     {
37         const parseTree = Numbers(number);
38         auto match = parseTree.matches;
39         assert(parseTree.successful, "Expected to parse successfully number " ~ number);
40         assert(match == [number], "Expected " ~ number ~ " but was " ~ match[0]); // Shall parse
41     }
42 
43     // Failures
44     testNumbers =
45     [
46         ".", ".0", "0.", "123..456",
47         "", "abc", "+", "-", "+.", "-.",
48         "--1", "++1", "+-1", "-+1",
49         "1e", "1e+", "1e-","1ee"
50     ];
51 
52     foreach(number; testNumbers)
53     {
54         assert(Numbers(number).matches != [number], "Number \"" ~ number ~ "\" musn't be parsed"); // None shall parse
55     }
56 
57     // Hexadecimal numbers
58     testNumbers =
59     [
60         "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
61         "A", "B", "C", "D", "E", "F",
62         "a", "b", "c", "d", "e", "f",
63         "0123456789ABCDEF",
64         "0123456789abcdef",
65         "DEADBEEF", "0123BEEF", "DEAD0123",
66         "deadbeef", "0123beef", "dead0123",
67         "123E", "123e"
68     ];
69 
70     foreach(number; testNumbers)
71     {
72         const parseTree = Numbers.decimateTree(Numbers.Hexa(number));
73         auto match = parseTree.matches;
74         assert(parseTree.successful, "Expected to parse successfully number " ~ number);
75         assert(match == [number], "Expected " ~ number ~ " but was " ~ match[0]); // Shall parse
76     }
77 
78     // Hexadecimal failures
79     testNumbers =
80     [
81         "", "G", "g", "-1", "123.456", "123e+100"
82     ];
83 
84     foreach(number; testNumbers)
85     {
86         assert(Numbers.decimateTree(Numbers.Hexa(number)).matches != [number],
87                 "Number \"" ~ number ~ "\" musn't be parsed"); // None shall parse
88     }
89 
90     // Binary numbers
91     testNumbers =
92     [
93         "0b0", "0b1", "0b0000", "0b0001", "0b11110000", "0b0000_1111", "0b1010_00_11"
94     ];
95 
96     foreach(number; testNumbers)
97     {
98         const parseTree = Numbers.decimateTree(Numbers.Binary(number));
99         auto match = parseTree.matches;
100         assert(parseTree.successful, "Expected to parse successfully number " ~ number);
101         assert(match == [number], "Expected " ~ number ~ " but was " ~ match[0]); // Shall parse
102     }
103 
104     // Hexadecimal failures
105     testNumbers =
106     [
107         "", "G", "g", "-1", "123.456", "123e+100", "0b", "01010", "0b3456"
108     ];
109 
110     foreach(number; testNumbers)
111     {
112         assert(Numbers.decimateTree(Numbers.Binary(number)).matches != [number],
113                 "Number \"" ~ number ~ "\" musn't be parsed"); // None shall parse
114     }
115 }