machine.go.rl (3309B)
1 package urn 2 3 import ( 4 "fmt" 5 ) 6 7 var ( 8 errPrefix = "expecting the prefix to be the \"urn\" string (whatever case) [col %d]" 9 errIdentifier = "expecting the identifier to be string (1..31 alnum chars, also containing dashes but not at its start) [col %d]" 10 errSpecificString = "expecting the specific string to be a string containing alnum, hex, or others ([()+,-.:=@;$_!*']) chars [col %d]" 11 errNoUrnWithinID = "expecting the identifier to not contain the \"urn\" reserved string [col %d]" 12 errHex = "expecting the specific string hex chars to be well-formed (%%alnum{2}) [col %d]" 13 errParse = "parsing error [col %d]" 14 ) 15 16 %%{ 17 machine urn; 18 19 # unsigned alphabet 20 alphtype uint8; 21 22 action mark { 23 m.pb = m.p 24 } 25 26 action tolower { 27 m.tolower = append(m.tolower, m.p - m.pb) 28 } 29 30 action set_pre { 31 output.prefix = string(m.text()) 32 } 33 34 action set_nid { 35 output.ID = string(m.text()) 36 } 37 38 action set_nss { 39 raw := m.text() 40 output.SS = string(raw) 41 // Iterate upper letters lowering them 42 for _, i := range m.tolower { 43 raw[i] = raw[i] + 32 44 } 45 output.norm = string(raw) 46 } 47 48 action err_pre { 49 m.err = fmt.Errorf(errPrefix, m.p) 50 fhold; 51 fgoto fail; 52 } 53 54 action err_nid { 55 m.err = fmt.Errorf(errIdentifier, m.p) 56 fhold; 57 fgoto fail; 58 } 59 60 action err_nss { 61 m.err = fmt.Errorf(errSpecificString, m.p) 62 fhold; 63 fgoto fail; 64 } 65 66 action err_urn { 67 m.err = fmt.Errorf(errNoUrnWithinID, m.p) 68 fhold; 69 fgoto fail; 70 } 71 72 action err_hex { 73 m.err = fmt.Errorf(errHex, m.p) 74 fhold; 75 fgoto fail; 76 } 77 78 action err_parse { 79 m.err = fmt.Errorf(errParse, m.p) 80 fhold; 81 fgoto fail; 82 } 83 84 pre = ([uU][rR][nN] @err(err_pre)) >mark %set_pre; 85 86 nid = (alnum >mark (alnum | '-'){0,31}) %set_nid; 87 88 hex = '%' (digit | lower | upper >tolower){2} $err(err_hex); 89 90 sss = (alnum | [()+,\-.:=@;$_!*']); 91 92 nss = (sss | hex)+ $err(err_nss); 93 94 fail := (any - [\n\r])* @err{ fgoto main; }; 95 96 main := (pre ':' (nid - pre %err(err_urn)) $err(err_nid) ':' nss >mark %set_nss) $err(err_parse); 97 98 }%% 99 100 %% write data noerror noprefix; 101 102 // Machine is the interface representing the FSM 103 type Machine interface { 104 Error() error 105 Parse(input []byte) (*URN, error) 106 } 107 108 type machine struct { 109 data []byte 110 cs int 111 p, pe, eof, pb int 112 err error 113 tolower []int 114 } 115 116 // NewMachine creates a new FSM able to parse RFC 2141 strings. 117 func NewMachine() Machine { 118 m := &machine{} 119 120 %% access m.; 121 %% variable p m.p; 122 %% variable pe m.pe; 123 %% variable eof m.eof; 124 %% variable data m.data; 125 126 return m 127 } 128 129 // Err returns the error that occurred on the last call to Parse. 130 // 131 // If the result is nil, then the line was parsed successfully. 132 func (m *machine) Error() error { 133 return m.err 134 } 135 136 func (m *machine) text() []byte { 137 return m.data[m.pb:m.p] 138 } 139 140 // Parse parses the input byte array as a RFC 2141 string. 141 func (m *machine) Parse(input []byte) (*URN, error) { 142 m.data = input 143 m.p = 0 144 m.pb = 0 145 m.pe = len(input) 146 m.eof = len(input) 147 m.err = nil 148 m.tolower = []int{} 149 output := &URN{} 150 151 %% write init; 152 %% write exec; 153 154 if m.cs < first_final || m.cs == en_fail { 155 return nil, m.err 156 } 157 158 return output, nil 159 }