-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathjson-pop.rs
128 lines (116 loc) · 3.54 KB
/
json-pop.rs
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use clap::arg_enum;
cfg_if::cfg_if! {
if #[cfg(feature = "pretty_errors")] {
use codespan_reporting::term::termcolor::StandardStream;
use codespan_reporting::term::{self, ColorArg};
}
}
use json_pop::lex::Token;
use json_pop::parser::jsonParser as parser;
use json_pop::value;
use logos::Logos;
use std::io;
use std::io::BufRead;
use std::io::Read;
use structopt::StructOpt;
arg_enum! {
#[derive(Debug)]
#[allow(non_camel_case_types)]
enum Mode {
lex,
parse,
}
}
#[derive(Debug, StructOpt)]
#[structopt(name = "options", about = "json-pop options.")]
struct Opts {
/// whether to lex or parse
#[structopt(possible_values = &Mode::variants(), case_insensitive = true, default_value = "parse")]
mode: Mode,
/// parse each line as a separate json file.
#[structopt(short, long)]
line: bool,
#[cfg(feature = "pretty_errors")]
#[structopt(
long = "color",
default_value = "auto",
possible_values = ColorArg::VARIANTS,
case_insensitive = true,
)]
pub color: ColorArg,
}
fn main() -> anyhow::Result<()> {
let opt = Opts::from_args();
match opt.mode {
Mode::parse => {
if opt.line {
parse_stdin_line()
} else {
parse_stdin()
}
}
Mode::lex => lex_stdin_lalr(),
}
}
fn parse_stdin() -> anyhow::Result<()> {
let mut buffer = String::new();
let stdin = io::stdin();
let mut handle = stdin.lock();
handle.read_to_string(&mut buffer)?;
let tokens = Token::lexer(&buffer).spanned().map(Token::to_lalr_triple);
let parsed = parser::new().parse(tokens);
display_value_or_error(&buffer, parsed)
}
// each line is parsed as though it were a valid json object
// It fails to parse things like: "{ \n "foo" : "bar" }"
// since "{\n" isn't a valid json object.
fn parse_stdin_line() -> anyhow::Result<()> {
let reader = io::BufReader::new(io::stdin());
for input_line in reader.lines() {
let input_line = input_line?;
let tokens = Token::lexer(&input_line.as_str())
.spanned()
.map(Token::to_lalr_triple);
let parsed = parser::new().parse(tokens);
if let Some(_) = display_value_or_error(&input_line, parsed).ok() {
continue;
}
}
Ok(())
}
/// Dumps lexer tokens...
fn lex_stdin_lalr() -> anyhow::Result<()> {
let reader = io::BufReader::new(io::stdin());
for line in reader.lines() {
let line = line?;
let tokens = Token::lexer(line.as_str());
for tok in tokens {
println!("{:?}", tok);
}
}
Ok(())
}
fn display_value_or_error(
_source: &str,
parsed: Result<value::Value, json_pop::parser::ParseError>,
) -> anyhow::Result<()> {
match parsed {
Ok(value) => println!("{}", value),
Err(error) => {
cfg_if::cfg_if! {
if #[cfg(feature = "pretty_errors")] {
let opts = Opts::from_args();
let writer = StandardStream::stderr(opts.color.into());
let config = codespan_reporting::term::Config::default();
let (files, diagnostic) = json_pop::extra::codespan::from_parse_error("stdin", &_source, &error);
term::emit(&mut writer.lock(), &config, &files, &diagnostic)?;
} else {
use std::io::Write;
write!(io::stderr().lock(), "{:#?}", error)?
}
}
anyhow::bail!("Parse error");
}
}
Ok(())
}