use crate::handler::GCodeValueHandler;
use std::str::Chars;
pub struct GCodeParser<'a> {
gcode: &'a String,
}
#[derive(Clone, Debug)]
pub struct GCodeValue {
pub command: String,
pub value: String,
}
impl GCodeValue {
pub fn new() -> Self {
Self {
command: "".to_string(),
value: "".to_string(),
}
}
pub fn to_string(&self) -> String {
format!("{}{}", self.command, self.value)
}
pub fn is_xy(&self) -> bool {
self.is_x() || self.is_y()
}
pub fn is_x(&self) -> bool {
self.command == "X"
}
pub fn is_y(&self) -> bool {
self.command == "Y"
}
pub fn value_f32(&self) -> f32 {
self.value.parse::<f32>().unwrap_or(0.0)
}
pub fn value_f64(&self) -> f64 {
self.value.parse::<f64>().unwrap_or(0.0)
}
}
impl std::fmt::Display for GCodeValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} {}", self.command, self.value)
}
}
impl<'a> GCodeParser<'a> {
pub fn new(gcode: &'a String) -> Self {
Self { gcode }
}
pub fn parse(&mut self, handler: &mut impl GCodeValueHandler) {
let mut chars = self.gcode.chars();
handler.start();
loop {
let (line, is_end) = self._read_gcode_value_line(&mut chars);
if !line.is_empty() {
handler.handle_gcode_value(line);
}
if is_end {
break;
}
}
handler.end();
}
fn _read_gcode_value_line(&self, chars: &mut Chars) -> (Vec<GCodeValue>, bool) {
let mut values: Vec<GCodeValue> = Vec::new();
let mut value = GCodeValue::new();
while let Some(c) = chars.next() {
match c {
'G' | 'M' | 'X' | 'Y' | 'I' | 'J' | 'S' | 'F' | 'Z' => {
if !value.command.is_empty() {
values.push(value);
value = GCodeValue::new();
}
value.command.push(c);
}
'0'..='9' | '.' | '-' | '+' | 'E' | 'e' => {
value.value.push(c);
}
';' => {
self._skip_until_newline(chars);
if !value.command.is_empty() {
values.push(value);
}
return (values, false);
}
_ => {
if self._is_newline(c) {
if !value.command.is_empty() {
values.push(value);
}
return (values, false);
}
}
}
}
if !value.command.is_empty() {
values.push(value);
}
(values, true)
}
fn _skip_until_newline(&self, chars: &mut Chars) {
while let Some(c) = chars.next() {
if self._is_newline(c) {
break;
}
}
}
fn _is_newline(&self, c: char) -> bool {
c == '\n' || c == '\r'
}
}