1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
use anyhow::{bail, Result};
use codespan_reporting::{
diagnostic::{Diagnostic, Label},
files::SimpleFiles,
term::{
emit,
termcolor::{ColorChoice, StandardStream},
Config,
},
};
use move_command_line_common::character_sets::is_permitted_chars;
use move_ir_to_bytecode_syntax::syntax::{self, ParseError};
use move_ir_types::{ast, location::*};
fn verify_string(string: &str) -> Result<()> {
string
.chars()
.enumerate()
.find(|(idx, _)| !is_permitted_chars(string.as_bytes(), *idx))
.map_or(Ok(()), |(_, c)| {
bail!(
"Parser Error: invalid character {} found when reading file.\
Only ascii printable, tabs (\\t), lf (\\n) and crlf (\\r\\n) characters are permitted.",
c
)
})
}
pub fn parse_script_or_module(s: &str) -> Result<ast::ScriptOrModule> {
verify_string(s)?;
syntax::parse_script_or_module_string(s).or_else(|e| handle_error(e, s))
}
pub fn parse_script(script_str: &str) -> Result<ast::Script> {
verify_string(script_str)?;
syntax::parse_script_string(script_str).or_else(|e| handle_error(e, script_str))
}
pub fn parse_module(modules_str: &str) -> Result<ast::ModuleDefinition> {
verify_string(modules_str)?;
syntax::parse_module_string(modules_str).or_else(|e| handle_error(e, modules_str))
}
fn handle_error<T>(e: syntax::ParseError<Loc, anyhow::Error>, code_str: &str) -> Result<T> {
let location = match &e {
ParseError::InvalidToken { location, .. } => location,
ParseError::User { location, .. } => location,
};
let mut files = SimpleFiles::new();
let id = files.add(location.file_hash(), code_str.to_string());
let lbl = match &e {
ParseError::InvalidToken { message, .. } => Label::primary(id, location.usize_range())
.with_message(format!("Invalid Token: {}", message)),
ParseError::User { error, .. } => {
Label::primary(id, location.usize_range()).with_message(format!("{}", error))
}
};
let message = lbl.message.clone();
let error = Diagnostic::error()
.with_message("Parser Error")
.with_labels(vec![lbl]);
let writer = &mut StandardStream::stderr(ColorChoice::Auto);
emit(writer, &Config::default(), &files, &error).unwrap();
bail!("ParserError: {}", message)
}
#[cfg(test)]
mod tests {
#[test]
fn verify_character_allowlist() {
let mut good_chars = (0x20..=0x7E).collect::<Vec<u8>>();
good_chars.push(0x0A);
good_chars.push(0x09);
let mut bad_chars = (0x0..0x09).collect::<Vec<_>>();
bad_chars.append(&mut vec![0x0B, 0x0C]);
bad_chars.append(&mut (0x0E..=0x1F).collect::<Vec<_>>());
bad_chars.push(0x7F);
{
let s = std::str::from_utf8(&good_chars)
.expect("Failed to construct string containing an invalid character. This shouldn't happen.");
assert!(super::verify_string(s).is_ok());
}
for bad_char in bad_chars {
good_chars.push(bad_char);
let s = std::str::from_utf8(&good_chars)
.expect("Failed to construct string containing an invalid character. This shouldn't happen.");
assert!(super::verify_string(s).is_err());
good_chars.pop();
}
}
}