use num::ToPrimitive;
use num_rational::Ratio;
use paste::paste;
use std::borrow::Cow;
use std::fmt;
use crate::parse::token::Field as ParsedField;
use crate::parse::token::Value as ParsedValue;
#[derive(Clone, PartialEq, Debug)]
pub enum Token<'a> {
Field(Field<'a>),
Comment {
is_inline: bool,
inner: Cow<'a, str>,
},
Checksum(u8),
}
impl<'input> From<&ParsedField<'input>> for Token<'input> {
fn from(field: &ParsedField<'input>) -> Self {
Self::Field(field.into())
}
}
impl fmt::Display for Token<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use Token::*;
match self {
Field(field) => write!(f, "{}", field),
Comment { is_inline, inner } => match is_inline {
true => write!(f, "({})", inner),
false => write!(f, ";{}", inner),
},
Checksum(c) => write!(f, "{}", c),
}
}
}
#[derive(Clone, PartialEq, Debug)]
pub struct Field<'a> {
pub letters: Cow<'a, str>,
pub value: Value<'a>,
}
impl<'a> fmt::Display for Field<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}{}", self.letters, self.value)
}
}
impl<'input> From<&ParsedField<'input>> for Field<'input> {
fn from(field: &ParsedField<'input>) -> Self {
Self {
letters: field.letters.into(),
value: Value::from(&field.value),
}
}
}
impl<'a> From<Field<'a>> for Token<'a> {
fn from(field: Field<'a>) -> Token<'a> {
Token::Field(field)
}
}
#[derive(Clone, PartialEq, Debug)]
pub enum Value<'a> {
Rational(Ratio<i64>),
Float(f64),
Integer(usize),
String(Cow<'a, str>),
}
impl Value<'_> {
pub fn as_f64(&self) -> Option<f64> {
match self {
Self::Rational(r) => r.to_f64(),
Self::Integer(i) => Some(*i as f64),
Self::Float(f) => Some(*f),
Self::String(_) => None,
}
}
}
impl<'input> From<&ParsedValue<'input>> for Value<'input> {
fn from(val: &ParsedValue<'input>) -> Self {
use ParsedValue::*;
match val {
Rational(r) => Self::Rational(*r),
Integer(i) => Self::Integer(*i),
String(s) => Self::String(Cow::Borrowed(s)),
}
}
}
impl fmt::Display for Value<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Rational(r) => write!(f, "{}", r.to_f64().ok_or(fmt::Error)?),
Self::Float(float) => write!(f, "{}", float),
Self::Integer(i) => write!(f, "{}", i),
Self::String(s) => write!(f, "\"{}\"", s),
}
}
}
#[macro_export]
macro_rules! command {
($commandName: ident {
$($arg: ident : $value: expr,)*
}) => {
{
use g_code::emit::*;
use paste::paste;
paste::expr!{
[<$commandName:snake:lower>](
vec![$(
Field {
letters: Cow::Borrowed(stringify!([<$arg:upper>])),
value: Value::Float($value),
}
,)*].drain(..)
)
}
}
};
}
macro_rules! impl_commands {
($($(#[$outer:meta])* $commandName: ident {$letters: expr, $value: literal, {$($(#[$inner:meta])* $arg: ident), *} },)*) => {
paste! {
$(
$(#[$outer])*
pub fn [<$commandName:snake:lower>]<'a, I: Iterator<Item = Field<'a>>>(args: I) -> Command<'a> {
Command {
name: [<$commandName:snake:upper _FIELD>].clone(),
args: args.filter(|arg| {
match arg.letters.to_ascii_uppercase().as_str() {
$(stringify!($arg) => true,)*
_ => false
}
}).collect(),
}
}
pub const [<$commandName:snake:upper _FIELD>]: Field<'static> = Field {
letters: Cow::Borrowed($letters),
value: Value::Integer($value),
};
)*
}
#[derive(Clone, PartialEq, Debug)]
pub struct Command<'a> {
name: Field<'a>,
args: Vec<Field<'a>>,
}
impl<'a> Command<'a> {
pub fn push(&mut self, arg: Field<'a>) {
match &self.name {
$(x if *x == paste!{[<$commandName:snake:upper _FIELD>]}.clone() => {
if match arg.letters.to_ascii_uppercase().as_str() {
$(stringify!($arg) => {true},)*
_ => false,
} {
self.args.push(arg);
} else {
}
},)*
_ => {
dbg!(&self.name);
dbg!(&arg);
}
}
}
pub fn iter(&self) -> impl Iterator<Item = &Field> {
std::iter::once(&self.name).chain(self.args.iter())
}
pub fn into_token_vec(mut self) -> Vec<Token<'a>> {
std::iter::once(self.name).chain(self.args.drain(..)).map(|f| f.into()).collect()
}
pub fn iter_args(&self) -> impl Iterator<Item = &Field> {
self.iter().skip(1)
}
pub fn iter_mut_args(&mut self) -> impl Iterator<Item = &mut Field<'a>> {
self.args.iter_mut()
}
pub fn get(&'_ self, letters: &str) -> Option<&'_ Field> {
let letters = letters.to_ascii_uppercase();
self.iter_args().find(|arg| arg.letters == letters)
}
pub fn set(&mut self, letters: &str, value: Value<'a>) {
let letters = letters.to_ascii_uppercase();
for i in 0..self.args.len() {
if self.args[i].letters == letters {
self.args[i].value = value;
break;
}
}
}
}
};
}
impl_commands!(
RapidPositioning {
"G", 0, {
X,
Y,
Z,
E,
F,
H,
R,
S,
A,
B,
C
}
},
LinearInterpolation {
"G", 1, {
X,
Y,
Z,
E,
F,
H,
R,
S,
A,
B,
C
}
},
Dwell {
"G", 4, {
P
}
},
UnitsInches {
"G", 20, {}
},
UnitsMillimeters {
"G", 21, {}
},
AbsoluteDistanceMode {
"G", 90, {}
},
RelativeDistanceMode {
"G", 91, {}
},
FeedRateUnitsPerMinute {
"G", 94, {}
},
StartSpindleClockwise {
"M", 3, {
P
}
},
StartSpindleCounterclockwise {
"M", 4, {
P
}
},
StopSpindle {
"M", 5, {}
},
ProgramEnd {
"M", 20, {}
},
);