Skip to content

Switch to a push-parser that gives the caller control over parsing #51

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 0 additions & 21 deletions .appveyor.yml

This file was deleted.

64 changes: 64 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
on: [push, pull_request]

name: Continuous integration


jobs:
check:
name: Compile and Test
runs-on: ubuntu-latest
strategy:
matrix:
rust:
- nightly
- stable
# MSRV - Relatively recent compiler version
- 1.46.0
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ matrix.rust }}
override: true
- uses: actions-rs/cargo@v1
with:
command: check
args: --verbose --manifest-path gcode/Cargo.toml
- uses: actions-rs/cargo@v1
with:
command: build
args: --verbose --manifest-path gcode/Cargo.toml
- uses: actions-rs/cargo@v1
with:
command: test
args: --verbose --manifest-path gcode/Cargo.toml

api-docs:
name: Publish API Docs to GitHub Pages
runs-on: ubuntu-latest
strategy:
matrix:
rust:
- nightly
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ matrix.rust }}
override: true
- uses: actions-rs/cargo@v1
with:
command: doc
args: --verbose --manifest-path gcode/Cargo.toml
- name: Redirect top-level GitHub Pages
run: "echo '<meta http-equiv=\"refresh\" content=\"0; url=gcode/index.html\" />' > gcode/target/doc/index.html"
shell: bash
- name: GitHub Pages
uses: crazy-max/ghaction-github-pages@v2
with:
target_branch: gh-pages
build_dir: gcode/target/doc
env:
GITHUB_TOKEN: ${{ secrets.GH_PAGES_ACCESS_TOKEN }}
74 changes: 0 additions & 74 deletions .travis.yml

This file was deleted.

14 changes: 4 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
# gcode-rs
# GCode

[![Crates.io version](https://img.shields.io/crates/v/gcode.svg)](https://crates.io/crates/gcode)
[![Docs](https://docs.rs/gcode/badge.svg)](https://docs.rs/gcode/)
[![Build Status](https://travis-ci.org/Michael-F-Bryan/gcode-rs.svg?branch=master)](https://travis-ci.org/Michael-F-Bryan/gcode-rs)
[![Continuous integration](https://github.com/Michael-F-Bryan/gcode-rs/workflows/Continuous%20integration/badge.svg?branch=master)](https://github.com/Michael-F-Bryan/gcode-rs/actions)

A gcode parser designed for use in `#[no_std]` environments.

For an example of the `gcode` crate in use, see
[@etrombly][etrombly]'s [`gcode-yew`][gc-y].
A zero-copy gcode parser designed for use in `#[no_std]` environments.

## Useful Links

- [The thread that kicked this idea off][thread]
- [Rendered Documentation][docs]
- [Rendered Documentation for `master`][docs]
- [NIST GCode Interpreter Spec][nist]

## License
Expand Down Expand Up @@ -41,9 +38,6 @@ conditions.

[thread]:https://users.rust-lang.org/t/g-code-interpreter/10930
[docs]: https://michael-f-bryan.github.io/gcode-rs/
[p3]: https://github.com/Michael-F-Bryan/gcode-rs/blob/master/tests/data/program_3.gcode
[nist]: http://ws680.nist.gov/publication/get_pdf.cfm?pub_id=823374
[cargo-c]: https://github.com/lu-zero/cargo-c
[etrombly]: https://github.com/etrombly
[gc-y]: https://github.com/etrombly/gcode-yew
[crev]: https://github.com/crev-dev/cargo-crev
11 changes: 4 additions & 7 deletions gcode/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,12 @@ maintenance = { status = "actively-developed" }

[features]
default = ["std"]
std = ["arrayvec/std"]
serde-1 = ["serde", "serde_derive", "arrayvec/serde"]
std = []

[dependencies]
cfg-if = "0.1.9"
arrayvec = { version ="0.5", default-features = false }
serde = { version = "1.0", optional = true }
serde_derive = { version = "1.0", optional = true }
libm = "0.2"
num-derive = "0.3.3"
num-traits = "0.2.14"

[dev-dependencies]
arrayvec = "0.5.2"
pretty_assertions = "0.6.1"
27 changes: 0 additions & 27 deletions gcode/benches/example_files.rs

This file was deleted.

110 changes: 110 additions & 0 deletions gcode/examples/incremental-parsing.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
//! This example shows how you would use the `gcode` library in a resource
//! constrained environment where all code must use have bounded memory usage
//! (i.e. no dynamic allocation) and the parsing process must periodically yield
//! to allow other tasks to make progress.
//!
//! We drop down to the `gcode::low_level` layer and use the callback mechanism
//! to incrementally fill a buffer with new commands. Parsing is paused when
//! the buffer becomes full or we reach the end of the line, allowing us to
//! limit memory usage to a fixed buffer and make sure we don't spend too much
//! time parsing in one hit.

use arrayvec::ArrayVec;
use gcode::{
syntax::{Callbacks, Continuation, Parser, ParserState, Word},
Span,
};
use std::fmt::Debug;

/// Some example gcode taken from
/// https://github.com/dillonhuff/gpr/blob/e1e417e6d724544fe81b26dcf0db38b9fefbb13e/gcode_samples/cura_3D_printer.gcode
const TEXT_TO_PARSE: &str = r#"
;FLAVOR:RepRap
;TIME:713
;Filament used: 0.153994m
;Layer height: 0.1
;Generated with Cura_SteamEngine 2.5.0
M190 S60
M104 S200
M109 S200
G28 ;Home
G1 Z15.0 F6000 ;Move the platform down 15mm
;Prime the extruder
G92 E0
G1 F200 E3
G92 E0
;LAYER_COUNT:58
;LAYER:0
M107
G0 F3600 X43.256 Y45.828 Z0.3
;TYPE:SKIRT
G1 F1800 X43.853 Y45.312 E0.01484
G1 X44.5 Y44.859 E0.0297
G1 X45.15 Y44.493 E0.04373
"#;

fn main() -> Result<(), Box<dyn std::error::Error + 'static>> {
let mut interpreter = Interpreter::default();
let mut text = TEXT_TO_PARSE;
let mut parser = Parser::new();

loop {
// process as much of the text as we can
parser.process(text, &mut interpreter)?;

// do something with the parsed commands
println!("{:?}", interpreter.words);

// the commands have been processed so we don't need them any more
interpreter.words.clear();

// We probably stopped parsing before reaching the end of our buffer.
// Update the buffer so we can continue where we left off.
match interpreter.stopped_at.take() {
Some(span) => text = &text[span.start..],
None => break,
}
}

Ok(())
}

/// A simple, incremental G-Code interpreter which will stop at the end of each
/// line or when its word buffer gets full.
#[derive(Debug, Default, Clone, PartialEq)]
struct Interpreter {
words: ArrayVec<[Word; 4]>,
stopped_at: Option<Span>,
}

impl Callbacks for Interpreter {
type Error = core::convert::Infallible;

fn on_word(
&mut self,
span: Span,
word: Word,
_state: &ParserState,
) -> Continuation<Self::Error> {
if self.words.try_push(word).is_err() {
// looks like we ran out of buffer space, remember the current
// position and bail.
self.stopped_at = Some(span);
Continuation::Break
} else {
Continuation::Continue
}
}

fn on_end_line(
&mut self,
span: Span,
_line: &str,
_state: &ParserState,
) -> Continuation<Self::Error> {
// we want to stop at the end of each line (e.g. to make sure we don't
// spend our full time budget parsing)
self.stopped_at = Some(span);
Continuation::Break
}
}
Loading