File size: 24,291 Bytes
65da30d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
# rmscript - Kid-Friendly Robot Programming

rmscript is a natural language-inspired programming language designed to make robot programming accessible and fun for children. It compiles to Python code that controls the Reachy Mini robot.

## Example Usage

Create a file `hello.rmscript`:

```rmscript
DESCRIPTION Wave hello to someone
antenna up
wait 1s
antenna down
look left
look right
look center
```

Test the script using the provided runner (after starting the reachy-mini-daemon):

```
python src/reachy_mini_conversation_app/rmscript/run_rmscript.py path/to/hello.rmscript
```


## Language Syntax

### File Structure

Every rmscript file has a simple structure:

```rmscript
DESCRIPTION Wave hello to someone
# Your commands here
look left
wait 1s
```

**Key features:**
- **Tool name**: Automatically derived from the filename (e.g., `wave_hello.rmscript` → tool name is `wave_hello`)
- **DESCRIPTION** (optional): One-line description used for LLM tool registration

### Basic Commands

```rmscript
# Comments start with #

# Movement commands
look left
turn right
antenna up
head forward 10

# Wait command
wait 2s
wait 0.5s

# Camera command
picture

# Sound playback
play mysound
play othersound pause
```

### Case Insensitivity

rmscript is case-insensitive for keywords:

```rmscript
LOOK left    # Same as "look left"
Look Left    # Same as "look left"
WAIT 1s      # Same as "wait 1s"
```

## Movement Commands

### Look (Head Orientation)

Control the robot's head orientation (pitch and yaw):

```rmscript
look left          # Turn head left (30° default)
look right 45      # Turn head right 45°
look up           # Tilt head up (30° default)
look down 20      # Tilt head down 20°
look center       # Return to center position

# Synonyms
look straight     # Same as center
look neutral      # Same as center
```

**Physical Limits:**
- Pitch (up/down): ±40°
- Yaw (left/right): ±180° absolute, ±65° relative to body

### Turn (Body Rotation)

Rotate the robot's body (the head rotates together with the body):

```rmscript
turn left         # Rotate body and head left (30° default)
turn right 90     # Rotate body and head right 90°
turn center       # Face forward
```

**Note:** The `turn` command rotates both the body yaw and the head yaw together, since the body carries the head.

**Physical Limits:**
- Body yaw: ±160° (safe range)

### Antenna

Control the antenna positions using multiple syntaxes:

**Clock Position (Numeric 0-12):**
```rmscript
antenna both 0       # 0 o'clock = 0° (straight up)
antenna both 3       # 3 o'clock = 90° (external/right)
antenna both 6       # 6 o'clock = 180° (straight down)
antenna both 9       # 9 o'clock = -90° (internal/left)
antenna left 4.5     # Left antenna to 4.5 o'clock (135°)
```

**Clock Keywords:**
```rmscript
antenna both high    # 0° (high position)
antenna both ext     # 90° (external)
antenna both low     # 180° (low position)
antenna both int     # -90° (internal)
```

**Directional Keywords (Natural Language):**
```rmscript
antenna both up      # 0° (up)
antenna both right   # 90° (right/external)
antenna both down    # 180° (down)
antenna both left    # -90° (left/internal)

# Individual antenna control
antenna left up      # Left antenna pointing up
antenna right down   # Right antenna pointing down

# Even in potentially confusing cases, it works naturally:
antenna left left    # Left antenna pointing left (-90°)
antenna right right  # Right antenna pointing right (90°)
```

**Physical Limits:**
- Antenna angle: ±180° (safe recommended: ±120°)

### Head Translation

Move the head forward/back/left/right/up/down in space:

```rmscript
head forward 10    # Move head forward 10mm
head back 5        # Move head back 5mm
head backward 5    # Same as "back" (synonym)
head backwards 5   # Same as "back" (synonym)
head left 8        # Move head left 8mm
head right 8       # Move head right 8mm
head up 5          # Move head up 5mm
head down 3        # Move head down 3mm
```

**Physical Limits:**
- X (forward/back): ±50mm typical
- Y (left/right): ±50mm typical
- Z (up/down): +20mm / -30mm typical

### Tilt (Head Roll)

Tilt the head side-to-side:

```rmscript
tilt left 15       # Tilt head left
tilt right 15      # Tilt head right
tilt center        # Return to level
```

**Physical Limits:**
- Roll: ±40°

### Wait

Pause between movements:

```rmscript
wait 1s           # Wait 1 second
wait 0.5s         # Wait 0.5 seconds
wait 2.5s         # Wait 2.5 seconds
```

**Important:** The `s` suffix is **required** for consistency. `wait 1` will produce a compilation error.

### Picture

Take a picture with the robot's camera and add it to the conversation:

```rmscript
picture           # Take a picture
```

The picture command captures a frame from the camera and returns it as a base64-encoded image.

**How it works:**

1. **During execution**: The `picture` command is queued as a movement (instant duration: 0.01s)
2. **Capture timing**: Picture is captured at the right moment in the movement sequence
3. **File storage**: Pictures are automatically saved to `/tmp/reachy_picture_YYYYMMDD_HHMMSS_microseconds.jpg`
4. **Color handling**: Images are captured in RGB format with proper color conversion
5. **LLM integration**: When called by the LLM, pictures are automatically added to the conversation

**Single picture (LLM-compatible):**
```rmscript
DESCRIPTION Check behind by taking a picture
turn left maximum
wait 0.5s
picture
wait 0.5s
turn center
```

This script:
- Turns the robot to look behind (120° left)
- Waits for movement to stabilize
- Captures a picture at the right moment
- Returns to center position
- The LLM receives the picture and can describe what it sees

**Important timing notes:**
- Add `wait` commands before `picture` to let movements complete
- The tool automatically waits for all movements + picture capture before returning
- Pictures are captured when the movement queue reaches them (not immediately)

**Multiple pictures:**
```rmscript
DESCRIPTION Look around and take pictures
look left
wait 0.5s
picture
look right
wait 0.5s
picture
look center
```

Each `picture` command captures a separate image. For single-picture scripts, the image is returned in Camera-compatible format (`b64_im`) for LLM integration. For multi-picture scripts, images are returned as an array.

**Use with `run_rmscript.py`:**

When testing scripts with `run_rmscript.py`, pictures are:
- Saved to `/tmp` with timestamped filenames
- Logged with full paths for easy access
- Displayed in the execution summary

**Use with LLM conversation:**

When called as a tool by the LLM:
- Single picture: Automatically fed to the LLM conversation (like Camera tool)
- Multiple pictures: Available as base64 data (future enhancement for multi-image display)
- Gradio UI: Picture thumbnails displayed in the conversation interface

**Example conversation:**
```
User: "Can you check what's behind you?"
→ LLM calls: check_behind()
→ Robot: turns, captures picture, returns to center
→ LLM sees the picture and responds: "I can see a desk with a laptop and some books!"
```

### Play Sound

Play sound files (.wav, .mp3, .ogg, .flac) during script execution:

```rmscript
play soundname         # Play sound in background (async, continues immediately)
play soundname pause   # Wait for sound to finish before continuing (blocking)
play soundname 5s      # Play sound for exactly 5 seconds (blocking)
```

The script automatically searches for sound files in this order:
1. **Directory containing the .rmscript file** (highest priority - co-located sounds)
2. Current working directory
3. `sounds/` subdirectory

**Asynchronous playback (default):**
```rmscript
play intro            # Starts playing, script continues immediately
look left
wait 1s
```
The sound plays in the background while movements execute.

**Blocking playback (wait for sound to finish):**
```rmscript
play mysound pause    # Waits for sound to finish
# OR use synonyms:
play mysound fully
play mysound wait
play mysound block
play mysound complete
```

The script pauses until the sound finishes playing, then continues.

**Duration-limited playback:**
```rmscript
play mysound 10s      # Play sound for exactly 10 seconds (blocks for 10s)
```

Plays the sound for the specified duration. If the sound is shorter, it will finish early. If longer, it will be cut off.

### Loop Sound

Loop a sound file repeatedly for a specified duration:

```rmscript
loop soundname        # Loop sound for 10 seconds (default)
loop soundname 5s     # Loop sound for 5 seconds
loop soundname 30s    # Loop sound for 30 seconds
```

The loop command automatically repeats the sound until the duration expires. This is useful for background music or ambient sounds.

**Example use case:**
```rmscript
DESCRIPTION Greet with sound and movement
play hello pause      # Play greeting sound fully
wait 0.5s
antenna both up       # Wave antennas
wait 1s
antenna both down
```

**Sound files:**
- File extensions: `.wav`, `.mp3`, `.ogg`, `.flac`
- Example: `play hello` looks for `hello.wav`, `hello.mp3`, etc.
- **Best practice**: Place sound files in the same directory as your .rmscript file
- Alternative locations: working directory or `sounds/` subdirectory

## Advanced Features

### Keyword Reuse with `and`

Chain multiple directions with the same action keyword:

```rmscript
# Reuse "look" keyword
look left and up 25
# → Equivalent to: look left + look up 25
# → Merged into single command

# Different keywords - no reuse
turn left and look right
# → turn left + look right
# → Two separate commands merged
```

This creates more natural, flowing descriptions while optimizing execution.

### Qualitative Strength

Use descriptive words instead of numbers - works for both angles and distances:

```rmscript
# 5 levels of strength - values are context-aware!

# Examples:
turn left tiny          # 10° (body yaw can handle larger movements)
look up tiny            # 5° (head pitch limited by cone constraint)
head forward tiny       # 2mm

turn left maximum       # 120° (body yaw safe maximum)
look up maximum         # 38° (respects ±40° pitch limit)
look left maximum       # 60° (respects ±65° body-head differential)
head forward maximum    # 28mm (under 30mm limit)
```

**Available Qualitative Keywords:**
- **VERY_SMALL**: minuscule, mini, verysmall, tiny
- **SMALL**: little, slightly, small, alittle
- **MEDIUM**: medium, normal, regular, standard, normally
- **LARGE**: lot, big, large, very, alot, huge, strong, strongly
- **VERY_LARGE**: verybig, enormous, verylarge, maximum

**Context-Aware Values:**

The same qualitative keyword maps to different values depending on the movement type, respecting physical limits:

| Keyword | VERY_SMALL | SMALL | MEDIUM | LARGE | VERY_LARGE |
|---------|------------|-------|--------|-------|------------|
| **Body Yaw (turn)** | 10° | 30° | 60° | 90° | 120° |
| **Head Pitch/Roll (look up/down, tilt)** | 5° | 10° | 20° | 30° | 38° |
| **Head Yaw (look left/right)** | 5° | 15° | 30° | 45° | 60° |
| **Head Translation (head forward/back/etc)** | 2mm | 5mm | 10mm | 20mm | 28mm |
| **Antennas** | 10° | 30° | 60° | 90° | 110° |

**Note:** These values are carefully chosen to stay within the robot's physical limits while maximizing safe range of motion.

### Duration Keywords

Use descriptive speed words:

```rmscript
look left superfast    # 0.2 seconds
look right fast        # 0.5 seconds
look up slow           # 2.0 seconds
look up slowly         # 2.0 seconds (synonym for slow)
look down superslow    # 3.0 seconds
```

Combine with 'and':

```rmscript
turn left and look right fast
# Both movements complete in 0.5 seconds
```

### Repeat Blocks

Repeat a sequence of commands:

```rmscript
repeat 3
    look left
    wait 0.5s
    look right
    wait 0.5s

# Nested repeat blocks work too
repeat 2
    antenna up
    repeat 3
        look left
        look right
    antenna down
```

**Indentation:** Use spaces or tabs consistently (tabs = 4 spaces).

### Combining Movements

Combine multiple movements into a single smooth motion:

```rmscript
# All happen simultaneously
antenna both up and look up 25 and turn left 30
```

This merges into a single `goto_target()` call with all parameters.

**Important:** The `and` keyword can only combine movement commands (turn, look, head, tilt, antenna). You **cannot** combine movements with control commands (picture, play, loop, wait) using `and`. Use separate lines instead:

```rmscript
# ❌ ERROR - Cannot combine movement with picture
look left and picture

# ✓ CORRECT - Use separate lines
look left
wait 0.5s
picture

# ❌ ERROR - Cannot combine movement with play
turn right and play mysound

# ✓ CORRECT - Use separate lines
turn right
play mysound
```

## Compiler Architecture

### Overview

The compiler uses a 5-stage pipeline:

```
Source Code → Lexer → Parser → Semantic Analyzer → Optimizer → Code Generator
```

### Stage 1: Lexer (Tokenization)

Converts source text into tokens with indentation tracking:

```python
from reachy_mini.rmscript.lexer import Lexer

source = "look left\nwait 1s"
lexer = Lexer(source)
tokens = lexer.tokenize()
# → [Token(KEYWORD_LOOK, 'look', L1:C1), Token(DIRECTION, 'left', L1:C6), ...]
```

Features:
- Python-like INDENT/DEDENT tokens
- Case-insensitive keyword matching
- Comment stripping
- Line/column tracking for error messages

### Stage 2: Parser (AST Construction)

Builds an Abstract Syntax Tree from tokens:

```python
from reachy_mini.rmscript.parser import Parser

ast = Parser(tokens).parse()
# → Program(tool_name='...', description='...', statements=[...])
```

Features:
- Keyword reuse detection for `and` chains
- Direction validation per keyword
- Repeat block nesting
- Syntax error reporting

### Stage 3: Semantic Analyzer

Applies defaults, validates ranges, and generates IR:

```python
from reachy_mini.rmscript.semantic import SemanticAnalyzer

ir, errors, warnings = SemanticAnalyzer().analyze(ast)
# → List[Action | WaitAction], List[CompilationError], List[CompilationError]
```

Features:
- Default value resolution (30° for angles, 1s for duration)
- Qualitative → quantitative conversion
- Duration keyword mapping
- Physical limit validation
- Action merging (multiple SingleActions → one Action)

### Stage 4: Optimizer

Optimizes the IR:

```python
from reachy_mini.rmscript.optimizer import Optimizer

optimized_ir = Optimizer().optimize(ir)
```

Features:
- Consecutive wait merging
- No-op action removal

### Stage 5: Code Generator

Creates executable Python functions:

```python
from reachy_mini.rmscript.codegen import CodeGenerator

executable = CodeGenerator().generate(tool_name, description, ir)
# → Callable that executes the behavior
```

Features:
- Direct execution: `executable(mini)`
- Readable code generation: `to_python_code()`
- Proper imports and function signatures

## API Reference

### rmscriptCompiler

Main compiler class.

```python
from reachy_mini.rmscript import rmscriptCompiler

compiler = rmscriptCompiler(log_level="INFO")
tool = compiler.compile(source_code)
```

**Parameters:**
- `log_level` (str): Logging level - "DEBUG", "INFO", "WARNING", "ERROR"

**Returns:**
- `CompiledTool`: Compilation result object

### verify_rmscript

Verification function that checks if rmscript code compiles without generating executable code.

```python
from reachy_mini_conversation_app.rmscript import verify_rmscript

is_valid, errors = verify_rmscript(source_code)
if not is_valid:
    for error in errors:
        print(error)
```

**Parameters:**
- `source_code` (str): rmscript source code to verify

**Returns:**
- `tuple[bool, list[str]]`: Tuple of (is_valid, error_messages)
  - `is_valid`: True if compilation succeeds, False otherwise
  - `error_messages`: List of error and warning messages (empty if valid)

**Example:**

```python
script = """
DESCRIPTION Test script
look left and picture
"""

is_valid, errors = verify_rmscript(script)
# is_valid = False
# errors = ["❌ Line 3: Cannot combine movement with 'picture' using 'and'. Use separate lines instead."]
```

### CompiledTool

Result of compilation.

```python
class CompiledTool:
    name: str                        # Tool name
    description: str                      # Tool description
    executable: Callable             # Function to execute on robot
    success: bool                    # Compilation succeeded?
    errors: List[CompilationError]   # Compilation errors
    warnings: List[CompilationError] # Compilation warnings
    source_code: str                 # Original source
    ir: List[Action | WaitAction]    # Intermediate representation

    def execute(self, mini: ReachyMini) -> None:
        """Execute the compiled behavior on the robot."""

    def to_python_code(self) -> str:
        """Generate readable Python code."""
```

**Usage:**

```python
if tool.success:
    # Execute on robot
    with ReachyMini() as mini:
        tool.execute(mini)

    # Or get Python code
    print(tool.to_python_code())
else:
    # Handle errors
    for error in tool.errors:
        print(f"{error.severity} Line {error.line}: {error.message}")
```

### CompilationError

Error or warning from compilation.

```python
@dataclass
class CompilationError:
    line: int          # Line number (1-indexed)
    column: int        # Column number (1-indexed)
    message: str       # Error/warning message
    severity: str      # "ERROR" or "WARNING"
```

### Action (IR)

Intermediate representation of a movement.

```python
@dataclass
class Action:
    head_pose: Optional[np.ndarray]  # 4x4 transformation matrix
    antennas: Optional[np.ndarray]   # [left, right] angles in radians
    body_yaw: Optional[float]        # Body rotation in radians
    duration: float                  # Movement duration in seconds
    interpolation: str               # "minjerk", "linear", "ease", "cartoon"
```

### WaitAction (IR)

Intermediate representation of a wait/pause.

```python
@dataclass
class WaitAction:
    duration: float  # Wait duration in seconds
```

## Examples

### Example 1: Simple Greeting

```rmscript

DESCRIPTION Greet someone warmly
# Wave with antennas
antenna up
wait 0.5s
antenna down
wait 0.5s

# Nod
look down 20
wait 0.3s
look up 20
wait 0.3s
look center
```

### Example 2: Search Pattern

```rmscript

DESCRIPTION Look around to search for something
# Scan left to right
look left 45
wait 1s
look center
wait 0.5s
look right 45
wait 1s
look center

# Check up and down
look up 30
wait 0.5s
look down 30
wait 0.5s
look center
```

### Example 3: Dance Choreography

```rmscript

DESCRIPTION Perform a fun dance routine
# Opening pose
antenna both up and look up 25
wait 1s

# Main sequence
repeat 2
    # Move left
    turn left 30 and look right 20
    wait 0.5s

    # Move right
    turn right 30 and look left 20
    wait 0.5s

# Ending pose
turn center and look center and antenna down
```

### Example 4: Using Qualitative Strength

```rmscript

DESCRIPTION Wave shyly at someone
# Small movements
look down slightly
antenna left up a little
wait 1s
antenna left down
look center
```

### Example 5: Complex Combination

```rmscript

DESCRIPTION Show excitement when greeting
# Quick movements
repeat 3
    antenna both up superfast
    antenna both down superfast

# Energetic looking around
look left and up fast
wait 0.2s
look right and up fast
wait 0.2s
look center slow
```

## Error Handling

### Compilation Errors

Errors prevent code generation:

```rmscript
# Invalid keyword
jump up
```
**Output:** `❌ Line 1: Unexpected token: 'jump'`

```rmscript
# Invalid direction for keyword
turn up
```
**Output:** `❌ Line 1: Invalid direction 'up' for keyword 'turn'`

```rmscript
# Missing indentation
repeat 3
look left
```
**Output:** `❌ Line 2: Expected indented block after 'repeat'`

### Compilation Warnings

Warnings allow compilation but alert to potential issues:

```rmscript
# Exceeds safe range
turn left 200
```
**Output:** `⚠️  Line 1: Body yaw 200.0° exceeds safe range (±160.0°), will be clamped`

```rmscript
# Exceeds physical limit
look up 50
```
**Output:** `⚠️  Line 1: Head pitch 50.0° exceeds limit (±40.0°), will be clamped`

### Handling Errors in Code

```python
tool = compiler.compile(source)

if not tool.success:
    print("Compilation failed!")
    for error in tool.errors:
        print(f"  ❌ Line {error.line}: {error.message}")
    exit(1)

if tool.warnings:
    print("Compilation succeeded with warnings:")
    for warning in tool.warnings:
        print(f"  ⚠️  Line {warning.line}: {warning.message}")

# Safe to execute
tool.execute(mini)
```

## Tips and Best Practices

### 1. Use Comments

```rmscript
# Good: explain what you're doing
# Opening sequence: get attention
antenna up
wait 1s

# Main behavior: scan the room
look left 45
wait 1s
look right 45
```

### 2. Use Descriptive Tool Names

```python
# Good




# Avoid



```

### 3. Break Complex Behaviors into Steps

```rmscript
# Good: readable steps
antenna up
wait 1s
look left
wait 0.5s
look right
wait 0.5s
look center

# Avoid: too compressed
antenna up and look left and wait 1s and look right
```

### 4. Use Repeat for Patterns

```rmscript
# Good: use repeat
repeat 3
    antenna up
    antenna down

# Avoid: repetitive
antenna up
antenna down
antenna up
antenna down
antenna up
antenna down
```

### 5. Test with Small Values First

```rmscript
# Start conservative
look left 10
turn right 15

# Then increase if needed
look left 45
turn right 90
```

## Physical Safety Limits

rmscript validates all movements against these limits:

| Movement | Limit | Warning Threshold |
|----------|-------|------------------|
| Body yaw (turn) | ±180° | ±160° |
| Head yaw (look left/right) | ±180° absolute | ±65° relative to body |
| Head pitch (look up/down) | ±40° | ±40° |
| Head roll (tilt) | ±40° | ±40° |
| Antenna | ±90° | ±65° |
| Head X (forward/back) | ±50mm typical | - |
| Head Y (left/right) | ±50mm typical | - |
| Head Z (up/down) | +20mm / -30mm typical | - |

Exceeding these limits generates **warnings** but still compiles. The robot's hardware will clamp values to safe ranges.

## Default Values

When values aren't specified:

| Parameter | Default |
|-----------|---------|
| Angle (look, turn, tilt) | 30° |
| Distance (head translation) | 10mm |
| Antenna angle | 45° |
| Duration | 1.0s |

**Qualitative Strength Values:**

| Level | Keywords | Angle | Distance |
|-------|----------|-------|----------|
| VERY_SMALL | tiny, minuscule, mini, verysmall | 5° | 2mm |
| SMALL | little, slightly, small, alittle | 15° | 5mm |
| MEDIUM | medium, normal, regular, standard, normally | 30° | 10mm |
| LARGE | strong, lot, big, large, very, alot, huge, strongly | 45° | 20mm |
| VERY_LARGE | enormous, verybig, verylarge, maximum | 60° | 30mm |

## Troubleshooting

### Issue: "Inconsistent indentation"

**Cause:** Mixing tabs and spaces, or inconsistent indentation levels.

**Solution:** Use only spaces or only tabs (not mixed). Ensure nested blocks are indented consistently.

```rmscript
# Bad: mixed tabs and spaces
repeat 3
    look left      # 4 spaces
→   look right     # 1 tab (shows as →)

# Good: consistent spaces
repeat 3
    look left      # 4 spaces
    look right     # 4 spaces
```

### Issue: "Expected indented block after 'repeat'"

**Cause:** No indentation after `repeat`.

**Solution:** Indent the repeat block body.

```rmscript
# Bad
repeat 3
look left

# Good
repeat 3
    look left
```

### Issue: Generated code has unexpected angles

**Cause:** Sign convention confusion (left/right).

**Solution:** Remember: `left` = positive yaw, `right` = negative yaw in the generated code. `up` = negative pitch, `down` = positive pitch. This matches the robot's actual coordinate system.


## Contributing

To extend rmscript:

1. **Add new keywords**: Update `lexer.py`, `parser.py`, and `semantic.py`
2. **Add new features**: Modify the appropriate compiler stage
3. **Add tests**: Create integration tests in `tests/test_rmscript/`
4. **Update docs**: Keep this README and examples current

## License

Same as Reachy Mini SDK.