pax_global_header 0000666 0000000 0000000 00000000064 13410044113 0014501 g ustar 00root root 0000000 0000000 52 comment=ec1f3fc5460500f0a6778b489106cc5255de741b
pythonic-gcode-machine-ec1f3fc5460500f0a6778b489106cc5255de741b-PythonicGcodeMachine/ 0000775 0000000 0000000 00000000000 13410044113 0026700 5 ustar 00root root 0000000 0000000 PythonicGcodeMachine/ 0000775 0000000 0000000 00000000000 13410044113 0032645 5 ustar 00root root 0000000 0000000 pythonic-gcode-machine-ec1f3fc5460500f0a6778b489106cc5255de741b-PythonicGcodeMachine Gcode/ 0000775 0000000 0000000 00000000000 13410044113 0033666 5 ustar 00root root 0000000 0000000 pythonic-gcode-machine-ec1f3fc5460500f0a6778b489106cc5255de741b-PythonicGcodeMachine/PythonicGcodeMachine Fanuc/ 0000775 0000000 0000000 00000000000 13410044113 0034722 5 ustar 00root root 0000000 0000000 pythonic-gcode-machine-ec1f3fc5460500f0a6778b489106cc5255de741b-PythonicGcodeMachine/PythonicGcodeMachine/Gcode __init__.py 0000664 0000000 0000000 00000000000 13410044113 0037021 0 ustar 00root root 0000000 0000000 pythonic-gcode-machine-ec1f3fc5460500f0a6778b489106cc5255de741b-PythonicGcodeMachine/PythonicGcodeMachine/Gcode/Fanuc data/ 0000775 0000000 0000000 00000000000 13410044113 0035633 5 ustar 00root root 0000000 0000000 pythonic-gcode-machine-ec1f3fc5460500f0a6778b489106cc5255de741b-PythonicGcodeMachine/PythonicGcodeMachine/Gcode/Fanuc fanuc-gcodes-lathe.yaml 0000664 0000000 0000000 00000003656 13410044113 0042162 0 ustar 00root root 0000000 0000000 pythonic-gcode-machine-ec1f3fc5460500f0a6778b489106cc5255de741b-PythonicGcodeMachine/PythonicGcodeMachine/Gcode/Fanuc/data F:
meaning: set feed rate
G00:
meaning: Rapid traverse
G01:
meaning: Linear interpolation
G02:
meaning: Circular interpolation CW
G03:
meaning: Circular interpolation CCW
G04:
meaning: Dwell
G09:
meaning: Exact stop
G10:
meaning: Programmable data input
G20:
meaning: Input in inch
G21:
meaning: Input in mm
G22:
meaning: Stored stroke check function ON
G23:
meaning: Stored stroke check function OFF
G27:
meaning: Reference position return check
G28:
meaning: Return to reference position
G32:
meaning: Thread cutting
G40:
meaning: Tool nose radius compensation cancel
G41:
meaning: Tool nose radius compensation left
G42:
meaning: Tool nose radius compensation right
G70:
meaning: Finish machining cycle
G71:
meaning: Turning cycle
G72:
meaning: Facing cycle
G73:
meaning: Pattern repeating cycle
G74:
meaning: Peck drilling cycle
G75:
meaning: Grooving cycle
G76:
meaning: Threading cycle
G92:
meaning: Coordinate system setting or max. spindle speed setting
G94:
meaning: Feed per minute
G95:
meaning: Feed per revolution
G96:
meaning: Constant surface speed control
G97:
meaning: Constant surface speed control cancel
M00:
meaning: Program stop
M01:
meaning: Optional program stop
M02:
meaning: End of program
M03:
meaning: Spindle start forward CW
M04:
meaning: Spindle start reverse CCW
M05:
meaning: Spindle stop
M08:
meaning: Coolant ON
M09:
meaning: Coolant OFF
M29:
meaning: Rigid tap mode
M30:
meaning: End of program reset
M40:
meaning: Spindle gear at middle
M41:
meaning: Low gear select
M42:
meaning: High gear select
M68:
meaning: Hydraulic chuck close
M69:
meaning: Hydraulic chuck open
M78:
meaning: Tailstock advancing
M79:
meaning: Tailstock reversing
M94:
meaning: Cancel mirror image
M95:
meaning: Mirror image of X axis
M98:
meaning: Subprogram call
M99:
meaning: End of subprogram
S:
meaning: set spindle speed
T:
meaning: select tool
fanuc-gcodes-mill.yaml 0000664 0000000 0000000 00000005225 13410044113 0042014 0 ustar 00root root 0000000 0000000 pythonic-gcode-machine-ec1f3fc5460500f0a6778b489106cc5255de741b-PythonicGcodeMachine/PythonicGcodeMachine/Gcode/Fanuc/data F:
meaning: set feed rate
G00:
meaning: Rapid traverse
G01:
meaning: Linear interpolation
G02:
meaning: Circular interpolation CW
G03:
meaning: Circular interpolation CCW
G04:
meaning: Dwell
G17:
meaning: X Y plane selection
G18:
meaning: Z X plane selection
G19:
meaning: Y Z plane selection
G28:
meaning: Return to reference position
G30:
meaning: 2nd, 3rd and 4th reference position return
G40:
meaning: Cutter compensation cancel
G41:
meaning: Cutter compensation left
G42:
meaning: Cutter compensation right
G43:
meaning: Tool length compensation + direction
G44:
meaning: Tool length compensation - direction
G49:
meaning: Tool length compensation cancel
G53:
meaning: Machine coordinate system selection
G54:
meaning: Workpiece coordinate system 1 selection
G55:
meaning: Workpiece coordinate system 2 selection
G56:
meaning: Workpiece coordinate system 3 selection
G57:
meaning: Workpiece coordinate system 4 selection
G58:
meaning: Workpiece coordinate system 5 selection
G59:
meaning: Workpiece coordinate system 6 selection
G68:
meaning: Coordinate rotation
G69:
meaning: Coordinate rotation cancel
G73:
meaning: Peck drilling cycle
G74:
meaning: Left-spiral cutting circle
G76:
meaning: Fine boring cycle
G80:
meaning: Canned cycle cancel
G81:
meaning: Drilling cycle, spot boring cycle
G82:
meaning: Drilling cycle or counter boring cycle
G83:
meaning: Peck drilling cycle
G84:
meaning: Tapping cycle
G85:
meaning: Boring cycle
G86:
meaning: Boring cycle
G87:
meaning: Back boring cycle
G88:
meaning: Boring cycle
G89:
meaning: Boring cycle
G90:
meaning: Absolute command
G91:
meaning: Increment command
G92:
meaning: Setting for work coordinate system or clamp at maximum spindle speed
G98:
meaning: Return to initial point in canned cycle
G99:
meaning: Return to R point in canned cycle
M00:
meaning: Program stop
M01:
meaning: Optional program stop
M02:
meaning: End of program
M03:
meaning: Spindle start forward CW
M04:
meaning: Spindle start reverse CCW
M05:
meaning: Spindle stop
M06:
meaning: Tool change
M07:
meaning: Coolant ON – Mist coolant/Coolant thru spindle
M08:
meaning: Coolant ON – Flood coolant
M09:
meaning: Coolant OFF
M19:
meaning: Spindle orientation
M28:
meaning: Return to origin
M29:
meaning: Rigid tap mode
M30:
meaning: End of program (Reset)
M41:
meaning: Low gear select
M42:
meaning: High gear select
M94:
meaning: Cancel mirror image
M95:
meaning: Mirror image of X axis
M96:
meaning: Mirror image of Y axis
M98:
meaning: Subprogram call
M99:
meaning: End of subprogram
S:
meaning: set spindle speed
T:
meaning: select tool
Heidenhain/ 0000775 0000000 0000000 00000000000 13410044113 0035722 5 ustar 00root root 0000000 0000000 pythonic-gcode-machine-ec1f3fc5460500f0a6778b489106cc5255de741b-PythonicGcodeMachine/PythonicGcodeMachine/Gcode __init__.py 0000664 0000000 0000000 00000000000 13410044113 0040021 0 ustar 00root root 0000000 0000000 pythonic-gcode-machine-ec1f3fc5460500f0a6778b489106cc5255de741b-PythonicGcodeMachine/PythonicGcodeMachine/Gcode/Heidenhain data/ 0000775 0000000 0000000 00000000000 13410044113 0036633 5 ustar 00root root 0000000 0000000 pythonic-gcode-machine-ec1f3fc5460500f0a6778b489106cc5255de741b-PythonicGcodeMachine/PythonicGcodeMachine/Gcode/Heidenhain heidenhain-TNC-426-mcodes.yaml 0000664 0000000 0000000 00000001256 13410044113 0044022 0 ustar 00root root 0000000 0000000 pythonic-gcode-machine-ec1f3fc5460500f0a6778b489106cc5255de741b-PythonicGcodeMachine/PythonicGcodeMachine/Gcode/Heidenhain/data M0:
meaning: Program stop
M1:
meaning: Optional stop (program stop only with opt. stop)
M2:
meaning: Program end
M3:
meaning: Spindle ON clockwise
M4:
meaning: Spindle ON counterclockwise
M5:
meaning: Spindle Stop
M6:
meaning: Tool change
M8:
meaning: Coolant ON
M9:
meaning: Coolant OFF
M10:
meaning: Dividing head, clamping ON
M11:
meaning: Dividing head, clamping OFF
M17:
meaning: End of subprogram
M25:
meaning: Open clamp/ machine vice
M26:
meaning: Close clamp/ machine vice
M27:
meaning: Swivel dividing head
M30:
meaning: Main program end
M71:
meaning: Puff blowing device ON
M72:
meaning: Puff blowing device OFF
M99:
meaning: Cycle call
heidenhain-gcodes.yaml 0000664 0000000 0000000 00000027144 13410044113 0043065 0 ustar 00root root 0000000 0000000 pythonic-gcode-machine-ec1f3fc5460500f0a6778b489106cc5255de741b-PythonicGcodeMachine/PythonicGcodeMachine/Gcode/Heidenhain/data F:
meaning: set feed rate
# Tool movements
G00:
meaning: Straight-line interpolation, Cartesian coordinates, rapid traverse
G01:
meaning: Straight-line interpolation, Cartesian coordinates
G02:
meaning: Circular interpolation, Cartesian coordinates, clockwise
G03:
meaning: Circular interpolation, Cartesian coordinates, counterclockwise
G05:
meaning: Circular interpolation, Cartesian coordinates, without indication of direction
G06:
meaning: Circular interpolation, Cartesian coordinates, tangential contour approach
G07:
meaning: Paraxial positioning block
G10:
meaning: Straight-line interpolation, polar coordinates, rapid traverse
G11:
meaning: Straight-line interpolation, polar coordinates
G12:
meaning: Circular interpolation, polar coordinates, clockwise
G13:
meaning: Circular interpolation, polar coordinates, counterclockwise
G15:
meaning: Circular interpolation, polar coordinates, without indication of direction
G16:
meaning: Circular interpolation, polar coordinates, tangential contour approach
# Chamfer/Rounding/Approach contour/Depart contour
G24:
meaning: Chamfer with length R
G25:
meaning: Corner rounding with radius R
G26:
meaning: Tangential contour approach with radius R
G27:
meaning: Tangential contour approach with radius R
# Tool definition
G99:
meaning: With tool number T, length L, radius R
# Tool radius compensation
G40:
meaning: No tool radius compensation
G41:
meaning: Tool radius compensation, left of the contour
G42:
meaning: Tool radius compensation, right of the contour
G43:
meaning: Paraxial compensation for G07, lengthening
G44:
meaning: Paraxial compensation for G07, shortening
# Blank form definition for graphics
G30:
meaning: (G17/G18/G19) min. point
G31:
meaning: (G90/G91) max. point
# Cycles for drilling, tapping and thread milling
G83:
meaning: Pecking
G84:
meaning: Tapping with a floating tap holder
G85:
meaning: Rigid tapping
G86:
meaning: Thread cutting
G200:
meaning: Drilling
G201:
meaning: Reaming
G202:
meaning: Boring
G203:
meaning: Universal drilling
G204:
meaning: Back boring
G205:
meaning: Universal pecking
G206:
meaning: Tapping with a floating tap holder
G207:
meaning: Rigid tapping
G208:
meaning: Bore milling
G209:
meaning: Tapping with chip breaking
Cycles for creating point patterns
# G220:
meaning: Circular pattern
G221:
meaning: Linear pattern
SL Cycles, group 1
# G37:
meaning: Contour geometry, list of subcontour program numbers
G56:
meaning: Pilot drilling
G57:
meaning: Rough-out
G58:
meaning: Contour milling in clockwise direction (finishing)
G59:
meaning: Contour milling, counterclockwise (finishing)
# SL cycles, group 2
G37:
meaning: Contour geometry, list of subcontour program numbers
G120:
meaning: Contour data (applies to G121 to G124)
G121:
meaning: Pilot drilling
G122:
meaning: Rough-out
G123:
meaning: Floor finishing
G124:
meaning: Side finishing
G125:
meaning: Contour train (machining open contour)
G127:
meaning: Cylinder surface
G128:
meaning: Cylindrical surface slot
# Coordinate transformation
G53:
meaning: Datum shift in datum table
G54:
meaning: Datum shift in program
G28:
meaning: Mirror image
G73:
meaning: Rotation of the coordinate system
G72:
meaning: Scaling factor (reduce or enlarge contour)
G80:
meaning: Tilting the working plane
G247:
meaning: Datum setting
# Cycles for multipass milling
G60:
meaning: Run 3-D data
G230:
meaning: Multipass milling of plane surfaces
G231:
meaning: Multipass milling of tilted surfaces
G232:
meaning: Face milling
# Special cycles
G04:
meaning: Dwell time with F seconds
G36:
meaning: Spindle orientation
G39:
meaning: Program call
G62:
meaning: Tolerance deviation for fast contour milling
G440:
meaning: Measure axis shift
G441:
meaning: Fast probing
# Define machining plane
G17:
meaning: Working plane X/Y, tool axis Z
G18:
meaning: Working plane Z/X, tool axis Y
G19:
meaning: Working plane Y/Z, tool axis X
G20:
meaning: Tool axis IV
# Dimensions
G90:
meaning: Absolute dimensions
G91:
meaning: Incremental dimensions
# Unit of measure
G70:
meaning: Inches (set at start of program)
G71:
meaning: Millimeters (set at start of program)
# Other G functions
G29:
meaning: Transfer the last nominal position value as a pole (circle center)
G38:
meaning: STOP program run
G51:
meaning: Next tool number (with central tool file)
G79:
meaning: Cycle call
G98:
meaning: Set label number
# Cycles for drilling, tapping and thread milling
G240:
meaning: Centering
G200:
meaning: Drilling
G201:
meaning: Reaming
G202:
meaning: Boring
G203:
meaning: Universal drilling
G204:
meaning: Back boring
G205:
meaning: Universal pecking
G206:
meaning: Tapping with a floating tap holder
G207:
meaning: Rigid tapping
G208:
meaning: Bore milling
G209:
meaning: Tapping with chip breaking
G241:
meaning: Single-lip deep-hole drilling
# Cycles for drilling, tapping and thread milling
G262:
meaning: Thread milling
G263:
meaning: Thread milling/countersinking
G264:
meaning: Thread drilling/milling
G265:
meaning: Helical thread drilling/milling
G267:
meaning: External thread milling
# Cycles for milling pockets, studs and slots
G74:
meaning: Slot milling
G75:
meaning: Rectangular pocket milling in clockwise direction
G76:
meaning: Rectangular pocket milling in counterclockwise direction
G77:
meaning: Circular pocket milling in clockwise direction
G78:
meaning: Circular pocket milling in counterclockwise direction
G210:
meaning: Slot milling with reciprocating plunge
G211:
meaning: Round slot with reciprocating plunge
G212:
meaning: Rectangular pocket finishing
G213:
meaning: Rectangular stud finishing
G214:
meaning: Circular pocket finishing
G215:
meaning: Circular stud finishing
# Cycles for milling pockets, studs and slots
G251:
meaning: Rectangular pocket, complete
G252:
meaning: Circular pocket, complete
G253:
meaning: Slot, complete
G254:
meaning: Circular slot, complete
G256:
meaning: Rectangular stud
G257:
meaning: Circular stud
# Cycles for creating point patterns
G220:
meaning: Circular point pattern
G221:
meaning: Point patterns on lines
# Cycles for multipass milling
G230:
meaning: Multipass milling of smooth surfaces
G231:
meaning: Multipass milling of tilted surfaces
G232:
meaning: Face milling
# Touch probe cycles for measuring workpiece misalignment
G400:
meaning: Basic rotation using two points
G401:
meaning: Basic rotation from two holes
G402:
meaning: Basic rotation from two studs
G403:
meaning: Compensate a basic rotation via a rotary axis
G404:
meaning: Set basic rotation
G405:
meaning: Compensating misalignment with the C axis
# Touch probe cycles for datum setting
G408:
meaning: Slot center reference point
G409:
meaning: Reference point at center of hole
G410:
meaning: Datum from inside of rectangle
G411:
meaning: Datum from outside of rectangle
G412:
meaning: Datum from inside of circle
G413:
meaning: Datum from outside of circle
G414:
meaning: Datum in outside corner
G415:
meaning: Datum in inside corner
G416:
meaning: Datum circle center
G417:
meaning: Datum in touch probe axis
G418:
meaning: Datum in center of 4 holes
G419:
meaning: Reference point in selectable axis
# Touch probe cycles for workpiece measurement
G55:
meaning: Measure any coordinate
G420:
meaning: Measure any angle
G421:
meaning: Measure hole
G422:
meaning: Measure cylindrical stud
G423:
meaning: Measure rectangular pocket
G424:
meaning: Measure rectangular stud
G425:
meaning: Measure slot
G426:
meaning: Measure ridge
G427:
meaning: Measure any coordinate
G430:
meaning: Measure circle center
G431:
meaning: Measure any plane
# Touch probe cycles for tool measurement
G480:
meaning: Calibrating the TT
G481:
meaning: Measure tool length
G482:
meaning: Measure tool radius
G483:
meaning: Measure tool length and tool radius
#
M0:
meaning: Stop program run (Spindle STOP, Coolant OFF)
M1:
meaning: Optional program STOP (Spindle STOP, Coolant OFF)
M2:
meaning: Stop program run (Spindle STOP,Coolant OFF,Go to block 1,Clear the status display(depending on machine parameter))
M3:
meaning: Spindle ON clockwise
M4:
meaning: Spindle ON counterclockwise
M5:
meaning: Spindle STOP
M6:
meaning: Tool change (STOP program run (depending on machine parameter),Spindle STOP)
M8:
meaning: Coolant ON
M9:
meaning: Coolant OFF
M13:
meaning: Spindle ON clockwise (Coolant ON)
M14:
meaning: Spindle ON counterclockwise (Coolant ON)
M30:
meaning: Same as M2
M89:
meaning: Vacant miscellaneous function or Cycle call, modally effective (depending on machine parameter)
M91:
meaning: Within the positioning block: Coordinates are referenced to machine datum
M92:
meaning: Within the positioning block: Coordinates are referenced to position defined by machine tool builder, such as tool change position
M94:
meaning: Reduce the rotary axis display to a value below 360°
M97:
meaning: Machine small contour steps
M98:
meaning: Machine open contours completely
M99:
meaning: Blockwise cycle call (Cycle call, NON-MODAL)
M101:
meaning: Automatic tool change with replacement tool if maximum tool life has expired
M102:
meaning: Reset M101
M103:
meaning: Reduce feed rate during plunging to factor F (percentage)
M104:
meaning: Reactivate the datum as last defined
M105:
meaning: Machining with second kv factor
M106:
meaning: Machining with first kv factor
M107:
meaning: Suppress error message for replacement tools with oversize
M108:
meaning: Reset M107
M109:
meaning: Constant contouring speed at tool cutting edge (increase and decrease feed rate)
M110:
meaning: Constant contouring speed at tool cutting edge (feed rate decrease only)
M111:
meaning: Reset M109/M110
M112:
meaning: Enter contour transition between two contour elements
M113:
meaning: Reset M112
M114:
meaning: Automatic compensation of machine geometry when working with tilted axes
M115:
meaning: Reset M114
M116:
meaning: Feed rate for rotary axes in mm/min
M117:
meaning: Reset M116
M118:
meaning: Superimpose handwheel positioning during program run
M120:
meaning: Pre-calculate radius-compensated contour (LOOK AHEAD)
M124:
meaning: Contour filter
M126:
meaning: Shortest-path traverse of rotary axes
M127:
meaning: Reset M126
M128:
meaning: Retain the position of the tool tip when positioning the tilted axes (TCPM)
M129:
meaning: Reset M128
M130:
meaning: Moving to position in an untilted coordinate system with a tilted working plane
M134:
meaning: Exact stop at nontangential contour transitions when positioning with rotary axes
M135:
meaning: Reset M134
M136:
meaning: Feed rate F in millimeters per spindle revolution
M137:
meaning: Reset M136
M138:
meaning: Selection of tilted axes
M140:
meaning: Retraction from the contour in the tool-axis direction
M141:
meaning: Suppress touch probe monitoring
M142:
meaning: Delete modal program information
M143:
meaning: Delete basic rotation
M144:
meaning: Compensating the machine’s kinematics configuration for ACTUAL/NOMINAL positions at end of block
M145:
meaning: Reset M144
M148:
meaning: Retract the tool automatically from the contour at NC stop
M149:
meaning: Reset M148
M150:
meaning: Suppress limit switch message
M200:
meaning: Laser cutting: Output programmed voltage directly
M201:
meaning: Laser cutting: Output voltage as a function of distance
M202:
meaning: Laser cutting: Output voltage as a function of speed
M203:
meaning: Laser cutting: Output voltage as a function of time (ramp)
M204:
meaning: Laser cutting: Output voltage as a function of time (pulse)
S:
meaning: set spindle speed
T:
meaning: select tool
LinuxCnc/ 0000775 0000000 0000000 00000000000 13410044113 0035411 5 ustar 00root root 0000000 0000000 pythonic-gcode-machine-ec1f3fc5460500f0a6778b489106cc5255de741b-PythonicGcodeMachine/PythonicGcodeMachine/Gcode __init__.py 0000664 0000000 0000000 00000000000 13410044113 0037510 0 ustar 00root root 0000000 0000000 pythonic-gcode-machine-ec1f3fc5460500f0a6778b489106cc5255de741b-PythonicGcodeMachine/PythonicGcodeMachine/Gcode/LinuxCnc data/ 0000775 0000000 0000000 00000000000 13410044113 0036322 5 ustar 00root root 0000000 0000000 pythonic-gcode-machine-ec1f3fc5460500f0a6778b489106cc5255de741b-PythonicGcodeMachine/PythonicGcodeMachine/Gcode/LinuxCnc linuxcnc-gcodes.yaml 0000664 0000000 0000000 00000012512 13410044113 0042274 0 ustar 00root root 0000000 0000000 pythonic-gcode-machine-ec1f3fc5460500f0a6778b489106cc5255de741b-PythonicGcodeMachine/PythonicGcodeMachine/Gcode/LinuxCnc/data # cf. http://linuxcnc.org/docs/2.7/html/gcode.html
F:
meaning: Set feed rate
# Motion (X Y Z A B C U V W apply to all motions)
G0:
meaning: Rapid Move
G1:
meaning: Linear Move
G23:
meaning: Arc Move
parameters: I J K or R, P
G3:
meaning: Arc Move
parameters: I J K or R, P
G4:
meaning: Dwell
parameters: P
G5:
meaning: Cubic Spline
parameters: I J P Q
G5.1:
meaning: Quadratic Spline
parameters: I J
G5.2:
meaning: NURBS
parameters: P L
G38.2:
meaning: Straight Probe
G38.3:
meaning: Straight Probe
G38.4:
meaning: Straight Probe
G38.5:
meaning: Straight Probe
G33:
meaning: Spindle Synchronized Motion
parameters: K
G33.1:
meaning: Rigid Tapping
parameters: K
G80:
meaning: Cancel Canned Cycle
# Canned cycles (X Y Z or U V W apply to canned cycles, depending on active plane)
G81:
meaning: Drilling Cycle
parameters: R L (P)
G82:
meaning: Drilling Cycle, Dwell
parameters: R L (P)
G83:
meaning: Drilling Cycle, Peck
parameters: R L Q
G73:
meaning: Drilling Cycle, Chip Breaking
parameters: R L Q
G85:
meaning: Boring Cycle, Feed Out
parameters: R L (P)
G89:
meaning: Boring Cycle, Dwell, Feed Out
parameters: R L (P)
G76:
meaning: Threading Cycle
parameters: P Z I J R K Q H L E
# Distance Mode
G90:
meaning: Distance Mode
G91:
meaning: Distance Mode
G90.1:
meaning: Arc Distance Mode
G91.1:
meaning: Arc Distance Mode
G7:
meaning: Lathe Diameter Mode
G8:
meaning: Lathe Radius Mode
# Feed Rate Mode
G93:
meaning: Feed Rate Mode
G94:
meaning: Feed Rate Mode
G95:
meaning: Feed Rate Mode
# Spindle Control
M3:
meaning: Spindle Control
parameters: S
M4:
meaning: Spindle Control
parameters: S
M5:
meaning: Spindle Control
parameters: S
M19:
meaning: Orient Spindle
G96:
meaning: Spindle Control Mode
parameters: S D
G97:
meaning: Spindle Control Mode
parameters: S D
# Coolant
M7:
meaning: Coolant Control
M8:
meaning: Coolant Control
M9:
meaning: Coolant Control
# Tool Length Offset
G43:
meaning: Tool Length Offset
parameters: H
G43.1:
meaning: Dynamic Tool Length Offset
G43.2:
meaning: Apply additional Tool Length Offset
parameters: H
G49:
meaning: Cancel Tool Length Compensation
# Stopping
M0:
meaning: Program Pause
M1:
meaning: Program Pause
M2:
meaning: Program End
M30:
meaning: Program End
M60:
meaning: Pallet Change Pause
# Units
G20:
meaning: Units (inch, mm)
G21:
meaning: Units (inch, mm)
# Plane Selection: (affects G2, G3, G81…G89, G40…G42)
G17:
meaning: Plane Select
G17.1:
meaning: Plane Select
G18:
meaning: Plane Select
G18.1:
meaning: Plane Select
G19:
meaning: Plane Select
G19.1:
meaning: Plane Select
# Cutter Radius Compensation
G40:
meaning: Compensation Off
G41:
meaning: Cutter Compensation
parameters: D
G42:
meaning: Cutter Compensation
parameters: D
G41.1:
meaning: Dynamic Cutter Compensation
parameters: D L
G42.1:
meaning: Dynamic Cutter Compensation
parameters: D L
# Path Control Mode
G61.1:
meaning: Exact Path Mode
G61.1:
meaning: Exact Path Mode
G64:
meaning: Path Blending
parameters: P Q
# Return Mode in Canned Cycles
G98:
meaning: Canned Cycle Return Level
G99:
meaning: Canned Cycle Return Level
# Other Modal Codes
F:
meaning: Set Feed Rate
S:
meaning: Set Spindle Speed
T:
meaning: Select Tool)
M48, M49:
meaning: Speed and Feed Override Control
M50:
meaning: Feed Override Control
parameters: P0 (off) or P1 (on)
M51:
meaning: Spindle Speed Override Control
parameters: P0 (off) or P1 (on)
M52:
meaning: Adaptive Feed Control
parameters: P0 (off) or P1 (on)
M53:
meaning: Feed Stop Control
parameters: P0 (off) or P1 (on)
G54-G59.3:
meaning: Select Coordinate System
# Flow-control Codes
## o sub:
## meaning: Subroutines, sub/endsub call
## o while:
## meaning: Looping, while/endwhile do/while
## o if:
## meaning: Conditional, if/else/endif
## o repeat:
## meaning: Repeat a loop of code
## '[]':
## meaning: Indirection
## o call:
## meaning: Call named file
M70:
meaning: Save modal state
M71:
meaning: Invalidate stored state
M72:
meaning: Restore modal state
M73:
meaning: Save and Auto-restore modal state
# Input/Output Codes
M62:
meaning: Digital Output Control
M63:
meaning: Digital Output Control
M64:
meaning: Digital Output Control
M65:
meaning: Digital Output Control
parameters: P
M66:
meaning: Wait on Input
parameters: P E L Q
M67:
meaning: Analog Output,Synchronized
parameters: T
M68:
meaning: Analog Output, Immediate
parameters: T
# Non-modal Codes
M6:
meaning: Tool Change
parameters: T
M61:
meaning: Set Current Too
l # Q
G10 L1:
meaning: Set Tool Table
parameters: P Q R
G10 L10:
meaning: Set Tool Table
parameters: P
G10 L11:
meaning: Set Tool Table
parameters: P
G10 L2:
meaning: Set Coordinate System
parameters: P R
G10 L20:
meaning: Set Coordinate System
parameters: P
G28:
meaning: Go/Set Predefined Position
G28.1:
meaning: Go/Set Predefined Position
G30:
meaning: Go/Set Predefined Position
G30.1:
meaning: Go/Set Predefined Position
G53:
meaning: Move in Machine Coordinates
G92:
meaning: Coordinate System Offset
G92.1:
meaning: Reset G92 Offsets
G92.2:
meaning: Reset G92 Offsets
G92.3:
meaning: Restore G92 Offsets
M101 - M199: # Don't expand !
meaning: User Defined Commands
parameters: P Q
S:
meaning: Set spindle speed
T:
meaning: Select tool
MachineKit/ 0000775 0000000 0000000 00000000000 13410044113 0035702 5 ustar 00root root 0000000 0000000 pythonic-gcode-machine-ec1f3fc5460500f0a6778b489106cc5255de741b-PythonicGcodeMachine/PythonicGcodeMachine/Gcode __init__.py 0000664 0000000 0000000 00000000000 13410044113 0040001 0 ustar 00root root 0000000 0000000 pythonic-gcode-machine-ec1f3fc5460500f0a6778b489106cc5255de741b-PythonicGcodeMachine/PythonicGcodeMachine/Gcode/MachineKit Rs274/ 0000775 0000000 0000000 00000000000 13410044113 0034507 5 ustar 00root root 0000000 0000000 pythonic-gcode-machine-ec1f3fc5460500f0a6778b489106cc5255de741b-PythonicGcodeMachine/PythonicGcodeMachine/Gcode Ast.py 0000664 0000000 0000000 00000040512 13410044113 0035612 0 ustar 00root root 0000000 0000000 pythonic-gcode-machine-ec1f3fc5460500f0a6778b489106cc5255de741b-PythonicGcodeMachine/PythonicGcodeMachine/Gcode/Rs274 ####################################################################################################
#
# PythonicGcodeMachine - @licence_header_description@
# Copyright (C) 2018 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
"""Module to implement an AST for RS-274 G-code.
"""
####################################################################################################
__all__ = [
'Program',
# 'LineItem',
'Line',
'Comment',
'Word',
# 'RealValue',
'ParameterMixin',
'ParameterSetting',
'Parameter',
# 'UnaryOperation',
'AbsoluteValue',
'ArcCosine',
'ArcSine',
'ArcTangent',
'Cosine',
'ERaisedTo',
'FixDown',
'FixUp',
'NaturalLogOf',
'Round',
'Sine',
'SquareRoot',
'Tangent',
# 'BinaryOperation',
'Power',
'DividedBy',
'Modulo',
'Multiply',
'And',
'ExclusiveOr',
'Subtraction',
'Or',
'Addition',
]
####################################################################################################
import math
import colors
####################################################################################################
class Program:
"""Class to implement a G-code program
Usage::
program = Program()
program += line
# Array interface
for line in programs:
print(line)
str(program)
"""
##############################################
def __init__(self):
self._lines = []
##############################################
def push(self, line):
self._lines.append(line)
def __iadd__(self, item):
self.push(item)
return self
##############################################
def __len__(self):
return len(self._lines)
def __iter__(self):
return iter(self._lines)
def __getitem__(self, _slice):
return self._lines[_slice]
def iter_on_not_deleted(self):
for line in self._lines:
if line:
yield line
##############################################
def __repr__(self):
text = 'Program(\n'
for line in self:
text += repr(line) + '\n'
text += ')\n'
return text
##############################################
def __str__(self):
return '\n'.join(map(str, self))
####################################################################################################
class LineItem:
def ansi_str(self):
return str(self)
####################################################################################################
class Line:
"""Class to implement a G-code line
Usage::
line = Line(deleted=False, line_number=1, comment='a comment')
line.deleted = True
print(line.deleted)
# same apply for line_number and comment
# Is line not deleted ?
bool(line)
# Push some items
# Note: order doesn't matter, see RS-274 for details
line += Word('G', 0)
line += Comment('move')
line += Word('X', 10)
line += Comment('Y value')
line += Word('Y', 20)
line += ParameterSetting('1', 1.2)
# using expression
line += Word('Z', Addition(30, Multiply(Parameter(100), Cosine(30))))
# Array interface
for item in line:
print(item)
str(line)
print(line.ansi_str()) # use ANSI colors, see Line.ANSI_... attributes
Expression can be evaluated using :code:`float(obj.value)`, excepted when we must access a parameter
value.
"""
ANSI_DELETED = colors.red
ANSI_LINE_NUMBER = colors.blue
ANSI_COMMENT = colors.green
ANSI_SETTING = colors.blue
ANSI_G = colors.red
ANSI_X = colors.blue
ANSI_VALUE = colors.black
##############################################
def __init__(self, deleted=False, line_number=None, comment=None):
self.deleted = deleted
self.line_number = line_number
self.comment = comment
self._items = []
##############################################
@property
def deleted(self):
return self._deleted
@deleted.setter
def deleted(self, value):
self._deleted = bool(value)
@property
def line_number(self):
return self._line_number
@line_number.setter
def line_number(self, value):
if value is not None:
value = float(value)
if value.is_integer():
value = int(value)
self._line_number = value
@property
def comment(self):
return self._comment
@comment.setter
def comment(self, value):
if value is not None:
self._comment = str(value)
else:
self._comment = None
##############################################
def push(self, item):
if isinstance(item, LineItem):
self._items.append(item)
else:
raise ValueError
def __iadd__(self, item):
self.push(item)
return self
##############################################
def __bool__(self):
return not self._deleted
##############################################
def __len__(self):
return len(self._items)
def __iter__(self):
return iter(self._items)
def __getitem__(self, _slice):
return self._items[_slice]
##############################################
def iter_on_word(self):
for item in self:
if isinstance(item, Word):
yield item
def iter_on_setting(self):
for item in self:
if isinstance(item, ParameterSetting):
yield item
##############################################
def __repr__(self):
items = []
if not self:
items.append('Deleted')
items += list(map(repr, self))
if self._comment:
items.append(self._comment)
return 'Line{}'.format(self, items)
##############################################
def __str__(self):
line = ''
if not self:
line += '/ '
if self._line_number:
line += 'N{} '.format(self._line_number)
line += ' '.join(map(str, self))
if self._comment:
line += ' ; ' + self._comment
return line
##############################################
def ansi_str(self):
line = ''
if not self:
# line += self.ANSI_DELETED('/ ')
return self.ANSI_DELETED(str(self))
if self._line_number:
line += self.ANSI_LINE_NUMBER('N{} '.format(self._line_number))
line += ' '.join([item.ansi_str() for item in self])
if self._comment:
line += ' ' + self.ANSI_COMMENT('; ' + self._comment)
return line
####################################################################################################
class Comment(LineItem):
"""Class to implement comment"""
##############################################
def __init__(self, text):
self.set(text)
##############################################
def set(self, text):
if '(' in text:
raise ValueError('Comment cannot contains a "("')
self._text = str(text)
##############################################
@property
def text(self):
return self._text
@text.setter
def text(self, value):
self.set(value)
##############################################
def __repr__(self):
return 'Comment({0._text})'.format(self)
def __str__(self):
return '({0._text})'.format(self)
def ansi_str(self):
return Line.ANSI_COMMENT(str(self))
####################################################################################################
class Word(LineItem):
"""Class to implement word"""
LETTERS = (
'A', 'B', 'C', 'D',
'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', # 'N',
'P', 'Q', 'R', 'S', 'T',
'X', 'Y', 'Z',
)
##############################################
def __init__(self, letter, value):
self.letter = letter
self.value = value
##############################################
@property
def letter(self):
return self._letter
@letter.setter
def letter(self, value):
value = str(value).upper()
if value not in self.LETTERS:
raise ValueError
self._letter = value
@property
def value(self):
return self._value
@value.setter
def value(self, value):
# float expression ...
self._value = value
##############################################
def __repr__(self):
return 'Word({0._letter} {0._value})'.format(self)
def __str__(self):
return '{0._letter}{0._value}'.format(self)
def ansi_str(self):
if self._letter in 'GM':
return Line.ANSI_G(str(self))
else:
return Line.ANSI_X(self._letter) + Line.ANSI_VALUE(str(self._value))
####################################################################################################
class RealValue:
pass
####################################################################################################
class ParameterMixin:
##############################################
def __init__(self, parameter):
self.parameter = parameter
##############################################
@property
def parameter(self):
return self._parameter
@parameter.setter
def parameter(self, value):
try:
value = int(value)
except ValueError:
value = str(value)
self._parameter = value
####################################################################################################
class ParameterSetting(LineItem, ParameterMixin):
"""Class to implement parameter setting"""
##############################################
def __init__(self, parameter, value):
ParameterMixin.__init__(self, parameter)
self.value = value
##############################################
@property
def value(self):
return self._value
@value.setter
def value(self, value):
# float expression ...
self._value = value
##############################################
def __repr__(self):
return 'ParameterSetting({0._parameter} = {0._value})'.format(self)
def __str__(self):
return '#{0._parameter}={0._value}'.format(self)
def ansi_str(self):
return Line.ANSI_SETTING('#{0._parameter}='.format(self)) + Line.ANSI_VALUE(str(self._value))
####################################################################################################
class Parameter(RealValue, ParameterMixin):
"""Class to implement parameter"""
##############################################
def __init__(self, parameter):
ParameterMixin.__init__(self, parameter)
##############################################
def __repr__(self):
return 'Parameter({0._parameter})'.format(self)
def __str__(self):
return '#{0._parameter}'.format(self)
##############################################
def __float__(self):
raise NotImplementedError
####################################################################################################
class UnaryOperation(RealValue):
__function__ = None
__gcode__ = None
##############################################
def __init__(self, arg):
self.arg = arg
##############################################
@property
def arg(self):
return self._arg
@arg.setter
def arg(self, value):
self._arg = value
##############################################
def __float__(self):
return self.__function__(float(self._arg))
##############################################
def __repr__(self):
return '{}({})'.format(self.__class__.__name__, repr(self._arg))
##############################################
def __str__(self):
return '{0.__gcode__}[{0._arg}]'.format(self)
####################################################################################################
class AbsoluteValue(UnaryOperation):
__function__ = staticmethod(abs)
__gcode__ = 'abs'
class ArcCosine(UnaryOperation):
__function__ = staticmethod(lambda x: math.acos(math.radians(x)))
__gcode__ = 'acos'
class ArcSine(UnaryOperation):
__function__ = staticmethod(lambda x: math.degrees(math.asin(x)))
__gcode__ = 'asin'
class ArcTangent(UnaryOperation):
__function__ = staticmethod(lambda x: math.degrees(math.atan(x)))
__gcode__ = 'atan'
class Cosine(UnaryOperation):
__function__ = staticmethod(lambda x: math.cos(math.radians(x)))
__gcode__ = 'cos'
class ERaisedTo(UnaryOperation):
__function__ = staticmethod(math.exp)
__gcode__ = 'exp'
class FixDown(UnaryOperation):
__function__ = staticmethod(math.ceil)
__gcode__ = 'fix'
class FixUp(UnaryOperation):
__function__ = staticmethod(math.floor)
__gcode__ = 'fup'
class NaturalLogOf(UnaryOperation):
__function__ = staticmethod(math.log)
__gcode__ = 'ln'
class Round(UnaryOperation):
__function__ = staticmethod(round)
__gcode__ = 'round'
class Sine(UnaryOperation):
__function__ = staticmethod(lambda x: math.sin(math.radians(x)))
__gcode__ = 'sin'
class SquareRoot(UnaryOperation):
__function__ = staticmethod(math.sqrt)
__gcode__ = 'sqrt'
class Tangent(UnaryOperation):
__function__ = staticmethod(lambda x: ath.tan(math.radians(x)))
__gcode__ = 'tan'
####################################################################################################
class BinaryOperation(RealValue):
__function__ = None
__gcode__ = None
##############################################
def __init__(self, arg1, arg2):
self.arg1 = arg1
self.arg2 = arg2
##############################################
@property
def arg1(self):
return self._arg1
@arg1.setter
def arg1(self, value):
self._arg1 = value
@property
def arg2(self):
return self._arg2
@arg2.setter
def arg2(self, value):
self._arg2 = value
##############################################
def __float__(self):
return self.__function__(float(self._arg1), float(self._arg2))
##############################################
def __repr__(self):
return '{}({})'.format(self.__class__.__name__, repr(self._arg))
##############################################
def __str__(self):
return '[{0._arg1} {0.__gcode__} {0._arg2}]'.format(self)
####################################################################################################
class Power(BinaryOperation):
__function__ = staticmethod(lambda a, b: a**b)
__gcode__ = '**'
class DividedBy(BinaryOperation):
__function__ = staticmethod(lambda a, b: a / b)
__gcode__ = '/'
class Modulo(BinaryOperation):
__function__ = staticmethod(lambda a, b: a % b)
__gcode__ = 'mod'
class Multiply(BinaryOperation):
__function__ = staticmethod(lambda a, b: a * b)
__gcode__ = '*'
class And(BinaryOperation):
__function__ = staticmethod(lambda a, b: a & b)
__gcode__ = 'and'
class ExclusiveOr(BinaryOperation):
__function__ = staticmethod(lambda a, b: a ^ b)
__gcode__ = 'xor'
class Subtraction(BinaryOperation):
__function__ = staticmethod(lambda a, b: a - b)
__gcode__ = '-'
class Or(BinaryOperation):
__function__ = staticmethod(lambda a, b: a | b)
__gcode__ = 'or'
class Addition(BinaryOperation):
__function__ = staticmethod(lambda a, b: a + b)
__gcode__ = '+'
Config.py 0000664 0000000 0000000 00000015170 13410044113 0036272 0 ustar 00root root 0000000 0000000 pythonic-gcode-machine-ec1f3fc5460500f0a6778b489106cc5255de741b-PythonicGcodeMachine/PythonicGcodeMachine/Gcode/Rs274 ####################################################################################################
#
# PythonicGcodeMachine - @licence_header_description@
# Copyright (C) 2018 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
"""
"""
####################################################################################################
__all__ = [
'Parameter',
'Parameters',
'Letters',
'Gcode',
'Gcodes',
'ModalGroups',
'ExecutionOrder',
]
####################################################################################################
import yaml
####################################################################################################
class YamlMixin:
def _load_yaml(self, yaml_path):
with open(yaml_path, 'r') as fh:
data = yaml.load(fh.read())
return data
####################################################################################################
class MeaningMixin:
##############################################
def __init__(self, meaning):
self._meaning = str(meaning)
##############################################
@property
def meaning(self):
return self._meaning
####################################################################################################
class Parameter(MeaningMixin):
##############################################
def __init__(self, index, meaning, value):
MeaningMixin.__init__(self, meaning)
self._index = int(index)
self._value = float(value)
##############################################
@property
def index(self):
return self._index
@property
def default_value(self):
return self._value
####################################################################################################
class Parameters(YamlMixin):
##############################################
def __init__(self, yaml_path):
data = self._load_yaml(yaml_path)
self._parameters = {}
for index, d in data.items():
parameter = Parameter(index, d['meaning'], d['value'])
self._parameters[index] = parameter
##############################################
def __len__(self):
return len(self._parameters)
def __iter__(self):
return iter(self._parameters.values())
def __getitem__(self, index):
return self._parameters[index]
####################################################################################################
class Letter(MeaningMixin):
##############################################
def __init__(self, letter, meaning):
MeaningMixin.__init__(self, meaning)
self._letter = str(letter)
##############################################
@property
def letter(self):
return self._letter
####################################################################################################
class Letters(YamlMixin):
##############################################
def __init__(self, yaml_path):
data = self._load_yaml(yaml_path)
self._letters = {}
for letter, d in data.items():
self._letters[letter] = Letter(letter, d['meaning'])
##############################################
def __len__(self):
return len(self._letters)
def __iter__(self):
return iter(self._letters.values())
def __getitem__(self, letter):
return self._letters[letter]
####################################################################################################
class Gcode(MeaningMixin):
##############################################
def __init__(self, code, meaning):
MeaningMixin.__init__(self, meaning)
self._code = str(code)
##############################################
@property
def code(self):
return self._code
####################################################################################################
class Gcodes(YamlMixin):
##############################################
def __init__(self, yaml_path):
data = self._load_yaml(yaml_path)
self._gcodes = {}
for code, d in data.items():
gcode = Gcode(code, d['meaning'])
self._gcodes[code] = gcode
##############################################
def __len__(self):
return len(self._gcodes)
def __iter__(self):
return iter(self._gcodes.values())
def __getitem__(self, code):
return self._gcodes[code]
####################################################################################################
class ExecutionOrder(YamlMixin):
##############################################
def __init__(self, yaml_path):
data = self._load_yaml(yaml_path)
self._order = []
count = 1
for index, gcodes in data.items():
if index != count:
raise ValueError('Unexpected index {} versus {}'.format(index, count))
count += 1
if not isinstance(gcodes, list):
gcodes = list(gcodes)
self._order.append(gcodes)
##############################################
def __len__(self):
return len(self._order)
def __iter__(self):
return iter(self._order.values())
def __getitem__(self, slice_):
return self._order[slice_]
####################################################################################################
class ModalGroups(YamlMixin):
##############################################
def __init__(self, yaml_path):
data = self._load_yaml(yaml_path)
self._groups = {}
for index, gcodes in data.items():
self._groups[index] = list(gcodes)
##############################################
def __len__(self):
return len(self._groups)
def __iter__(self):
return iter(self._groups.values())
def __getitem__(self, index):
return self._groups[index_]
Lexer.py 0000664 0000000 0000000 00000013175 13410044113 0036147 0 ustar 00root root 0000000 0000000 pythonic-gcode-machine-ec1f3fc5460500f0a6778b489106cc5255de741b-PythonicGcodeMachine/PythonicGcodeMachine/Gcode/Rs274 ####################################################################################################
#
# PythonicGcodeMachine - @licence_header_description@
# Copyright (C) 2018 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
__all__ = ['GcodeLexerError', 'GcodeLexer']
####################################################################################################
import re
try:
import ply.lex as lexer
except ModuleNotFoundError:
import PythonicGcodeMachine.PythonLexYacc.lex as lexer
####################################################################################################
class GcodeLexerError(ValueError):
pass
####################################################################################################
class GcodeLexer:
"""Class to implement a RS-274 G-code lexer.
"""
# List of token names.
tokens = (
'ABSOLUTE_VALUE',
'AND',
'ARC_COSINE',
'ARC_SINE',
'ARC_TANGENT',
'COSINE',
'DIVIDED_BY',
'EQUAL_SIGN',
'EXCLUSIVE_OR',
'E_RAISED_TO',
'FIX_DOWN',
'FIX_UP',
'LEFT_BRACKET',
# 'LEFT_PARENTHESIS',
'MINUS',
'MODULO',
'NATURAL_LOG_OF',
'NON_EXCLUSIVE_OR',
'PARAMETER_SIGN',
'PLUS',
'POWER',
'RIGHT_BRACKET',
# 'RIGHT_PARENTHESIS',
'ROUND',
'SINE',
'SQUARE_ROOT',
'TANGENT',
'TIMES',
# 'LETTER',
'A', 'B', 'C', 'D',
'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'P', 'Q', 'R', 'S', 'T',
'X', 'Y', 'Z',
'N',
'POSITIVE_INTEGER',
'POSITIVE_REAL',
'REAL',
'INLINE_COMMENT',
'EOF_COMMENT',
)
# Regular expression rules for simple tokens
t_ABSOLUTE_VALUE = r'abs'
t_AND = r'and'
t_ARC_COSINE = r'acos'
t_ARC_SINE = r'asin'
t_ARC_TANGENT = r'atan'
# t_BLOCK_DELETE = r'\/' # slash
t_COSINE = r'cos'
t_DIVIDED_BY = r'\/' # slash
t_EQUAL_SIGN = r'='
t_EXCLUSIVE_OR = r'xor'
t_E_RAISED_TO = r'exp'
t_FIX_DOWN = r'fix'
t_FIX_UP = r'fup'
t_LEFT_BRACKET = r'\['
# t_LEFT_PARENTHESIS = r'\('
t_MINUS = r'-'
t_MODULO = r'mod'
t_NATURAL_LOG_OF = r'ln'
t_NON_EXCLUSIVE_OR = r'or'
t_PARAMETER_SIGN = r'\#'
t_PLUS = r'\+'
t_POWER = r'\*\*'
t_RIGHT_BRACKET = r'\]'
# t_RIGHT_PARENTHESIS = r'\)'
t_ROUND = r'round'
t_SINE = r'sin'
t_SQUARE_ROOT = r'sqrt'
t_TANGENT = r'tan'
t_TIMES = r'\*'
# clash with acos ... ???
# t_LETTER = r'(' + '|'.join('abcd' + 'fghijklm' + 'pqrst' + 'xyz') + ')'
t_A = r'a'
T_B = r'b'
t_C = r'c'
t_D = r'd'
t_F = r'f'
t_G = r'g'
t_H = r'h'
t_I = r'i'
t_J = r'j'
t_K = r'k'
t_L = r'l'
t_M = r'm'
t_P = r'p'
t_Q = r'q'
t_R = r'r'
t_S = r's'
t_T = r't'
t_X = r'x'
t_Y = r'y'
t_Z = r'z'
t_N = r'n'
# def t_POSITIVE_INTEGER(self, t):
# r'\d+'
# t.value = int(t.value)
# return t
# def t_POSITIVE_REAL(self, t):
# r'\d*\.\d+'
# t.value = float(t.value)
# return t
def t_REAL(self, t):
# r'((-)?((\d*\.\d+)(E[\+-]?\d+)?|([1-9]\d*E[\+-]?\d+)))'
r'((\+|-)?(\d+\.\d*|(\.)?\d+))'
value = t.value
if '.' in value:
value = float(value)
if value > 0:
t.type = 'POSITIVE_REAL'
else:
t.type = 'REAL'
else:
value = int(value)
t.type = 'POSITIVE_INTEGER'
t.value = value
return t
def t_INLINE_COMMENT(self, t):
r'\([^\)]*\)'
value = t.value[1:-1]
position = value.find('(')
if position != -1:
raise GcodeLexerError(t.lexpos + position +1)
t.value = value
return t
def t_EOF_COMMENT(self, t):
r';.*'
t.value = t.value[1:].strip()
return t
# Ignored characters (spaces and tabs)
t_ignore = ' \t'
def t_error(self, t):
'Error handling rule'
# t.lexer.skip(1)
# raise GcodeLexerError("Illegal character @{} '{}'".format(t.lexpos, t.value))
raise GcodeLexerError(t.lexpos)
##############################################
def __init__(self):
self._build()
##############################################
def _build(self, **kwargs):
"""Build the lexer"""
self._lexer = lexer.lex(
module=self,
reflags=int(re.VERBOSE + re.IGNORECASE),
optimize=1,
**kwargs,
)
##############################################
def input(self, data):
return self._lexer.input(data)
##############################################
def tokenize(self, data):
self.input(data)
while True:
token = self._lexer.token()
if not token:
break
yield token
Parser.py 0000664 0000000 0000000 00000032027 13410044113 0036321 0 ustar 00root root 0000000 0000000 pythonic-gcode-machine-ec1f3fc5460500f0a6778b489106cc5255de741b-PythonicGcodeMachine/PythonicGcodeMachine/Gcode/Rs274 ####################################################################################################
#
# PythonicGcodeMachine - @licence_header_description@
# Copyright (C) 2018 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
"""Module to implement a RS-274 G-code parser.
Usage::
parser = GcodeParser()
ast_line = parser.parse(gcode_line)
ast_program = parser.parse_lines(gcode_lines)
**Implementation**
The parser is generated automatically from the grammar defined in this class using the generator
`PLY `_ which implement a LALR(1) parser similar to the
tools **lex** and **yacc**.
The parser construct an `abstract syntax tree (AST)
`_ during the parsing.
User can subclass this parser to support a derived G-code flavour.
**For references, see**
* `The NIST RS274NGC Interpreter — Version 3 — Appendix E. Production Rules for the RS274/NGC Language
`_
* http://linuxcnc.org/docs/2.7/html/gcode/overview.html
"""
__all__ = [
'GcodeParserError',
'GcodeParser',
'GcodeParserMixin',
'GcodeGrammarMixin',
]
####################################################################################################
# https://rply.readthedocs.io/en/latest/
from ply import yacc
from . import Ast
from .Lexer import GcodeLexer
####################################################################################################
class GcodeParserError(ValueError):
pass
####################################################################################################
class GcodeGrammarMixin:
"""Mixin to implement the grammar.
**Production Language for RS-274**
The symbols in the productions are mostly standard syntax notation. Meanings of the symbols
are:
* ``=`` The symbol on the left of the equal sign is equivalent to the expression on the right
* ``+`` followed by
* ``|`` or
* ``.`` end of production (a production may have several lines)
* ``[]`` zero or one of the expression inside square brackets may occur
* ``{}`` zero to many of the expression inside curly braces may occur
* ``()`` exactly one of the expression inside parentheses must occur
The productions are:
* arc_tangent_combo = arc_tangent + expression + divided_by + expression .
* binary_operation = binary_operation1 | binary_operation2 | binary_operation3 .
* binary_operation1 = power .
* binary_operation2 = divided_by | modulo | times .
* binary_operation3 = and | exclusive_or | minus | non_exclusive_or | plus .
* comment = message | ordinary_comment .
* expression = left_bracket + real_value + { binary_operation + real_value } + right_bracket .
* line = [block_delete] + [line_number] + {segment} + end_of_line .
* line_number = letter_n + digit + [digit] + [digit] + [digit] + [digit] .
* message = left_parenthesis + {white_space} + letter_m + {white_space} + letter_s +
{white_space} + letter_g + {white_space} + comma + {comment_character} +
right_parenthesis .
* mid_line_letter = letter_a | letter_b | letter_c| letter_d | letter_f | letter_g | letter_h | letter_i
| letter_j | letter_k | letter_l | letter_m | letter_p | letter_q | letter_r | letter_s | letter_t
| letter_x | letter_y | letter_z .
* mid_line_word = mid_line_letter + real_value .
* ordinary_comment = left_parenthesis + {comment_character} + right_parenthesis .
* ordinary_unary_combo = ordinary_unary_operation + expression .
* ordinary_unary_operation =
absolute_value | arc_cosine | arc_sine | cosine | e_raised_to |
fix_down | fix_up | natural_log_of | round | sine | square_root | tangent .
* parameter_index = real_value .
* parameter_setting = parameter_sign + parameter_index + equal_sign + real_value .
* parameter_value = parameter_sign + parameter_index .
* real_number =
[ plus | minus ] +
(( digit + { digit } + [decimal_point] + {digit}) | ( decimal_point + digit + {digit})) .
* real_value = real_number | expression | parameter_value | unary_combo .
* segment = mid_line_word | comment | parameter_setting .
* unary_combo = ordinary_unary_combo | arc_tangent_combo .
"""
# Build the operation map
# Note: sphinx show locals if not _foo
__operation_map__ = {}
for _cls_name in Ast.__all__:
_cls = getattr(Ast, _cls_name)
if hasattr(_cls, '__gcode__'):
__operation_map__[_cls.__gcode__] = _cls
##############################################
# Start symbol
def p_line(self, p):
'''line : DIVIDED_BY line_right
| line_right
'''
if len(p) == 3:
self._line.deleted = True
# p[0] = self._line
def p_line_right(self, p):
'''line_right : line_content
| line_content EOF_COMMENT
'''
if len(p) == 3:
self._line.comment = p[2]
# p[0] = self._line
def p_line_content(self, p):
'line_content : segments'
# p[0] = self._line
def p_numbered_line(self, p):
'line_content : line_number segments'
self._line.line_number = p[1]
# p[0] = self._line
def p_line_number(self, p):
'''line_number : N POSITIVE_INTEGER
| N POSITIVE_REAL
'''
p[0] = p[2]
def p_segments(self, p):
'''segments : segment
| segments segment
'''
if len(p) == 2:
self._line += p[1]
else:
self._line += p[2]
def p_segment(self, p):
'''segment : mid_line_word
| comment
| parameter_setting
'''
p[0] = p[1]
##############################################
def p_comment(self, p):
'comment : ordinary_comment'
# 'comment : message | ordinary_comment':
p[0] = p[1]
def p_ordinary_comment(self, p):
'ordinary_comment : INLINE_COMMENT'
p[0] = Ast.Comment(p[1])
# def p_message(self, p):
# 'message : left_parenthesis + {white_space} + letter_m + {white_space} + letter_s +
# {white_space} + letter_g + {white_space} + comma + {comment_character} +
# right_parenthesis .
##############################################
def p_mid_line_word(self, p):
'mid_line_word : mid_line_letter real_value'
p[0] = Ast.Word(p[1], p[2])
def p_mid_line_letter(self, p):
# LETTER
'''mid_line_letter : A
| B
| C
| D
| F
| G
| H
| I
| J
| K
| L
| M
| P
| Q
| R
| S
| T
| X
| Y
| Z
'''
p[0] = str(p[1])
def p_parameter_setting(self, p):
'parameter_setting : PARAMETER_SIGN parameter_index EQUAL_SIGN real_value'
p[0] = Ast.ParameterSetting(p[2], p[4])
def p_parameter_value(self, p):
'parameter_value : PARAMETER_SIGN parameter_index'
p[0] = Ast.Parameter(p[2])
def p_parameter_index(self, p):
'parameter_index : real_value'
p[0] = p[1]
def p_real_value(self, p):
'''real_value : POSITIVE_INTEGER
| POSITIVE_REAL
| REAL
| expression
| parameter_value
| unary_combo
'''
p[0] = p[1]
##############################################
def p_unary_combo(self, p):
'''unary_combo : ordinary_unary_combo
| arc_tangent_combo
'''
p[0] = p[1]
def p_ordinary_unary_combo(self, p):
'ordinary_unary_combo : ordinary_unary_operation expression'
p[0] = self.__operation_map__[p[1]](p[2])
def p_expression(self, p):
'expression : LEFT_BRACKET inner_expression RIGHT_BRACKET'
p[0] = p[2]
def p_inner_expression(self, p):
'''inner_expression : real_value
| inner_expression binary_operation real_value
'''
if len(p) == 2:
p[0] = p[1]
else:
p[0] = self.__operation_map__[p[2]](p[1], p[3])
def p_arc_tangent_combo(self, p):
# atan[1.5]/[1.0]
'arc_tangent_combo : ARC_TANGENT expression DIVIDED_BY expression'
p[0] = DividedBy(ArcTangent(p[2]), p[4])
def p_ordinary_unary_operation(self, p):
'''ordinary_unary_operation : ABSOLUTE_VALUE
| ARC_COSINE
| ARC_SINE
| COSINE
| E_RAISED_TO
| FIX_DOWN
| FIX_UP
| NATURAL_LOG_OF
| ROUND
| SINE
| SQUARE_ROOT
| TANGENT
'''
p[0] = p[1]
def p_binary_operation(self, p):
'''binary_operation : binary_operation1
| binary_operation2
| binary_operation3
'''
p[0] = p[1]
def p_binary_operation1(self, p):
'binary_operation1 : POWER'
p[0] = p[1]
def p_binary_operation2(self, p):
'''binary_operation2 : DIVIDED_BY
| MODULO
| TIMES
'''
p[0] = p[1]
def p_binary_operation3(self, p):
'''binary_operation3 : AND
| EXCLUSIVE_OR
| MINUS
| NON_EXCLUSIVE_OR
| PLUS
'''
p[0] = p[1]
##############################################
# def p_empty(self, p):
# 'empty :'
# pass
##############################################
def p_error(self, p):
raise GcodeParserError(p.lexpos)
####################################################################################################
class GcodeParserMixin:
"""Mixin to implement a RS-274 G-code parser"""
__lexer_cls__ = GcodeLexer
##############################################
def __init__(self):
self._build()
self._reset()
##############################################
def _reset(self):
self._line = None
##############################################
def _build(self, **kwargs):
"""Build the parser"""
self._lexer = self.__lexer_cls__()
self.tokens = self._lexer.tokens
self._parser = yacc.yacc(
module=self,
# debug=True,
optimize=0,
)
##############################################
def parse(self, line):
"""Parse a G-code line.
Return a :class:`PythonicGcodeMachine.Gcode.Rs274.Ast.Line` instance.
"""
line = line.strip()
self._line = Ast.Line()
ast = self._parser.parse(
line,
lexer=self._lexer._lexer,
# debug=True,
)
line = self._line
self._reset()
return line
##############################################
def parse_lines(self, lines):
"""Parse a G-code lines
Return a :class:`PythonicGcodeMachine.Gcode.Rs274.Ast.Program` instance.
"""
if not isinstance(lines, (list, tuple)):
lines = lines.split('\n')
program = Ast.Program()
for line in lines:
try:
program += self.parse(line)
except GcodeParserError as exception:
print('Parse Error:', line)
raise exception
return program
####################################################################################################
class GcodeParser(GcodeParserMixin, GcodeGrammarMixin):
pass
__init__.py 0000664 0000000 0000000 00000051325 13410044113 0036626 0 ustar 00root root 0000000 0000000 pythonic-gcode-machine-ec1f3fc5460500f0a6778b489106cc5255de741b-PythonicGcodeMachine/PythonicGcodeMachine/Gcode/Rs274 ####################################################################################################
#
# PythonicGcodeMachine - @licence_header_description@
# Copyright (C) 2018 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
"""Module to implement the G-code language.
.. Note::
Text derived from several sources, like NIST RS-274 paper and Linux CNC project.
cf. supra for references.
History
-------
The G-code language, also called RS-274, is a programming language for numerical control. It was
developed by the EIA in the early 1960s, and finally standardised by ISO in February 1980 as RS274D
/ ISO 6983.
The G-code language has several flavours and historical versions. A list of reference documents
follows :
* `The NIST RS274NGC Interpreter - Version 3, T. Kramer, F. Proctor, E. Messina, National Institute
of Standards and Technology, NISTIR 6556, August 17, 2000
`_
* The documentation of the `Linux CNC `_ project, formerly Enhanced Machine
Controller developed at NIST,
* EIA Standard RS-274-D Interchangeable Variable Block Data Format for Positioning, Contouring, and
Contouring/Positioning Numerically Controlled Machines, 2001 Eye Street, NW, Washington,
D.C. 20006: Electronic Industries Association, February 1979
Overview
--------
The RS274/NGC language is based on lines of code. Each line (also called a “block”) may include
commands to a machining center to do several different things.
A typical line of code consists of an optional line number at the beginning followed by one or more
“words.” A word consists of a letter followed by a number (or something that evaluates to a
number). A word may either give a command or provide an argument to a command. For example,
:code:`G1 X3` is a valid line of code with two words. :code:`G1` is a command meaning “move in a
straight line at the programmed feed rate,” and :code:`X3` provides an argument value (the value of
X should be 3 at the end of the move). Most RS274/NGC commands start with either G or M (for
miscellaneous). The words for these commands are called “G codes” and “M codes.”
Language View of a Machining Center
-----------------------------------
Parameters
~~~~~~~~~~
In the RS274/NGC language view, a machining center maintains an array of 5400 numerical
parameters. Many of them have specific uses. The parameter array should persist over time, even if
the machining center is powered down.
Coordinate Systems
~~~~~~~~~~~~~~~~~~
In the RS274/NGC language view, a machining center has an absolute coordinate system and nine
program coordinate systems.
You can set the offsets of the nine program coordinate systems using G10 L2 Pn (n is the number of
the coordinate system) with values for the axes in terms of the absolute coordinate system.
You can select one of the nine systems by using G54, G55, G56, G57, G58, G59, G59.1, G59.2, or
G59.3. It is not possible to select the absolute coordinate system directly. You can offset the
current coordinate system using G92 or G92.3. This offset will then apply to all nine program
coordinate systems. This offset may be cancelled with G92.1 or G92.2.
You can make straight moves in the absolute machine coordinate system by using G53 with either G0 or
G1.
Data for coordinate systems is stored in parameters.
During initialization, the coordinate system is selected that is specified by parameter 5220. A
value of 1 means the first coordinate system (the one G54 activates), a value of 2 means the second
coordinate system (the one G55 activates), and so on. It is an error for the value of parameter 5220
to be anything but a whole number between one and nine.
Format of a Line
----------------
A permissible line of input RS274/NGC code consists of the following, in order, with the restriction
that there is a maximum (currently 256) to the number of characters allowed on a line.
#. an optional block delete character, which is a slash :code:`/` .
#. an optional line number.
#. any number of words, parameter settings, and comments.
#. an end of line marker (carriage return or line feed or both).
Spaces and tabs are allowed anywhere on a line of code and do not change the meaning of the line,
except inside comments. This makes some strange-looking input legal. The line :code:`g0x +0. 12 34y
7` is equivalent to :code:`g0 x+0.1234 y7`, for example.
Blank lines are allowed in the input. They are to be ignored.
Input is case insensitive, except in comments, i.e., any letter outside a comment may be in upper or
lower case without changing the meaning of a line.
Line Number
~~~~~~~~~~~
A line number is the letter :code:`N` followed by an integer (with no sign) between 0 and 99999
written with no more than five digits (000009 is not OK, for example). Line numbers may be repeated
or used out of order, although normal practice is to avoid such usage. Line numbers may also be
skipped, and that is normal practice. A line number is not required to be used, but must be in the
proper place if used.
Word
~~~~
A word is a letter other than :code:`N` followed by a real value.
Words may begin with any of the letters shown in the following table. The table includes :code:`N`
for completeness, even though, as defined above, line numbers are not words. Several letters
(:code:`I`, :code:`J, K`, :code:`L`, :code:`P`, :code:`R`) may have different meanings in different
contexts.
Table. Linux CNC Words and their meanings Letter
====== =====================================================
Letter Meaning
====== =====================================================
A A axis of machine
B B axis of machine
C C axis of machine
D Tool radius compensation number
F Feed rate
G General function (See table Modal Groups)
H Tool length offset index
I X offset for arcs and G87 canned cycles
J Y offset for arcs and G87 canned cycles
K Z offset for arcs and G87 canned cycles.
Spindle-Motion Ratio for G33 synchronized movements.
L Generic parameter word for G10, M66 and others
M Miscellaneous function (See table Modal Groups)
N Line number
P Dwell time in canned cycles and with G4.
Key used with G10.
Q Feed increment in G73, G83 canned cycles
R Arc radius or canned cycle plane
S Spindle speed
T Tool selection
U U axis of machine
V V axis of machine
W W axis of machine
X X axis of machine
Y Y axis of machine
Z Z axis of machine
====== =====================================================
A real value is some collection of characters that can be processed to come up with a number. A real
value may be an explicit number (such as 341 or -0.8807), a parameter value, an expression, or a
unary operation value.
Number
~~~~~~
The following rules are used for (explicit) numbers. In these rules a digit is a single character
between 0 and 9.
* A number consists of (1) an optional plus or minus sign, followed by (2) zero to many digits,
followed, possibly, by (3) one decimal point, followed by (4) zero to many digits — provided that
there is at least one digit somewhere in the number.
* There are two kinds of numbers: integers and decimals. An integer does not have a decimal point in
it; a decimal does.
* Numbers may have any number of digits, subject to the limitation on line length.
* A non-zero number with no sign as the first character is assumed to be positive. Notice that
initial (before the decimal point and the first non-zero digit) and trailing (after the decimal
point and the last non-zero digit) zeros are allowed but not required. A number written with
initial or trailing zeros will have the same value when it is read as if the extra zeros were not
there.
Parameter Value
~~~~~~~~~~~~~~~
A parameter value is the pound character :code:`#` followed by a real value. The real value must
evaluate to an integer between 1 and 5399. The integer is a parameter number, and the value of the
parameter value is whatever number is stored in the numbered parameter.
The :code:`#` character takes precedence over other operations, so that, for example, :code:`#1+2`
means the number found by adding 2 to the value of parameter 1, not the value found in parameter
3. Of course, :code:`#[1+2]` does mean the value found in parameter 3. The :code:`#` character may
be repeated; for example :code:`##2` means the value of the parameter whose index is the (integer)
value of parameter 2.
Expressions and Binary Operations
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
An expression is a set of characters starting with a left bracket :code:`[` and ending with a
balancing right bracket :code:`]`. In between the brackets are numbers, parameter values,
mathematical operations, and other expressions. An expression may be evaluated to produce a
number. The expressions on a line are evaluated when the line is read, before anything on the line
is executed. An example of an expression is :code:`[ 1 + acos[0] - [#3 ** [4.0/2]]]`.
Binary operations appear only inside expressions. Nine binary operations are defined. There are four
basic mathematical operations: addition :code:`+`, subtraction :code:`-`, multiplication :code:`*`,
and division :code:`/`. There are three logical operations: non-exclusive or :code:`OR`, exclusive
or :code:`XOR`, and logical and :code:`AND`. The eighth operation is the modulus operation
:code:`MOD`. The ninth operation is the “power” operation :code:`**` of raising the number on the
left of the operation to the power on the right. The binary operations are divided into three
groups. The first group is: power. The second group is: multiplication, division, and modulus. The
third group is: addition, subtraction, logical non- exclusive or, logical exclusive or, and logical
and. If operations are strung together (for example in the expression :code:`[2.0 / 3 * 1.5 - 5.5 /
11.0]`), operations in the first group are to be performed before operations in the second group and
operations in the second group before operations in the third group. If an expression contains more
than one operation from the same group (such as the first / and * in the example), the operation on
the left is performed first. Thus, the example is equivalent to: :code:`[((2.0 / 3) * 1.5) - (5.5 /
11.0)]`, which simplifies to :code:`[1.0 - 0.5]`, which is 0.5.
The logical operations and modulus are to be performed on any real numbers, not just on integers.
The number zero is equivalent to logical false, and any non-zero number is equivalent to logical
true.
Unary Operation Value
~~~~~~~~~~~~~~~~~~~~~
A unary operation value is either :code:`ATAN` followed by one expression divided by another
expression (for example :code:`ATAN[2]/[1+3]`) or any other unary operation name followed by an
expression (for example :code:`SIN[90]`). The unary operations are: :code:`ABS` (absolute value),
:code:`ACOS` (arc cosine), :code:`ASIN` (arc sine), :code:`ATAN` (arc tangent), :code:`COS`
(cosine), :code:`EXP` (e raised to the given power), :code:`FIX` (round down), :code:`FUP` (round
up), :code:`LN` (natural logarithm), :code:`ROUND` (round to the nearest whole number), :code:`SIN`
(sine), :code:`SQRT` (square root), and :code:`TAN` (tangent). Arguments to unary operations which
take angle measures (:code:`COS`, :code:`SIN`, and :code:`TAN`) are in degrees. Values returned by
unary operations which return angle measures (:code:`ACOS`, :code:`ASIN`, and :code:`ATAN`) are also
in degrees.
The :code:`FIX` operation rounds towards the left (less positive or more negative) on a number line,
so that :code:`FIX[2.8] = 2` and :code:`FIX[-2.8] = -3`, for example. The :code:`FUP` operation
rounds towards the right (more positive or less negative) on a number line; :code:`FUP[2.8] = 3` and
:code:`FUP[-2.8] = -2`, for example.
Parameter Setting
~~~~~~~~~~~~~~~~~
A parameter setting is the following four items one after the other: (1) a pound character
:code:`#`, (2) a real value which evaluates to an integer between 1 and 5399, (3) an equal sign
:code:`=`, and (4) a real value. For example :code:`#3 = 15` is a parameter setting meaning “set
parameter 3 to 15.”
A parameter setting does not take effect until after all parameter values on the same line have
been found. For example, if parameter 3 has been previously set to 15 and the line :code:`#3=6 G1
x#3` is interpreted, a straight move to a point where x equals 15 will occur and the value of
parameter 3 will be 6.
Comments and Messages
~~~~~~~~~~~~~~~~~~~~~
Printable characters and white space inside parentheses is a comment. A left parenthesis always
starts a comment. The comment ends at the first right parenthesis found thereafter. Once a left
parenthesis is placed on a line, a matching right parenthesis must appear before the end of the
line. Comments may not be nested; it is an error if a left parenthesis is found after the start of
a comment and before the end of the comment. Here is an example of a line containing a comment:
:code:`G80 M5 (stop motion)`. Comments do not cause a machining center to do anything.
.. A comment contains a message if “MSG,” appears after the left parenthesis and before any other
printing characters. Variants of “MSG,” which include white space and lower case characters are
allowed. The rest of the characters before the right parenthesis are considered to be a message.
Messages should be displayed on the message display device. Comments not containing messages need
not be displayed there.
Item Repeats
~~~~~~~~~~~~
A line may have any number of :code:`G` words, but two :code:`G` words from the same modal group may
not appear on the same line.
A line may have zero to four :code:`M` words. Two :code:`M` words from the same modal group may not
appear on the same line.
For all other legal letters, a line may have only one word beginning with that letter.
If a parameter setting of the same parameter is repeated on a line, :code:`#3=15 #3=6`, for example,
only the last setting will take effect. It is silly, but not illegal, to set the same parameter
twice on the same line.
If more than one comment appears on a line, only the last one will be used; each of the other
comments will be read and its format will be checked, but it will be ignored thereafter. It is
expected that putting more than one comment on a line will be very rare.
Item order
~~~~~~~~~~
The three types of item whose order may vary on a line (as given at the beginning of this section)
are word, parameter setting, and comment. Imagine that these three types of item are divided into
three groups by type.
The first group (the words) may be reordered in any way without changing the meaning of the line.
If the second group (the parameter settings) is reordered, there will be no change in the meaning of
the line unless the same parameter is set more than once. In this case, only the last setting of the
parameter will take effect. For example, after the line :code:`#3=15 #3=6` has been interpreted, the
value of parameter 3 will be 6. If the order is reversed to :code:`#3=6 #3=15` and the line is
interpreted, the value of parameter 3 will be 15.
If the third group (the comments) contains more than one comment and is reordered, only the last
comment will be used.
If each group is kept in order or reordered without changing the meaning of the line, then the three
groups may be interleaved in any way without changing the meaning of the line. For example, the line
:code:`g40 g1 #3=15 (foo) #4=-7.0` has five items and means exactly the same thing in any of the 120
possible orders (such as :code:`#4=-7.0 g1 #3=15 g40 (foo)`) for the five items.
Commands and Machine Modes
~~~~~~~~~~~~~~~~~~~~~~~~~~
In RS274/NGC, many commands cause a machining center to change from one mode to another, and the
mode stays active until some other command changes it implicitly or explicitly. Such commands are
called “modal”. For example, if coolant is turned on, it stays on until it is explicitly turned
off. The :code:`G` codes for motion are also modal. If a :code:`G1` (straight move) command is given
on one line, for example, it will be executed again on the next line if one or more axis words is
available on the line, unless an explicit command is given on that next line using the axis words or
cancelling motion.
“Non-modal” codes have effect only on the lines on which they occur. For example, :code:`G4` (dwell)
is non-modal.
Modal Groups
------------
Modal commands are arranged in sets called “modal groups”, and only one member of a modal group may
be in force at any given time. In general, a modal group contains commands for which it is logically
impossible for two members to be in effect at the same time — like measure in inches vs. measure in
millimeters. A machining center may be in many modes at the same time, with one mode from each modal
group being in effect.
.. The modal groups are shown in Table 4.
For several modal groups, when a machining center is ready to accept commands, one member of the
group must be in effect. There are default settings for these modal groups. When the machining
center is turned on or otherwise re-initialized, the default values are automatically in effect.
Group 1, the first group on the table, is a group of :code:`G` codes for motion. One of these is
always in effect. That one is called the current motion mode.
It is an error to put a G-code from group 1 and a G-code from group 0 on the same line if both of
them use axis words. If an axis word-using G-code from group 1 is implicitly in effect on a line (by
having been activated on an earlier line), and a group 0 G-code that uses axis words appears on the
line, the activity of the group 1 G-code is suspended for that line. The axis word-using G-codes
from group 0 are :code:`G10`, :code:`G28`, :code:`G30`, and :code:`G92`.
G and Input Codes
-----------------
See ...
..
G codes of the RS274/NGC language are shown in Table 5 and described following that.
In the command prototypes, three dots (...) stand for a real value. As described earlier, a real
value may be (1) an explicit number, 4, for example, (2) an expression, :code:`[2+2]`, for example,
(3) a parameter value, #88, for example, or (4) a unary function value, :code:`acos[0]`, for
example. In most cases, if axis words (any or all of X..., Y..., Z..., A..., B..., C...) are given,
they specify a destination point. Axis numbers are in the currently active coordinate system, unless
explicitly described as being in the absolute coordinate system. Where axis words are optional, any
omitted axes will have their current value. Any items in the command prototypes not explicitly
described as optional are required. It is an error if a required item is omitted.
In the prototypes, the values following letters are often given as explicit numbers. Unless stated
otherwise, the explicit numbers can be real values. For example, :code:`G10 L2` could equally well
be written :code:`G[2*5] L[1+1]`. If the value of parameter 100 were 2, :code:`G10 L#100` would also
mean the same. Using real values which are not explicit numbers as just shown in the examples is
rarely useful.
If L... is written in a prototype the “...” will often be referred to as the “L number”. Similarly the
“...” in H... may be called the “H number”, and so on for any other letter.
Order of Execution
------------------
The order of execution of items on a line is critical to safe and effective machine operation. Items
are executed in a particular order if they occur on the same line.
"""
####################################################################################################
from pathlib import Path
from .Parser import GcodeParser, GcodeParserError
from .Config import (
Parameters,
Letters,
Gcodes,
ModalGroups,
ExecutionOrder,
)
_data_path = Path(__file__).parent.joinpath('data')
# Fixme: lazy
parameters = Parameters(_data_path.joinpath('rs274-default-parameter-file.yaml'))
execution_order = ExecutionOrder(_data_path.joinpath('rs274-execution-order.yaml'))
gcodes = Gcodes(_data_path.joinpath('rs274-gcodes.yaml'))
modal_groups = ModalGroups(_data_path.joinpath('rs274-modal-groups.yaml'))
letters = Letters(_data_path.joinpath('rs274-word-starting-letter.yaml'))
data/ 0000775 0000000 0000000 00000000000 13410044113 0035420 5 ustar 00root root 0000000 0000000 pythonic-gcode-machine-ec1f3fc5460500f0a6778b489106cc5255de741b-PythonicGcodeMachine/PythonicGcodeMachine/Gcode/Rs274 make-yaml.py 0000775 0000000 0000000 00000015647 13410044113 0037667 0 ustar 00root root 0000000 0000000 pythonic-gcode-machine-ec1f3fc5460500f0a6778b489106cc5255de741b-PythonicGcodeMachine/PythonicGcodeMachine/Gcode/Rs274/data #! /usr/bin/env python3
####################################################################################################
import pathlib
import yaml
####################################################################################################
dst_path = pathlib.Path(__file__).parent
####################################################################################################
def split_columns(*columns):
return zip(*[x.strip().split('\n') for x in columns])
def write_yaml(filename, data):
dst_file = dst_path.joinpath(filename)
yaml_data = yaml.dump(data, default_flow_style=False)
with (open(dst_file, 'w')) as fh:
fh.write(yaml_data)
print('Dumped', dst_file)
####################################################################################################
# cf. Table 2. Default Parameter File
parameters = '''
5161
5162
5163
5164
5165
5166
5181
5182
5183
5184
5185
5186
5211
5212
5213
5214
5215
5216
5220
5221
5222
5223
5224
5225
5226
5241
5242
5243
5244
5245
5246
5261
5262
5263
5264
5265
5266
5281
5282
5283
5284
5285
5286
5301
5302
5303
5304
5305
5306
5321
5322
5323
5324
5325
5326
5341
5342
5343
5344
5345
5346
5361
5362
5363
5364
5365
5366
5381
5382
5383
5384
5385
5386
'''
values = '''
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
1.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
'''
comments = '''
G28 home X
G28 home Y
G28 home Z
G28 home A
G28 home B
G28 home C
G30 home X
G30 home Y
G30 home Z
G30 home A
G30 home B
G30 home C
G92 offset X
G92 offset Y
G92 offset Z
G92 offset A
G92 offset B
G92 offset C
coord. system number
coord. system 1 X
coord. system 1 Y
coord. system 1 Z
coord. system 1 A
coord. system 1 B
coord. system 1 C
coord. system 2 X
coord. system 2 Y
coord. system 2 Z
coord. system 2 A
coord. system 2 B
coord. system 2 C
coord. system 3 X
coord. system 3 Y
coord. system 3 Z
coord. system 3 A
coord. system 3 B
coord. system 3 C
coord. system 4 X
coord. system 4 Y
coord. system 4 Z
coord. system 4 A
coord. system 4 B
coord. system 4 C
coord. system 5 X
coord. system 5 Y
coord. system 5 Z
coord. system 5 A
coord. system 5 B
coord. system 5 C
coord. system 6 X
coord. system 6 Y
coord. system 6 Z
coord. system 6 A
coord. system 6 B
coord. system 6 C
coord. system 7 X
coord. system 7 Y
coord. system 7 Z
coord. system 7 A
coord. system 7 B
coord. system 7 C
coord. system 8 X
coord. system 8 Y
coord. system 8 Z
coord. system 8 A
coord. system 8 B
coord. system 8 C
coord. system 9 X
coord. system 9 Y
coord. system 9 Z
coord. system 9 A
coord. system 9 B
coord. system 9 C
'''
####################################################################################################
data = {}
for parameter, value, comment in split_columns(parameters, values, comments):
comment = comment.replace('coord.', 'coordinate')
data[int(parameter)] = dict(value=float(value), meaning=comment)
write_yaml('rs274-default-parameter-file.yaml', data)
####################################################################################################
# Table 3. Word-starting Letters
letters = '''
A
B
C
D
F
G
H
I
J
K
L
M
N
P
Q
R
S
T
X
Y
Z
'''
meanings = '''
A-axis of machine
B-axis of machine
C-axis of machine
tool radius compensation number
feedrate
general function (see Table 5)
tool length offset index
X-axis offset for arcs / X offset in G87 canned cycle
Y-axis offset for arcs / Y offset in G87 canned cycle
Z-axis offset for arcs / Z offset in G87 canned cycle
number of repetitions in canned cycles / key used with G10
miscellaneous function (see Table 7)
line number
dwell time in canned cycles / dwell time with G4 / key used with G10
feed increment in G83 canned cycle
arc radius
canned cycle plane / spindle speed
tool selection
X-axis of machine
Y-axis of machine
Z-axis of machine
'''
####################################################################################################
data = {}
for letter, meaning in split_columns(letters, meanings):
data[letter] = dict(meaning=meaning)
write_yaml('rs274-word-starting-letter.yaml', data)
####################################################################################################
# Table 5. G Codes
# Table 7. M Codes
# 3.7 Other Input Codes
gcodes = '''
G0
G1
G2
G3
G4
G10
G17
G18
G19
G20
G21
G28
G30
G38.2
G40
G41
G42
G43
G49
G53
G54
G55
G56
G57
G58
G59
G59.1
G59.2
G59.3
G61
G61.1
G64
G80
G81
G82
G83
G84
G85
G86
G87
G88
G89
G90
G91
G92
G92.1
G92.2
G92.3
G93
G94
G98
G99
M0
M1
M2
M3
M4
M5
M6
M7
M8
M9
M30
M48
M49
M60
F
S
T
'''
meanings = '''
rapid positioning
linear interpolation
circular/helical interpolation (clockwise)
circular/helical interpolation (counterclockwise)
dwell
coordinate system origin setting
XY-plane selection
XZ-plane selection
YZ-plane selection
inch system selection
millimeter system selection
return to home
return to secondary home
straight probe
cancel cutter radius compensation
start cutter radius compensation left
start cutter radius compensation right
tool length offset (plus)
cancel tool length offset
motion in machine coordinate system
use preset work coordinate system 1
use preset work coordinate system 2
use preset work coordinate system 3
use preset work coordinate system 4
use preset work coordinate system 5
use preset work coordinate system 6
use preset work coordinate system 7
use preset work coordinate system 8
use preset work coordinate system 9
set path control mode: exact path
set path control mode: exact stop
set path control mode: continuous
cancel motion mode (including any canned cycle)
canned cycle: drilling
canned cycle: drilling with dwell
canned cycle: peck drilling
canned cycle: right hand tapping
canned cycle: boring, no dwell, feed out
canned cycle: boring, spindle stop, rapid out
canned cycle: back boring
canned cycle: boring, spindle stop, manual out
canned cycle: boring, dwell, feed out
absolute distance mode
incremental distance mode
offset coordinate systems and set parameters
cancel offset coordinate systems and set parameters to zero
cancel offset coordinate systems but do not reset parameters
apply parameters to offset coordinate systems
inverse time feed rate mode
units per minute feed rate mode
initial level return in canned cycles
R-point level return in canned cycles
program stop
optional program stop
program end
turn spindle clockwise
turn spindle counterclockwise
stop spindle turning
tool change
mist coolant on
flood coolant on
mist and flood coolant off
program end, pallet shuttle, and reset
enable speed and feed overrides
disable speed and feed overrides
pallet shuttle and program stop
set feed rate
set spindle speed
select tool
'''
####################################################################################################
data = {}
for gcode, meaning in split_columns(gcodes, meanings):
data[gcode] = dict(meaning=meaning)
write_yaml('rs274-gcodes.yaml', data)
rs274-default-parameter-file.yaml 0000664 0000000 0000000 00000007035 13410044113 0043507 0 ustar 00root root 0000000 0000000 pythonic-gcode-machine-ec1f3fc5460500f0a6778b489106cc5255de741b-PythonicGcodeMachine/PythonicGcodeMachine/Gcode/Rs274/data 5161:
meaning: G28 home X
value: 0.0
5162:
meaning: G28 home Y
value: 0.0
5163:
meaning: G28 home Z
value: 0.0
5164:
meaning: G28 home A
value: 0.0
5165:
meaning: G28 home B
value: 0.0
5166:
meaning: G28 home C
value: 0.0
5181:
meaning: G30 home X
value: 0.0
5182:
meaning: G30 home Y
value: 0.0
5183:
meaning: G30 home Z
value: 0.0
5184:
meaning: G30 home A
value: 0.0
5185:
meaning: G30 home B
value: 0.0
5186:
meaning: G30 home C
value: 0.0
5211:
meaning: G92 offset X
value: 0.0
5212:
meaning: G92 offset Y
value: 0.0
5213:
meaning: G92 offset Z
value: 0.0
5214:
meaning: G92 offset A
value: 0.0
5215:
meaning: G92 offset B
value: 0.0
5216:
meaning: G92 offset C
value: 0.0
5220:
meaning: coordinate system number
value: 1.0
5221:
meaning: coordinate system 1 X
value: 0.0
5222:
meaning: coordinate system 1 Y
value: 0.0
5223:
meaning: coordinate system 1 Z
value: 0.0
5224:
meaning: coordinate system 1 A
value: 0.0
5225:
meaning: coordinate system 1 B
value: 0.0
5226:
meaning: coordinate system 1 C
value: 0.0
5241:
meaning: coordinate system 2 X
value: 0.0
5242:
meaning: coordinate system 2 Y
value: 0.0
5243:
meaning: coordinate system 2 Z
value: 0.0
5244:
meaning: coordinate system 2 A
value: 0.0
5245:
meaning: coordinate system 2 B
value: 0.0
5246:
meaning: coordinate system 2 C
value: 0.0
5261:
meaning: coordinate system 3 X
value: 0.0
5262:
meaning: coordinate system 3 Y
value: 0.0
5263:
meaning: coordinate system 3 Z
value: 0.0
5264:
meaning: coordinate system 3 A
value: 0.0
5265:
meaning: coordinate system 3 B
value: 0.0
5266:
meaning: coordinate system 3 C
value: 0.0
5281:
meaning: coordinate system 4 X
value: 0.0
5282:
meaning: coordinate system 4 Y
value: 0.0
5283:
meaning: coordinate system 4 Z
value: 0.0
5284:
meaning: coordinate system 4 A
value: 0.0
5285:
meaning: coordinate system 4 B
value: 0.0
5286:
meaning: coordinate system 4 C
value: 0.0
5301:
meaning: coordinate system 5 X
value: 0.0
5302:
meaning: coordinate system 5 Y
value: 0.0
5303:
meaning: coordinate system 5 Z
value: 0.0
5304:
meaning: coordinate system 5 A
value: 0.0
5305:
meaning: coordinate system 5 B
value: 0.0
5306:
meaning: coordinate system 5 C
value: 0.0
5321:
meaning: coordinate system 6 X
value: 0.0
5322:
meaning: coordinate system 6 Y
value: 0.0
5323:
meaning: coordinate system 6 Z
value: 0.0
5324:
meaning: coordinate system 6 A
value: 0.0
5325:
meaning: coordinate system 6 B
value: 0.0
5326:
meaning: coordinate system 6 C
value: 0.0
5341:
meaning: coordinate system 7 X
value: 0.0
5342:
meaning: coordinate system 7 Y
value: 0.0
5343:
meaning: coordinate system 7 Z
value: 0.0
5344:
meaning: coordinate system 7 A
value: 0.0
5345:
meaning: coordinate system 7 B
value: 0.0
5346:
meaning: coordinate system 7 C
value: 0.0
5361:
meaning: coordinate system 8 X
value: 0.0
5362:
meaning: coordinate system 8 Y
value: 0.0
5363:
meaning: coordinate system 8 Z
value: 0.0
5364:
meaning: coordinate system 8 A
value: 0.0
5365:
meaning: coordinate system 8 B
value: 0.0
5366:
meaning: coordinate system 8 C
value: 0.0
5381:
meaning: coordinate system 9 X
value: 0.0
5382:
meaning: coordinate system 9 Y
value: 0.0
5383:
meaning: coordinate system 9 Z
value: 0.0
5384:
meaning: coordinate system 9 A
value: 0.0
5385:
meaning: coordinate system 9 B
value: 0.0
5386:
meaning: coordinate system 9 C
value: 0.0
rs274-execution-order.yaml 0000664 0000000 0000000 00000001665 13410044113 0042307 0 ustar 00root root 0000000 0000000 pythonic-gcode-machine-ec1f3fc5460500f0a6778b489106cc5255de741b-PythonicGcodeMachine/PythonicGcodeMachine/Gcode/Rs274/data # Table 8. Order of Execution
1: COMMENT # (includes message]
2: [G93, G94] # set feed rate mode (inverse time or per minute]
3: F # set feed rate
4: S # set spindle speed
5: T # select tool
6: M6 # change tool
7: [M3, M4, M5] # spindle on or off
8: [M7, M8, M9] # coolant on or off
9: [M48, M49] # enable or disable overrides
10: G4 # dwell
11: [G17, G18, G19] # set active plane
12: [G20, G21] # set length units
13: [G40, G41, G42] # cutter radius compensation on or off
14: [G43, G49] # cutter length compensation on or off
15: [G54, G55, G56, G57, G58, G59, G59.1, G59.2, G59.3] # coordinate system selection
16: [G61, G61.1, G64] # set path control mode
17: [G90, G91] # set distance mode
18: [G98, G99] # set retract mode
19: [G28, G30, G10, G92, G92.1, G92.2, G94] # home or change coordinate system data or set axis offsets
20: ['G0-G3', 'G80-G89', G53] # perform motion, as modified (possibly) by G53
21: [M0, M1, M2, M30, M60] # stop
rs274-gcodes.yaml 0000664 0000000 0000000 00000006217 13410044113 0040435 0 ustar 00root root 0000000 0000000 pythonic-gcode-machine-ec1f3fc5460500f0a6778b489106cc5255de741b-PythonicGcodeMachine/PythonicGcodeMachine/Gcode/Rs274/data F:
meaning: set feed rate
G0:
meaning: rapid positioning
G1:
meaning: linear interpolation
G10:
meaning: coordinate system origin setting
G17:
meaning: XY-plane selection
G18:
meaning: XZ-plane selection
G19:
meaning: YZ-plane selection
G2:
meaning: circular/helical interpolation (clockwise)
G20:
meaning: inch system selection
G21:
meaning: millimeter system selection
G28:
meaning: return to home
G3:
meaning: circular/helical interpolation (counterclockwise)
G30:
meaning: return to secondary home
G38.2:
meaning: straight probe
G4:
meaning: dwell
G40:
meaning: cancel cutter radius compensation
G41:
meaning: start cutter radius compensation left
G42:
meaning: start cutter radius compensation right
G43:
meaning: tool length offset (plus)
G49:
meaning: cancel tool length offset
G53:
meaning: motion in machine coordinate system
G54:
meaning: use preset work coordinate system 1
G55:
meaning: use preset work coordinate system 2
G56:
meaning: use preset work coordinate system 3
G57:
meaning: use preset work coordinate system 4
G58:
meaning: use preset work coordinate system 5
G59:
meaning: use preset work coordinate system 6
G59.1:
meaning: use preset work coordinate system 7
G59.2:
meaning: use preset work coordinate system 8
G59.3:
meaning: use preset work coordinate system 9
G61:
meaning: 'set path control mode: exact path'
G61.1:
meaning: 'set path control mode: exact stop'
G64:
meaning: 'set path control mode: continuous'
G80:
meaning: cancel motion mode (including any canned cycle)
G81:
meaning: 'canned cycle: drilling'
G82:
meaning: 'canned cycle: drilling with dwell'
G83:
meaning: 'canned cycle: peck drilling'
G84:
meaning: 'canned cycle: right hand tapping'
G85:
meaning: 'canned cycle: boring, no dwell, feed out'
G86:
meaning: 'canned cycle: boring, spindle stop, rapid out'
G87:
meaning: 'canned cycle: back boring'
G88:
meaning: 'canned cycle: boring, spindle stop, manual out'
G89:
meaning: 'canned cycle: boring, dwell, feed out'
G90:
meaning: absolute distance mode
G91:
meaning: incremental distance mode
G92:
meaning: offset coordinate systems and set parameters
G92.1:
meaning: cancel offset coordinate systems and set parameters to zero
G92.2:
meaning: cancel offset coordinate systems but do not reset parameters
G92.3:
meaning: apply parameters to offset coordinate systems
G93:
meaning: inverse time feed rate mode
G94:
meaning: units per minute feed rate mode
G98:
meaning: initial level return in canned cycles
G99:
meaning: R-point level return in canned cycles
M0:
meaning: program stop
M1:
meaning: optional program stop
M2:
meaning: program end
M3:
meaning: turn spindle clockwise
M30:
meaning: program end, pallet shuttle, and reset
M4:
meaning: turn spindle counterclockwise
M48:
meaning: enable speed and feed overrides
M49:
meaning: disable speed and feed overrides
M5:
meaning: stop spindle turning
M6:
meaning: tool change
M60:
meaning: pallet shuttle and program stop
M7:
meaning: mist coolant on
M8:
meaning: flood coolant on
M9:
meaning: mist and flood coolant off
S:
meaning: set spindle speed
T:
meaning: select tool
rs274-modal-groups.yaml 0000664 0000000 0000000 00000001651 13410044113 0041577 0 ustar 00root root 0000000 0000000 pythonic-gcode-machine-ec1f3fc5460500f0a6778b489106cc5255de741b-PythonicGcodeMachine/PythonicGcodeMachine/Gcode/Rs274/data # Table 4. Modal Groups
# The modal groups for G codes are
1: [G0, G1, G2, G3, G38.2, G80, G81, G82, G83, G84, G85, G86, G87, G88, G89]
2 : [G17, G18, G19] # plane selection
3 : [G90, G91] # distance mode
5 : [G93, G94] # feed rate mode
6 : [G20, G21] # units
7 : [G40, G41, G42] # cutter radius compensation
8 : [G43, G49] # tool length offset
10 : [G98, G99] # return mode in canned cycles
12 : [G54, G55, G56, G57, G58, G59, G59.1, G59.2, G59.3] # coordinate system selection
13 : [G61, G61.1, G64] # path control mode
# The modal groups for M codes are
4 : [M0, M1, M2, M30, M60] # stopping
6 : [M6] # tool change
7 : [M3, M4, M5] # spindle turning
8 : [M7, M8, M9] # coolant (special case: M7 and M8 may be active at the same time)
9 : [M48, M49] # enable/disable feed and speed override switches
# In addition to the above modal groups, there is a group for non-modal G codes
0 : [G4, G10, G28, G30, G53, G92, G92.1, G92.2, G92.3]
rs274-word-starting-letter.yaml 0000664 0000000 0000000 00000001646 13410044113 0043273 0 ustar 00root root 0000000 0000000 pythonic-gcode-machine-ec1f3fc5460500f0a6778b489106cc5255de741b-PythonicGcodeMachine/PythonicGcodeMachine/Gcode/Rs274/data A:
meaning: A-axis of machine
B:
meaning: B-axis of machine
C:
meaning: C-axis of machine
D:
meaning: tool radius compensation number
F:
meaning: feedrate
G:
meaning: general function (see Table 5)
H:
meaning: tool length offset index
I:
meaning: X-axis offset for arcs / X offset in G87 canned cycle
J:
meaning: Y-axis offset for arcs / Y offset in G87 canned cycle
K:
meaning: Z-axis offset for arcs / Z offset in G87 canned cycle
L:
meaning: number of repetitions in canned cycles / key used with G10
M:
meaning: miscellaneous function (see Table 7)
N:
meaning: line number
P:
meaning: dwell time in canned cycles / dwell time with G4 / key used with G10
Q:
meaning: feed increment in G83 canned cycle
R:
meaning: arc radius
S:
meaning: canned cycle plane / spindle speed
T:
meaning: tool selection
X:
meaning: X-axis of machine
Y:
meaning: Y-axis of machine
Z:
meaning: Z-axis of machine
__init__.py 0000664 0000000 0000000 00000000000 13410044113 0035765 0 ustar 00root root 0000000 0000000 pythonic-gcode-machine-ec1f3fc5460500f0a6778b489106cc5255de741b-PythonicGcodeMachine/PythonicGcodeMachine/Gcode Machine/ 0000775 0000000 0000000 00000000000 13410044113 0034211 5 ustar 00root root 0000000 0000000 pythonic-gcode-machine-ec1f3fc5460500f0a6778b489106cc5255de741b-PythonicGcodeMachine/PythonicGcodeMachine __init__.py 0000664 0000000 0000000 00000000000 13410044113 0036310 0 ustar 00root root 0000000 0000000 pythonic-gcode-machine-ec1f3fc5460500f0a6778b489106cc5255de741b-PythonicGcodeMachine/PythonicGcodeMachine/Machine PythonLexYacc/ 0000775 0000000 0000000 00000000000 13410044113 0035377 5 ustar 00root root 0000000 0000000 pythonic-gcode-machine-ec1f3fc5460500f0a6778b489106cc5255de741b-PythonicGcodeMachine/PythonicGcodeMachine __init__.py 0000664 0000000 0000000 00000000000 13410044113 0037476 0 ustar 00root root 0000000 0000000 pythonic-gcode-machine-ec1f3fc5460500f0a6778b489106cc5255de741b-PythonicGcodeMachine/PythonicGcodeMachine/PythonLexYacc lex.py 0000664 0000000 0000000 00000123361 13410044113 0036547 0 ustar 00root root 0000000 0000000 pythonic-gcode-machine-ec1f3fc5460500f0a6778b489106cc5255de741b-PythonicGcodeMachine/PythonicGcodeMachine/PythonLexYacc ####################################################################################################
#
# Forked from David Beazley Python Lex-Yacc
# http://www.dabeaz.com/ply/index.html
# https://github.com/dabeaz/ply
#
# Fork purpose : We just need a fast Py3 lexer
#
# ply: lex.py
#
# Copyright (C) 2001-2018
# David M. Beazley (Dabeaz LLC)
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of the David Beazley or Dabeaz LLC may be used to
# endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
####################################################################################################
# __version__ = '3.11'
__tabversion__ = '3.10'
####################################################################################################
import copy
import inspect
import os
import re
import sys
import types
####################################################################################################
# This regular expression is used to match valid token names
_is_identifier = re.compile(r'^[a-zA-Z0-9_]+$')
####################################################################################################
# Exception thrown when invalid token encountered and no default error handler is defined.
class LexError(Exception):
def __init__(self, message, s):
self.args = (message,)
self.text = s
####################################################################################################
class LexToken(object):
"""Token class. This class is used to represent the tokens produced."""
def __str__(self):
return 'LexToken(%s,%r,%d,%d)' % (self.type, self.value, self.lineno, self.lexpos)
def __repr__(self):
return str(self)
####################################################################################################
class PlyLogger(object):
"""This object is a stand-in for a logging object created by the logging module."""
def __init__(self, f):
self.f = f
def critical(self, msg, *args, **kwargs):
self.f.write((msg % args) + '\n')
def warning(self, msg, *args, **kwargs):
self.f.write('WARNING: ' + (msg % args) + '\n')
def error(self, msg, *args, **kwargs):
self.f.write('ERROR: ' + (msg % args) + '\n')
info = critical
debug = critical
class NullLogger(object):
"""Null logger is used when no output is generated. Does nothing."""
def __getattribute__(self, name):
return self
def __call__(self, *args, **kwargs):
return self
####################################################################################################
class Lexer:
"""Class to implement the Lexing Engine
The following Lexer class implements the lexer runtime. There are only a few public methods and
attributes:
* input() - Store a new string in the lexer
* token() - Get the next token
* clone() - Clone the lexer
* lineno - Current line number
* lexpos - Current position in the input string
"""
##############################################
def __init__(self):
self.lexre = None # Master regular expression. This is a list of
# tuples (re, findex) where re is a compiled
# regular expression and findex is a list
# mapping regex group numbers to rules
self.lexretext = None # Current regular expression strings
self.lexstatere = {} # Dictionary mapping lexer states to master regexs
self.lexstateretext = {} # Dictionary mapping lexer states to regex strings
self.lexstaterenames = {} # Dictionary mapping lexer states to symbol names
self.lexstate = 'INITIAL' # Current lexer state
self.lexstatestack = [] # Stack of lexer states
self.lexstateinfo = None # State information
self.lexstateignore = {} # Dictionary of ignored characters for each state
self.lexstateerrorf = {} # Dictionary of error functions for each state
self.lexstateeoff = {} # Dictionary of eof functions for each state
self.lexreflags = 0 # Optional re compile flags
self.lexdata = None # Actual input data (as a string)
self.lexpos = 0 # Current position in input text
self.lexlen = 0 # Length of the input text
self.lexerrorf = None # Error rule (if any)
self.lexeoff = None # EOF rule (if any)
self.lextokens = None # List of valid tokens
self.lexignore = '' # Ignored characters
self.lexliterals = '' # Literal characters that can be passed through
self.lexmodule = None # Module
self.lineno = 1 # Current line number
self.lexoptimize = False # Optimized mode
##############################################
def clone(self, object=None):
c = copy.copy(self)
# If the object parameter has been supplied, it means we are attaching the
# lexer to a new object. In this case, we have to rebind all methods in
# the lexstatere and lexstateerrorf tables.
if object:
newtab = {}
for key, ritem in self.lexstatere.items():
newre = []
for cre, findex in ritem:
newfindex = []
for f in findex:
if not f or not f[0]:
newfindex.append(f)
continue
newfindex.append((getattr(object, f[0].__name__), f[1]))
newre.append((cre, newfindex))
newtab[key] = newre
c.lexstatere = newtab
c.lexstateerrorf = {}
for key, ef in self.lexstateerrorf.items():
c.lexstateerrorf[key] = getattr(object, ef.__name__)
c.lexmodule = object
return c
##############################################
def writetab(self, lextab, outputdir=''):
"""Write lexer information to a table file"""
if isinstance(lextab, types.ModuleType):
raise IOError("Won't overwrite existing lextab module")
basetabmodule = lextab.split('.')[-1]
filename = os.path.join(outputdir, basetabmodule) + '.py'
with open(filename, 'w') as tf:
tf.write('# %s.py. This file automatically created by PLY (version %s). Don\'t edit!\n' % (basetabmodule, __version__))
tf.write('_tabversion = %s\n' % repr(__tabversion__))
tf.write('_lextokens = set(%s)\n' % repr(tuple(sorted(self.lextokens))))
tf.write('_lexreflags = %s\n' % repr(int(self.lexreflags)))
tf.write('_lexliterals = %s\n' % repr(self.lexliterals))
tf.write('_lexstateinfo = %s\n' % repr(self.lexstateinfo))
# Rewrite the lexstatere table, replacing function objects with function names
tabre = {}
for statename, lre in self.lexstatere.items():
titem = []
for (pat, func), retext, renames in zip(lre, self.lexstateretext[statename], self.lexstaterenames[statename]):
titem.append((retext, _funcs_to_names(func, renames)))
tabre[statename] = titem
tf.write('_lexstatere = %s\n' % repr(tabre))
tf.write('_lexstateignore = %s\n' % repr(self.lexstateignore))
taberr = {}
for statename, ef in self.lexstateerrorf.items():
taberr[statename] = ef.__name__ if ef else None
tf.write('_lexstateerrorf = %s\n' % repr(taberr))
tabeof = {}
for statename, ef in self.lexstateeoff.items():
tabeof[statename] = ef.__name__ if ef else None
tf.write('_lexstateeoff = %s\n' % repr(tabeof))
##############################################
def readtab(self, tabfile, fdict):
"""Read lexer information from a tab file"""
if isinstance(tabfile, types.ModuleType):
lextab = tabfile
else:
exec('import %s' % tabfile)
lextab = sys.modules[tabfile]
if getattr(lextab, '_tabversion', '0.0') != __tabversion__:
raise ImportError('Inconsistent PLY version')
self.lextokens = lextab._lextokens
self.lexreflags = lextab._lexreflags
self.lexliterals = lextab._lexliterals
self.lextokens_all = self.lextokens | set(self.lexliterals)
self.lexstateinfo = lextab._lexstateinfo
self.lexstateignore = lextab._lexstateignore
self.lexstatere = {}
self.lexstateretext = {}
for statename, lre in lextab._lexstatere.items():
titem = []
txtitem = []
for pat, func_name in lre:
titem.append((re.compile(pat, lextab._lexreflags), _names_to_funcs(func_name, fdict)))
self.lexstatere[statename] = titem
self.lexstateretext[statename] = txtitem
self.lexstateerrorf = {}
for statename, ef in lextab._lexstateerrorf.items():
self.lexstateerrorf[statename] = fdict[ef]
self.lexstateeoff = {}
for statename, ef in lextab._lexstateeoff.items():
self.lexstateeoff[statename] = fdict[ef]
self.begin('INITIAL')
##############################################
def input(self, s):
"""Push a new string into the lexer"""
# Pull off the first character to see if s looks like a string
c = s[:1]
if not isinstance(c, (str, bytes)):
raise ValueError('Expected a string')
self.lexdata = s
self.lexpos = 0
self.lexlen = len(s)
##############################################
def begin(self, state):
"""Changes the lexing state"""
if state not in self.lexstatere:
raise ValueError('Undefined state')
self.lexre = self.lexstatere[state]
self.lexretext = self.lexstateretext[state]
self.lexignore = self.lexstateignore.get(state, '')
self.lexerrorf = self.lexstateerrorf.get(state, None)
self.lexeoff = self.lexstateeoff.get(state, None)
self.lexstate = state
##############################################
def push_state(self, state):
"""Changes the lexing state and saves old on stack"""
self.lexstatestack.append(self.lexstate)
self.begin(state)
##############################################
def pop_state(self):
"""Restores the previous state"""
self.begin(self.lexstatestack.pop())
##############################################
def current_state(self):
"""Returns the current lexing state"""
return self.lexstate
##############################################
def skip(self, n):
"""Skip ahead n characters"""
self.lexpos += n
##############################################
def token(self):
"""Return the next token from the Lexer
Note: This function has been carefully implemented to be as fast as possible. Don't make
changes unless you really know what you are doing
"""
# Make local copies of frequently referenced attributes
lexpos = self.lexpos
lexlen = self.lexlen
lexignore = self.lexignore
lexdata = self.lexdata
while lexpos < lexlen:
# This code provides some short-circuit code for whitespace, tabs, and other ignored characters
if lexdata[lexpos] in lexignore:
lexpos += 1
continue
# Look for a regular expression match
for lexre, lexindexfunc in self.lexre:
m = lexre.match(lexdata, lexpos)
if not m:
continue
# Create a token for return
tok = LexToken()
tok.value = m.group()
tok.lineno = self.lineno
tok.lexpos = lexpos
i = m.lastindex
func, tok.type = lexindexfunc[i]
if not func:
# If no token type was set, it's an ignored token
if tok.type:
self.lexpos = m.end()
return tok
else:
lexpos = m.end()
break
lexpos = m.end()
# If token is processed by a function, call it
tok.lexer = self # Set additional attributes useful in token rules
self.lexmatch = m
self.lexpos = lexpos
newtok = func(tok)
# Every function must return a token, if nothing, we just move to next token
if not newtok:
lexpos = self.lexpos # This is here in case user has updated lexpos.
lexignore = self.lexignore # This is here in case there was a state change
break
# Verify type of the token. If not in the token map, raise an error
if not self.lexoptimize:
if newtok.type not in self.lextokens_all:
raise LexError("%s:%d: Rule '%s' returned an unknown token type '%s'" % (
func.__code__.co_filename, func.__code__.co_firstlineno,
func.__name__, newtok.type), lexdata[lexpos:])
return newtok
else:
# No match, see if in literals
if lexdata[lexpos] in self.lexliterals:
tok = LexToken()
tok.value = lexdata[lexpos]
tok.lineno = self.lineno
tok.type = tok.value
tok.lexpos = lexpos
self.lexpos = lexpos + 1
return tok
# No match. Call t_error() if defined.
if self.lexerrorf:
tok = LexToken()
tok.value = self.lexdata[lexpos:]
tok.lineno = self.lineno
tok.type = 'error'
tok.lexer = self
tok.lexpos = lexpos
self.lexpos = lexpos
newtok = self.lexerrorf(tok)
if lexpos == self.lexpos:
# Error method didn't change text position at all. This is an error.
raise LexError("Scanning error. Illegal character '%s'" % (lexdata[lexpos]), lexdata[lexpos:])
lexpos = self.lexpos
if not newtok:
continue
return newtok
self.lexpos = lexpos
raise LexError("Illegal character '%s' at index %d" % (lexdata[lexpos], lexpos), lexdata[lexpos:])
if self.lexeoff:
tok = LexToken()
tok.type = 'eof'
tok.value = ''
tok.lineno = self.lineno
tok.lexpos = lexpos
tok.lexer = self
self.lexpos = lexpos
newtok = self.lexeoff(tok)
return newtok
self.lexpos = lexpos + 1
if self.lexdata is None:
raise RuntimeError('No input string given with input()')
return None
##############################################
# Iterator interface
def __iter__(self):
return self
def next(self):
t = self.token()
if t is None:
raise StopIteration
return t
__next__ = next
####################################################################################################
#
# Lex Builder
#
# The functions and classes below are used to collect lexing information
# and build a Lexer object from it.
#
####################################################################################################
####################################################################################################
def _get_regex(func):
"""Returns the regular expression assigned to a function either as a doc string or as a .regex
attribute attached by the @TOKEN decorator.
"""
return getattr(func, 'regex', func.__doc__)
####################################################################################################
def get_caller_module_dict(levels):
"""This function returns a dictionary containing all of the symbols defined within a caller further
down the call stack. This is used to get the environment associated with the yacc() call if
none was provided.
"""
f = sys._getframe(levels)
ldict = f.f_globals.copy()
if f.f_globals != f.f_locals:
ldict.update(f.f_locals)
return ldict
####################################################################################################
def _funcs_to_names(funclist, namelist):
"""Given a list of regular expression functions, this converts it to a list suitable for output to a
table file
"""
result = []
for f, name in zip(funclist, namelist):
if f and f[0]:
result.append((name, f[1]))
else:
result.append(f)
return result
####################################################################################################
def _names_to_funcs(namelist, fdict):
"""Given a list of regular expression function names, this converts it back to functions.
"""
result = []
for n in namelist:
if n and n[0]:
result.append((fdict[n[0]], n[1]))
else:
result.append(n)
return result
####################################################################################################
def _form_master_re(relist, reflags, ldict, toknames):
"""This function takes a list of all of the regex components and attempts to form the master regular
expression. Given limitations in the Python re module, it may be necessary to break the master
regex into separate expressions.
"""
if not relist:
return []
regex = '|'.join(relist)
try:
lexre = re.compile(regex, reflags)
# Build the index to function map for the matching engine
lexindexfunc = [None] * (max(lexre.groupindex.values()) + 1)
lexindexnames = lexindexfunc[:]
for f, i in lexre.groupindex.items():
handle = ldict.get(f, None)
if type(handle) in (types.FunctionType, types.MethodType):
lexindexfunc[i] = (handle, toknames[f])
lexindexnames[i] = f
elif handle is not None:
lexindexnames[i] = f
if f.find('ignore_') > 0:
lexindexfunc[i] = (None, None)
else:
lexindexfunc[i] = (None, toknames[f])
return [(lexre, lexindexfunc)], [regex], [lexindexnames]
except Exception:
m = int(len(relist)/2)
if m == 0:
m = 1
llist, lre, lnames = _form_master_re(relist[:m], reflags, ldict, toknames)
rlist, rre, rnames = _form_master_re(relist[m:], reflags, ldict, toknames)
return (llist+rlist), (lre+rre), (lnames+rnames)
####################################################################################################
def _statetoken(s, names):
"""Given a declaration name s of the form "t_" and a dictionary whose keys are state names, this
function returns a tuple (states,tokenname) where states is a tuple of state names and tokenname
is the name of the token. For example, calling this with s = "t_foo_bar_SPAM" might return
(('foo','bar'),'SPAM')
"""
parts = s.split('_')
for i, part in enumerate(parts[1:], 1):
if part not in names and part != 'ANY':
break
if i > 1:
states = tuple(parts[1:i])
else:
states = ('INITIAL',)
if 'ANY' in states:
states = tuple(names)
tokenname = '_'.join(parts[i:])
return (states, tokenname)
####################################################################################################
class LexerReflect(object):
"""This class represents information needed to build a lexer as extracted from a user's input file.
"""
##############################################
def __init__(self, ldict, log=None, reflags=0):
self.ldict = ldict
self.error_func = None
self.tokens = []
self.reflags = reflags
self.stateinfo = {'INITIAL': 'inclusive'}
self.modules = set()
self.error = False
self.log = PlyLogger(sys.stderr) if log is None else log
##############################################
def get_all(self):
"""Get all of the basic information"""
self.get_tokens()
self.get_literals()
self.get_states()
self.get_rules()
##############################################
def validate_all(self):
"""Validate all of the information"""
self.validate_tokens()
self.validate_literals()
self.validate_rules()
return self.error
##############################################
def get_tokens(self):
""" Get the tokens map"""
tokens = self.ldict.get('tokens', None)
if not tokens:
self.log.error('No token list is defined')
self.error = True
return
if not isinstance(tokens, (list, tuple)):
self.log.error('tokens must be a list or tuple')
self.error = True
return
if not tokens:
self.log.error('tokens is empty')
self.error = True
return
self.tokens = tokens
##############################################
def validate_tokens(self):
"""Validate the tokens"""
terminals = {}
for n in self.tokens:
if not _is_identifier.match(n):
self.log.error("Bad token name '%s'", n)
self.error = True
if n in terminals:
self.log.warning("Token '%s' multiply defined", n)
terminals[n] = 1
##############################################
def get_literals(self):
"""Get the literals specifier"""
self.literals = self.ldict.get('literals', '')
if not self.literals:
self.literals = ''
##############################################
def validate_literals(self):
"""Validate literals"""
try:
for c in self.literals:
if not isinstance(c, (str, bytes)) or len(c) > 1:
self.log.error('Invalid literal %s. Must be a single character', repr(c))
self.error = True
except TypeError:
self.log.error('Invalid literals specification. literals must be a sequence of characters')
self.error = True
##############################################
def get_states(self):
self.states = self.ldict.get('states', None)
# Build statemap
if self.states:
if not isinstance(self.states, (tuple, list)):
self.log.error('states must be defined as a tuple or list')
self.error = True
else:
for s in self.states:
if not isinstance(s, tuple) or len(s) != 2:
self.log.error("Invalid state specifier %s. Must be a tuple (statename,'exclusive|inclusive')", repr(s))
self.error = True
continue
name, statetype = s
if not isinstance(name, (str, bytes)):
self.log.error('State name %s must be a string', repr(name))
self.error = True
continue
if not (statetype == 'inclusive' or statetype == 'exclusive'):
self.log.error("State type for state %s must be 'inclusive' or 'exclusive'", name)
self.error = True
continue
if name in self.stateinfo:
self.log.error("State '%s' already defined", name)
self.error = True
continue
self.stateinfo[name] = statetype
##############################################
def get_rules(self):
"""Get all of the symbols with a t_ prefix and sort them into various categories (functions,
strings, error functions, and ignore characters)
"""
tsymbols = [f for f in self.ldict if f[:2] == 't_']
# Now build up a list of functions and a list of strings
self.toknames = {} # Mapping of symbols to token names
self.funcsym = {} # Symbols defined as functions
self.strsym = {} # Symbols defined as strings
self.ignore = {} # Ignore strings by state
self.errorf = {} # Error functions by state
self.eoff = {} # EOF functions by state
for s in self.stateinfo:
self.funcsym[s] = []
self.strsym[s] = []
if len(tsymbols) == 0:
self.log.error('No rules of the form t_rulename are defined')
self.error = True
return
for f in tsymbols:
t = self.ldict[f]
states, tokname = _statetoken(f, self.stateinfo)
self.toknames[f] = tokname
if hasattr(t, '__call__'):
if tokname == 'error':
for s in states:
self.errorf[s] = t
elif tokname == 'eof':
for s in states:
self.eoff[s] = t
elif tokname == 'ignore':
line = t.__code__.co_firstlineno
file = t.__code__.co_filename
self.log.error("%s:%d: Rule '%s' must be defined as a string", file, line, t.__name__)
self.error = True
else:
for s in states:
self.funcsym[s].append((f, t))
elif isinstance(t, (str, bytes)):
if tokname == 'ignore':
for s in states:
self.ignore[s] = t
if '\\' in t:
self.log.warning("%s contains a literal backslash '\\'", f)
elif tokname == 'error':
self.log.error("Rule '%s' must be defined as a function", f)
self.error = True
else:
for s in states:
self.strsym[s].append((f, t))
else:
self.log.error('%s not defined as a function or string', f)
self.error = True
# Sort the functions by line number
for f in self.funcsym.values():
f.sort(key=lambda x: x[1].__code__.co_firstlineno)
# Sort the strings by regular expression length
for s in self.strsym.values():
s.sort(key=lambda x: len(x[1]), reverse=True)
##############################################
def validate_rules(self):
""" Validate all of the t_rules collected"""
for state in self.stateinfo:
# Validate all rules defined by functions
for fname, f in self.funcsym[state]:
line = f.__code__.co_firstlineno
file = f.__code__.co_filename
module = inspect.getmodule(f)
self.modules.add(module)
tokname = self.toknames[fname]
if isinstance(f, types.MethodType):
reqargs = 2
else:
reqargs = 1
nargs = f.__code__.co_argcount
if nargs > reqargs:
self.log.error("%s:%d: Rule '%s' has too many arguments", file, line, f.__name__)
self.error = True
continue
if nargs < reqargs:
self.log.error("%s:%d: Rule '%s' requires an argument", file, line, f.__name__)
self.error = True
continue
if not _get_regex(f):
self.log.error("%s:%d: No regular expression defined for rule '%s'", file, line, f.__name__)
self.error = True
continue
try:
c = re.compile('(?P<%s>%s)' % (fname, _get_regex(f)), self.reflags)
if c.match(''):
self.log.error("%s:%d: Regular expression for rule '%s' matches empty string", file, line, f.__name__)
self.error = True
except re.error as e:
self.log.error("%s:%d: Invalid regular expression for rule '%s'. %s", file, line, f.__name__, e)
if '#' in _get_regex(f):
self.log.error("%s:%d. Make sure '#' in rule '%s' is escaped with '\\#'", file, line, f.__name__)
self.error = True
# Validate all rules defined by strings
for name, r in self.strsym[state]:
tokname = self.toknames[name]
if tokname == 'error':
self.log.error("Rule '%s' must be defined as a function", name)
self.error = True
continue
if tokname not in self.tokens and tokname.find('ignore_') < 0:
self.log.error("Rule '%s' defined for an unspecified token %s", name, tokname)
self.error = True
continue
try:
c = re.compile('(?P<%s>%s)' % (name, r), self.reflags)
if (c.match('')):
self.log.error("Regular expression for rule '%s' matches empty string", name)
self.error = True
except re.error as e:
self.log.error("Invalid regular expression for rule '%s'. %s", name, e)
if '#' in r:
self.log.error("Make sure '#' in rule '%s' is escaped with '\\#'", name)
self.error = True
if not self.funcsym[state] and not self.strsym[state]:
self.log.error("No rules defined for state '%s'", state)
self.error = True
# Validate the error function
efunc = self.errorf.get(state, None)
if efunc:
f = efunc
line = f.__code__.co_firstlineno
file = f.__code__.co_filename
module = inspect.getmodule(f)
self.modules.add(module)
if isinstance(f, types.MethodType):
reqargs = 2
else:
reqargs = 1
nargs = f.__code__.co_argcount
if nargs > reqargs:
self.log.error("%s:%d: Rule '%s' has too many arguments", file, line, f.__name__)
self.error = True
if nargs < reqargs:
self.log.error("%s:%d: Rule '%s' requires an argument", file, line, f.__name__)
self.error = True
for module in self.modules:
self.validate_module(module)
##############################################
def validate_module(self, module):
"""" This checks to see if there are duplicated t_rulename() functions or strings in the parser
input file. This is done using a simple regular expression match on each line in the
source code of the given module.
"""
try:
lines, linen = inspect.getsourcelines(module)
except IOError:
return
fre = re.compile(r'\s*def\s+(t_[a-zA-Z_0-9]*)\(')
sre = re.compile(r'\s*(t_[a-zA-Z_0-9]*)\s*=')
counthash = {}
linen += 1
for line in lines:
m = fre.match(line)
if not m:
m = sre.match(line)
if m:
name = m.group(1)
prev = counthash.get(name)
if not prev:
counthash[name] = linen
else:
filename = inspect.getsourcefile(module)
self.log.error('%s:%d: Rule %s redefined. Previously defined on line %d', filename, linen, name, prev)
self.error = True
linen += 1
####################################################################################################
#
# lex(module)
#
# Build all of the regular expression rules from definitions in the supplied module
def lex(module=None,
object=None,
debug=False,
optimize=False,
lextab='lextab',
reflags=int(re.VERBOSE),
nowarn=False,
outputdir=None,
debuglog=None,
errorlog=None,
):
if lextab is None:
lextab = 'lextab'
global lexer
ldict = None
stateinfo = {'INITIAL': 'inclusive'}
lexobj = Lexer()
lexobj.lexoptimize = optimize
global token, input
if errorlog is None:
errorlog = PlyLogger(sys.stderr)
if debug:
if debuglog is None:
debuglog = PlyLogger(sys.stderr)
# Get the module dictionary used for the lexer
if object:
module = object
# Get the module dictionary used for the parser
if module:
_items = [(k, getattr(module, k)) for k in dir(module)]
ldict = dict(_items)
# If no __file__ attribute is available, try to obtain it from the __module__ instead
if '__file__' not in ldict:
ldict['__file__'] = sys.modules[ldict['__module__']].__file__
else:
ldict = get_caller_module_dict(2)
# Determine if the module is package of a package or not.
# If so, fix the tabmodule setting so that tables load correctly
pkg = ldict.get('__package__')
if pkg and isinstance(lextab, str):
if '.' not in lextab:
lextab = pkg + '.' + lextab
# Collect parser information from the dictionary
linfo = LexerReflect(ldict, log=errorlog, reflags=reflags)
linfo.get_all()
if not optimize:
if linfo.validate_all():
raise SyntaxError("Can't build lexer")
if optimize and lextab:
try:
lexobj.readtab(lextab, ldict)
token = lexobj.token
input = lexobj.input
lexer = lexobj
return lexobj
except ImportError:
pass
# Dump some basic debugging information
if debug:
debuglog.info('lex: tokens = %r', linfo.tokens)
debuglog.info('lex: literals = %r', linfo.literals)
debuglog.info('lex: states = %r', linfo.stateinfo)
# Build a dictionary of valid token names
lexobj.lextokens = set()
for n in linfo.tokens:
lexobj.lextokens.add(n)
# Get literals specification
if isinstance(linfo.literals, (list, tuple)):
lexobj.lexliterals = type(linfo.literals[0])().join(linfo.literals)
else:
lexobj.lexliterals = linfo.literals
lexobj.lextokens_all = lexobj.lextokens | set(lexobj.lexliterals)
# Get the stateinfo dictionary
stateinfo = linfo.stateinfo
regexs = {}
# Build the master regular expressions
for state in stateinfo:
regex_list = []
# Add rules defined by functions first
for fname, f in linfo.funcsym[state]:
regex_list.append('(?P<%s>%s)' % (fname, _get_regex(f)))
if debug:
debuglog.info("lex: Adding rule %s -> '%s' (state '%s')", fname, _get_regex(f), state)
# Now add all of the simple rules
for name, r in linfo.strsym[state]:
regex_list.append('(?P<%s>%s)' % (name, r))
if debug:
debuglog.info("lex: Adding rule %s -> '%s' (state '%s')", name, r, state)
regexs[state] = regex_list
# Build the master regular expressions
if debug:
debuglog.info('lex: ==== MASTER REGEXS FOLLOW ====')
for state in regexs:
lexre, re_text, re_names = _form_master_re(regexs[state], reflags, ldict, linfo.toknames)
lexobj.lexstatere[state] = lexre
lexobj.lexstateretext[state] = re_text
lexobj.lexstaterenames[state] = re_names
if debug:
for i, text in enumerate(re_text):
debuglog.info("lex: state '%s' : regex[%d] = '%s'", state, i, text)
# For inclusive states, we need to add the regular expressions from the INITIAL state
for state, stype in stateinfo.items():
if state != 'INITIAL' and stype == 'inclusive':
lexobj.lexstatere[state].extend(lexobj.lexstatere['INITIAL'])
lexobj.lexstateretext[state].extend(lexobj.lexstateretext['INITIAL'])
lexobj.lexstaterenames[state].extend(lexobj.lexstaterenames['INITIAL'])
lexobj.lexstateinfo = stateinfo
lexobj.lexre = lexobj.lexstatere['INITIAL']
lexobj.lexretext = lexobj.lexstateretext['INITIAL']
lexobj.lexreflags = reflags
# Set up ignore variables
lexobj.lexstateignore = linfo.ignore
lexobj.lexignore = lexobj.lexstateignore.get('INITIAL', '')
# Set up error functions
lexobj.lexstateerrorf = linfo.errorf
lexobj.lexerrorf = linfo.errorf.get('INITIAL', None)
if not lexobj.lexerrorf:
errorlog.warning('No t_error rule is defined')
# Set up eof functions
lexobj.lexstateeoff = linfo.eoff
lexobj.lexeoff = linfo.eoff.get('INITIAL', None)
# Check state information for ignore and error rules
for s, stype in stateinfo.items():
if stype == 'exclusive':
if s not in linfo.errorf:
errorlog.warning("No error rule is defined for exclusive state '%s'", s)
if s not in linfo.ignore and lexobj.lexignore:
errorlog.warning("No ignore rule is defined for exclusive state '%s'", s)
elif stype == 'inclusive':
if s not in linfo.errorf:
linfo.errorf[s] = linfo.errorf.get('INITIAL', None)
if s not in linfo.ignore:
linfo.ignore[s] = linfo.ignore.get('INITIAL', '')
# Create global versions of the token() and input() functions
token = lexobj.token
input = lexobj.input
lexer = lexobj
# If in optimize mode, we write the lextab
if lextab and optimize:
if outputdir is None:
# If no output directory is set, the location of the output files
# is determined according to the following rules:
# - If lextab specifies a package, files go into that package directory
# - Otherwise, files go in the same directory as the specifying module
if isinstance(lextab, types.ModuleType):
srcfile = lextab.__file__
else:
if '.' not in lextab:
srcfile = ldict['__file__']
else:
parts = lextab.split('.')
pkgname = '.'.join(parts[:-1])
exec('import %s' % pkgname)
srcfile = getattr(sys.modules[pkgname], '__file__', '')
outputdir = os.path.dirname(srcfile)
try:
lexobj.writetab(lextab, outputdir)
if lextab in sys.modules:
del sys.modules[lextab]
except IOError as e:
errorlog.warning("Couldn't write lextab module %r. %s" % (lextab, e))
return lexobj
####################################################################################################
#
# runmain()
#
# This runs the lexer as a main program
# def runmain(lexer=None, data=None):
# if not data:
# try:
# filename = sys.argv[1]
# f = open(filename)
# data = f.read()
# f.close()
# except IndexError:
# sys.stdout.write('Reading from standard input (type EOF to end):\n')
# data = sys.stdin.read()
# if lexer:
# _input = lexer.input
# else:
# _input = input
# _input(data)
# if lexer:
# _token = lexer.token
# else:
# _token = token
# while True:
# tok = _token()
# if not tok:
# break
# sys.stdout.write('(%s,%r,%d,%d)\n' % (tok.type, tok.value, tok.lineno, tok.lexpos))
####################################################################################################
#
# @TOKEN(regex)
#
# This decorator function can be used to set the regex expression on a function
# when its docstring might need to be set in an alternative way
def TOKEN(r):
def set_regex(f):
if hasattr(r, '__call__'):
f.regex = _get_regex(r)
else:
f.regex = r
return f
return set_regex
# Alternative spelling of the TOKEN decorator
Token = TOKEN
__init__.py 0000664 0000000 0000000 00000000000 13410044113 0034744 0 ustar 00root root 0000000 0000000 pythonic-gcode-machine-ec1f3fc5460500f0a6778b489106cc5255de741b-PythonicGcodeMachine/PythonicGcodeMachine