I have created a snap to snapblocks convertor, one that takes into account pretty much every single thing a block can have, which results in an almost perfect conversion.
You can get the snapblocks script of any snap script
script variables ((step)) @addInput
set [step V] to (([width V] of [Stage V]) / (200))
forever {
warp {
go to x: (-300) y: (-170)
clear
pen down
for ((i)) = (1) to (200) {
go to x: ((x position) + (step) @delInput @verticalEllipsis @addInput) y: ((item (i) of (microphone [samples V])) − (170))
}
pen up
}
}
This can also get block definitions.
{(+ multiline + ((text ¶)) + :: operators)} :: define
report (text)
You can even get the definition of this block in snapblocks.
{(+ to snapblocks + ((script λ)) + definition? + ((definition? ?)) + :: rgb(3,192,60))} :: define /* Convert any script to snapblocks.
If the definition? tooggle is on, it will convert the definition of the block to snapblocks \(including the block definition hat\). */
warp {
script variables ((split block)) ((ambiguous blocks)) @delInput @addInput
set [ambiguous blocks V] to (list [_ of _] @delInput @addInput)
set [split block V] to (split blocks (script) :: operators) /* This will split the input script by blocks. This is different from the regular \(split \[\] by \[blocks\]\) block, because this one actually tells you how many inputs are in a variadic input.
IMPORTANT NOTE: This uses codification to determine how many slots are in a variadic input, so if your project relies upon codification, don't use this block. */
if <is (item (1 v) of (split block)) a [[list] V] ?> {
script variables ((codes)) @addInput
set [codes V] to (map ((to snapblocks (join Input list: (block)) definition? (definition?) :: rgb(3,192,60)) /* Convert any script to snapblocks.
If the definition? tooggle is on, it will convert the definition of the block to snapblocks \(including the block definition hat\). */ input names: ((block)) @delInput @addInput) over (split block))
if (definition?) {
report (combine (codes) using ((join [] (multiline [
] :: operators) [] @delInput @verticalEllipsis @addInput) @addInput))
} @addInput
report ([[lines] V] of (codes) :: lists)
} @addInput
script variables ((snapblocks parts)) ((snapblocks code)) ((block)) ((input values)) ((slots info)) ((inputs)) ((labels)) ((shape)) ((overrides)) @delInput @addInput
set [snapblocks parts V] to (list @addInput)
set [snapblocks code V] to []
set [overrides V] to (list @addInput)
set [labels V] to (list @addInput)
set [inputs V] to (list @addInput)
set [block V] to (item (1 v) of (split block))
set [input values V] to (all but first of (split block))
set [slots info V] to (list @addInput)
set [shape V] to ([[type] V] of block (block))
if <([[selector] V] of block (block)) = [reportGetVar] @delInput @verticalEllipsis @addInput> {
script variables ((name)) @addInput
set [name V] to (snapblocks escape (item (1 v) of (input values)) :: rgb(3,192,60)) /* Escape snapblocks special characters */
report (join [\(] (name) (if ($blitz find first item (<<([[selector] V] of block (() @addInput)) ≠ [reportGetVar] @delInput @verticalEllipsis @addInput> and <([[type] V] of block (() @addInput)) = [2] @delInput @verticalEllipsis @addInput> and <not ([[custom?] V] of block (() @addInput))> and <([[label] V] of block (() @addInput)) = (name) @delInput @verticalEllipsis @addInput> @delInput @verticalEllipsis @addInput> @addInput) in (extension [ide_blocks V] @verticalEllipsis @addInput)) then [ :: variables] else []) [\)] @delInput @verticalEllipsis @addInput)
} @addInput
for each ((item)) in (list [slots] [menus] [editables] [replaceables] [separators] [collapses] [expands] [min slots] [max slots] [defaults] @delInput @addInput) {
replace item (item) of (slots info) with (call (([ V] of block (block)) @addInput) with inputs (item) @delInput @verticalEllipsis @addInput)
}
replace item (name v) of (slots info) with ([[input names] V] of ([[definition] V] of block (block)) :: sensing)
if <JavaScript extensions? :: operators> {
replace item (spec v) of (slots info) with (get input specs (block) :: operators)
} @addInput
((#)) for each ((value)) of (input values) {
script variables ((info)) @addInput
set [info V] to (list @addInput)
replace item (value v) of (info) with (value)
replace item (block v) of (info) with (block)
for each ((item)) in (slots info) {
replace item (item (1 v) of (item)) of (info) with (item (#) of (item (2 v) of (item)))
}
add (info) to (inputs)
} @loop :: lists /* This block carries out the given script for each item of the given list, like the primitive FOR EACH.
What's different is that it provides the # variable, which will contain the item number in the list of each item in turn, 1 while processing item 1, and so on. */
if <(list [%rr] [%rc] @delInput @addInput) contains (item (1 v) of (item (slots v) of (slots info)))> {
set [shape V] to [2]
} @addInput
set [labels V] to (split block label (block) :: rgb(3,192,60))
script variables ((has loop)) @addInput
((#)) for each ((label)) of (labels) {
if <<not (definition?)> and <text (label) contains [{$nl}] :: operators> /* Reports True if the first input string contains the second input string, otherwise false.
Comparison is case-independent by default; use USE CASE-INDEPENDENT COMPARISONS to change that. */ @delInput @verticalEllipsis @addInput> {
script variables ((lines)) ((line)) @delInput @addInput
set [line V] to []
set [lines V] to (list @addInput)
for each ((part)) in (split (label) by [ v]) {
if <(part) = [$nl] @delInput @verticalEllipsis @addInput> {
add (line) to (lines)
set [line V] to []
} else if <t> {
if <(line) ≠ [] @delInput @verticalEllipsis @addInput> {
set [line V] to (join (line) [ ] @delInput @verticalEllipsis @addInput)
} @addInput
set [line V] to (join (line) (part) @delInput @verticalEllipsis @addInput)
} @delInput @addInput
}
add (line) to (lines)
if (has loop) {
replace item (1 v) of (lines) with (join (item (1 v) of (lines)) [ \@loopArrow] @delInput @verticalEllipsis @addInput)
set [has loop V] to <<false>>
} @addInput
set [label V] to (combine (lines) using ((join (#1) (if <(shape) = [1] @delInput @verticalEllipsis @addInput> then [\\] else []) (multiline [
] :: operators) (#2) @delInput @verticalEllipsis @addInput) input names: ((#1)) ((#2)) @delInput @addInput))
} @addInput
if (label) {
if (definition?) {
add [+] to (snapblocks parts)
} @addInput
add (label) to (snapblocks parts)
} @addInput
if (item (#) of (inputs)) {
if (definition?) {
if (definition?) {
add [+] to (snapblocks parts)
} @addInput
add (snapblocks paramater (item (#) of (inputs)) :: rgb(3,192,60)) to (snapblocks parts)
} else if <t> {
if <(has loop) and <(list [5] [15] @delInput @addInput) contains (item (slots v) of (item (#) of (inputs)))> @delInput @verticalEllipsis @addInput> {
add [\@loop] to (snapblocks parts)
set [has loop V] to <<false>>
} @addInput
add (snapblocks input (item (#) of (inputs)) :: rgb(3,192,60)) to (snapblocks parts)
if <<(item (slots v) of (item (#) of (inputs))) = [15] @delInput @verticalEllipsis @addInput> and ([[custom?] V] of block (block)) @delInput @verticalEllipsis @addInput> {
set [has loop V] to <<true>>
} @addInput
} @delInput @addInput
} @addInput
} @loop :: lists /* This block carries out the given script for each item of the given list, like the primitive FOR EACH.
What's different is that it provides the # variable, which will contain the item number in the list of each item in turn, 1 while processing item 1, and so on. */
if (definition?) {
add [+] to (snapblocks parts)
} @addInput
if (has loop) {
add [\@loop] to (snapblocks parts)
set [has loop V] to <<false>>
} @addInput
if <([[custom?] V] of block (block)) or (definition?) or <(ambiguous blocks) contains ([[label] V] of block (block))> @delInput @verticalEllipsis @addInput> {
add (snapblocks category (block) :: rgb(3,192,60)) /* Get the category name of a block. If the block is in a custom category, it will be "other". However, if javascript is enabled, it will get the custom category color \(as "rgb\(r,g,b\)"\). */ to (overrides)
} @addInput
if <not ([[global?] V] of block (block))> {
add [local] to (overrides)
} @addInput
set [snapblocks code V] to (join (combine (snapblocks parts) using ((join [] [ ] [] @delInput @verticalEllipsis @addInput) @addInput)) (if ([[length] V] of (overrides) :: lists) then (join [ :: ] (combine (overrides) using ((join [] [ ] [] @delInput @verticalEllipsis @addInput) @addInput)) @delInput @verticalEllipsis @addInput) else []) [] [] @delInput @verticalEllipsis @addInput)
set [snapblocks code V] to (join (if (definition?) then [\{] else []) (item ((shape) − (1)) of (list [\(] [\<] @delInput @addInput)) (snapblocks code) (item ((shape) − (1)) of (list [\)] [\>] @delInput @addInput)) (if (definition?) then [\}] else []) @delInput @verticalEllipsis @addInput)
if <(definition?) and <JavaScript extensions? :: operators> @delInput @verticalEllipsis @addInput> {
script variables ((variable names)) @addInput
set [variable names V] to (block vars (block) :: operators) /* Get block variables in a block. Requires javascript. */
if ([[length] V] of (variable names) :: lists) {
set [snapblocks code V] to (join (snapblocks code) (multiline [ \\
block variables ] :: operators) ([[text] V] of (map ((join [\(\(] [] [\)\)] @delInput @verticalEllipsis @addInput) @addInput) over (variable names)) :: lists) [ \@delInput \@addInput] @delInput @verticalEllipsis @addInput)
} @addInput
} @addInput
if (definition?) {
set [snapblocks code V] to (join (snapblocks code) [ :: define] @delInput @verticalEllipsis @addInput)
} @addInput
if ([[comment] V] of block (block)) {
set [snapblocks code V] to (join (snapblocks code) [ /* ] (snapblocks escape ([[comment] V] of block (block)) :: rgb(3,192,60)) /* Escape snapblocks special characters */ [ \*/] @delInput @verticalEllipsis @addInput)
} @addInput
if <(definition?) and <not <is ([[definition] V] of block (block)) empty ring? :: operators>> @delInput @verticalEllipsis @addInput> {
script variables ((definition)) @addInput
set [definition V] to ([[definition] V] of block (block))
if <not <is (definition) a [[command] V] ?>> {
set [definition V] to (join ({report []} @addInput) (definition) @delInput @verticalEllipsis @addInput)
} @addInput
set [snapblocks code V] to (join (snapblocks code) (multiline [
] :: operators) (to snapblocks (definition) definition? <f> :: rgb(3,192,60)) /* Convert any script to snapblocks.
If the definition? tooggle is on, it will convert the definition of the block to snapblocks \(including the block definition hat\). */ @delInput @verticalEllipsis @addInput)
} @addInput
report (snapblocks code)
}
This isn't even actually the full definition, because it doesn't include the helper blocks
This also works with the new primitive block definitions.
{+ go to x: + ((x #)) + y: + ((y #)) + :: motion} :: define
<t> primitive [gotoXY V]
go to (list (x) (y) @delInput @addInput)
Every primitive block will have it's category and loop arrow (for c-blocks) omitted, unless you edit the primitive. If it is a custom block, it's category will be set.
I'm not going to bore you with showing everything that this supports, because to be frank, it supports everything, so why don't you just go ahead and play with it yourself (and with the ability to copy the result to the clipboard, it's super easy to just paste into https://snap-blocks.github.io/).
This blocks runs completely without javascript, however there are a few things that I cannot get without javascript. Those are:
- Custom category colors
There is no way to get custom category colors, so the category will be set to "other". However, if javascript is enabled, it will get the category color, and use that instead. - Block variables
There is no way to get block variables in a block definition without javascript, so if javascript it turned off, it will not add block variables into the block definition. - Perfect block label parsing
If you get the block label from, inputs will be shown as underscores. However if the block has a label (not an input) that is an underscore, there is no way to distinguish between an input and a label.
Can this please be changed?
However, with javascript enabled, I can get the actual block spec that snap uses to parse the block, which does not use underscores as inputs, so I can use that instead (and, I tried the(block spec ())
block in the microworlds library, but that reports the block selector for primitives, which I don't want, so I actually can't get it without javascript on).
Unfortunately since looping through every block in a script, is a taxing process, this block is not very fast (especially when it's more than 1 block). However, the results are very good.
One use for this project is to make it easier to put block definitions on the snap wiki (because it uses snapblocks instead of script pics).
While I was creating this, I found out that the minus block doesn't actually use the hyphen that is on a keyboard, which means that currently that block will be red when you paste it into snapblocks, but I will fix that in the next snapblocks update.