1use crate::*;
2use mech_core::*;
3use nom::{
4 IResult,
5 bytes::complete::tag,
6 branch::alt,
7 bytes::complete::{take_while, take_until, take_till1},
8 combinator::{opt, not},
9 multi::{separated_list1, separated_list0},
10 character::complete::{space0,space1,digit1},
11 sequence::tuple as nom_tuple,
12};
13
14#[derive(Debug, Clone)]
15pub enum ReplCommand {
16 Help,
17 Quit,
18 Save(String),
22 Docs(Option<String>),
23 Code(Vec<(String,MechSourceCode)>),
24 Ls,
25 Profile(bool),
26 Cd(String),
27 Step(Option<usize>,Option<u64>),
28 Load(Vec<String>),
29 Whos(Vec<String>),
30 Plan,
31 Symbols(Option<String>),
32 Clear(Option<String>),
33 Clc,
34 }
37
38pub fn parse_repl_command(input: &str) -> IResult<&str, ReplCommand> {
39 let (input, _) = tag(":")(input)?;
40 let (input, command) = alt((
41 cd_rpl,
42 step_rpl,
43 clear_rpl,
44 clc_rpl,
45 load_rpl,
46 code_rpl,
47 help_rpl,
48 quit_rpl,
49 save_rpl,
50 symbols_rpl,
51 profile_rpl,
52 plan_rpl,
53 ls_rpl,
54 whos_rpl,
55 docs_rpl,
56 ))(input)?;
57 let (input, _) = opt(tag("\r"))(input)?;
58 let (input, _) = opt(tag("\n"))(input)?;
59 if !input.is_empty() {
60 return Err(nom::Err::Error(nom::error::Error::new(input, nom::error::ErrorKind::Eof)));
61 }
62 Ok((input, command))
63}
64
65fn save_rpl(input: &str) -> IResult<&str, ReplCommand> {
66 let (input, _) = tag("save")(input)?;
67 let (input, _) = space1(input)?;
68 let (input, path) = take_while(|c: char| c.is_alphanumeric() || c == '/' || c == '.' || c == '_')(input)?;
69 Ok((input, ReplCommand::Save(path.to_string())))
70}
71
72fn code_rpl(input: &str) -> IResult<&str, ReplCommand> {
73 let (input, _) = alt((tag("code"), tag("c")))(input)?;
74 let (input, _) = space0(input)?;
75 let (input, code) = take_while(|_| true)(input)?;
76 Ok((input, ReplCommand::Code(vec![("repl".to_string(), MechSourceCode::String(code.to_string()))])))
77}
78
79fn profile_rpl(input: &str) -> IResult<&str, ReplCommand> {
80 let (input, _) = tag("profile")(input)?;
81 let (input, _) = space0(input)?;
82 let (input, on_off) = alt((tag("on"), tag("off")))(input)?;
83 Ok((input, ReplCommand::Profile(on_off == "on")))
84}
85
86fn docs_rpl(input: &str) -> IResult<&str, ReplCommand> {
87 let (input, _) = alt((tag("docs"), tag("d")))(input)?;
88 let (input, _) = space0(input)?;
89 let (input, name) = opt(take_till1(|c| c == '\r' || c == '\n'))(input)?;
90 let name = name.map(|s| s.to_string());
91 Ok((input, ReplCommand::Docs(name)))
92}
93
94fn help_rpl(input: &str) -> IResult<&str, ReplCommand> {
95 let (input, _) = alt((tag("help"),tag("h")))(input)?;
96 Ok((input, ReplCommand::Help))
97}
98
99fn quit_rpl(input: &str) -> IResult<&str, ReplCommand> {
100 let (input, _) = alt((tag("quit"), tag("exit"), tag("q")))(input)?;
101 Ok((input, ReplCommand::Quit))
102}
103
104fn cd_rpl(input: &str) -> IResult<&str, ReplCommand> {
105 let (input, _) = tag("cd")(input)?;
106 let (input, _) = space0(input)?;
107 let (input, path) = take_until("\r\n")(input)?;
108 Ok((input, ReplCommand::Cd(path.to_string())))
109}
110
111fn symbols_rpl(input: &str) -> IResult<&str, ReplCommand> {
112 let (input, _) = alt((tag("symbols"), tag("s")))(input)?;
113 let (input, _) = space0(input)?;
114 let (input, name) = opt(take_while(|c: char| c.is_alphanumeric()))(input)?;
115 Ok((input, ReplCommand::Symbols(name.map(|s| s.to_string()))))
116}
117
118fn plan_rpl(input: &str) -> IResult<&str, ReplCommand> {
119 let (input, _) = alt((tag("plan"), tag("p")))(input)?;
120 Ok((input, ReplCommand::Plan))
121}
122
123fn identifier(input: &str) -> IResult<&str, String> {
124 let (input, id) = take_till1(|c| c == ' ' || c == '\n' || c == '\r')(input)?;
125 Ok((input, id.to_string()))
126}
127
128fn whos_rpl(input: &str) -> IResult<&str, ReplCommand> {
129 let (input, _) = alt((tag("whos"), tag("w")))(input)?;
130 let (input, _) = space0(input)?;
131 let (input, names) = separated_list0(many1(tag(" ")), identifier)(input)?;
132 Ok((input, ReplCommand::Whos(names)))
133}
134
135fn clear_rpl(input: &str) -> IResult<&str, ReplCommand> {
136 let (input, _) = tag("clear")(input)?;
137 Ok((input, ReplCommand::Clear(None)))
138}
139
140fn clc_rpl(input: &str) -> IResult<&str, ReplCommand> {
141 let (input, _) = tag("clc")(input)?;
142 Ok((input, ReplCommand::Clc))
143}
144
145fn ls_rpl(input: &str) -> IResult<&str, ReplCommand> {
146 let (input, _) = tag("ls")(input)?;
147 Ok((input, ReplCommand::Ls))
148}
149
150fn load_rpl(input: &str) -> IResult<&str, ReplCommand> {
151 let (input, _) = tag("load")(input)?;
152 let (input, _) = space1(input)?;
153 let (input, path_strings) = separated_list1(space1, alt((take_until(" "),take_until("\r\n"))))(input)?;
154 Ok((input, ReplCommand::Load(path_strings.iter().map(|s| s.to_string()).collect())))
155}
156
157fn step_rpl(input: &str) -> IResult<&str, ReplCommand> {
158 let (input, _) = tag("step")(input)?;
159 let (input, _) = space1(input)?;
160 let (input, step_id) = opt(nom_tuple((tag("#"), digit1, space1)))(input)?;
161 let (input, count) = opt(digit1)(input)?;
162 let step_id = match step_id {
163 Some((_, id_str, _)) => match id_str.parse::<usize>() {
164 Ok(id) => Some(id),
165 Err(_) => None,
166 },
167 _ => None,
168 };
169 let count = match count {
170 Some(count_str) => match count_str.parse::<u64>() {
171 Ok(count) => Some(count),
172 Err(_) => None,
173 },
174 _ => None,
175 };
176 Ok((input, ReplCommand::Step(step_id, count)))
177}