@@ -10,6 +10,7 @@ use smallvec::smallvec;
10
10
pub enum LispObject {
11
11
Symbol ( String ) ,
12
12
Keyword ( String ) ,
13
+ UnibyteStr ( Vec < u8 > ) ,
13
14
Str ( String ) ,
14
15
Int ( i64 ) ,
15
16
Float ( String ) , // use string for Eq and Ord
@@ -41,23 +42,43 @@ impl LispObject {
41
42
LispObject :: Keyword ( s) => format ! ( ":{}" , s) ,
42
43
LispObject :: Str ( s) => {
43
44
let mut result = String :: new ( ) ;
45
+ result. reserve ( s. len ( ) * 2 + 2 ) ;
44
46
result. push ( '"' ) ;
45
- let mut last_is_escape = false ;
46
47
for c in s. chars ( ) {
47
- if c == '"' || c == '\\' {
48
+ if c == '\ "' || c == '\\' {
48
49
result. push ( '\\' ) ;
49
50
result. push ( c) ;
50
- last_is_escape = false ;
51
- } else if ( c as u32 ) < 32 || ( ( c as u32 ) > 126 && ( c as u32 ) < 256 ) {
52
- result += & format ! ( "\\ {:o}" , c as u32 ) ;
53
- last_is_escape = true ;
51
+ } else if ( c as u32 ) < 32 || ( c as u32 ) == 127 { // not printable
52
+ result += & format ! ( "\\ {:03o}" , c as u32 ) ;
54
53
} else {
55
- // https://www.gnu.org/software/emacs/manual/html_node/elisp/Non_002dASCII-in-Strings.html
56
- if last_is_escape && ( '0' ..='8' ) . contains ( & c) {
57
- result += "\\ " ;
58
- }
59
54
result. push ( c) ;
60
- last_is_escape = false ;
55
+ }
56
+ }
57
+ result. push ( '"' ) ;
58
+ result
59
+ } ,
60
+ LispObject :: UnibyteStr ( vec) => {
61
+ let mut result = String :: new ( ) ;
62
+ result. reserve ( vec. len ( ) * 4 + 2 ) ;
63
+ result. push ( '"' ) ;
64
+ for c in vec {
65
+ match * c {
66
+ 7 => result += "\\ a" ,
67
+ 8 => result += "\\ b" ,
68
+ 9 => result += "\\ t" ,
69
+ 10 => result += "\\ n" ,
70
+ 11 => result += "\\ v" ,
71
+ 12 => result += "\\ f" ,
72
+ 13 => result += "\\ r" ,
73
+ 127 => result += "\\ d" ,
74
+ 27 => result += "\\ e" ,
75
+ 0 ..=26 => { // \^@ \^A \^B ... \^Z
76
+ result += & format ! ( "\\ ^{}" , ( * c as u32 + 64 ) as u8 as char ) ;
77
+ } ,
78
+ 27 ..=31 | 128 ..=255 | 34 | 92 => { // oct, for unprintable and '"' and '\\'
79
+ result += & format ! ( "\\ {:03o}" , * c as u32 ) ;
80
+ } ,
81
+ _ => result. push ( * c as char ) , // printable
61
82
}
62
83
}
63
84
result. push ( '"' ) ;
@@ -386,7 +407,7 @@ impl BytecodeCompiler {
386
407
fn into_repl ( self ) -> Result < String > {
387
408
let ( code, constants, max_stack_size) = self . into_bytecode ( ) ?;
388
409
Ok ( format ! ( "#[0 {} {} {}]" ,
389
- LispObject :: Str ( code. into_iter ( ) . map ( |x| x as char ) . collect ( ) ) . to_repl( ) ,
410
+ LispObject :: UnibyteStr ( code) . to_repl( ) ,
390
411
LispObject :: Vector ( constants) . to_repl( ) ,
391
412
max_stack_size) )
392
413
}
@@ -401,3 +422,13 @@ pub fn generate_bytecode_repl(value: &json::Value, options: BytecodeOptions) ->
401
422
compiler. compile ( value) ;
402
423
compiler. into_repl ( )
403
424
}
425
+
426
+
427
+ #[ test]
428
+ fn test_string_repl ( ) {
429
+ assert_eq ! ( LispObject :: UnibyteStr ( "\x00 " . into( ) ) . to_repl( ) , r#""\^@""# ) ;
430
+ assert_eq ! ( LispObject :: UnibyteStr ( "\x1a " . into( ) ) . to_repl( ) , r#""\^Z""# ) ;
431
+ assert_eq ! ( LispObject :: UnibyteStr ( "\x20 " . into( ) ) . to_repl( ) , r#"" ""# ) ;
432
+ assert_eq ! ( LispObject :: UnibyteStr ( "\x7f " . into( ) ) . to_repl( ) , r#""\d""# ) ;
433
+ assert_eq ! ( LispObject :: UnibyteStr ( vec![ 0xff ] ) . to_repl( ) , r#""\377""# ) ;
434
+ }
0 commit comments