Create brainfckinterpreter.js placeholder (for now)#26
Create brainfckinterpreter.js placeholder (for now)#26GraisonAtSchoolAgain wants to merge 2 commits into
Conversation
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
WalkthroughA new file ChangesBrainfk interpreter file**
Estimated code review effort🎯 1 (Trivial) | ⏱️ ~2 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@extensions/GvYoutube/brainfckinterpreter.js`:
- Line 6: Fix the typo in the metadata comment tag on the line beginning with
"Tags:" by changing "interperter" to the correct spelling "interpreter" so the
tag reads "code interpreter"; update the comment string in the file's header
(the line containing "// Tags: code interperter") to reflect this corrected
value.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 04ea51fa-ae1b-4ec0-8f23-4eb0aef33b96
📒 Files selected for processing (1)
extensions/GvYoutube/brainfckinterpreter.js
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
|
@coderabbitai could you help code this extension? i'm kinda rusty in javascript. |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chainSure! Let me first read the placeholder file and look at existing extensions to understand the required structure. 🏁 Script executed: Length of output: 31733 🏁 Script executed: Length of output: 4487
// Name: Brainf**k Interpreter
// ID: gvyoutubebrainfck
// Description: Interpreter for the Brainf**k language.
// By: GvYoutube <https://scratch.mit.edu/users/GvYoutube/>
// License: MIT
// Tags: code interpreter
(function (Scratch) {
'use strict';
let lastOutput = '';
let lastError = '';
const MAX_STEPS = 100000;
function runBrainfuck(code, input) {
const tape = new Uint8Array(30000);
let pointer = 0;
let inputIndex = 0;
let output = '';
let steps = 0;
// Pre-compute matching bracket positions for fast jumps
const bracketMap = {};
const stack = [];
for (let i = 0; i < code.length; i++) {
if (code[i] === '[') {
stack.push(i);
} else if (code[i] === ']') {
if (stack.length === 0) {
return { output: '', error: 'Unmatched ] at position ' + i };
}
const open = stack.pop();
bracketMap[open] = i;
bracketMap[i] = open;
}
}
if (stack.length > 0) {
return { output: '', error: 'Unmatched [ at position ' + stack[stack.length - 1] };
}
let pc = 0;
while (pc < code.length) {
if (++steps > MAX_STEPS) {
return { output, error: 'Exceeded ' + MAX_STEPS + ' steps — possible infinite loop.' };
}
switch (code[pc]) {
case '>':
pointer = (pointer + 1) % 30000;
break;
case '<':
pointer = (pointer + 29999) % 30000;
break;
case '+':
tape[pointer] = (tape[pointer] + 1) & 0xFF;
break;
case '-':
tape[pointer] = (tape[pointer] + 255) & 0xFF;
break;
case '.':
output += String.fromCharCode(tape[pointer]);
break;
case ',':
tape[pointer] = inputIndex < input.length
? input.charCodeAt(inputIndex++)
: 0;
break;
case '[':
if (tape[pointer] === 0) pc = bracketMap[pc];
break;
case ']':
if (tape[pointer] !== 0) pc = bracketMap[pc];
break;
}
pc++;
}
return { output, error: '' };
}
class BrainfckInterpreter {
getInfo() {
return {
id: 'gvyoutubebrainfck',
name: Scratch.translate('Brainf**k'),
color1: '`#5C2D91`',
blocks: [
{
opcode: 'runCode',
blockType: Scratch.BlockType.REPORTER,
text: Scratch.translate('run [CODE] with input [INPUT]'),
arguments: {
CODE: {
type: Scratch.ArgumentType.STRING,
// Classic "Hello World!" program
defaultValue: '++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.'
},
INPUT: {
type: Scratch.ArgumentType.STRING,
defaultValue: ''
}
}
},
{
opcode: 'lastOutput',
blockType: Scratch.BlockType.REPORTER,
text: Scratch.translate('last output'),
arguments: {}
},
{
opcode: 'lastError',
blockType: Scratch.BlockType.REPORTER,
text: Scratch.translate('last error'),
arguments: {}
},
{
opcode: 'hasError',
blockType: Scratch.BlockType.BOOLEAN,
text: Scratch.translate('had error?'),
arguments: {}
}
]
};
}
runCode(args) {
const result = runBrainfuck(
Scratch.Cast.toString(args.CODE),
Scratch.Cast.toString(args.INPUT)
);
lastOutput = result.output;
lastError = result.error;
return result.output;
}
lastOutput() {
return lastOutput;
}
lastError() {
return lastError;
}
hasError() {
return lastError !== '';
}
}
Scratch.extensions.register(new BrainfckInterpreter());
})(Scratch);What each block does
Safety features
The default code in the block is the classic "Hello, World!" program, so you can test it right away. Let me know if you'd like any changes, such as a configurable step limit block or a way to reset state! 🐇 |
|
@coderabbitai |
|
@coderabbitai I would like to clarify, the BF interpreter translates BF into scratchblocks basically, so in BF, if say(hello, world!) was imputted, it would basically run that. |
|
Tip For best results, initiate chat on the files or code changes. The Comment image shows a browser alert dialog from ampmod.codeberg.page with the error:
I also spotted a bug I accidentally introduced in my previous code — the // ❌ Wrong (backticks from my Markdown rendering)
color1: '`#5C2D91`',
// ✅ Correct
color1: '`#5C2D91`',The safest fix is to make sure the entire file is clean ASCII. Here's a corrected, minimal version of the top of getInfo() {
return {
id: 'gvyoutubebrainfck',
name: Scratch.translate('Brainfuck'), // no special characters
color1: '`#5C2D91`', // no backticks
blocks: [ /* ... */ ]
};
}A quick checklist before saving the file:
If after those fixes the error persists, it would be an ampmod-side issue (it should use |
|
Tip For best results, initiate chat on the files or code changes.
(゚v゚)🐇 Option A — Output-as-commands: The BF code runs normally and produces a string output (e.g. Option B — Direct mapping: Each BF command/cell value maps directly to a specific Scratch block (e.g. cell value Option C — Something else entirely? A couple of follow-up questions to make sure I get it right:
Once I know exactly how the mapping should work, I can rewrite the extension cleanly! 🐇 ✏️ Learnings added
|
|
@coderabbitai it's basically option A.
|
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 7987
// Name: Brainf**k Interpreter
// ID: gvyoutubebrainfck
// Description: Interpreter for the Brainf**k language.
// By: GvYoutube <https://scratch.mit.edu/users/GvYoutube/>
// License: MIT
// Tags: code interpreter
(function (Scratch) {
'use strict';
const MAX_STEPS = 100000;
let lastOutput = '';
let lastError = '';
// ── BF engine ──────────────────────────────────────────────────────────────
function runBrainfuck(code, input) {
const tape = new Uint8Array(30000);
let dp = 0, ip = 0, inputIdx = 0, output = '';
let steps = 0;
const map = {};
const stack = [];
for (let i = 0; i < code.length; i++) {
if (code[i] === '[') { stack.push(i); }
else if (code[i] === ']') {
if (!stack.length) return { output: '', error: 'Unmatched ] at ' + i };
const open = stack.pop();
map[open] = i;
map[i] = open;
}
}
if (stack.length) return { output: '', error: 'Unmatched [ at ' + stack[stack.length - 1] };
while (ip < code.length) {
if (++steps > MAX_STEPS)
return { output, error: 'Step limit reached (possible infinite loop)' };
switch (code[ip]) {
case '>': dp = (dp + 1) % 30000; break;
case '<': dp = (dp + 29999) % 30000; break;
case '+': tape[dp] = (tape[dp] + 1) & 0xFF; break;
case '-': tape[dp] = (tape[dp] + 255) & 0xFF; break;
case '.': output += String.fromCharCode(tape[dp]); break;
case ',': tape[dp] = inputIdx < input.length ? input.charCodeAt(inputIdx++) : 0; break;
case '[': if (tape[dp] === 0) ip = map[ip]; break;
case ']': if (tape[dp] !== 0) ip = map[ip]; break;
}
ip++;
}
return { output, error: '' };
}
// ── Command dispatcher ─────────────────────────────────────────────────────
// Output format: "COMMAND arg1 arg2 ..."
// e.g. "say Hello World" | "move 10" | "turnright 90" | "show"
function dispatchCommand(raw, target, util) {
const parts = raw.trim().split(/\s+/);
if (!parts.length || parts[0] === '') return '';
const cmd = parts[0].toLowerCase();
const arg1 = parts[1] !== undefined ? parts[1] : '';
const arg2 = parts[2] !== undefined ? parts[2] : '';
const rest = parts.slice(1).join(' '); // full text after the command word
switch (cmd) {
// ── Motion ──
case 'move':
target.setXY(
target.x + Math.cos((target.direction - 90) * Math.PI / 180) * Number(arg1),
target.y + Math.sin((target.direction - 90) * Math.PI / 180) * Number(arg1)
);
break;
case 'turnright':
target.setDirection(target.direction + Number(arg1));
break;
case 'turnleft':
target.setDirection(target.direction - Number(arg1));
break;
case 'setx':
target.setXY(Number(arg1), target.y);
break;
case 'sety':
target.setXY(target.x, Number(arg1));
break;
case 'goto':
target.setXY(Number(arg1), Number(arg2));
break;
case 'point':
target.setDirection(Number(arg1));
break;
case 'bounce':
if (target.isTouchingEdge()) target.bounceOffEdge();
break;
// ── Looks ──
case 'say':
target.say(rest, null);
break;
case 'think':
target.think(rest, null);
break;
case 'show':
target.setVisible(true);
break;
case 'hide':
target.setVisible(false);
break;
case 'setsize':
target.setSize(Number(arg1));
break;
case 'changesize':
target.setSize(target.size + Number(arg1));
break;
case 'nextcostume':
target.setCostume(target.currentCostume + 1);
break;
case 'setcostume':
target.setCostume(rest);
break;
case 'nextbackdrop': {
const stage = target.runtime.getTargetForStage();
if (stage) stage.setCostume(stage.currentCostume + 1);
break;
}
case 'setbackdrop': {
const stage = target.runtime.getTargetForStage();
if (stage) stage.setCostume(rest);
break;
}
case 'seteffect':
// seteffect <effectname> <value> e.g. "seteffect color 50"
target.setEffect(arg1, Number(arg2));
break;
case 'cleareffects':
target.clearEffects();
break;
// ── Sound ──
case 'playsound':
Scratch.vm.runtime.startSound(target, rest, false);
break;
case 'stopsounds':
Scratch.vm.runtime.stopAllSounds();
break;
// ── Events ──
case 'broadcast':
util.startHats('event_whenbroadcastreceived', { BROADCAST_OPTION: rest });
break;
// ── Control ──
case 'wait':
return new Promise(resolve => setTimeout(resolve, Number(arg1) * 1000));
case 'stop':
util.stopThisScript();
break;
case 'stopall':
target.runtime.stopAll();
break;
default:
return 'Unknown command: ' + cmd;
}
return '';
}
// ── Extension class ────────────────────────────────────────────────────────
class BrainfckInterpreter {
getInfo() {
return {
id: 'gvyoutubebrainfck',
name: Scratch.translate('Brainf**k'),
color1: '`#5C2D91`',
blocks: [
{
opcode: 'runAndExecute',
blockType: Scratch.BlockType.COMMAND,
text: Scratch.translate('run BF [CODE] with input [INPUT]'),
arguments: {
CODE: {
type: Scratch.ArgumentType.STRING,
// Outputs "say Hello World!"
defaultValue: '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++'
},
INPUT: {
type: Scratch.ArgumentType.STRING,
defaultValue: 'say Hello World!'
}
}
},
{ blockType: Scratch.BlockType.LABEL, text: 'Results' },
{
opcode: 'getLastOutput',
blockType: Scratch.BlockType.REPORTER,
text: Scratch.translate('last BF output'),
arguments: {}
},
{
opcode: 'getLastError',
blockType: Scratch.BlockType.REPORTER,
text: Scratch.translate('last error'),
arguments: {}
},
{
opcode: 'hadError',
blockType: Scratch.BlockType.BOOLEAN,
text: Scratch.translate('had error?'),
arguments: {}
}
]
};
}
runAndExecute(args, util) {
const code = Scratch.Cast.toString(args.CODE);
const input = Scratch.Cast.toString(args.INPUT);
const result = runBrainfuck(code, input);
lastOutput = result.output;
lastError = result.error;
if (result.error) return;
const maybePromise = dispatchCommand(result.output, util.target, util);
if (maybePromise instanceof Promise) return maybePromise;
}
getLastOutput() { return lastOutput; }
getLastError() { return lastError; }
hadError() { return lastError !== ''; }
}
Scratch.extensions.register(new BrainfckInterpreter());
})(Scratch);How it worksThe BF program's output (everything printed by
Command reference
The quickest way to test it: use ✏️ Learnings added
|
|
Tip For best results, initiate chat on the files or code changes. The Comment image shows the same
// Name: Brainf**k Interpreter
// ID: gvyoutubebrainfck
// Description: Interpreter for the Brainf**k language.
// By: GvYoutube <https://scratch.mit.edu/users/GvYoutube/>
// License: MIT
// Tags: code interpreter
(function (Scratch) {
'use strict';
const MAX_STEPS = 100000;
let lastOutput = '';
let lastError = '';
// -- BF engine --
function runBrainfuck(code, input) {
const tape = new Uint8Array(30000);
let dp = 0, ip = 0, inputIdx = 0, output = '';
let steps = 0;
const map = {};
const stack = [];
for (let i = 0; i < code.length; i++) {
if (code[i] === '[') { stack.push(i); }
else if (code[i] === ']') {
if (!stack.length) return { output: '', error: 'Unmatched ] at ' + i };
const open = stack.pop();
map[open] = i;
map[i] = open;
}
}
if (stack.length) return { output: '', error: 'Unmatched [ at ' + stack[stack.length - 1] };
while (ip < code.length) {
if (++steps > MAX_STEPS)
return { output, error: 'Step limit reached (possible infinite loop)' };
switch (code[ip]) {
case '>': dp = (dp + 1) % 30000; break;
case '<': dp = (dp + 29999) % 30000; break;
case '+': tape[dp] = (tape[dp] + 1) & 0xFF; break;
case '-': tape[dp] = (tape[dp] + 255) & 0xFF; break;
case '.': output += String.fromCharCode(tape[dp]); break;
case ',': tape[dp] = inputIdx < input.length ? input.charCodeAt(inputIdx++) : 0; break;
case '[': if (tape[dp] === 0) ip = map[ip]; break;
case ']': if (tape[dp] !== 0) ip = map[ip]; break;
}
ip++;
}
return { output, error: '' };
}
// -- Command dispatcher --
// Output format: "COMMAND arg1 arg2 ..."
// e.g. "say Hello World" | "move 10" | "turnright 90" | "show"
function dispatchCommand(raw, target, util) {
const parts = raw.trim().split(/\s+/);
if (!parts.length || parts[0] === '') return '';
const cmd = parts[0].toLowerCase();
const arg1 = parts[1] !== undefined ? parts[1] : '';
const arg2 = parts[2] !== undefined ? parts[2] : '';
const rest = parts.slice(1).join(' ');
switch (cmd) {
// -- Motion --
case 'move':
target.setXY(
target.x + Math.cos((target.direction - 90) * Math.PI / 180) * Number(arg1),
target.y + Math.sin((target.direction - 90) * Math.PI / 180) * Number(arg1)
);
break;
case 'turnright':
target.setDirection(target.direction + Number(arg1));
break;
case 'turnleft':
target.setDirection(target.direction - Number(arg1));
break;
case 'setx':
target.setXY(Number(arg1), target.y);
break;
case 'sety':
target.setXY(target.x, Number(arg1));
break;
case 'goto':
target.setXY(Number(arg1), Number(arg2));
break;
case 'point':
target.setDirection(Number(arg1));
break;
case 'bounce':
if (target.isTouchingEdge()) target.bounceOffEdge();
break;
// -- Looks --
case 'say':
target.say(rest, null);
break;
case 'think':
target.think(rest, null);
break;
case 'show':
target.setVisible(true);
break;
case 'hide':
target.setVisible(false);
break;
case 'setsize':
target.setSize(Number(arg1));
break;
case 'changesize':
target.setSize(target.size + Number(arg1));
break;
case 'nextcostume':
target.setCostume(target.currentCostume + 1);
break;
case 'setcostume':
target.setCostume(rest);
break;
case 'nextbackdrop': {
const stage = target.runtime.getTargetForStage();
if (stage) stage.setCostume(stage.currentCostume + 1);
break;
}
case 'setbackdrop': {
const stage = target.runtime.getTargetForStage();
if (stage) stage.setCostume(rest);
break;
}
case 'seteffect':
target.setEffect(arg1, Number(arg2));
break;
case 'cleareffects':
target.clearEffects();
break;
// -- Sound --
case 'playsound':
Scratch.vm.runtime.startSound(target, rest, false);
break;
case 'stopsounds':
Scratch.vm.runtime.stopAllSounds();
break;
// -- Events --
case 'broadcast':
util.startHats('event_whenbroadcastreceived', { BROADCAST_OPTION: rest });
break;
// -- Control --
case 'wait':
return new Promise(function(resolve) { setTimeout(resolve, Number(arg1) * 1000); });
case 'stop':
util.stopThisScript();
break;
case 'stopall':
target.runtime.stopAll();
break;
default:
return 'Unknown command: ' + cmd;
}
return '';
}
// -- Extension class --
class BrainfckInterpreter {
getInfo() {
return {
id: 'gvyoutubebrainfck',
name: Scratch.translate('Brainf**k'),
color1: '`#5C2D91`',
blocks: [
{
opcode: 'runAndExecute',
blockType: Scratch.BlockType.COMMAND,
text: Scratch.translate('run BF [CODE] with input [INPUT]'),
arguments: {
CODE: {
type: Scratch.ArgumentType.STRING,
defaultValue: ',[.,]'
},
INPUT: {
type: Scratch.ArgumentType.STRING,
defaultValue: 'say Hello World!'
}
}
},
{ blockType: Scratch.BlockType.LABEL, text: 'Results' },
{
opcode: 'getLastOutput',
blockType: Scratch.BlockType.REPORTER,
text: Scratch.translate('last BF output'),
arguments: {}
},
{
opcode: 'getLastError',
blockType: Scratch.BlockType.REPORTER,
text: Scratch.translate('last error'),
arguments: {}
},
{
opcode: 'hadError',
blockType: Scratch.BlockType.BOOLEAN,
text: Scratch.translate('had error?'),
arguments: {}
}
]
};
}
runAndExecute(args, util) {
const code = Scratch.Cast.toString(args.CODE);
const input = Scratch.Cast.toString(args.INPUT);
const result = runBrainfuck(code, input);
lastOutput = result.output;
lastError = result.error;
if (result.error) return;
const maybePromise = dispatchCommand(result.output, util.target, util);
if (maybePromise instanceof Promise) return maybePromise;
}
getLastOutput() { return lastOutput; }
getLastError() { return lastError; }
hadError() { return lastError !== ''; }
}
Scratch.extensions.register(new BrainfckInterpreter());
})(Scratch);The key fixes in this version:
(゚-゚)🐇 ✏️ Learnings added
|


creates a placeholder to allow this pr to be open until final extension is finished with an image.