use std::collections::HashSet;
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::character::complete::digit1;
use nom::character::complete::line_ending;
use nom::character::complete::not_line_ending;
use nom::character::complete::space0;
use nom::combinator::map;
use nom::combinator::map_res;
use nom::multi::many;
use nom::sequence::preceded;
use nom::sequence::terminated;
use nom::IResult;
use nom::Parser;
use crate::arc::parse_arc_a;
use crate::arc::parse_arc_b;
use crate::arc::parse_arc_c;
use crate::arc::parse_arc_e;
use crate::arc::parse_arc_f;
use crate::arc::parse_arc_i;
use crate::arc::parse_arc_j;
use crate::arc::parse_arc_s;
use crate::arc::parse_arc_u;
use crate::arc::parse_arc_v;
use crate::arc::parse_arc_w;
use crate::arc::parse_arc_x;
use crate::arc::parse_arc_y;
use crate::arc::parse_arc_z;
use crate::arc::ArcVal;
use crate::arc::Form as ArcForm;
use crate::params::head::parse_a;
use crate::params::head::parse_b;
use crate::params::head::parse_c;
use crate::params::head::parse_e;
use crate::params::head::parse_f;
use crate::params::head::parse_s;
use crate::params::head::parse_u;
use crate::params::head::parse_v;
use crate::params::head::parse_w;
use crate::params::head::parse_x;
use crate::params::head::parse_y;
use crate::params::head::parse_z;
use crate::params::head::PosVal;
use crate::params::mp::parse_mp_c;
use crate::params::mp::parse_mp_p;
use crate::params::mp::parse_mp_s;
use crate::params::mp::parse_mp_t;
use crate::params::mp::parse_mp_u;
use crate::params::mp::MultiPartVal;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Command {
G0(HashSet<PosVal>),
G1(HashSet<PosVal>),
G2(ArcForm),
G3(ArcForm),
G20,
G21,
G90,
G91,
G92(HashSet<PosVal>),
M486(MultiPartVal),
GDrop(u16),
MDrop(u16),
Comment(String),
Nop,
}
impl Command {
pub fn parse_line(line: &str) -> IResult<&str, Self> {
alt((
parse_g1,
parse_g0,
parse_g2,
parse_g3,
map(tag("G20"), |_| Self::G20),
map(tag("G21"), |_| Self::G21),
map(tag("G90"), |_| Self::G90),
map(tag("G91"), |_| Self::G91),
parse_g92,
parse_comment,
parse_486,
map(g_drop, Self::GDrop),
map(m_drop, Self::MDrop),
map(tag(""), |_| Self::Nop),
))
.parse(line)
}
}
pub fn g_drop(i: &str) -> IResult<&str, u16> {
map_res(preceded(tag("G"), digit1), str::parse).parse(i)
}
fn parse_comment(i: &str) -> IResult<&str, Command> {
preceded(
(space0, tag(";")),
terminated(
map(not_line_ending, |v: &str| Command::Comment(v.to_string())),
line_ending,
),
)
.parse(i)
}
fn parse_g0(i: &str) -> IResult<&str, Command> {
preceded(
(alt((tag("G00"), tag("G0"))), space0),
map(pos_many, |vals: Vec<PosVal>| {
let hs = HashSet::from_iter(vals);
Command::G0(hs)
}),
)
.parse(i)
}
fn parse_g1(i: &str) -> IResult<&str, Command> {
preceded(
(alt((tag("G01"), tag("G1"))), space0),
map(pos_many, |vals: Vec<PosVal>| {
let hs = HashSet::from_iter(vals);
Command::G1(hs)
}),
)
.parse(i)
}
fn parse_g2(i: &str) -> IResult<&str, Command> {
preceded(
(alt((tag("G02"), tag("G2"))), space0),
map_res(arc_many, |vals: Vec<ArcVal>| {
let hs = HashSet::from_iter(vals);
let mut has_ij = false;
let mut has_r = false;
for val in &hs {
match val {
ArcVal::I(_) | ArcVal::J(_) => {
has_ij = true;
}
ArcVal::R(_) => {
has_r = true;
}
_ => {}
}
}
match (has_ij, has_r) {
(true, false) => Ok(Command::G2(ArcForm::IJ(hs))),
(false, true) => Ok(Command::G2(ArcForm::R(hs))),
_ => {
Err("Invalid G2 command: must have either I,J or R but not both")
}
}
}),
)
.parse(i)
}
fn parse_g3(i: &str) -> IResult<&str, Command> {
preceded(
(alt((tag("G3"), tag("G03"))), space0),
map_res(arc_many, |vals: Vec<ArcVal>| {
let hs = HashSet::from_iter(vals);
let mut has_ij = false;
let mut has_r = false;
for val in &hs {
match val {
ArcVal::I(_) | ArcVal::J(_) => {
has_ij = true;
}
ArcVal::R(_) => {
has_r = true;
}
_ => {}
}
}
match (has_ij, has_r) {
(true, false) => Ok(Command::G3(ArcForm::IJ(hs))),
(false, true) => Ok(Command::G3(ArcForm::R(hs))),
_ => {
Err("Invalid G2 command: must have either I,J or R but not both")
}
}
}),
)
.parse(i)
}
fn parse_g92(i: &str) -> IResult<&str, Command> {
preceded(
(tag("G92"), space0),
map(pos_many, |vals: Vec<PosVal>| {
let hs = HashSet::from_iter(vals);
Command::G92(hs)
}),
)
.parse(i)
}
fn parse_486(i: &str) -> IResult<&str, Command> {
preceded(
(tag("M486"), space0),
map(multipart_val, |val: MultiPartVal| {
Command::M486(val)
}),
)
.parse(i)
}
fn pos_many(i: &str) -> IResult<&str, Vec<PosVal>> {
many(1..12, pos_val).parse(i)
}
fn arc_many(i: &str) -> IResult<&str, Vec<ArcVal>> {
many(1..16, arc_val).parse(i)
}
fn pos_val(i: &str) -> IResult<&str, PosVal> {
alt((
parse_a, parse_b, parse_c, parse_e, parse_f, parse_s, parse_u, parse_v, parse_w, parse_x,
parse_y, parse_z,
))
.parse(i)
}
fn multipart_val(i: &str) -> IResult<&str, MultiPartVal> {
alt((parse_mp_c, parse_mp_p, parse_mp_s, parse_mp_t, parse_mp_u)).parse(i)
}
fn arc_val(i: &str) -> IResult<&str, ArcVal> {
alt((
parse_arc_a,
parse_arc_b,
parse_arc_c,
parse_arc_e,
parse_arc_f,
parse_arc_i,
parse_arc_j,
parse_arc_s,
parse_arc_u,
parse_arc_v,
parse_arc_w,
parse_arc_x,
parse_arc_y,
parse_arc_z,
))
.parse(i)
}
pub fn m_drop(i: &str) -> IResult<&str, u16> {
map_res(preceded((tag("M"), space0), digit1), str::parse).parse(i)
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn comments() {
let text_commands = [
(
"; perimeters extrusion width = 0.67mm\n",
Ok((
"",
Command::Comment(String::from(" perimeters extrusion width = 0.67mm")),
)),
),
(
"; 7K6Ho8Q5vPBT4ZkdDGAk/t/wOw4rChXwlVJwAAAABJRU5ErkJggg==\n",
Ok((
"",
Command::Comment(String::from(
" 7K6Ho8Q5vPBT4ZkdDGAk/t/wOw4rChXwlVJwAAAABJRU5ErkJggg==",
)),
)),
),
(
"; generated by Slic3r 1.2.9 on 2015-10-01 at 20:51:53
; external perimeters extrusion width = 0.40mm",
Ok((
"
; external perimeters extrusion width = 0.40mm",
Command::Comment(" generated by Slic3r 1.2.9 on 2015-10-01 at 20:51:53".into()),
)),
),
];
for (line, expected) in text_commands {
let actual = Command::parse_line(line);
assert_eq!(actual, expected, "line: {line}");
}
}
#[test]
fn g0() {
let text_commands = [
(
"G0E-2.7F4200",
Ok((
"",
Command::G0([PosVal::E(-2.7), PosVal::F(4200_f64)].into()),
)),
),
(
"G00 E20",
Ok(("", Command::G0([PosVal::E(20_f64)].into()))),
),
(
"G00E20",
Ok(("", Command::G0([PosVal::E(20_f64)].into()))),
),
];
for (line, expected) in text_commands {
let actual = Command::parse_line(line);
assert_eq!(actual, expected, "line: {line}");
}
}
#[test]
fn g1() {
let text_commands = [
("G1 Z5", Ok(("", Command::G1([PosVal::Z(5_f64)].into())))),
(
"G1 Z5 F5000 ; lift nozzle",
Ok((
" ; lift nozzle",
Command::G1([PosVal::Z(5_f64), PosVal::F(5000_f64)].into()),
)),
),
(
"G1 E1.00000 F1800.00000 ; text",
Ok((
" ; text",
Command::G1([PosVal::E(1.0_f64), PosVal::F(1800_f64)].into()),
)),
),
(
"G1 Z0.350 F7800.000",
Ok((
"",
Command::G1([PosVal::Z(0.350_f64), PosVal::F(7800_f64)].into()),
)),
),
(
"G1Z0.350F7800.000",
Ok((
"",
Command::G1([PosVal::Z(0.350_f64), PosVal::F(7800_f64)].into()),
)),
),
(
"G1 Z0.350F7800.000",
Ok((
"",
Command::G1([PosVal::Z(0.350_f64), PosVal::F(7800_f64)].into()),
)),
),
(
"G1X888F1000",
Ok((
"",
Command::G1([PosVal::X(888_f64), PosVal::F(1000_f64)].into()),
)),
),
(
"G01X100E20",
Ok((
"",
Command::G1([PosVal::X(100_f64), PosVal::E(20_f64)].into()),
)),
),
(
"G1 ",
Ok((" ", Command::GDrop(1))),
),
];
for (line, expected) in text_commands {
let actual = Command::parse_line(line);
assert_eq!(actual, expected, "line: {line}");
}
}
#[test]
fn g2() {
let text_commands = [
(
"G2 X125 Y32 I10.5 J10.5; arc",
Ok((
"; arc",
Command::G2(ArcForm::IJ(
[
ArcVal::X(125_f64),
ArcVal::Y(32_f64),
ArcVal::I(10.5),
ArcVal::J(10.5),
]
.into(),
)),
)),
),
(
"G2 I20 J20; X and Y can be omitted to do a complete circle.",
Ok((
"; X and Y can be omitted to do a complete circle.",
Command::G2(ArcForm::IJ([ArcVal::I(20_f64), ArcVal::J(20_f64)].into())),
)),
),
(
"G02X100J20",
Ok((
"",
Command::G2(ArcForm::IJ([ArcVal::X(100_f64), ArcVal::J(20_f64)].into())),
)),
),
];
for (line, expected) in text_commands {
let actual = Command::parse_line(line);
assert_eq!(actual, expected, "line: {line}");
}
}
#[test]
fn g3() {
let text_commands = [
(
"G3 X125 Y32 I10.5 J10.5; arc",
Ok((
"; arc",
Command::G3(ArcForm::IJ(
[
ArcVal::X(125_f64),
ArcVal::Y(32_f64),
ArcVal::I(10.5),
ArcVal::J(10.5),
]
.into(),
)),
)),
),
(
"G3 I20 J20; X and Y can be omitted to do a complete circle.",
Ok((
"; X and Y can be omitted to do a complete circle.",
Command::G3(ArcForm::IJ([ArcVal::I(20_f64), ArcVal::J(20_f64)].into())),
)),
),
(
"G03X100J20",
Ok((
"",
Command::G3(ArcForm::IJ([ArcVal::X(100_f64), ArcVal::J(20_f64)].into())),
)),
),
];
for (line, expected) in text_commands {
let actual = Command::parse_line(line);
assert_eq!(actual, expected, "line: {line}");
}
}
#[test]
fn m486() {
let text_commands = [
(
"M486 C; cancel the current object (use with care)",
Ok((
"; cancel the current object (use with care)",
Command::M486(MultiPartVal::C),
)),
),
(
"M486 S3; Indicate that the 4th object is starting now",
Ok((
"; Indicate that the 4th object is starting now",
Command::M486(MultiPartVal::S(3, None)),
)),
),
(
"M486 P10; Cancel object with index 10 (the 11th object)",
Ok((
"; Cancel object with index 10 (the 11th object)",
Command::M486(MultiPartVal::P(10)),
)),
),
(
"M486 U2; Un-cancel object with index 2 (the 3rd object)",
Ok((
"; Un-cancel object with index 2 (the 3rd object)",
Command::M486(MultiPartVal::U(2)),
)),
),
(
"M486 T12; Total of 12 objects (otherwise the firmware must count)",
Ok((
"; Total of 12 objects (otherwise the firmware must count)",
Command::M486(MultiPartVal::T(12)),
)),
),
(
"M486 S-1",
Ok(("", Command::M486(MultiPartVal::S(-1, None)))),
),
("M486 T12", Ok(("", Command::M486(MultiPartVal::T(12))))),
("M486 U2", Ok(("", Command::M486(MultiPartVal::U(2))))),
("M486 P1", Ok(("", Command::M486(MultiPartVal::P(1))))),
("M486 S2", Ok(("", Command::M486(MultiPartVal::S(2, None))))),
("M486 T3", Ok(("", Command::M486(MultiPartVal::T(3))))),
("M486 U-1", Ok(("", Command::M486(MultiPartVal::U(-1))))),
];
for (line, expected) in text_commands {
let actual = Command::parse_line(line);
assert_eq!(actual, expected, "line: {line}");
}
}
#[test]
const fn parse_g_drop() {}
}