char/avril
(wip) why'd you have to go and make things so complicaaaaated?
git clone https://git.t4t.associates/char/avril
f4a0f5b
main
1use std:: collections:: HashMap ; 2use std:: fmt; 3use std:: ops:: Range ; 4 5/// UTF-8 byte offsets into the original source. 6pub type Span =Range < usize >; 7 8# [ derive ( Debug , Clone , PartialEq )] 9pub struct Value { 10pub kind : ValueKind , 11pub span : Span , 12} 13 14# [ derive ( Debug , Clone , PartialEq )] 15pub enum ValueKind { 16Null , 17Bool ( bool ), 18// Preserve precision until the config schema chooses a numeric type. 19Number ( String ), 20String ( String ), 21Array ( Vec < Value >), 22Object ( Vec < Member >), 23} 24 25# [ derive ( Debug , Clone , PartialEq )] 26pub struct Member { 27pub key : String , 28pub key_span : Span , 29pub value : Value , 30} 31 32# [ derive ( Debug , Clone , Copy , PartialEq , Eq )] 33pub enum ErrorKind { 34Syntax , 35Unicode , 36Number , 37Nesting , 38} 39 40# [ derive ( Debug , Clone , PartialEq , Eq )] 41pub struct Error { 42pub kind : ErrorKind , 43pub message : String , 44pub span : Span , 45pub previous : Option < Span >, 46} 47 48impl Error { 49/// Render against the same source that was parsed. 50pub fn render ( & self , name : & str , source : & str ) ->String { 51let location = |offset : usize |{ 52let mut line =1 ; 53let mut column =1 ; 54let mut previous_cr =false ; 55for chin source[ ..offset]. chars () { 56match ch{ 57'\r' =>{ 58 line +=1 ; 59 column =1 ; 60} 61'\n' if previous_cr =>{} 62'\n' =>{ 63 line +=1 ; 64 column =1 ; 65} 66 _ => column +=1 , 67} 68 previous_cr = ch =='\r' ; 69} 70format! ( "{name}:{line}:{column}" ) 71}; 72let mut message =format! ( "{}: {}" , location ( self . span . start ), self . message ); 73if let Some ( previous) =& self . previous { 74 message. push_str ( & format! ( 75"\n{}: First defined here" , 76location ( previous. start ) 77)); 78} 79 message 80} 81} 82 83impl fmt:: Display for Error { 84fn fmt ( & self , f : & mut fmt:: Formatter < ' _ >) -> fmt:: Result { 85 f. write_str ( & self . message ) 86} 87} 88 89impl std:: error:: Error for Error {} 90 91pub fn parse_object ( source : & str ) ->Result < Value , Error > { 92parse_object_with ( source, & crate :: Options :: default ()) 93} 94 95pub fn parse_object_with ( source : & str , options : & crate :: Options ) ->Result < Value , Error > { 96let mut parser =Parser { 97 source, 98offset : 0 , 99max_depth : options. max_depth , 100}; 101 parser. trivia () ?; 102let value = parser. object ( parser. peek () ==Some ( '{' ), 0 ) ?; 103 parser. trivia () ?; 104if parser. peek (). is_some () { 105return Err ( parser. error ( "Unexpected content after the root object" )); 106} 107Ok ( value) 108} 109 110pub ( crate ) fn nesting_limit ( span : Span , limit : usize ) ->Error { 111Error { 112kind : ErrorKind :: Nesting , 113 span, 114message : format! ( "fJSON nesting exceeds {limit} levels below the root" ), 115previous : None , 116} 117} 118 119pub ( crate ) fn invalid ( span : Span , message : impl Into < String >) ->Error { 120Error { 121kind : ErrorKind :: Syntax , 122 span, 123message : message. into (), 124previous : None , 125} 126} 127 128pub fn parse ( source : & str ) ->Result < Value , Error > { 129parse_with ( source, & crate :: Options :: default ()) 130} 131 132pub fn parse_with ( source : & str , options : & crate :: Options ) ->Result < Value , Error > { 133let mut parser =Parser { 134 source, 135offset : 0 , 136max_depth : options. max_depth , 137}; 138 parser. trivia () ?; 139let start = parser. offset ; 140let implicit =match parser. peek () { 141None =>true , 142Some ( '"' |'\'' ) =>{ 143 parser. string () ?; 144 parser. trivia () ?; 145 parser. peek () ==Some ( ':' ) 146} 147Some ( ch) if ch. is_ascii_alphabetic () || ch =='_' || ch =='$' =>{ 148 parser. identifier (); 149 parser. trivia () ?; 150 parser. peek () ==Some ( ':' ) 151} 152 _ =>false , 153}; 154 parser. offset = start; 155let value =if implicit{ 156 parser. object ( false , 0 ) ? 157} else { 158 parser. value ( 0 ) ? 159}; 160 parser. trivia () ?; 161if parser. peek (). is_some () { 162return Err ( parser. error ( "Unexpected content after the root value" )); 163} 164Ok ( value) 165} 166 167struct Parser < ' a > { 168source : &' a str , 169offset : usize , 170max_depth : usize , 171} 172 173impl Parser < ' _ > { 174fn peek ( & self ) ->Option < char > { 175self . source [ self . offset ..]. chars (). next () 176} 177 178fn bump ( & mut self ) ->Option < char > { 179let ch =self . peek () ?; 180self . offset += ch. len_utf8 (); 181Some ( ch) 182} 183 184fn error ( & self , message : impl Into < String >) ->Error { 185Error { 186kind : ErrorKind :: Syntax , 187message : message. into (), 188span : self . offset ..self . offset +self . peek (). map_or ( 0 , char:: len_utf8), 189previous : None , 190} 191} 192 193fn expect ( & mut self , expected : char ) ->Result <(), Error > { 194if self . peek () !=Some ( expected) { 195return Err ( self . error ( format! ( "Expected `{expected}`" ))); 196} 197self . bump (); 198Ok (()) 199} 200 201fn trivia ( & mut self ) ->Result < bool , Error > { 202let mut newline =false ; 203loop { 204match self . peek () { 205Some ( ' ' |'\t' ) =>{ 206self . bump (); 207} 208Some ( '\r' |'\n' ) =>{ 209 newline =true ; 210self . bump (); 211} 212Some ( '/' ) if self . source [ self . offset ..]. starts_with ( "//" ) =>{ 213while self . peek (). is_some_and ( |ch| ch !='\r' && ch !='\n' ) { 214self . bump (); 215} 216} 217Some ( '/' ) if self . source [ self . offset ..]. starts_with ( "/*" ) =>{ 218let start =self . offset ; 219self . offset +=2 ; 220let Some ( end) =self . source [ self . offset ..]. find ( "*/" ) else { 221return Err ( Error { 222kind : ErrorKind :: Syntax , 223message : "Unterminated block comment" . into (), 224span : start..start +2 , 225previous : None , 226}); 227}; 228self . offset += end +2 ; 229} 230 _ =>return Ok ( newline), 231} 232} 233} 234 235fn end ( & mut self , closing : Option < char >) ->Result < bool , Error > { 236match ( closing, self . peek ()) { 237( Some ( expected), Some ( ch)) if ch == expected =>{ 238self . bump (); 239Ok ( true ) 240} 241( Some ( expected), None ) =>Err ( self . error ( format! ( "Expected `{expected}`" ))), 242( None , None ) =>Ok ( true ), 243 _ =>Ok ( false ), 244} 245} 246 247fn more ( & mut self , closing : Option < char >) ->Result < bool , Error > { 248let newline =self . trivia () ?; 249if self . end ( closing) ?{ 250return Ok ( false ); 251} 252if self . peek () ==Some ( ',' ) { 253self . bump (); 254self . trivia () ?; 255} else if !newline{ 256return Err ( self . error ( "Expected `,` or newline" )); 257} 258Ok ( !self . end ( closing) ?) 259} 260 261fn object ( & mut self , braced : bool , depth : usize ) ->Result < Value , Error > { 262let start =self . offset ; 263if braced{ 264self . expect ( '{' ) ?; 265} 266let closing = braced. then_some ( '}' ); 267let mut members =Vec :: new (); 268let mut end = start; 269let mut keys =HashMap :: new (); 270self . trivia () ?; 271if !self . end ( closing) ?{ 272loop { 273let key_start =self . offset ; 274let key =match self . peek () { 275Some ( '"' |'\'' ) =>self . string () ?, 276Some ( ch) if ch. is_ascii_alphabetic () || ch =='_' || ch =='$' =>{ 277self . identifier (). to_owned () 278} 279 _ =>return Err ( self . error ( "Expected an object key" )), 280}; 281let key_span = key_start..self . offset ; 282if let Some ( previous) = keys. insert ( key. clone (), key_span. clone ()) { 283return Err ( Error { 284kind : ErrorKind :: Syntax , 285message : format! ( "Duplicate key {key:?}" ), 286span : key_span, 287previous : Some ( previous), 288}); 289} 290self . trivia () ?; 291self . expect ( ':' ) ?; 292self . trivia () ?; 293let value =self . value ( depth +1 ) ?; 294 end =self . offset ; 295 members. push ( Member { key, key_span, value}); 296if !self . more ( closing) ?{ 297break ; 298} 299} 300} 301Ok ( Value { 302kind : ValueKind :: Object ( members), 303span : start..if braced{ self . offset } else { end}, 304}) 305} 306 307fn array ( & mut self , depth : usize ) ->Result < Value , Error > { 308let start =self . offset ; 309self . expect ( '[' ) ?; 310self . trivia () ?; 311let mut values =Vec :: new (); 312if !self . end ( Some ( ']' )) ?{ 313loop { 314 values. push ( self . value ( depth +1 ) ?); 315if !self . more ( Some ( ']' )) ?{ 316break ; 317} 318} 319} 320Ok ( Value { 321kind : ValueKind :: Array ( values), 322span : start..self . offset , 323}) 324} 325 326fn value ( & mut self , depth : usize ) ->Result < Value , Error > { 327let start =self . offset ; 328let kind =match self . peek () { 329Some ( '{' |'[' ) if depth >self . max_depth =>{ 330return Err ( nesting_limit ( start..start +1 , self . max_depth )); 331} 332Some ( '{' ) =>return self . object ( true , depth), 333Some ( '[' ) =>return self . array ( depth), 334Some ( '"' |'\'' ) =>ValueKind :: String ( self . string () ?), 335Some ( '-' |'0' ..='9' ) =>ValueKind :: Number ( self . number () ?), 336Some ( ch) if ch. is_ascii_alphabetic () || ch =='_' || ch =='$' =>{ 337let word =self . identifier (); 338match word{ 339"true" =>ValueKind :: Bool ( true ), 340"false" =>ValueKind :: Bool ( false ), 341"null" =>ValueKind :: Null , 342 _ =>{ 343return Err ( Error { 344kind : ErrorKind :: Syntax , 345message : format! ( "Unknown value {word:?}; strings must be quoted" ), 346span : start..self . offset , 347previous : None , 348}); 349} 350} 351} 352 _ =>return Err ( self . error ( "Expected a value" )), 353}; 354Ok ( Value { kind, span : start..self . offset }) 355} 356 357fn identifier ( & mut self ) ->& str { 358let start =self . offset ; 359while self 360. peek () 361. is_some_and ( |ch| ch. is_ascii_alphanumeric () || ch =='_' || ch =='$' ) 362{ 363self . bump (); 364} 365& self . source [ start..self . offset ] 366} 367 368fn number ( & mut self ) ->Result < String , Error > { 369let start =self . offset ; 370if self . peek () ==Some ( '-' ) { 371self . bump (); 372} 373match self . peek () { 374Some ( '0' ) =>{ 375self . bump (); 376if self . peek (). is_some_and ( |ch| ch. is_ascii_digit ()) { 377return Err ( self . error ( "Leading zeros are not allowed" )); 378} 379} 380Some ( '1' ..='9' ) =>self . digits () ?, 381 _ =>return Err ( self . error ( "Expected a digit" )), 382} 383if self . peek () ==Some ( '.' ) { 384self . bump (); 385self . digits () ?; 386} 387if matches! ( self . peek (), Some ( 'e' |'E' )) { 388self . bump (); 389if matches! ( self . peek (), Some ( '+' |'-' )) { 390self . bump (); 391} 392self . digits () ?; 393} 394if self 395. peek () 396. is_some_and ( |ch| ch. is_ascii_alphanumeric () ||matches! ( ch, '.' |'_' |'$' )) 397{ 398return Err ( self . error ( "Invalid decimal number" )); 399} 400Ok ( self . source [ start..self . offset ]. to_owned ()) 401} 402 403fn digits ( & mut self ) ->Result <(), Error > { 404if !self . peek (). is_some_and ( |ch| ch. is_ascii_digit ()) { 405return Err ( self . error ( "Expected a digit" )); 406} 407while self . peek (). is_some_and ( |ch| ch. is_ascii_digit ()) { 408self . bump (); 409} 410Ok (()) 411} 412 413fn string ( & mut self ) ->Result < String , Error > { 414let start =self . offset ; 415let quote =self . bump (). unwrap (); 416let mut value =String :: new (); 417loop { 418let ch =self . peek (). ok_or_else ( ||Error { 419kind : ErrorKind :: Syntax , 420message : "Unterminated string" . into (), 421span : start..start +1 , 422previous : None , 423}) ?; 424match ch{ 425 chif ch == quote =>{ 426self . bump (); 427return Ok ( value); 428} 429'\\' =>{ 430self . bump (); 431let escaped =match self . peek () { 432Some ( '"' |'\'' |'\\' |'/' ) =>self . bump (). unwrap (), 433Some ( 'b' ) =>{ 434self . bump (); 435'\u{0008}' 436} 437Some ( 'f' ) =>{ 438self . bump (); 439'\u{000c}' 440} 441Some ( 'n' ) =>{ 442self . bump (); 443'\n' 444} 445Some ( 'r' ) =>{ 446self . bump (); 447'\r' 448} 449Some ( 't' ) =>{ 450self . bump (); 451'\t' 452} 453Some ( 'u' ) =>{ 454self . bump (); 455let unicode_start =self . offset ; 456let first =self . hex_quad () ?; 457let scalar =match first{ 4580xd800 ..=0xdbff =>{ 459if !self . source [ self . offset ..]. starts_with ( "\\u" ) { 460return Err ( Error { 461kind : ErrorKind :: Unicode , 462message : "Unpaired Unicode surrogate" . into (), 463span : unicode_start..self . offset , 464previous : None , 465}); 466} 467self . offset +=2 ; 468let second =self . hex_quad () ?; 469if !( 0xdc00 ..=0xdfff ). contains ( & second) { 470return Err ( Error { 471kind : ErrorKind :: Unicode , 472message : "Expected a low Unicode surrogate" . into (), 473span : self . offset -4 ..self . offset , 474previous : None , 475}); 476} 4770x10000 +(( first -0xd800 ) <<10 ) + second -0xdc00 478} 4790xdc00 ..=0xdfff =>{ 480return Err ( Error { 481kind : ErrorKind :: Unicode , 482message : "Unexpected low Unicode surrogate" . into (), 483span : unicode_start..self . offset , 484previous : None , 485}); 486} 487 _ => first, 488}; 489 char:: from_u32 ( scalar). unwrap () 490} 491 _ =>return Err ( self . error ( "Invalid string escape" )), 492}; 493 value. push ( escaped); 494} 495 chif ch <='\u{001f}' =>{ 496return Err ( self . error ( "Control characters must be escaped in strings" )); 497} 498 _ =>{ 499 value. push ( ch); 500self . bump (); 501} 502} 503} 504} 505 506fn hex_quad ( & mut self ) ->Result < u32 , Error > { 507let mut value =0 ; 508for _in 0 ..4 { 509let digit =self 510. peek () 511. and_then ( |ch| ch. to_digit ( 16 )) 512. ok_or_else ( ||self . error ( "Expected four hexadecimal digits in Unicode escape" )) ?; 513self . bump (); 514 value = value* 16 + digit; 515} 516Ok ( value) 517} 518}