Snap to snapblocks converter

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.

to snapblocks script pic (17)

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/).

https://snap.berkeley.edu/snap/snap.html#present:Username=ego-lay_atman-bay&ProjectName=to%20snapblocks

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 to snapblocks script pic, 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.
    to snapblocks script pic (1)
    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.

ok so I hate to nitpick but convertor is spelled converter

yeah I created a forum post about that but jens didnt support the idea of getting a blockspec and just asked me to use label.


Technically this project does fit into the definition of "convertor".

(from brave search)
convertor
/kən-vûr′tər/
noun

  1. a device for changing one substance or form or state into another

However since I don't want to take your glory over your project, I think I'm just gonna rename the topid to "Snap to snapblcoks converter".

(It also doesn't help that the spell checker says "convertor" is a word, and not a misspelling of "converter")

Probably not, but make a proposal.

One way to solve this, is to add a backslash before the underscore. Seriously, adding an escape character would help in many more things than just telling the difference between a label underscore, and an input underscore.

I personally believe that backslashes should be added as an escape character for any place in snap that uses special characters for a different meaning, such as an equal sign in custom dropdown menus.

Backslash before the special use, or backslash before the ordinary character use? The grownup languages do it both ways; \n means newline, but \\ means the backslash character itself.

In the programming part, I feel like a backslash should only be used in block labels, and lisp code. I feel like it should be able to be used on any character, but I feel like it would still be fine to only use it before special use characters, like \_ (and \\_ to display a backslash next to an underscore). I did actually check, and the lisp code already uses backslashes, so it can escape the quote character in a block label, so it's not completely out of the question.

In terms of the ide stuff, like custom dropdown menus and custom blocks, it should be able to be used on any character. This would solve the entire "it's impossible to put an equal sign in a dropdown menu without javascript" issue that has plagued snap for years, by allowing \= to just be the equal sign. Plus, it would allow you to put a dollar sign at the start of a custom block word without it turning into the formatted text (or icon). Heck, even putting a backslash after the dollar sign, and before an icon name, it should display it as the formatted text, instead of the icon (that's actually what I do in snapblocks, so...).

Also, maybe this should be split into another topic.

So, you're saying, backslash the use as an ordinary character?

when was this added?
image

The "snapblocks" part is an extra custom tag that you can put on topics, it's not created by the snap team.

imo the character should be % because you can not have a % in a block label except for as a single character by itself.

Love to see projects (in general) like these. It would go in handy for the wiki as now one can now put snapblocks code without having to check to see if there's problems. (flawlessly most of the time)

Theoretically, thanks to the EDC library Script Pic Costumes, it is possible to get the category color through its costume by sampling the main color. That's quite eccentric as it seems, but it would mean a lot for the users who doesn't enjoy having to turn on JavaScript.

You still have to do that. What this project accomplishes is being able to quickly get the snapblocks of a large script that would take a while to type up manually. It's not a way to verify that the script is perfect, but instead a shortcut to creating snapblocks scripts.

Soooo when's this being added to the forums?

Michael (the guy who manages the forum) hasn't responded to me yet (also, I'm waiting before messaging him again, because I want to get the next snapblocks update out before it gets added to the forum, mainly because it fixes some errors).

I’m going to try to make the category Color detection

I just updated this project to use my new syntax for local blocks.

Local blocks? How do you detect if it's local or not? Also, I can't wait for it!

In snap, you can just check if the block is not global.

oh. I haven't used Snap in a while haha