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\n"))(input)?;
58 if !input.is_empty() {
59 return Err(nom::Err::Error(nom::error::Error::new(input, nom::error::ErrorKind::Eof)));
60 }
61 Ok((input, command))
62}
63
64fn save_rpl(input: &str) -> IResult<&str, ReplCommand> {
65 let (input, _) = tag("save")(input)?;
66 let (input, _) = space1(input)?;
67 let (input, path) = take_while(|c: char| c.is_alphanumeric() || c == '/' || c == '.' || c == '_')(input)?;
68 Ok((input, ReplCommand::Save(path.to_string())))
69}
70
71fn code_rpl(input: &str) -> IResult<&str, ReplCommand> {
72 let (input, _) = alt((tag("code"), tag("c")))(input)?;
73 let (input, _) = space0(input)?;
74 let (input, code) = take_while(|_| true)(input)?;
75 Ok((input, ReplCommand::Code(vec![("repl".to_string(), MechSourceCode::String(code.to_string()))])))
76}
77
78fn profile_rpl(input: &str) -> IResult<&str, ReplCommand> {
79 let (input, _) = tag("profile")(input)?;
80 let (input, _) = space0(input)?;
81 let (input, on_off) = alt((tag("on"), tag("off")))(input)?;
82 Ok((input, ReplCommand::Profile(on_off == "on")))
83}
84
85fn docs_rpl(input: &str) -> IResult<&str, ReplCommand> {
86 let (input, _) = alt((tag("docs"), tag("d")))(input)?;
87 let (input, _) = space0(input)?;
88 let (input, name) = opt(take_till1(|c| c == '\r' || c == '\n'))(input)?;
89 let name = name.map(|s| s.to_string());
90 Ok((input, ReplCommand::Docs(name)))
91}
92
93fn help_rpl(input: &str) -> IResult<&str, ReplCommand> {
94 let (input, _) = alt((tag("help"),tag("h")))(input)?;
95 Ok((input, ReplCommand::Help))
96}
97
98fn quit_rpl(input: &str) -> IResult<&str, ReplCommand> {
99 let (input, _) = alt((tag("quit"), tag("exit"), tag("q")))(input)?;
100 Ok((input, ReplCommand::Quit))
101}
102
103fn cd_rpl(input: &str) -> IResult<&str, ReplCommand> {
104 let (input, _) = tag("cd")(input)?;
105 let (input, _) = space0(input)?;
106 let (input, path) = take_until("\r\n")(input)?;
107 Ok((input, ReplCommand::Cd(path.to_string())))
108}
109
110fn symbols_rpl(input: &str) -> IResult<&str, ReplCommand> {
111 let (input, _) = alt((tag("symbols"), tag("s")))(input)?;
112 let (input, _) = space0(input)?;
113 let (input, name) = opt(take_while(|c: char| c.is_alphanumeric()))(input)?;
114 Ok((input, ReplCommand::Symbols(name.map(|s| s.to_string()))))
115}
116
117fn plan_rpl(input: &str) -> IResult<&str, ReplCommand> {
118 let (input, _) = alt((tag("plan"), tag("p")))(input)?;
119 Ok((input, ReplCommand::Plan))
120}
121
122fn identifier(input: &str) -> IResult<&str, String> {
123 let (input, id) = take_till1(|c| c == ' ' || c == '\n' || c == '\r')(input)?;
124 Ok((input, id.to_string()))
125}
126
127fn whos_rpl(input: &str) -> IResult<&str, ReplCommand> {
128 let (input, _) = alt((tag("whos"), tag("w")))(input)?;
129 let (input, _) = space0(input)?;
130 let (input, names) = separated_list0(many1(tag(" ")), identifier)(input)?;
131 Ok((input, ReplCommand::Whos(names)))
132}
133
134fn clear_rpl(input: &str) -> IResult<&str, ReplCommand> {
135 let (input, _) = tag("clear")(input)?;
136 Ok((input, ReplCommand::Clear(None)))
137}
138
139fn clc_rpl(input: &str) -> IResult<&str, ReplCommand> {
140 let (input, _) = tag("clc")(input)?;
141 Ok((input, ReplCommand::Clc))
142}
143
144fn ls_rpl(input: &str) -> IResult<&str, ReplCommand> {
145 let (input, _) = tag("ls")(input)?;
146 Ok((input, ReplCommand::Ls))
147}
148
149fn load_rpl(input: &str) -> IResult<&str, ReplCommand> {
150 let (input, _) = tag("load")(input)?;
151 let (input, _) = space1(input)?;
152 let (input, path_strings) = separated_list1(space1, alt((take_until(" "),take_until("\r\n"))))(input)?;
153 Ok((input, ReplCommand::Load(path_strings.iter().map(|s| s.to_string()).collect())))
154}
155
156fn step_rpl(input: &str) -> IResult<&str, ReplCommand> {
157 let (input, _) = tag("step")(input)?;
158 let (input, _) = space1(input)?;
159 let (input, step_id) = opt(nom_tuple((tag("#"), digit1, space1)))(input)?;
160 let (input, count) = opt(digit1)(input)?;
161 let step_id = match step_id {
162 Some((_, id_str, _)) => match id_str.parse::<usize>() {
163 Ok(id) => Some(id),
164 Err(_) => None,
165 },
166 _ => None,
167 };
168 let count = match count {
169 Some(count_str) => match count_str.parse::<u64>() {
170 Ok(count) => Some(count),
171 Err(_) => None,
172 },
173 _ => None,
174 };
175 Ok((input, ReplCommand::Step(step_id, count)))
176}