Skip to content

A dynamic, object-oriented, and intuitive scripting language born from curiosity and intuition.

License

Notifications You must be signed in to change notification settings

Shimakaze-Kan/GNScript

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

62 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

img

Introduction

Ever since I started coding, I've dreamt of creating my own programming language. Now that I'm more experienced, I decided to make that dream a reality. The result is GN Script: a dynamically-typed, object-oriented language designed to be as intuitive as possible. When developing GN Script, I relied heavily on my gut instincts about how language elements should behave. For example, dividing a string by an integer breaks it into chunks, and multiplying an array by an integer repeats its elements.

In GN Script, you get:

  • Dynamic Typing: Variables can change types freely, e.g., x = 12 can later become x = "test".
  • Duck Typing: If it looks like a duck and quacks like a duck, it’s a duck.
  • Object-Oriented: Supports classes (refboxes) with inheritance, abstract classes/methods, and two access modifiers (exposed and guarded).
  • Immutable Arrays and Strings: Both arrays and strings are immutable; creating a new instance is necessary for any modification.
  • Reference Types: Refboxes are reference types.
  • Looping Constructs: Standard iteration capabilities.
  • Properties: Built-in types have properties like length for strings and arrays, and methods like toArray for converting strings to arrays. Some properties take parameters, like :hasField("x") to check for a field in a refbox.
  • File I/O: Read and write files, and execute scripts from files within the current interpreter instance.

GN Script is not just a language, it's an extension of my thought process, a tool that behaves just as I intuitively expect it to. I wrote it just for fun, to see how far my creativity and intuition could take me in building something unique.

Code (check amicable numbers) rangeLib.txt
import "rangeLib.txt"

function sumOfProperDivisors(n)
  total = 0
  range = rangeArray(1, n / 2 + 1)
  for i = 0; i < range:length; i = i + 1
    number = range[i]
    if n % number == 0
      total = total + number
    end
  end
return total

function areAmicableNumbers(a, b)
  return (sumOfProperDivisors(a) == b) && (sumOfProperDivisors(b) == a)

number1 = 220
number2 = 284

if areAmicableNumbers(number1, number2)
  print "The numbers " + number1 + " and " + number2 + " are amicable."
else
  print "The numbers " + number1 + " and " + number2 + " are not amicable."
end
function rangeArray(startNum, endNum)
  result = []
  for i = startNum; i < endNum; i = i + 1
    result = result + i
  end
return result

Examples

As a first step, I recommend running the examples located in the examples folder. They will help you take your first step in learning GN script.

File Description
GCD.txt Calculates the greatest common divisor (GCD) of two numbers.
bubble_sort.txt Implements the bubble sort algorithm to sort a list of numbers.
count_occurrences.txt Counts the occurrences of elements in a list and displays how many times each element appears.
fibonacci_iterative.txt Generates the Fibonacci sequence iteratively.
fibonacci_recursive.txt Generates the Fibonacci sequence recursively.
pascals_triangle.txt Prints Pascal's Triangle up to a given number of rows.
perfect_number.txt Checks if a number is a perfect number, i.e., a natural number that is equal to the sum of its divisors
prime_numbers.txt Prints all prime numbers below a number specified by the user.

Documentation

  1. Variables and Scopes
  2. Variable Types
  3. Displaying the State of the Interpreter
  4. Conditional Statements
  5. Loops
  6. Arrays
  7. Functions
  8. Refboxes
  9. Extensions
  10. User-defined extensions
  11. Operators
  12. IO Operations
  13. Interpreter Instructions
  14. Importing Scripts
  15. Language Keywords

Variables and Scopes

Variables are created by specifying their name, followed by an equals sign =, and then their value. When creating variables, it is important to consider that they respect scopes. This means that if you create a variable inside a function or loop, it will not be accessible outside of them. To make a variable accessible in other scopes, you must first declare it in a higher scope, such as the body of a function. Then, if you create a variable with the same name inside a loop (i.e., in a nested scope), you will change the value of the variable in the function body.

x = 2
x = "test"

Thus, it can be said that if a variable with a given name exists in a parent scope, its value can be changed in nested scopes. The same principle applies to accessing the variable's value. It is first searched for in the current scope, then in the parent scope, and so on until it is found, or an exception is thrown if it is not found.

The behavior of parameters and variables declared within a for loop is different. In these cases, references will terminate at those variables, and no operation on them will affect the higher scope. For example, if a variable i exists in the parent scope and i is declared as an iterator in a for loop, adding a number to 'i' within the loop will only modify the iterator variable, leaving the variable in the parent scope unchanged.

function scope_test()
  i = 20
  for i = 0; i < 5 ; i = i + 1
    print "inside loop: " + i
    i = i + 1
  end
  print "i: " + i
return void

Output:

inside loop: 0
inside loop: 2
inside loop: 4
i: 20

Variable Types

Int and String

Int and String are self-explanatory value types. A String cannot change its value once created; it is necessary to create a new String to modify the content.

Array

Array is a value type and cannot change its value directly. To modify an element in an array, you need to create a new array with the desired changes.

RefBox

RefBox is a reference type that can store variables and functions. In other languages, this is often referred to as a class. Variables and functions within a RefBox can have access modifiers.

Void

If we assign the result of a function returning void to a variable, the variable will be of type void. This type is mostly useless but was necessary for the consistency of the language.

Variable names can contains letters, digits and underscore, but they cannot start with digit.

More information about arrays and RefBox can be found in their respective sections.

Displaying the State of the Interpreter

To display the contents of a variable, you can use the print or printInline instructions. The former adds a newline character, while the latter does not. Side note: input is used to get user input into variable.

print "This will be followed by a new line"
printInline "This will not be followed by a new line"

Another way is to use the interpreter instruction dump, which will display all variables, functions, and RefBoxes.

> dump
[Variables]
  Scope level: 0
  {a: 12} [Int]
  {b: [1, 2, 3]} [Array]

[Functions]
  bubbleSort <- {arr}

[RefBoxes]
  my_refbox : {[Guarded] x, [Guarded] my_func <- (a)}

Conditional Statements

Conditional statements are declared using the keyword if followed by a condition. Similar to the C language, 0 means false and numbers greater than 0 mean true. The statement must end with the keyword end.

if a < 5
  print "less than " + 5
end

It is possible to add an else block, which will be executed if the condition is not met.

if a < 5
  print "less than " + 5
else
  print "greater than or equal to " + 5
end

There is no else if statement. To achieve this effect, you need to place another if inside the else block.

if a < 5
  print "less than " + 5
else
  if a == 5
    print "equals to " + 5
  else
    print "greater than " + 5
  end
end

Loops

There are two types of loops available: for and while.

To declare a for loop, write the keyword for, declare an iterator, specify the condition, and the iterator increment. The body of the loop must end with the keyword end.

for i = 0; i < 10; i = i + 1
  print i
end

To declare a while loop, write the keyword while followed by a condition. The loop declaration must end with the keyword end.

a = 0
while a < 5
  print a
  a = a + 1
end

Arrays

As mentioned earlier, arrays are a value type, they are also immutable. Consequently, every change to the array's value requires creating a new array, which is done using extensions. Extensions are a very important concept in this language and are covered in a separate section. But returning to arrays, they are declared as follows:

a = [1, 2, 3, "my array"]

Arrays can store any type.

Access to array elements is done using square brackets [].

> a = [1, 2, 3, "my array", [6, 7, [33]]]

> print a[0]

1

> print a[a:length - 1][2][0]

33

> print [1, 2, 3][2]

3

Functions

To declare a function, use the keyword function followed by its name and parentheses. Parameters can be included within these parentheses. Every function must end with the keyword return followed by a value or the keyword void (or wuwei; alias for void). A function can return different types or not return anything, so you don't need to worry about the return type of data, as it will be determined at the time the function is called.

function myFunction(a, b)
  if a < b
    print b + " is bigger than " + a
    return void
  else
    if a > b
      return a
    end
  end
return "they are equal"

Output:

> myFunction(1, 2)

"2 is bigger than 1"

> print myFunction(2, 1)

1

> print myFunction(2, 2)

2

Even though the language is dynamically typed, sometimes we need to ensure that a variable is of the correct type. For this purpose, we can use the extension :type, which returns the type as a string, allowing us to compare it. Below is an example function that calculates the sum of an array. If the input is not an array, an exception is thrown.

function sumArray(arr)
  if arr:type:tolower <> "array"
    throw "Expected array"
  end
  sum = 0
  for i = 0; i < arr:length; i = i + 1
    sum = sum + arr[i]
  end
return sum

Refboxes

RefBoxes are reference data types that can contain fields and functions with one of two access modifiers: exposed and guarded. The first one, exposed, is the default (i.e., if you don't specify anything, it will be exposed) and allows access to the RefBox instance from outside (equivalent to public in other languages). The second one, guarded, means that access is restricted to within the RefBox itself and its inheriting RefBoxes.

The declaration starts with the keyword refbox, followed by the name and the body of the RefBox, which ends with the keyword end. We can create refbox instance using create keyword.

refbox myRefbox
  guarded x = 1
  exposed y = 2
  function printX()
    print x
  return void
end

Instance:

> myInstance = create myRefbox

> myInstance.printX()

1

> print myInstance.x

Error: Cannot access guarded field

> myInstance.y = 100

Inheritance

Every RefBox can be inherited. If the child RefBox already has a function or field with the same name as the base RefBox, the elements of the RefBox will be overridden.

refbox base
  x = 10
  function fun()
    print "I'm from base refbox"
    return void
  end

refbox child : base
  y = "dddd"
  function fun()
    print "I'm from child refbox"
  return void
end

Dump:

> dump
[Variables]
  No variables to display.

[Functions]
  No functions to display.

[RefBoxes]
  base : {[Exposed] x, [Exposed] fun <- ()}
  child : {[Exposed] y, [Exposed] x, [Exposed] fun <- ()} [base: base]

Overwritten function:

> child = create child

> child.fun()

I'm from child refbox

Abstract RefBoxes

A RefBox can be abstract, which can be achieved by adding the keyword abstract after the refbox keyword during declaration. Such RefBoxes can define abstract functions that must be overridden in the inheriting refbox. Note that the definition of an abstract function does not end with the keyword return or end and does not contain a function body.

refbox abstract abstr
  f = 122
  abstract function test(x)
end

Inheritance:

> refbox noOverwritten : abstr
  end

Error: Refbox cannot have not overrided functions: test

> refbox overwritten : abstr
  function test(x)
  return 123
end

Const RefBoxes

RefBoxes can be const, meaning that any subsequent attempt to declare a RefBox with the same name will fail. By default, like other data types, re-declaring a RefBox would overwrite the previous one. Therefore, the const keyword can be useful when writing scripts intended to be imported into other programs, to prevent the programmer from accidentally overwriting the definition in their program.

refbox const myConstRef
  f = 1234
  function test()
    print "You can't redefine me"
  return void
end

Redefining:

> refbox myConstRef
  end

Error: Ref box 'myConstRef' is const, cannot create ref box definition with the same name

Passing Data

Since RefBoxes are reference types, they can be used to pass data not only via the return keyword but also as parameters.

refbox keyValuePair
  key = 0
  value = 0
end

function findValue(kv)
  arr = [["a", 97], ["b", 98], ["c", 99], ["d", 100], ["e", 101], ["f", 102]]
  for i = 0; i < arr:length; i = i + 1
    if arr[i][0] == kv.key
      kv.value = arr[i][1]
      return void
    end
  end
return void

Usage:

> kv = create keyValuePair

> kv.key = "e"

> findValue(kv)

> print kv.value

101

Extensions

Extensions are special methods implemented directly in the language that can be called on variables or directly on values. Extensions are case-sensitive, so they must be written in all lowercase. Below is a list of all extensions along with the types they can be called on.

Common

Extension Input Result Description
Type [1,2,3,4]:type "Array" Returns type as string
100:type "Int"
myRef:type "RefBox"
"sss" "String"

Array

Extension Input Result Description
Length [1,2,3,4]:length 4 Length of the array
Reverse [1,2,3,4]:reverse [4,3,2,1] Create reversed array
ToString [1,2,3]:tostring [1,2,3]:tostring("-") "123" "1-2-3" It concatenates array elements, using an empty string by default, but a custom delimiter can be specified
RemoveAt [1,2,3]:removeat(2) [1,2] It removes the element at the specified index, but only in a single dimension
AddAt [1,2,3]:addat(1,"f") [1,"f",2,3] It inserts an element at the specified index, but only in a single dimension
Append [1,2,3]:append("f") [1,2,3,"f"] It adds an element to the end of the array
Prepend [1,2,3]:prepend("f") ["f",1,2,3] It adds an element to the beginning of an array
ReplaceAt [1,2,3]:replaceat(1, "g") [1,2,[5,6,["h",2]]]:replaceat(2,2,0,"g") [1,"g",3] [1, 2, [5, 6, ["g", 2]]] It replaces the element at the specified index. The last argument is the value to replace the element with, and the preceding arguments are the indices for each dimension. This function works on multidimensional arrays
Has [1,2,3]:has("f") ["e","f","g"]:has("f") 0 1 Returns 1 if the array contains the specified element, otherwise returns 0

String

Extension Input Result Description
ToLower "ABC":tolower "abc" Converts to lowercase
ToUpper "abc":toupper "ABC" Converts to uppercase
Reverse "abc":reverse "cba" Reverses the string
ToArray "abc":toarray ["a","b","c"] Converts string to array
Length "abc":length 3 Returns length
Split "abcbdbe":split("b") "a b c":split ["a", "c", "d", "e"] Splits a string, the default separator is a space
ReplaceAt "abcd":replaceat(1, "a") "aacd" Replaces character at a specified index with value
ToInt "123":toint 123 Parse string to int
CanConvertToInt "123":canconverttoint 1 Returns 1 when the string can be parsed into int or 0 otherwise.

Refbox

refbox test1
  x = 1
  function test(x)
    print x
  return void
end

refbox test2
  x = 2
  function test(x)
    print x + " 2"
  return void
end

t1 = create test1
Extension Input Result Description
IsInstanceOf t1:isinstanceof("test2") 1 Checks if it is an instance of the specified refbox, using duck typing
HasField t1:hasfield("x") 1 Checks if the instance has a field with the given name
HasFunction t1:hasfunction("test", 1) 1 Checks if the instance has a function with the given name and number of arguments
ReflectionSetField t1:reflectionsetfield("x", 44) Allows setting the value of a field with a 'guarded' modifier using reflection

Int

Extension Input Result Description
ToString 12:tostring "12" Converts number to the string

User-defined extensions

User-defined extensions are custom extensions declared by the user. To declare your own extension, you need to create a const refbox and write a function that will act as the new extension. The refbox must be const to ensure that the function is not overwritten during the program's execution. The function itself must have at least one parameter, as the first parameter will be the value on which the extension is invoked. For example, if you call a custom extension on an integer like 42:myextension, the first parameter of the function will take the value 42.

Example of declaring an extension

refbox const intExtensions
  function cumulativeSum(num)
    if num <= 0
      throw "Number must be positive"
    end
    sum = 0
    for i = num; i > 0; i = i - 1
      sum = sum + i
    end
  return sum
end
> createExtension("int", "intExtensions", "cumulativeSum", 1)

> print 5:cumulativeSum

15

In this example:

  • The first argument "int" is the type to which the extension applies.
  • "intExtensions" is the name of the const refbox.
  • "cumulativeSum" is the name of the function to be used as the extension
  • The last argument 1 indicates the number of parameters the function takes.

Example of extension overloading

Since extensions are written in lowercase, declaring functions like myFunction and MyFunction with the same number of parameters is not allowed. The second declaration will overwrite the first. However, if the functions have different numbers of parameters, they can coexist, as shown in the example below:

refbox const stringExtensions
  function subString(str, startIndex)
    if startIndex < 0 || startIndex >= str:length
      throw "Index out of range"
    end
    subArray = str:reverse:toarray - startIndex
    return subArray:tostring:reverse
  function subString(str, startIndex, count)
    if startIndex < 0 || startIndex >= str:length
      throw "Index out of range"
    end
    strArray = str:toarray
    result = []
    maxIndex = strArray:length
    if startIndex + count < maxIndex
      maxIndex = startIndex + count
    end
    for i = 0; i < maxIndex; i = i + 1
      if i >= startIndex
        result = result + strArray[i]
      end
    end
    return result:tostring
end
> createExtension("string", "stringExtensions", "subString", 2)

> createExtension("string", "stringExtensions", "subString", 3)

> print "NGS_code":substring(4)

code
> print "NGS_code":substring(4, 2)

co

In this case, the two versions of subString will not conflict because they accept different numbers of parameters.

Overriding built-in extensions

User-defined extensions take precedence over existing language extensions, so you can effectively "override" a built-in extension by creating your own with the same name. The only exception to this rule is the type extension, which operates on all types and does not accept external arguments. The type extension cannot be overridden.

User-defined extension scope

An important point to note is that functions used to create extensions cannot reference other functions or fields within the refbox. During code execution, the function will run in the current scope, so the functions and fields from the refbox will no longer be accessible. However, the function will have normal access to variables in the current scope. This means that if the function references a variable x and a variable with that name exists in the current scope, it will be used.

This behavior highlights the importance of being mindful of the scope when defining extensions, ensuring that the function relies only on variables available in the scope where it is invoked.

Operators

Int And Int

Operator first argument second argument result Description
+ 3 5 8 Adds two integers.
- 8 5 3 Subtracts the second integer from the first.
* 4 3 12 Multiplies two integers.
/ 10 2 5 Divides the first integer by the second.
% 10 3 1 Returns the remainder of the division of the first integer by the second.
&& 1 0 0 Logical AND: returns 1 if both integers are non-zero, otherwise returns 0.
|| 0 2 1 Logical OR: returns 1 if at least one of the integers is non-zero, otherwise returns 0.
^ 2 3 8 Raises the first integer to the power of the second.
> 5 3 1 Returns 1 if the first integer is greater than the second, otherwise returns 0.
>= 5 5 1 Returns 1 if the first integer is greater than or equal to the second, otherwise returns 0.
< 3 5 1 Returns 1 if the first integer is less than the second, otherwise returns 0.
<= 3 5 1 Returns 1 if the first integer is less than or equal to the second, otherwise returns 0.
== 5 5 1 Returns 1 if the two integers are equal, otherwise returns 0.
<> 5 3 1 Returns 1 if the two integers are not equal, otherwise returns 0.

Int And String

Operator first argument (int) second argument (string) result Description
+ 3 "test" "3test" Concatenates the integer and the string.
* 3 "abc" "abcabcabc" Repeats the string a specified number of times.
> 5 "hello" 0 Returns 1 if the integer is greater than the length of the string, otherwise returns 0.
== 5 "hello" 1 Returns 1 if the integer is equal to the length of the string, otherwise returns 0.
!= 3 "hello" 1 Returns 1 if the integer is not equal to the length of the string, otherwise returns 0.

String And Int

Operator first argument (string) second argument (int) result Description
+ "test" 3 "test3" Concatenates the string and the integer.
- "teststring" 6 "test" Removes the last n characters from the string.
* "abc" 3 "abcabcabc" Repeats the string a specified number of times.
/ "abcdefgh" 2 "abcd" Truncates the string to the length obtained by dividing its length by the integer.
< "hello" 10 1 Returns 1 if the length of the string is less than the integer, otherwise returns 0.
<= "hello" 5 1 Returns 1 if the length of the string is less than or equal to the integer, otherwise returns 0.
== "hello" 5 1 Returns 1 if the length of the string is equal to the integer, otherwise returns 0.
!= "hello" 3 1 Returns 1 if the length of the string is not equal to the integer, otherwise returns 0.

Array And Int

Operator first argument (array) second argument (int) result Description
+ [1, 2, 3] 4 [1, 2, 3, 4] Adds an integer to the end of the array.
- [1, 2, 3, 4, 5] 2 [1, 2, 3] Removes the last n elements from the array.
/ [1, 2, 3, 4, 5] 2 [[1, 2], [3, 4], [5]] Splits the array into chunks of the specified size.
* [1, 2] 3 [1, 2, 1, 2, 1, 2] Repeats the array a specified number of times.
< [1, 2, 3] 5 1 Returns 1 if the length of the array is less than the integer, otherwise returns 0.
<= [1, 2, 3] 3 1 Returns 1 if the length of the array is less than or equal to the integer, otherwise returns 0.
== [1, 2, 3] 3 1 Returns 1 if the length of the array is equal to the integer, otherwise returns 0.
!= [1, 2, 3] 4 1 Returns 1 if the length of the array is not equal to the integer, otherwise returns 0.

Int And Array

Operator first argument (int) second argument (array) result Description
+ 5 [1, 2, 3] [5, 1, 2, 3] Adds the integer to the beginning of the array.

String And Array

Operator first argument (string) second argument (array) result Description
+ "hello" [1, 2, 3] ["hello", 1, 2, 3] Adds the string to the beginning of the array.

Array And String

Operator first argument (array) second argument (string) result Description
+ [1, 2, 3] "test" [1, 2, 3, "test"] Adds the string to the end of the array.

Array And Array

Operator first argument (array) second argument (array) result Description
+ [1, 2, 3] [4, 5] [1, 2, 3, 4, 5] Concatenates the second array to the end of the first array.
- [1, 2, 3, 4, 5] [4, 5] [1, 2, 3] Removes the elements in the second array from the end of the first array, if they match.
/ [1, 2, 1, 2, 1, 2] [1, 2] 3 Returns the count of how many the second array fits within the first array.
* [1, 2] [3, 4] [[1, 1, 1], [2, 2, 2, 2]] Multiplies the first array by the integers specified in the second array, arrays have to have same length
< [1, 2, 3] [1, 2, 3, 4] 1 Returns 1 if the length of the first array is less than the length of the second array, otherwise returns 0.
> [1, 2, 3, 4] [1, 2, 3] 1 Returns 1 if the length of the first array is greater than the length of the second array, otherwise returns 0.
== [1, 2, 3] [1, 2, 3] 1 Returns 1 if the first array is equal to the second array, otherwise returns 0.
!= [1, 2, 3] [4, 5, 6] 1 Returns 1 if the first array is not equal to the second array, otherwise returns 0.

String And String

Operator first argument (string) second argument (string) result Description
+ "hello" "world" "helloworld" Concatenates the second string to the end of the first string.
- "hello world" " world" "hello" Removes the trailing occurrences of the second string from the end of the first string.
/ "hellohellohello" "hello" 3 Counts the number of occurrences of the second string within the first string.
> "b" "a" 1 Returns 1 if the first string is lexicographically greater than the second string, otherwise returns 0.
>= "a" "a" 1 Returns 1 if the first string is lexicographically greater than or equal to the second string, otherwise returns 0.
< "a" "b" 1 Returns 1 if the first string is lexicographically less than the second string, otherwise returns 0.
<= "a" "b" 1 Returns 1 if the first string is lexicographically less than or equal to the second string, otherwise returns 0.
== "hello" "hello" 1 Returns 1 if the first string is equal to the second string, otherwise returns 0.
!= "hello" "world" 1 Returns 1 if the first string is not equal to the second string, otherwise returns 0.

IO Operations

  • fileExists

    • Checks if a file exists at the specified path.
    • Example: fileExists("rangeLib.txt")
    • Returns: 1 if the file exists, 0 otherwise.
  • readFile

    • Reads the content of a file and returns it as an array, with each element being a line from the file.
    • Example: readFile("rangeLib.txt")
    • Result:
      ["function rangeArray(startNum, endNum)", "  result = []", "  for i = startNum; i < endNum; i = i + 1", "    result = result + i", "  end", "return result"]
      
  • readWholeFile (obsolete)

    • Reads the entire content of a file and returns it as a single string.
    • Example: readWholeFile("rangeLib.txt")
    • Result:
      function rangeArray(startNum, endNum)
        result = []
        for i = startNum; i < endNum; i = i + 1
          result = result + i
        end
      return result
      

However, it is recommended to use readFile instead of readWholeFile to maintain code readability. Operating on arrays is easier.

Interpreter Instructions

  • READ: Reads a script from a file and executes it.

  • READCLS: Resets the current state of the interpreter, then reads and executes a script from a file. This is useful when you want to start with a clean state before executing a new script.

  • CLS: Resets the interpreter state, clearing variables, functions, refboxes, and other elements. Use this to completely clear the current context.

  • DUMP: Outputs the current state of the interpreter, including all variables (with their types), declared functions, and refboxes. This is useful for debugging and inspecting the current interpreter state.

  • EXIT: Closes the interpreter, terminating the session. This should be used when you are done with all operations and want to end the interpreter's execution.

Importing Scripts

To load and execute another script while maintaining the current state of the interpreter, you should use the import instruction followed by the file path. The file path provided to import does not have to be a direct string; it can also be a variable or a function that returns the file path. This flexibility allows for dynamic script loading based on varying conditions or inputs.

It is important to note that import is an instruction within the language itself, not an interpreter command like READ. This means that import is handled as part of the script execution and can be used to modularize and manage code effectively.

Code Formatting

The language does not rely on indentation for code structure, unlike Python. This means that all code can be written on a single line without affecting its interpretation. For example, the following code:

arr = [1, 2, 3, 4, 5, 6, 7] for i = 0; i < arr:length; i = i + 1 print arr[i] end

is interpreted the same way as:

arr = [1, 2, 3, 4, 5, 6, 7]
for i = 0; i < arr:length; i = i + 1
  print arr[i]
end

However, the interpreter requires that there are no blank lines between instructions. A double line break is treated as a command to execute the written code fragment. This rule does not apply to scripts imported using the import instruction.

Language Keywords

The following are language keywords that should not be used as variable names, function names, parameters, or refbox names:

print function refbox abstract const
printInline return create import wuwei
if while throw readFile void
else for guarded readWholeFile
input end exposed fileExists

Future of the Project

Looking ahead, this language has promising potential for integration into other programs. Much like VBA (Visual Basic for Applications) is used within Excel to automate tasks and extend functionality, this language could serve as an embedded scripting tool in various applications.