1

GCode is language used to tell multi-axis (CNC) robots how to move. It looks like this :

M3 S5000 (Start Spindle Clockwise at 5000 RPM)
G21 (All units in mm)
G00 Z1.000000 (lift Z axis up by 1mm)
G00 X94.720505 Y-14.904622 (Go to this XY coordinate)
G01 Z0.000000 F100.0 (Penetrate at 100mm/m)
G01 X97.298434 Y-14.870127 F400 (cut to here)
G03 X98.003848 Y-14.275867 I-0.028107 J0.749174 (cut an arc)
G00 Z1.000000 (lift Z axis)
etc.

I have layed these commands out in sentences, but each token could be on a separate line. And in fact there are no rules about numbers being concatenated to their respective code letters. Yet I already have a LEX parser which can get me the tokens as described below.

Note that certain commands (M or G codes) have parameters. In the case of M3, it can have an S (spindle speed) parameter. G0 and G1 can have X,Y,Z,F etc. G3 can have X,Y,Z,I,J,R... However each G code does not require ALL those parameters, just one, many or all.

One thing to note here is that we are cutting a single path, then lifting the z axis. That is, we move to a location above the work surface, penetrate, cut a path then lift off. I would call this a 'block' or a 'path' and it is this that I'm interested in.

I need to be able to parse GCode in any messy format and then create a structure of 'blocks', where a block is any series of 'commands' between an z axis down and up.

I can tokenise this language using LEX (python PLY specifically). And get :

type M value 3
type S value 5000
type COMMENT value "Start Spindle Clockwise at 5000 RPM"
type G value 31
type COMMENT value "All unites in mm"
type G value 0
type Z value 1.0
etc.

Now using Lexx I need a rule for a thing called a 'command'.

A command is any comment, or : A 'G' or 'M' code followed by ANY of the appropriate parameter codes (X,Y,Z etc.) Command ends when another command (comment, G or M) is encountered.

Then I need a thing called a 'block', where a block is any set of 'commands' that come after a Z down and before a Z up.

There are 100 G codes and 100 M Codes and 25 parameter codes (A-Z minus G and M)

A rule for 'command' might look like :

command : G F H I J K L S T W X Y Z (how to specify ONE OF)
    | M S F (How to specify one of)
    | COMMENT

And then how would we define block!?

I realise this is a very long post, but if anyone can give me even an idea as to whether YACC can do this? Otherwise I'll just write some code that converts the lex tokens into a tree manually.

Addendum @rici

Thank you for taking the time to understand this question. By way of feedback: My task in full is to get YACC to do the heavy lifting of separating chunks of code into blocks based on different use cases.

For example When 'engraving', often a block will represent a letter or some other shape (in the xy plane). So a block will be defined by the movement of the z axis in and out of the xy plane.

I want to be able to post process blocks:

  • hatch fill a 'block'. which will involve some fairly complicated calculation of path boundaries, tangents to those boundaries, tool diameter etc. This is the most pressing use case and I haven't a good solution to this yet but I know it can be done because it can be done in Inkscape (vector graphics application)
  • rotate by n degrees. A fairly simply coordinate translation, I have a solution for this already.
  • iteratively deepen (extrude). Copy blocks and adjust Z depth on each iteration. Simple.
  • etc.
Richard
  • 1,070
  • 9
  • 22
  • In your example, the `Z` word only appears by itself (inside a `G` command). But that's not a requirement of the language, as I understand it. Perhaps you could clarify? – rici Jan 06 '22 at 15:19
  • Also, when you ask (if that is what you are doing) "How to specify ONE OF", do you mean "how do I write a rule in which options can come in any order but each one appears only once?" If that's your question, then the answer is that you can't. The uniqueness check must be done in a semantic action. But it would be good if you could clarify what you mean. – rici Jan 06 '22 at 15:19
  • @rici It is the case that an X,Y or Z location only has an effect if it is part of a G code instruction. So you can set X, Y, or Z but without a G00 etc. it will do nothing, and the G00 must come first. For the 'command' rule, I need to tell Yacc that a command is any G or M followed by AT LEAST ONE OF x,y,z,f.... etc. – Richard Jan 06 '22 at 16:46
  • i don't think you understood my first question, probably because I phrased it badly. In your examples, a Z word is the only location in the G instruction. There is no X or Y. But there could be, right? Or no? Please edit the clarifications into your question. Thanks – rici Jan 06 '22 at 17:31
  • @rici Ahh yes.. A G command does not need to have all three coordinates, a minimum of one of X, Y or Z.. but it can have all three. – Richard Jan 08 '22 at 12:54

1 Answers1

2

If you just want to ensure that a G command is followed by something, you can do this:

g_modifier: F | H | I | J | K | L | S | T | W | X | Y | Z
m_modifier: S | F
g_command: G g_modifier | g_command g_modifier
m_command: M m_modifier | m_command m_modifier
command: g_command | m_command | COMMENT

If you want to split those into sequences using the presence of a Z modifier, that can be done. You might want the lexer to be able to produce two different Z token types, based on the sign of the argument, because the parser can only make syntax decision based on tokens, not on semantic values.

Your question provides at least two different definitions of a block, making it a bit difficult to provide a clear answer.

  1. "That is, we move to a location above the work surface, penetrate, cut a path then lift off. I would call this a 'block' or a 'path' and it is this that I'm interested in."

    That would be, for example:

    G00 X94.7 Y-14.9 (Move)
    G01 Z0.0 (Penetrate)
    G01 X97.2 Y-14.8 G03 X98.0 Y-14.2 I-0.02 J0.7 (Path)
    G00 Z1.0 (Lift)
    
  2. But later you say, "a block is any set of 'commands' that come after a Z down and before a Z up.

    That would be just this part of the previous example:

    G01 X97.2 Y-14.8 G03 X98.0 Y-14.2 I-0.02 J0.7 (Path)
    

Those are both possible, but obviously different. Here are some possible building blocks:

# This list doesn't include Z words
g_modifier: F | H | I | J | K | L | S | T | W | X | Y
g_command_no_z: G g_modifier
              | g_command_no_z g_modifier

# This doesn't distinguish between Z up and Z down. If you want that to
# affect syntax, you need two different Z tokens, and then two different
# with_z non-terminals.
g_command_with_z: G Z
                | g_command_no_z Z 
                | g_command_with_z g_modifier

# You might or might not want this.
# It's a non-empty sequence of G or M commands with no Z's.
path: command_no_z
    | path command_no_z
command_no_z: COMMENT
            | m_command
            | g_command_no_z
rici
  • 234,347
  • 28
  • 237
  • 341
  • Wow, thanks for the answer.. I just logged on to reply to your last comment above and found this. I haven't time to digest your answer yet. I'll get back to you soon. – Richard Jan 08 '22 at 12:52
  • thank you very much for your answer. I think from here I can work out the smaller details. You've been extremely helpful. – Richard Jan 10 '22 at 11:17