embedded_commands_rs/
lib.rs1use std::borrow::Cow;
2pub use phf::{phf_map,Map};
3
4#[derive(Debug, Clone)]
6pub enum Token<'a> {
7 Int(i64),
8 Float(f64),
9 Str(&'a str),
10 Bool(bool),
11}
12
13pub type CommandFn<S> = for<'a> fn(&mut S, &[Token<'a>]);
15
16pub struct Interpreter<S:'static> {
17 commands: &'static phf::Map<&'static str, CommandFn<S>>,
19}
20
21impl<S> Interpreter<S> {
22 #[inline]
24 pub const fn new(commands: &'static phf::Map<&'static str, CommandFn<S>>) -> Self {
25 Self { commands }
26 }
27
28 pub fn interpret<'src>(&mut self, state: &mut S, code: &'src str) {
30 for raw_line in code.lines() {
31 let mut line = raw_line.trim();
32 if line.is_empty(){
33 continue;
34 }
35 if let Some(stripped) = line.strip_suffix(';') {
36 line = stripped.trim();
37 }
38
39 let (command, args_body) = match (line.find('('), line.rfind(')')) {
40 (Some(open), Some(close)) if close > open => {
41 (line[..open].trim(), line[open + 1..close].trim())
42 }
43 _ => {
44 eprintln!("syntax error: expected command(args)");
45 continue;
46 }
47 };
48
49 let Some(&func) = self.commands.get(command) else {
50 eprintln!("unknown command by the name of '{}' detected",command);
51 continue;
52 };
53
54 let mut parsed_args: Vec<Token<'src>> = Vec::new();
56 let arg_strs = split_args(args_body);
57 let mut failed = false;
58
59
60 match parse_tokens(&arg_strs) {
61 Ok(tokens) => parsed_args = tokens,
62 Err(err) => {
63 eprintln!(
64 "parsing error for of type : {err}",
65 );
66 failed = true;
67 break;
68 }
69 }
70
71 if failed {
72 continue;
73 }
74
75 func(state, &parsed_args);
76 }
77 }
78}
79
80#[inline(always)]
82fn split_args(s: &str) -> Vec<&str> {
83 let mut out = Vec::new();
84 let mut start = 0;
85 let mut in_str = false;
86 let mut escaped = false;
87 let bytes = s.as_bytes();
88
89 for (i, &b) in bytes.iter().enumerate() {
90 match b {
91 b'"' if !escaped => in_str = !in_str,
92 b'\\' if in_str => escaped = !escaped,
93 b',' if !in_str => {
94 let segment = &s[start..i];
96 let segment = segment.trim();
97 if !segment.is_empty() {
98 out.push(segment);
99 }
100 start = i + 1;
101 escaped = false;
102 }
103 _ => escaped = false,
104 }
105 }
106
107 if start < s.len() {
108 let segment = &s[start..].trim();
109 if !segment.is_empty() {
110 out.push(segment);
111 }
112 }
113
114 out
115}
116
117#[inline(always)]
119pub fn parse_tokens<'a>(inputs: &[&'a str]) -> Result<Vec<Token<'a>>, String> {
120 let mut out: Vec<Token<'a>> = Vec::with_capacity(inputs.len());
121
122 for (i, raw) in inputs.iter().enumerate() {
123 let s = raw.trim();
124 if s.is_empty() {
125 return Err(format!("argument {} is empty", i + 1));
126 }
127
128 if let Some(stripped) = s.strip_prefix('"').and_then(|x| x.strip_suffix('"')) {
130 out.push(Token::Str(stripped));
131 continue;
132 }
133
134 match s {
136 "true" | "TRUE" | "True" => {
137 out.push(Token::Bool(true));
138 continue;
139 }
140 "false" | "FALSE" | "False" => {
141 out.push(Token::Bool(false));
142 continue;
143 }
144 _ => {}
145 }
146
147 let mut is_float = false;
149 for &b in s.as_bytes() {
150 if b == b'.' || b == b'e' || b == b'E' {
151 is_float = true;
152 break;
153 }
154 }
155
156 if !is_float {
157 match lexical_core::parse::<i64>(s.as_bytes()) {
158 Ok(v) => out.push(Token::Int(v)),
159 Err(_) => return Err(format!("invalid integer at argument {}: {}", i + 1, s)),
160 }
161 } else {
162 match lexical_core::parse::<f64>(s.as_bytes()) {
163 Ok(v) => out.push(Token::Float(v)),
164 Err(_) => return Err(format!("invalid float at argument {}: {}", i + 1, s)),
165 }
166 }
167 }
168
169 Ok(out)
170}
171
172
173
174