---
title: Tutorial 29 Converting DSL → Pure G-code
description: Building a compiler for your language.
---
Tutorial 29 Converting DSL → Pure G-code
1. Lesson Header
- Lesson Number: 29
- Level: Advanced
- Title: Converting DSL → Pure G-code
- Estimated Duration: 50 Minutes
- Prerequisites: Lesson 28 (DSL Design)
- What You Will Build: A Parser for a text-based "Shape Language".
2. Concept Introduction
The Compiler Pipeline.
In Lesson 28, we used Python functions. Now we want to read a text file written in our own language and convert it.
Input: my_shape.txt
START
SPEED 3000
SQUARE 10
CIRCLE 20
END
Output: my_shape.gcode
G28
G1 F3000
G1 X10...
This requires Parsing (reading text) and Code Generation (writing G-code).
3. Machine State Explanation
The Tokenizer.
We need to split the input string into "Tokens"."SQUARE 10" -> ["SQUARE", "10"].
Then we switch based on the first token (Command) and use the second (Argument).
4. Command Breakdown
- Python
split(): Break lines into words. - Python
strip(): Remove whitespace. - Switch/Match: Dispatch commands.
5. Minimal Working Example
The Interpreter Loop.
lines = ["SPEED 1000", "MOVE 10"]
for line in lines:
parts = line.split()
cmd = parts[0]
arg = int(parts[1])
if cmd == "SPEED":
print(f"G1 F{arg}")
elif cmd == "MOVE":
print(f"G1 X{arg}")
6. Visual Representation
Interactive preview is available in the interactive reader.
7. Build Exercise
Task: Build a Parser for ShapeLang.
Supported Commands:
- HOME: G28.
- SPEED [F]: Set Feedrate.
- LINE [X] [Y]: Draw line to X,Y.
- RECT [W] [H]: Draw rectangle of size WxH at current position.
Implementation:
Read input.txt.
Parse line by line.
Call the GcodeTurtle methods from Lesson 28 to execute logic.
8. Deep Insight Section
Error Handling.
What if the user writes RECT (no args)?
Your parser must check len(parts) before accessing parts[1].
Good compilers give helpful error messages:"Error on line 5: RECT command requires 2 arguments."
9. Common Failure Modes
- Type Conversion: Reading "10.5" as
int()crashes. Usefloat(). - Case Sensitivity:
rectvsRECT. Standardize with.upper().
10. Real-World Application
G-code Flavors.
Slicers often have "Post-Processing Scripts" that parse the G-code they just generated to modify it (e.g., "Find all M104 and add 5 degrees").
This is essentially parsing a DSL (G-code itself) and transpiling it.
11. Final Clean Version
The Compiler Script:
import sys
# Reuse our Turtle logic (simplified)
class Compiler:
def __init__(self):
self.output = []
self.x = 0
self.y = 0
def emit(self, gcode):
self.output.append(gcode)
def parse(self, filename):
with open(filename, 'r') as f:
lines = f.readlines()
for i, line in enumerate(lines):
line = line.strip()
if not line or line.startswith("#"): continue
parts = line.split()
cmd = parts[0].upper()
try:
if cmd == "HOME":
self.emit("G28")
self.x, self.y = 0, 0
elif cmd == "SPEED":
speed = int(parts[1])
self.emit(f"G1 F{speed}")
elif cmd == "LINE":
target_x = float(parts[1])
target_y = float(parts[2])
self.emit(f"G1 X{target_x} Y{target_y} E...") # Add extrusion logic
self.x, self.y = target_x, target_y
elif cmd == "RECT":
w = float(parts[1])
h = float(parts[2])
# Draw rect relative to current
self.emit(f"G1 X{self.x + w} Y{self.y}")
self.emit(f"G1 X{self.x + w} Y{self.y + h}")
self.emit(f"G1 X{self.x} Y{self.y + h}")
self.emit(f"G1 X{self.x} Y{self.y}") # Close loop
else:
print(f"Unknown command line {i}: {cmd}")
except IndexError:
print(f"Syntax Error line {i}: Missing arguments")
except ValueError:
print(f"Syntax Error line {i}: Invalid number format")
def save(self, output_file):
with open(output_file, 'w') as f:
f.write("G21\nG90\n")
f.write("\n".join(self.output))
# Usage
c = Compiler()
c.parse("input.txt")
c.save("output.gcode")
12. Stretch Challenge
Challenge: Add Variables to your language.SET width 10RECT $width 20
You need a dictionary variables = {}.
When parsing args, check if it starts with $. If so, look it up.