From df9dde40de3bfb2c2957d9568c34d67138d92feb Mon Sep 17 00:00:00 2001 From: aspizu Date: Thu, 8 Oct 2026 23:56:43 +0530 Subject: [PATCH] Fix documentation examples and enum numbering --- docs/configuration.md | 3 +- docs/contributing.md | 14 +- docs/getting-started/basic-examples.md | 250 ++++++++++++++----------- docs/getting-started/index.md | 1 - docs/language/blocks/looks.md | 2 +- docs/language/functions.md | 58 +++--- docs/language/macros.md | 7 +- docs/language/sounds.md | 5 +- docs/recipes/workarounds.md | 20 +- src/visitor/pass0.rs | 2 +- 10 files changed, 207 insertions(+), 155 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index bb002434..71dfde0d 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -5,7 +5,8 @@ configuration. ## Standard Library Version -If not provided, the latest version is picked (Updates fetched daily) +If not provided, the latest version is picked. The selected version is cached for +seven days before checking for updates on a subsequent build. ```toml std = "2.1.0" # default is unset diff --git a/docs/contributing.md b/docs/contributing.md index 774163d2..9da037ea 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -29,6 +29,18 @@ nightly too: rustup toolchain install nightly ``` +The development scripts also require Python 3.9 or later, Node.js, and the pnpm +version declared in `package.json`. Install Prettier separately and ensure the +`prettier` command is available on your `PATH`. From the repository root, install +the workspace dependencies: + +```bash +pnpm install --frozen-lockfile +``` + +This provides `tsx` and its dependencies for schema validation. Run the development +commands below from the repository root. + ## Development To make development easier, and to validate the generated Scratch project -- use @@ -39,7 +51,7 @@ tools/run.py --validate # or `-v` ``` This assumes that you have set-up a testing project at `playground/`. (You can create a -testing project by running `goboscript new -G playground`). It will compile the project, +testing project by running `cargo run -- new -G playground`). It will compile the project, validate it using the schemas from `scratch-parser`. If the validation fails, Scratch will refuse to load the project. diff --git a/docs/getting-started/basic-examples.md b/docs/getting-started/basic-examples.md index 5ffc13c9..0c59b074 100644 --- a/docs/getting-started/basic-examples.md +++ b/docs/getting-started/basic-examples.md @@ -2,6 +2,9 @@ Let's start with simple examples of Goboscript syntax. +Try one example at a time in your sprite's `.gs` file, keeping the existing +`costumes` line. + ```goboscript # This is a single-line comment @@ -25,98 +28,103 @@ var not_flag = false; # Will be compiled as 0 ### Variables and Operators ```goboscript -# Variable declaration and assignment -x = 10; -y = 20; -sum = x + y; # Addition: 30 -diff = x - y; # Subtraction: -10 -product = x * y; # Multiplication: 200 -quotient = y / x; # Division: 2 -floor_div = y // x; # Floor division: 2 -remainder = y % x; # Modulo: 0 -text = "Hello" & " World"; # String concatenation: "Hello World" - -# Compound assignment -x += 5; # x = x + 5 -y -= 3; # y = y - 3 -product *= 2; # product = product * 2 -quotient /= 4; # quotient = quotient / 4 -text &= "!"; # text = text & "!" - -# Comparison operators -is_equal = x == y; # Equal to -not_equal = x != y; # Not equal to -greater = x > y; # Greater than -less = x < y; # Less than -greater_equal = x >= y; # Greater than or equal to -less_equal = x <= y; # Less than or equal to - -# Logical operators -both = x > 0 and y > 0; # Logical AND -either = x > 0 or y < 0; # Logical OR -inverse = not x > 0; # Logical NOT +onflag { + # Variable declaration and assignment + x = 10; + y = 20; + sum = x + y; # Addition: 30 + diff = x - y; # Subtraction: -10 + product = x * y; # Multiplication: 200 + quotient = y / x; # Division: 2 + floor_div = y // x; # Floor division: 2 + remainder = y % x; # Modulo: 0 + text = "Hello" & " World"; # String concatenation: "Hello World" + + # Compound assignment + x += 5; # x = x + 5 + y -= 3; # y = y - 3 + product *= 2; # product = product * 2 + quotient /= 4; # quotient = quotient / 4 + text &= "!"; # text = text & "!" + + # Comparison operators + is_equal = x == y; # Equal to + not_equal = x != y; # Not equal to + greater = x > y; # Greater than + less = x < y; # Less than + greater_equal = x >= y; # Greater than or equal to + less_equal = x <= y; # Less than or equal to + + # Logical operators + both = x > 0 and y > 0; # Logical AND + either = x > 0 or y < 0; # Logical OR + inverse = not (x > 0); # Logical NOT +} ``` ### Control Flow ```goboscript -condition = true; -x = 10; +onflag { + x = 10; -# Simple if statement -if x > 5 { - say "x is greater than 5"; -} + # Simple if statement + if x > 5 { + say "x is greater than 5"; + } -# If-else statement -if x % 2 == 0 { - say "x is even"; -} else { - say "x is odd"; -} + # If-else statement + if x % 2 == 0 { + say "x is even"; + } else { + say "x is odd"; + } -# If-elif-else statement -if x < 0 { - say "x is negative"; -} elif x == 0 { - say "x is zero"; -} else { - say "x is positive"; -} + # If-elif-else statement + if x < 0 { + say "x is negative"; + } elif x == 0 { + say "x is zero"; + } else { + say "x is positive"; + } -# Boolean coercion -if timer() { # Equivalent to: if timer() == 1 - say "Timer is active"; + # Boolean coercion + if timer() { # Equivalent to: if timer() == 1 + say "Timer equals 1"; + } } ``` ### Loops ```goboscript -# Repeat loop (fixed number of iterations) -repeat 5 { - say "Repeated message"; -} +onflag { + # Repeat loop (fixed number of iterations) + repeat 5 { + say "Repeated message"; + } -# Repeat with counter -i = 1; -repeat 10 { - say "Iteration " & i; - i++; -} + # Repeat with counter + i = 1; + repeat 10 { + say "Iteration " & i; + i++; + } -# Until loop (continues until condition is true) -counter = 0; -until counter > 5 { - say "Counter: " & counter; - counter++; -} + # Until loop (continues until condition is true) + counter = 0; + until counter > 5 { + say "Counter: " & counter; + counter++; + } -# Forever loop (infinite loop) -forever { - say "Press stop to exit"; - if key space pressed? { - stop_this_script; + # Forever loop (infinite loop) + forever { + say "Press stop to exit"; + if key_pressed("space") { + stop_this_script; + } } } ``` @@ -154,64 +162,76 @@ onflag { Functions are similar to procedures but return values. +The `greet_world` procedure runs without screen refresh, so it can call functions +safely. + ```goboscript # Define a function -func add(x, y) { +func sum(x, y) { return $x + $y; } -# Function with type +# Define a function returning a string func create_greeting(name) { return "Hello, " & $name & "!"; } -onflag { +proc greet_world { message = create_greeting("World"); say message; # Outputs: "Hello, World!" } + +onflag { + greet_world; +} ``` ### Data Structures #### Lists +For the file-backed list, create `data.txt` in the project directory with one item +per line. + ```goboscript # Declare an empty list list my_list; -# Add items to a list -add "apple" to my_list; -add "banana" to my_list; -add "cherry" to my_list; +# Load list from file +list data "data.txt"; -# Access list items (1-indexed) -first_item = my_list[1]; # "apple" +onflag { + # Add items to a list + add "apple" to my_list; + add "banana" to my_list; + add "cherry" to my_list; -# Replace items -my_list[2] = "blueberry"; # Replace "banana" with "blueberry" + # Access list items (1-indexed) + first_item = my_list[1]; # "apple" -# Insert at position -insert "apricot" at my_list[1]; # Insert at the beginning + # Replace items + my_list[2] = "blueberry"; # Replace "banana" with "blueberry" -# Delete an item -delete my_list[3]; + # Insert at position + insert "apricot" at my_list[1]; # Insert at the beginning -# Check length -size = length my_list; + # Delete an item + delete my_list[3]; -# Check if an item is in the list -if "cherry" in my_list { - say "Found cherry!"; -} + # Check length + size = length my_list; -# Get random item -random_fruit = my_list["random"]; + # Check if an item is in the list + if "cherry" in my_list { + say "Found cherry!"; + } -# Clear the list -delete my_list; + # Get random item + random_fruit = my_list["random"]; -# Load list from file -list data = file ```data.txt```; + # Clear the list + delete my_list; +} ``` #### Structures and Enums @@ -223,12 +243,6 @@ struct Point { y } -# Create a struct instance -Point p = Point { x: 10, y: 20 }; - -# Access struct fields -say "Coordinates: " & p.x & ", " & p.y; - # Define an enum enum Direction { North, # 0 @@ -237,12 +251,6 @@ enum Direction { West # 3 } -# Use enum values -current_direction = Direction.North; -if current_direction == Direction.North { - say "Heading north"; -} - # Enum with explicit values enum Color { Red = "red", @@ -250,5 +258,19 @@ enum Color { Blue = "blue" } -say "Selected color: " & Color.Red; +onflag { + # Create a struct instance + Point p = Point { x: 10, y: 20 }; + + # Access struct fields + say "Coordinates: " & p.x & ", " & p.y; + + # Use enum values + current_direction = Direction.North; + if current_direction == Direction.North { + say "Heading north"; + } + + say "Selected color: " & Color.Red; +} ``` diff --git a/docs/getting-started/index.md b/docs/getting-started/index.md index e099b135..6e2c83e7 100644 --- a/docs/getting-started/index.md +++ b/docs/getting-started/index.md @@ -21,7 +21,6 @@ This will create a new project with the following structure: ├── .gitignore ├── goboscript.toml ├── main.gs -├── playground.sb3 └── stage.gs ``` diff --git a/docs/language/blocks/looks.md b/docs/language/blocks/looks.md index 2141a819..fe850e70 100644 --- a/docs/language/blocks/looks.md +++ b/docs/language/blocks/looks.md @@ -60,7 +60,7 @@ next_costume; next costume ``` -### switch backdrop to (next backdrop) +### switch backdrop to () ```goboscript switch_backdrop "backdrop"; diff --git a/docs/language/functions.md b/docs/language/functions.md index 8393904c..b19c5c67 100644 --- a/docs/language/functions.md +++ b/docs/language/functions.md @@ -1,17 +1,16 @@ # Functions -Functions are reusable procedures (custom blocks) that can return values, including -primitives or structs. Functions always run in **Run without screen refresh** mode and -**must only be called** from other **Run without screen refresh** procedures or +Functions are reusable procedures (custom blocks) that can return values, including +primitives or structs. Functions always run in **Run without screen refresh** mode and +**must only be called** from other **Run without screen refresh** procedures or functions to prevent undefined behavior. -Each function must **end with a `return` statement**. Using `stop_this_script` inside -a function is undefined behavior. - +Each function must **end with a `return` statement** — the compiler does not check this. +Using `stop_this_script` inside a function is undefined behavior. ## Declaring a Function -Use the `func` keyword to define a function. Optionally, include a return type for +Use the `func` keyword to define a function. Optionally, include a return type for functions that return a struct. ```goboscript @@ -51,21 +50,28 @@ func vec_add(Vector lhs, Vector rhs) Vector { ### Using the Returned Struct ```goboscript -# Create vectors -Vector vec1 = Vector { x: 10, y: 20 }; -Vector vec2 = Vector { x: 5, y: 15 }; +proc show_vector_sum { + # Create vectors + Vector vec1 = Vector { x: 10, y: 20 }; + Vector vec2 = Vector { x: 5, y: 15 }; + + # Call function that returns a struct + Vector result = vec_add(vec1, vec2); -# Call function that returns a struct -Vector result = vec_add(vec1, vec2); + # Access the returned struct's fields + say result.x; # Outputs: 15 + say result.y; # Outputs: 35 +} -# Access the returned struct's fields -say result.x; # Outputs: 15 -say result.y; # Outputs: 35 +onflag { + show_vector_sum; +} ``` -!!!NOTE +!!! note When returning struct variables from functions, the return type must be explicitly - specified when returning a struct + specified when returning a struct. + --- ## Default Argument Values @@ -85,7 +91,7 @@ func greet(name = "world") { ## Calling a Function -Functions are called by name with argument values: +Call a function by name with argument values: ```goboscript say my_function(1, 2); @@ -96,14 +102,14 @@ say my_function(1, 2); ## Keyword Arguments You can also call functions using **keyword arguments**, which specify parameter names - explicitly. This is useful when using default arguments or calling functions with many - parameters: +explicitly. This is useful when using default arguments or calling functions with many +parameters: ```goboscript -greet(name: "aspizu") +say greet(name: "aspizu"); ``` -This behaves the same as `greet("aspizu")`, but makes the call more readable—especially +The call returns the same value as `greet("aspizu")`, but makes the call more readable—especially when multiple parameters are involved: ```goboscript @@ -115,15 +121,15 @@ func introduce(name, title = "developer", location = "unknown") { Call it with keyword arguments: ```goboscript -introduce(name: "aspizu", location: "India") -# Equivalent to: introduce("aspizu", "developer", "India") +say introduce(name: "aspizu", location: "India"); +# Equivalent to: say introduce("aspizu", "developer", "India"); ``` -!!!NOTE +!!! note Keyword arguments can be used in any order, as long as the required parameters are provided: ```goboscript - introduce(location: "Berlin", name: "Kai"); + say introduce(location: "Berlin", name: "Kai"); # Still valid ``` diff --git a/docs/language/macros.md b/docs/language/macros.md index 20e73b30..9d898f2c 100644 --- a/docs/language/macros.md +++ b/docs/language/macros.md @@ -121,14 +121,15 @@ All tokens inside the parentheses are joined with spaces and produced as a singl STRINGIFY(hello world) # becomes "hello world" ``` -This is useful when you need to turn a macro expansion or a sequence of tokens into a -string at compile time: +This is useful when you need to turn a sequence of tokens into a string at compile time. +Tokens inside `STRINGIFY` are stringified as written; macros inside its argument are +not expanded: ```goboscript %define VERSION 1 2 3 onflag { - say STRINGIFY(VERSION); # says "1 2 3" + say STRINGIFY(VERSION); # says "VERSION" } ``` diff --git a/docs/language/sounds.md b/docs/language/sounds.md index 0eefff86..c8385c0f 100644 --- a/docs/language/sounds.md +++ b/docs/language/sounds.md @@ -1,9 +1,8 @@ # Sounds !!! note - Scratch only supports MP3 and WAV files. - Other formats will (as of now) not raise a warning or error, and the project will - refuse to load in Scratch. + Scratch only supports MP3 and WAV files. goboscript accepts `.mp3`, `.wav`, and + `.wave` file extensions and reports an error for unsupported sound formats. You can add sounds to a sprite by specifying their file paths relative to the project directory. diff --git a/docs/recipes/workarounds.md b/docs/recipes/workarounds.md index d96753a8..ab6614f8 100644 --- a/docs/recipes/workarounds.md +++ b/docs/recipes/workarounds.md @@ -23,15 +23,23 @@ Use an `until` loop with an empty body: until (condition) {} ``` -### Encapsulating Variables +### Namespacing Variables -Use `%define` to alias a long unique name to a short local one, then `%undef` it at the end of the file so other files can't access it directly. +Use `%define` to alias a long unique variable name to a short name within a header, +then `%undef` it at the end of the header to prevent the alias from affecting later +code. Declare the variable before reading it. + +The underlying variable remains accessible to other code by its full name. +After `%undef`, assigning `private_variable = 10;` declares a separate variable +named `private_variable`. `lib/my_module.gs` ```goboscript %define private_variable __my_module__private_variable +var private_variable = 0; + func get_my_variable() { return private_variable; } @@ -44,9 +52,13 @@ func get_my_variable() { ```goboscript %include lib/my_module -onflag { +proc show_my_variable { say get_my_variable(); - # private_variable = 10; # error — alias no longer defined +} + +onflag { + show_my_variable; + # say private_variable; # error — alias no longer defined } ``` diff --git a/src/visitor/pass0.rs b/src/visitor/pass0.rs index b3d80950..0fa472a3 100644 --- a/src/visitor/pass0.rs +++ b/src/visitor/pass0.rs @@ -116,7 +116,7 @@ fn visit_enum(enum_: &mut Enum) { for variant in &mut enum_.variants { if let Some((value, _)) = &variant.value { if let Value::Number(number) = value { - index = *number; + index = *number + 1.0; } } else { variant.value = Some((Value::Number(index), variant.span.clone()));