use lazy_static::lazy_static;
use num::ToPrimitive;
use num_rational::Ratio;
use paste::paste;
use std::fmt;
use crate::parse::token::Field as TokField;
use crate::parse::token::Value as TokValue;
#[derive(Clone, PartialEq, Debug)]
pub enum Token {
Field(Field),
Comment { is_inline: bool, inner: String },
Checksum(u8),
}
impl<'a, 'input: 'a> From<&'a TokField<'input>> for Token {
fn from(field: &'a TokField<'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 {
pub letters: String,
pub value: Value,
}
impl fmt::Display for Field {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}{}", self.letters, self.value)
}
}
impl<'a, 'input: 'a> From<&'a TokField<'input>> for Field {
fn from(field: &'a TokField<'input>) -> Self {
Self {
letters: field.letters.to_string(),
value: Value::from(&field.value),
}
}
}
impl Into<Token> for Field {
fn into(self) -> Token {
Token::Field(self)
}
}
#[derive(Clone, PartialEq, Debug)]
pub enum Value {
Rational(Ratio<i64>),
Float(f64),
Integer(usize),
String(String),
}
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<'a, 'input: 'a> From<&'a TokValue<'input>> for Value {
fn from(val: &'a TokValue<'input>) -> Self {
use TokValue::*;
match val {
Rational(r) => Self::Rational(*r),
Integer(i) => Self::Integer(*i),
String(s) => Self::String(s.to_string()),
}
}
}
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: stringify!([<$arg:upper>]).to_string(),
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>]<I: Iterator<Item = Field>>(args: I) -> Command {
Command {
name: [<$commandName:snake:upper _FIELD>].clone(),
args: args.filter(|arg| {
match arg.letters.to_ascii_uppercase().as_str() {
$(stringify!($arg) => true,)*
_ => false
}
}).collect(),
}
}
lazy_static! {
pub static ref [<$commandName:snake:upper _FIELD>]: Field = Field {
letters: $letters.to_string(),
value:Value::Integer($value),
};
}
)*
}
#[derive(Clone, PartialEq, Debug)]
pub struct Command {
name: Field,
args: Vec<Field>,
}
impl Command {
pub fn push(&mut self, arg: Field) {
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 as_token_vec(mut self) -> Vec<Token> {
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> {
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) {
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, {}
},
);