Discuss Scratch
- Discussion Forums
- » Advanced Topics
- » Boiga: Generate Scratch code using Python
- NFlex23
-
1000+ posts
Boiga: Generate Scratch code using Python
I've successfully implemented my scope engine in Boiga. I also made it portable by abstracting it into a class so multiple sprites can have the same engine.
I didn't use syntax highlighting so the code doesn't get squished together, which looks even worse than having no highlighting IMO.
from boiga import *
class ScopeEngine:
def __init__(self, sprite, error_func = lambda locals, message: []):
current_scope_names = sprite.new_list('current scope names')
current_scope_values = sprite.new_list('current scope values')
scopes = sprite.new_list('scopes', monitor=[320, 0, 160, 120])
variable_names = sprite.new_list('variable names', monitor=[0, 0, 160, 120])
variable_values = sprite.new_list('variable values', monitor=[160, 0, 160, 120])
@sprite.proc_def('Error: message [message]')
def error(locals, message): return error_func(locals, message)
@sprite.proc_def('Reset')
def reset(locals): return [
scopes.delete_all(),
variable_names.delete_all(),
variable_values.delete_all(),
]
@sprite.proc_def('Get scope [scope]', locals_prefix='[get scope] _')
def get_scope(locals, scope): return [
current_scope_names.delete_all(),
current_scope_values.delete_all(),
locals.i[scopes.item(scope):variable_names.len()+1] >> [
current_scope_names.append(variable_names.item(locals.i)),
current_scope_values.append(variable_values.item(locals.i)),
],
]
@sprite.proc_def('Open scope')
def open_scope(locals): return [
scopes.append(variable_names.len() + 1),
]
@sprite.proc_def('Close scope')
def close_scope(locals): return [
If (scopes.len() == 0) [
error("Can't close nonexistent scope")
].Else [
RepeatUntil(variable_names.len() == scopes.item('last') - 1) [
variable_names.delete_at1(scopes.item('last')),
variable_values.delete_at1(scopes.item('last')),
],
scopes.delete_at1('last'),
],
]
@sprite.proc_def('Define variable: name [name] value [value]')
def define_variable(locals, name, value): return [
get_scope('last'),
If (current_scope_names.contains(name)) [
error(Literal('Variable already exists, "').join(name).join('"')),
].Else [
variable_names.append(name),
variable_values.append(value),
],
]
@sprite.proc_def('Set variable: name [name] value [value]', locals_prefix='[set variable] _')
def set_variable(locals, name, value): return [
If (variable_names.contains(name).NOT()) [
error(Literal('Variable not found, "').join(name).join('"')),
].Else [
locals.i <= variable_names.len(),
Forever [
If (variable_names.item(locals.i) == name) [
variable_values.item(locals.i) <= value,
StopThisScript(),
],
locals.i.changeby(-1),
],
],
]
@sprite.proc_def('Get variable: name [name]', locals_prefix='[get variable] _')
def get_variable(locals, name): return [
If (variable_names.contains(name).NOT()) [
error(Literal('Variable not found, "').join(name).join('"')),
].Else [
locals.i <= variable_names.len(),
Forever [
If (variable_names.item(locals.i) == name) [
locals.value <= variable_values.item(locals.i),
StopThisScript(),
],
locals.i.changeby(-1),
],
],
]
self.sprite = sprite
self.error = error
self.reset = reset
self.get_scope = get_scope
self.open_scope = open_scope
self.close_scope = close_scope
self.define_variable = define_variable
self.set_variable = set_variable
self.get_variable = get_variable
from boiga import *
from scope_engine import ScopeEngine
project = Project()
sprite = project.new_sprite('Engine')
demo = sprite.new_list('demo', monitor=[0, 120, 480, 240])
engine = ScopeEngine(sprite, lambda _, message: [
demo.delete_at1('last'),
demo.append(Literal('error> ').join(message)),
])
sprite.on_flag([
Hide(),
demo.delete_all(),
engine.reset(),
engine.open_scope(),
demo.append('[o]pen scope; [c]lose scope; [d]efine variable; [s]et variable; [g]et variable'),
])
sprite.on_press('o', [
demo.append('> New scope opened'),
engine.open_scope(),
])
sprite.on_press('c', [
demo.append('> Last scope closed'),
engine.close_scope(),
])
demo_name = sprite.new_var('[demo] _name')
sprite.on_press('d', [
AskAndWait('Name'),
demo_name <= Answer(),
AskAndWait('Value'),
demo.append(
Literal('> Variable "')
.join(demo_name)
.join('" defined with value "')
.join(Answer())
.join('"')
),
engine.define_variable(demo_name, Answer()),
])
sprite.on_press('s', [
AskAndWait('Name'),
demo_name <= Answer(),
AskAndWait('Value'),
demo.append(
Literal('> Variable "')
.join(demo_name)
.join('" set to value "')
.join(Answer())
.join('"')
),
engine.set_variable(demo_name, Answer()),
])
sprite.on_press('g', [
AskAndWait('Name'),
demo.append(Literal('> Getting value of "').join(Answer()).join('"')),
engine.get_variable(Answer()),
If (
(demo.item('last')[4] == 'r').AND
(demo.item('last')[5] == '>').NOT()
) [
demo.append(engine.get_variable.value),
],
])
project.save('scope.sb3')
Last edited by NFlex23 (June 22, 2022 21:51:16)
- MagicCrayon9342
-
1000+ posts
Boiga: Generate Scratch code using Python
Alright… 3D game engine?
- Retr0id
-
68 posts
Boiga: Generate Scratch code using Python
I've successfully implemented my scope engine in Boiga. I also made it portable by abstracting it into a class so multiple sprites can have the same engine.
-snip-
Very cool
- NFlex23
-
1000+ posts
Boiga: Generate Scratch code using Python
(#23)Thanks! Boiga is super cool… but why is it called “Boiga”?I've successfully implemented my scope engine in Boiga. I also made it portable by abstracting it into a class so multiple sprites can have the same engine.
-snip-
Very cool
- Retr0id
-
68 posts
Boiga: Generate Scratch code using Python
(#23)Thanks! Boiga is super cool… but why is it called “Boiga”?I've successfully implemented my scope engine in Boiga. I also made it portable by abstracting it into a class so multiple sprites can have the same engine.
-snip-
Very cool
Thank you too. That's a good question…
https://en.wikipedia.org/wiki/Boiga
“Boiga is a large genus of opisthoglyphous (rear-fanged), mildly venomous snakes, known commonly as cat-eyed snakes or simply cat snakes”
Scratch Cat + Python Snake => Cat Snake => Boiga
- NFlex23
-
1000+ posts
Boiga: Generate Scratch code using Python
(#25)That's really neat and unique.(#23)Thanks! Boiga is super cool… but why is it called “Boiga”?I've successfully implemented my scope engine in Boiga. I also made it portable by abstracting it into a class so multiple sprites can have the same engine.
-snip-
Very cool
Thank you too. That's a good question…
https://en.wikipedia.org/wiki/Boiga
“Boiga is a large genus of opisthoglyphous (rear-fanged), mildly venomous snakes, known commonly as cat-eyed snakes or simply cat snakes”
Scratch Cat + Python Snake => Cat Snake => Boiga
- NFlex23
-
1000+ posts
Boiga: Generate Scratch code using Python
Bump. Has anyone else made anything cool with Boiga?
- MagicCrayon9342
-
1000+ posts
Boiga: Generate Scratch code using Python
I've been meaning to try Boiga, it's in the queue of ideas. My time is being invested in rust though.
- NFlex23
-
1000+ posts
Boiga: Generate Scratch code using Python
Bump. I think a library like this but for Node.js would be really cool.
- Retr0id
-
68 posts
Boiga: Generate Scratch code using Python
By the way, I have a cool project coming up which will make use of boiga - I'm going to port cnlohr's rv32ima emulator to Scratch, such that it can boot real Linux. It'll probably be very slow, but it should *work* at least.
- NFlex23
-
1000+ posts
Boiga: Generate Scratch code using Python
That's a good idea; I'll definitely check it out when its finished. By the way, I have a cool project coming up which will make use of boiga - I'm going to port cnlohr's rv32ima emulator to Scratch, such that it can boot real Linux. It'll probably be very slow, but it should *work* at least.
- GFXAJ
-
1 post
Boiga: Generate Scratch code using Python
Any similar repo (except for leopard) that supports ts / js env development?
- caftingdead261
-
100+ posts
Boiga: Generate Scratch code using Python
Please don't Necropost the last post on this topic was 3 years ago Any similar repo (except for leopard) that supports ts / js env development?
Last edited by caftingdead261 (April 4, 2025 17:09:35)
- gilbert_given_189
-
1000+ posts
Boiga: Generate Scratch code using Python
I wish there's a better way of “decorating” sprites than this monstrosity I devised:
I can only do this because I'm already very fluent at Python; if this was written in Lua or—God forbid—Rust, I would probably have stopped very early on the project
from boiga import * from boiga.boiga.codegen import * from utils import Namespace, For, export __namespace__ = Namespace("Shoutbox") # appends "Shoutbox." to a bunch of things _ns = __namespace__ def impl_reader(sprite: Sprite) -> Sprite: """Provides procedures to read Shoutbox messages.""" number = sprite.new_var(_ns("number")) source = sprite.new_var(_ns("source")) dest = sprite.new_var(_ns("dest")) subject_id = sprite.new_var(_ns("subject_id")) subject = sprite.new_var(_ns("subject")) body = sprite.new_var(_ns("body")) @sprite.proc_def("Shoutbox: parse message [msg]") @__namespace__ def parse(locals, msg): return [ locals.i <= 1, # A Shoutbox message is structured like this: # NUM:SOURCE>DEST:SUBJECT.ID=BODY # - NUM: The global message number. # - SOURCE: The sender clone ID of this message. # - DEST: The recepient clone ID of this message. # - SUBJECT: What the message is about. # - ID (optional): An identifier of this message, used for marking interprogram discussions. # - BODY: The main matter of the message. number <= "", source <= "", dest <= "", subject_id <= "", subject <= "", body <= "", *For (None, msg.item(locals.i) == ":", locals.i.changeby(1)) [ # bespoke While loop number <= number.join(msg.item(locals.i)), ], locals.i.changeby(1), # Skip the ":" *For (None, msg.item(locals.i) == ">", locals.i.changeby(1)) [ source <= source.join(msg.item(locals.i)), ], locals.i.changeby(1), # Skip the ">" *For (None, msg.item(locals.i) == ":", locals.i.changeby(1)) [ dest <= dest.join(msg.item(locals.i)), ], locals.i.changeby(1), # Skip the ":" *For (None, (msg.item(locals.i) == "=").OR(msg.item(locals.i) == "."), locals.i.changeby(1)) [ subject <= subject.join(msg.item(locals.i)), ], If (msg.item(locals.i) == ".") [ locals.i.changeby(1), *For (None, msg.item(locals.i) == "=", locals.i.changeby(1)) [ subject_id <= subject_id.join(msg.item(locals.i)), ], ], locals.i.changeby(1), # Skip either the "=" or the "." *For (None, msg.item(locals.i) == "", locals.i.changeby(1)) [ body <= body.join(msg.item(locals.i)), ], ] return export(_ns.name, locals()) # makes a pseudo-module (which is just a named tuple) # There should be a impl_writer decorator which I didn't implement before giving up
from boiga import * from shoutbox import * project = Project() test = project.new_sprite("test") Shoutbox = impl_reader(test)
I can only do this because I'm already very fluent at Python; if this was written in Lua or—God forbid—Rust, I would probably have stopped very early on the project

Last edited by gilbert_given_189 (April 4, 2025 17:58:53)
- Discussion Forums
- » Advanced Topics
-
» Boiga: Generate Scratch code using Python