Skip to main content

adapton/
parse_val.rs

1//! Parses the output of Rust `Debug` strings into reflected values of
2//! type `Val`.  (See `engine::reflect` module).
3//!
4//! We use this parse as a non-intrusive mechanism for
5//! building the values in the reflected DCG, which consists of
6//! crawling user-defined data structures, and following their
7//! articulations. We use the values' `Debug` strings to do this
8//! traversal.
9
10use std::fmt::Debug;
11use crate::adapton::reflect::{Loc,Path,Val,ArtContent,Const};
12use crate::adapton::engine::{Name, name_of_str, name_of_string};
13
14/// _Balanced tokens_: Tokens that must be balanced with a left and
15/// right instance and well-nested balanced tokens between them.
16#[derive(Debug, Eq, PartialEq)]
17enum BalTok {
18    Paren, 
19    Bracket, 
20    Brace
21}
22
23/// _Tokens_: The unit of input for parsing Rust `Debug` strings
24/// into reflected `Val` values.
25#[derive(Debug, Eq, PartialEq)]
26enum Tok {
27    /// Left (and right) balanced tokens
28    Left(BalTok), 
29    /// Right (and left) balanced tokens
30    Right(BalTok),
31    /// Constant values that can immediately be injected into reflected `Val` type
32    Const(Const),
33    /// Identifers name fields of structs and constructors (of enums and structs)
34    Ident(String),
35    /// Colons separate field names from field values in structs.  Co
36    Colon,
37    /// Commas separate arguments to a constructor; for struct constructors, they separate fields
38    Comma, 
39}
40
41
42/// Transform most(*) Rust data that derives `Debug` into a reflected
43/// `Val`.
44/// 
45/// This parsing logic handles user-defined `struct` and `enum`
46/// types, tuples and vectors.  It recognizes the `Debug` output of
47/// these structures and parses them into trees of type
48/// `Val`. Importantly, it recognizes articulations in this `Debug`
49/// output and parses those into reflected locations (of type `Loc`)
50/// and articulations (of type `Art`).  The reflected `DCG` maps
51/// reflected articulations (whose reflected locations are of type
52/// `Loc`) to reflected nodes that contain more reflected values.
53/// 
54/// (*) Note: Though this parsing logic handles vectors, tuples and
55/// user-defined data types, this parsing logic is probably
56/// incomplete for all of Rust's standard collections. (It does not
57/// yet handle `HashMap<_,_>` debug output, or anything else of this
58/// complexity as of yet).
59pub fn parse_val <V:Debug> (v:&V) -> Val {
60    let s = format!("{:?}", v);
61    //println!("reflect_val({:?})", v);
62    let toks = lex(s.into_bytes());
63    //println!("toks = {:?}", toks);
64    parse_toks(toks)
65}
66
67/// Tokenize the characters of input into lexical tokens of type `Tok`
68fn lex (mut chars: Vec<u8>) -> Vec<Tok> {
69    let mut toks = vec![];
70    chars.reverse(); // TODO rewrite to avoid this
71    loop {
72        match chars.pop() {
73            None => return toks,
74            Some(c) => {
75                let c : char = c as char ;
76                if      c == ' ' { continue }
77                else if c == ':' { toks.push(Tok::Colon); continue }
78                else if c == ',' { toks.push(Tok::Comma); continue }
79                else if c == '{' { toks.push(Tok::Left (BalTok::Brace));   continue }
80                else if c == '[' { toks.push(Tok::Left (BalTok::Bracket)); continue }
81                else if c == '(' { toks.push(Tok::Left (BalTok::Paren));   continue }
82                else if c == '}' { toks.push(Tok::Right(BalTok::Brace));   continue }
83                else if c == ']' { toks.push(Tok::Right(BalTok::Bracket)); continue }
84                else if c == ')' { toks.push(Tok::Right(BalTok::Paren));   continue }
85                else if c == '"' {
86                    let mut string_chars = vec![];
87                    loop {
88                        match chars.pop() {
89                            None => break,
90                            Some(c) => {
91                                let c : char = c as char ;
92                                if c == '"' { break } else { 
93                                    string_chars.push(c);
94                                    continue 
95                                }
96                            }
97                        }
98                    };
99                    toks.push(Tok::Const(Const::String( 
100                        string_chars.into_iter().collect() 
101                    )));
102                    continue
103                }
104                else if c == '-' || (c >= '0' && c <= '9') {
105                    let mut digs = vec![c];
106                    loop {
107                        match chars.pop() {
108                            None    => break,
109                            Some(c) => { 
110                                let c : char = c as char ;
111                                if c >= '0' && c <= '9' { 
112                                    digs.push(c); 
113                                    continue 
114                                } else { 
115                                    chars.push(c as u8); 
116                                    break 
117                                }
118                            }
119                        }
120                    };
121                    if c == '-' {
122                        let s : String = digs.into_iter().collect();
123                        toks.push(Tok::Const(Const::Num( 
124                            isize::from_str_radix(s.as_str(), 10).unwrap()
125                        )));              
126                    } else {
127                        let s : String = digs.into_iter().collect();
128                        toks.push(Tok::Const(Const::Nat( 
129                            usize::from_str_radix(s.as_str(), 10).unwrap()
130                        )));
131                    }
132                    continue
133                }
134                else if (c >= 'a' && c <= 'z') ||
135                    (c >= 'A' && c <= 'Z') ||
136                    (c == '_') 
137                {
138                    let mut ident = vec![c];
139                    loop {
140                        match chars.pop() {
141                            None    => break,
142                            Some(c) => { 
143                                let c : char = c as char ;
144                                if (c >= 'a' && c <= 'z') ||
145                                    (c >= 'A' && c <= 'Z') ||
146                                    (c >= '0' && c <= '9') ||
147                                    (c == '_')  
148                                {
149                                    ident.push(c); 
150                                    continue 
151                                } else { 
152                                    chars.push(c as u8); 
153                                    break 
154                                }
155                            }
156                        }
157                    };
158                    toks.push(Tok::Ident( ident.into_iter().collect() ));
159                    continue           
160                }
161            }
162        }
163    } ;
164}
165
166/// Parse a sequence of fields (appending to `fields`) until right
167/// balanced token `bal`.  Return fields and remaining tokens.
168fn parse_fields (mut toks:Vec<Tok>, mut fields:Vec<(Name, Val)>, bal:Tok) -> (Vec<(Name, Val)>, Vec<Tok>) {
169    match toks.pop() {
170        None => panic!("parse_vals: expected more vals, or end of sequence; but no more tokens"),
171        Some(t) => {
172            if t == bal { (fields, toks) } 
173            else if t == Tok::Comma { 
174                return parse_fields(toks, fields, bal)
175            } else {
176                match t {
177                    Tok::Ident(i) => {
178                        let toks = expect_tok(toks, Tok::Colon);
179                        let (v, toks) = parse_val_rec(toks);
180                        fields.push((name_of_string(i), v));
181                        return parse_fields(toks, fields, bal)
182                    }
183                    t => {
184                        panic!("parse_fields: expected identifier, but found {:?}", t)
185                    }
186                }
187            }
188        }
189    }
190}
191
192/// Parse a sequence of values (appending to `vals`) until right
193/// balanced token `bal`.  Return fields and remaining tokens.
194fn parse_vals (mut toks:Vec<Tok>, mut vals:Vec<Val>, bal:Tok) -> (Vec<Val>, Vec<Tok>) {
195    match toks.pop() {
196        None => panic!("parse_vals: expected more vals, or end of sequence; but no more tokens"),
197        Some(t) => {
198            if t == bal { (vals, toks) } 
199            else if t == Tok::Comma { 
200                return parse_vals(toks, vals, bal)
201            } 
202            else {
203                toks.push(t);
204                let (v, toks) = parse_val_rec(toks);
205                vals.push(v);
206                return parse_vals(toks, vals, bal)
207            }
208        }
209    }
210}
211
212/// Expect next token to be `tok` and panic otherwise.
213fn expect_tok (mut toks: Vec<Tok>, tok:Tok) -> Vec<Tok> {
214    match toks.pop() {
215        None => panic!("expected token `{:?}`, but, no more tokens", tok),
216        Some(t) => {
217            if t == tok { toks } 
218            else { panic!("expected token `{:?}`, but instead found token `{:?}`", tok, t) }
219        }
220    }
221}
222
223fn parse_toks(mut toks:Vec<Tok>) -> Val {
224    toks.reverse();
225    let (v, toks) = parse_val_rec(toks);
226    assert!(toks.len() == 0);
227    v
228}
229
230fn path_of_val ( p:&Val ) -> Path {
231    match *p {
232        Val::Vec( ref vs ) => vs.iter().map( name_of_val ).collect(),
233        _ => panic!("expected a vector of values representing names"),
234    }
235}
236
237fn name_of_val ( n:&Val ) -> Name {
238    use crate::engine::*;
239
240    match *n {
241        Val::Constr( ref cons_name, ref cons_args ) => {
242            if *cons_name == name_of_str("Unit") {
243                name_unit()
244            }
245            else if *cons_name == name_of_str("Hash64") {
246                name_of_hash64( 0 ) // TODO/XXX
247            }
248            else if *cons_name == name_of_str("String") {
249                name_of_string( match cons_args[0] {
250                    Val::Const( Const::String( ref s ) ) => s.clone(),
251                    _ => panic!("expected a String"),
252                })
253            }
254            else if *cons_name == name_of_str("Usize") {
255                name_of_usize( match cons_args[0] {
256                    Val::Const( Const::Nat( ref n ) ) => n.clone(),
257                    _ => panic!("expected a Nat"),
258                })
259            }
260            else if *cons_name == name_of_str("Isize") {
261                panic!("")
262            }
263            else if *cons_name == name_of_str("Pair") {
264                let n1 = name_of_val( & cons_args[0] );
265                let n2 = name_of_val( & cons_args[1] );
266                name_pair(n1, n2)
267            }
268            else if *cons_name == name_of_str("ForkL") {
269                let n = name_of_val( & cons_args[0] );
270                name_fork(n).0
271            }
272            else if *cons_name == name_of_str("ForkR") {
273                let n = name_of_val( & cons_args[0] );
274                name_fork(n).1
275            }
276            else {
277                unreachable!()
278            }        
279        },
280        Val::Name(ref n) => n.clone(),
281        _ => panic!("expected a constructor for a NameSym")
282    }
283}
284
285fn name_option_of_val ( n:&Val ) -> Option<Name> {
286    use crate::engine::*;
287    
288    match *n {
289        Val::Constr( ref cons_name, ref cons_args ) => {
290            if *cons_name == name_of_str("Unit") {
291                Some(name_unit())
292            }
293            else if *cons_name == name_of_str("Hash64") {
294                Some(name_of_hash64( 0 )) // XXX \ TODO -- We are actually missing the hash here!
295            }
296            else if *cons_name == name_of_str("String") {
297                if cons_args.len() < 1 { None } else {
298                    match cons_args[0] {            
299                        Val::Const( Const::String( ref s ) ) => 
300                            Some(name_of_string( s.clone() )),
301                        _ => None,
302                    }}
303            }
304            else if *cons_name == name_of_str("Usize") {
305                if cons_args.len() < 1 { None } else {
306                    match cons_args[0] {          
307                        Val::Const( Const::Nat( ref n ) ) => Some( name_of_usize( n.clone() ) ),
308                        _ => None,
309                    }}
310            }
311            else if *cons_name == name_of_str("Isize") {
312                panic!("TODO")
313            }
314            else if *cons_name == name_of_str("Pair") {          
315                if cons_args.len() < 2 { None } else {
316                    let n1 = name_option_of_val( & cons_args[0] );
317                    let n2 = name_option_of_val( & cons_args[1] );
318                    if cons_args.len() < 2 { None } else {
319                        match (n1,n2) {
320                            (Some(n1),Some(n2)) => Some(name_pair(n1, n2)),
321                            (_, _) => None,
322                        }}}
323            }
324            else if *cons_name == name_of_str("ForkL") {
325                if cons_args.len() < 1 { None } else {
326                    let n = name_option_of_val( & cons_args[0] );
327                    match n {
328                        None => None,
329                        Some(n) => Some(name_fork(n).0)
330                    }}
331            }
332            else if *cons_name == name_of_str("ForkR") {
333                if cons_args.len() < 1 { None } else {
334                    let n = name_option_of_val( & cons_args[0] );
335                    match n {
336                        None => None,
337                        Some(n) => Some(name_fork(n).1)
338                    }}
339            }
340            else { None }
341        },
342        Val::Name(ref n) => Some(n.clone()),
343        _ => None,
344    }
345}
346
347
348/// Attempts to parse a reflected value into an `Art` value case,
349/// which consists of parsing a location represented as a `Val`
350/// structure into a `Loc` represented as a Rust data type (in the
351/// `reflect` module).  If it fails to parse a value into an art, it
352/// returns None.
353fn parse_art_val ( i:&String, fields:&Vec<(Name, Val)> ) -> Option<Val> {
354    if i == "Art" && fields.len() == 1 { 
355        match fields[0] { 
356            (ref nf, ref vf) =>
357                if *nf == name_of_str("art") {
358                    // OK: it's a struct called Art with exactly one field
359                    // called art.  We are going to parse this into an Art.
360
361                    match *vf { 
362                        Val::Struct( ref j, ref ws ) => 
363                            if *j == name_of_str("Loc") 
364                            && ws.len() == 2
365                            && ws[0].0 == name_of_str("path") 
366                            && ws[1].0 == name_of_str("id")
367                        {
368                            // Now we are confident that the rest ought to parse.
369                            // Any further parse errors are panics.
370                            let path = path_of_val( & ws[0].1 );
371                            let name = name_of_val( & ws[1].1 );
372                            Some( Val::Art(Loc{path:path, name:name}, ArtContent::Unknown) )
373                        } 
374
375
376                        else { 
377                            None 
378                        },
379                        _ => None,
380                    }
381                } else { None }
382        }} else { None }           
383}
384
385/// Parse a value from the tokens `toks` and return it.  Panic if the next tokens do not parse into value.
386fn parse_val_rec (mut toks:Vec<Tok>) -> (Val, Vec<Tok>) {
387    //println!("{:?}", toks);
388    let (v, toks) = match toks.pop() {
389        None => panic!("expected value; but, no more tokens"),
390        Some(Tok::Right(r)) => panic!("expected value, but found {:?} instead", Tok::Right(r)),
391        Some(Tok::Comma) => panic!("expected value, but found Comma instead"),
392        Some(Tok::Colon) => panic!("expected value, but found Colon instead"),
393        Some(Tok::Left(BalTok::Bracket)) => {
394            // Parse a vector: Begins with '[', then a list of comma-separated values, then ']'.
395            let (vs, toks) = parse_vals(toks, vec![], Tok::Right(BalTok::Bracket));
396            (Val::Vec(vs), toks)
397        },
398        Some(Tok::Left(BalTok::Paren)) => {
399            // Parse a tuple: Begins with '(', then a list of comma-separated values, then ')'.
400            let (vs, toks) = parse_vals(toks, vec![], Tok::Right(BalTok::Paren));
401            (Val::Tuple(vs), toks)
402        },
403        Some(Tok::Left(l)) => panic!("expected value, but found {:?} instead", Tok::Left(l)),     
404        Some(Tok::Ident(i)) => {
405            match toks.pop() {
406                None => {
407                    // Constructors with no arguments (e.g., Nil)
408                    (Val::Constr(name_of_string(i), vec![]), toks)
409                }
410                Some(Tok::Left(BalTok::Brace)) => {
411                    //println!("parsing struct: {:?}", i);
412                    let (fields, toks) = parse_fields(toks, vec![], Tok::Right(BalTok::Brace));
413                    let art_op = parse_art_val(&i, &fields);
414                    let v = match art_op {
415                        Some(a) => a,
416                        None => Val::Struct(name_of_string(i.clone()), fields.clone())
417                    };    
418                    (v, toks)
419                }
420                Some(Tok::Left(BalTok::Paren)) => {
421                    //println!("parsing constructor: {:?}", i);
422                    let (vs, toks) = parse_vals(toks, vec![], Tok::Right(BalTok::Paren));
423                    let v = Val::Constr(name_of_string(i), vs);
424                    match name_option_of_val(&v) {
425                        Some(n) => (Val::Name(n), toks),
426                        None => (v, toks)
427                    }
428                },
429                Some(Tok::Comma) => {
430                    toks.push(Tok::Comma);
431                    (Val::Constr(name_of_string(i), vec![]), toks)
432                },
433                Some(Tok::Right(baltok)) => {
434                    toks.push(Tok::Right(baltok));
435                    (Val::Constr(name_of_string(i), vec![]), toks)
436                },
437                Some(t) => {
438                    panic!("expected left balanced token, or comma, but instead found token {:?}", t)
439                }}},
440        Some(Tok::Const(Const::Nat(n)))    => (Val::Const(Const::Nat(n)), toks),
441        Some(Tok::Const(Const::Num(n)))    => (Val::Const(Const::Num(n)), toks),
442        Some(Tok::Const(Const::String(s))) => (Val::Const(Const::String(s)), toks)
443    };
444    (v,toks)
445}