123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195(* Claude Code
*
* Copyright (C) 2026 Yoann Padioleau
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* (LGPL) as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*)(* See Scratch_blocks.mli *)typecategory=Motion|Looks|Events|Control|Sensing|Operators|Variables|Pen|Lists|Othertypeshape=Hat|Stack|Cap|C_block|C_cap|Reporter|Predicate|Ring|Command_ringtypepart=Wordofstring|Numofstring|Textofstring|Menuofstring|Bool|Lambdaofstringtypespec={op:string;category:category;shape:shape;lines:partlistlist}typearg=Litofstring|Blockofblockandblock={op:string;args:arglist;mouths:blocklistlist}typescript={x:float;y:float;blocks:blocklist}letcategories=[Motion;Looks;Events;Control;Sensing;Operators;Variables;Pen](* Snap!'s palette: the events with the control blocks, the lists and
the custom blocks (Other) added *)letsnap_categories=[Motion;Looks;Control;Sensing;Operators;Variables;Pen;Lists;Other]letcategory_name=function|Motion->"Motion"|Looks->"Looks"|Events->"Events"|Control->"Control"|Sensing->"Sensing"|Operators->"Operators"|Variables->"Variables"|Pen->"Pen"|Lists->"Lists"|Other->"Other"(* "move %n steps" with ["10"]: the words, and the slots taking the
defaults in turn; | between the lines of a C block *)letdefineopcategoryshapetemplatedefaults:spec=letdefaults=refdefaultsinletnext()=match!defaultswithd::rest->defaults:=rest;d|[]->""inletpartw=matchwwith|"%n"->Num(next())|"%s"->Text(next())|"%m"->Menu(next())|"%b"->Bool|"%r"->Lambda"snap_reifyreporter"|"%p"->Lambda"snap_reifypredicate"|"%c"->Lambda"snap_reifyscript"|w->Wordwinletlinel=List.mappart(List.filter((<>)"")(String.split_on_char' 'l))in{op;category;shape;lines=List.mapline(String.split_on_char'|'template)}letspecs=[define"motion_movesteps"MotionStack"move %n steps"["10"];define"motion_turnright"MotionStack"turn right %n degrees"["15"];define"motion_turnleft"MotionStack"turn left %n degrees"["15"];define"motion_pointindirection"MotionStack"point in direction %n"["90"];define"motion_pointtowards"MotionStack"point towards %m"["mouse-pointer"];define"motion_gotoxy"MotionStack"go to x: %n y: %n"["0";"0"];define"motion_glidesecstoxy"MotionStack"glide %n secs to x: %n y: %n"["1";"0";"0"];define"motion_changexby"MotionStack"change x by %n"["10"];define"motion_setx"MotionStack"set x to %n"["0"];define"motion_changeyby"MotionStack"change y by %n"["10"];define"motion_sety"MotionStack"set y to %n"["0"];define"motion_ifonedgebounce"MotionStack"if on edge, bounce"[];define"motion_setrotationstyle"MotionStack"set rotation style %m"["left-right"];define"motion_xposition"MotionReporter"x position"[];define"motion_yposition"MotionReporter"y position"[];define"motion_direction"MotionReporter"direction"[];define"looks_sayforsecs"LooksStack"say %s for %n seconds"["Hello!";"2"];define"looks_say"LooksStack"say %s"["Hello!"];define"looks_switchcostumeto"LooksStack"switch costume to %m"["1"];define"looks_nextcostume"LooksStack"next costume"[];define"looks_changesizeby"LooksStack"change size by %n"["10"];define"looks_setsizeto"LooksStack"set size to %n %"["100"];define"looks_show"LooksStack"show"[];define"looks_hide"LooksStack"hide"[];define"looks_size"LooksReporter"size"[];define"event_whenflagclicked"EventsHat"when flag clicked"[];define"event_whenkeypressed"EventsHat"when %m key pressed"["space"];define"event_whenthisspriteclicked"EventsHat"when this sprite clicked"[];define"event_whenbroadcastreceived"EventsHat"when I receive %m"["message1"];define"event_broadcast"EventsStack"broadcast %m"["message1"];define"control_wait"ControlStack"wait %n seconds"["1"];define"control_repeat"ControlC_block"repeat %n"["10"];define"control_forever"ControlC_cap"forever"[];define"control_if"ControlC_block"if %b then"[];define"control_if_else"ControlC_block"if %b then|else"[];define"control_wait_until"ControlStack"wait until %b"[];define"control_repeat_until"ControlC_block"repeat until %b"[];define"control_stop"ControlCap"stop %m"["all"];define"sensing_touchingobject"SensingPredicate"touching %m ?"["edge"];define"sensing_keypressed"SensingPredicate"key %m pressed?"["space"];define"sensing_mousedown"SensingPredicate"mouse down?"[];define"sensing_mousex"SensingReporter"mouse x"[];define"sensing_mousey"SensingReporter"mouse y"[];define"sensing_timer"SensingReporter"timer"[];define"sensing_resettimer"SensingStack"reset timer"[];define"operator_add"OperatorsReporter"%n + %n"[];define"operator_subtract"OperatorsReporter"%n - %n"[];define"operator_multiply"OperatorsReporter"%n * %n"[];define"operator_divide"OperatorsReporter"%n / %n"[];define"operator_random"OperatorsReporter"pick random %n to %n"["1";"10"];define"operator_lt"OperatorsPredicate"%s < %s"[];define"operator_equals"OperatorsPredicate"%s = %s"[];define"operator_gt"OperatorsPredicate"%s > %s"[];define"operator_and"OperatorsPredicate"%b and %b"[];define"operator_or"OperatorsPredicate"%b or %b"[];define"operator_not"OperatorsPredicate"not %b"[];define"operator_join"OperatorsReporter"join %s %s"["hello ";"world"];define"operator_mod"OperatorsReporter"%n mod %n"[];define"operator_round"OperatorsReporter"round %n"[];define"data_setvariableto"VariablesStack"set %m to %s"["score";"0"];define"data_changevariableby"VariablesStack"change %m by %n"["score";"1"];define"data_variable"VariablesReporter"%m"["score"];define"pen_clear"PenStack"clear"[];define"pen_stamp"PenStack"stamp"[];define"pen_pendown"PenStack"pen down"[];define"pen_penup"PenStack"pen up"[];define"pen_setpencolorto"PenStack"set pen color to %n"["0"];define"pen_changepencolorby"PenStack"change pen color by %n"["10"];define"pen_setpensizeto"PenStack"set pen size to %n"["1"];define"pen_changepensizeby"PenStack"change pen size by %n"["1"];](*****************************************************************************)(* Snap!'s *)(*****************************************************************************)letsnap_specs=[define"procedures_definition"OtherHat"define %m %s"["command";"my block %input"];define"procedures_report"ControlCap"report %s"[];define"snap_scriptvariables"VariablesStack"script variables %m"["a"];define"snap_reifyreporter"OperatorsRing"{ %s }"[];define"snap_reifypredicate"OperatorsRing"{ %b }"[];define"snap_reifyscript"OperatorsCommand_ring"{ }"[];define"snap_call"ControlReporter"call %r"[];define"snap_callwith"ControlReporter"call %r with inputs %s"[];define"snap_run"ControlStack"run %c"[];define"snap_runwith"ControlStack"run %c with inputs %s"[];define"snap_list"ListsReporter"list %s %s %s"[];define"snap_numbers"ListsReporter"numbers from %n to %n"["1";"10"];define"snap_item"ListsReporter"item %n of %s"["1"];define"snap_length"ListsReporter"length of %s"[];define"snap_cons"ListsReporter"%s in front of %s"[];define"snap_cdr"ListsReporter"all but first of %s"[];define"snap_isempty"ListsPredicate"is %s empty?"[];define"snap_contains"ListsPredicate"%s contains %s"["";"thing"];define"snap_add"ListsStack"add %s to %s"["thing"];define"snap_delete"ListsStack"delete %n of %s"["1"];define"snap_replace"ListsStack"replace item %n of %s with %s"["1";"";"thing"];define"snap_map"ListsReporter"map %r over %s"[];define"snap_keep"ListsReporter"keep items %p from %s"[];define"snap_combine"ListsReporter"combine %s using %r"[];](* a custom block's op: "custom:reporter:factorial %s" -- its kind and
its template, each parameter's name made a slot *)letis_paramw=String.lengthw>1&&w.[0]='%'letparamstemplate=List.filter_map(funw->ifis_paramwthenSome(String.subw1(String.lengthw-1))elseNone)(String.split_on_char' 'template)letcustom_opkindtemplate="custom:"^kind^":"^String.concat" "(List.map(funw->ifis_paramwthen"%s"elsew)(List.filter((<>)"")(String.split_on_char' 'template)))letcustom_specop=matchString.split_on_char':'opwith|"custom"::kind::rest->letshape=matchkindwith"reporter"->Reporter|"predicate"->Predicate|_->StackinSome(defineopOthershape(String.concat":"rest)[])|_->Noneletspecop=matchList.find_opt(fun(s:spec)->s.op=op)specswith|Somes->s|None->(matchList.find_opt(fun(s:spec)->s.op=op)snap_specswith|Somes->s|None->(matchcustom_specopwithSomes->s|None->raiseNot_found))letslots(s:spec)=List.filter(functionWord_->false|_->true)(List.concats.lines)letrecmakeop=lets=specopinletarg=functionNumd|Textd|Menud->Litd|Lambdaring->Block(makering)|Bool|Word_->Lit""inletmouths=matchs.shapewithC_block|C_cap|Command_ring->List.map(fun_->[])s.lines|_->[]in{op;args=List.maparg(slotss);mouths}letvariablename={op="data_variable";args=[Litname];mouths=[]}