-
-
Notifications
You must be signed in to change notification settings - Fork 0
Docs
_ __ __
| | / /_ _______/ /_
| | /| / / / / / ___/ __ \
| |/ |/ / /_/ (__ ) /_/ /
|__/|__/\__, /____/_.___/
/____/
The syntax of Wysb is heavily influenced by other C-like languages, particularly Go, JavaScript, and PHP. It is crafted to feel intuitive for developers transitioning from these languages, prioritizing simplicity and predictability.
Scripts are written in plain text files with a .wys
file extension. Wysb doesn't require pre-compilation: the code is executed directly from the source, running sequentially from top to bottom like typical scripting languages.
Comments
Single-line comments start with //
and continue until the end of the line.
// This is a single-line comment
Multi-line or block comments are enclosed within /* ... */
, allowing them to span multiple lines.
/*
This is a block comment.
*/
Comments, treated like whitespace, are ignored during execution. While single-line comments are standard, block comments are helpful when you need to disable sections of code or write comments within expressions.
Reserved Keywords
Wysb includes a concise set of reserved keywords used for predefined functions and control structures. These words must not be used as identifiers in your scripts.
function true false if else return while and or
Identifiers
The rules for naming identifiers in Wysb are similar to those of other programming languages. An identifier must begin with either a letter or an underscore (_
), followed by any combination of letters, digits, and underscores. Identifiers are case-sensitive.
exampleName
camelCase
PascalCase
_underscore_variable
abc123
ALL_CAPS
Blocks
In Wysb, curly braces {}
are used to define blocks of code. Blocks can be placed wherever a statement is expected, such as within control flow constructs.
weight = 5.4
if (weight > 4.4) {
print("Warning: The weight has increased a lot.")
}
Here’s a reworked and reworded version of the text, with "Wysb" replacing "Ghost" and other significant changes:
In Wysb, everything is built from basic object types. All values are treated as objects and may come with their own methods for manipulation. Let’s briefly introduce some key types.
Booleans
Booleans represent logical true or false conditions. Wysb has two boolean values: true
and false
.
Lists
Lists in Wysb are ordered collections of elements that can be of mixed types, each accessible by its index. Lists are created as comma-separated elements and enclosed in square brackets. They can store any kind of value:
[
"Wysb",
57.3,
function (x) {
return x * x
},
]
Each element in a list is accessed by its numerical index, starting from zero.
Maps
Maps, sometimes known as associative arrays, dictionaries, or hashes in other languages, store key-value pairs. Each pair is separated by a colon and the entire structure is enclosed in curly braces. The keys can be strings or other primitive types, and the values can be of any type.
{
"name": "Wysb",
"value": 57.3,
"handler": function(x) { return x * x }
}
Null
Like many programming languages, Wysb has a special value null
, representing the absence of a value. When a function returns nothing, its return value is null
.
Numbers
Wysb simplifies working with numbers by using a single numeric type: decimals. While other languages might differentiate between integers, floats, and doubles, Wysb opts for a single type to make handling numbers straightforward.
Though decimals may be slightly slower than pure integers, Wysb's arbitrary-precision, fixed-point system makes its numbers extremely accurate. Number representations are similar to what you’d expect in other languages:
0
1234 - 5678
3.14159
1.0 - 12.34
Strings
Strings in Wysb represent sequences of bytes, and their length is always non-negative. Strings can be enclosed in either single or double quotes and can handle Unicode characters.
string = "Hello, world!"
Variables in Wysb are named placeholders for storing data. You can create a new variable using the =
operator. For example:
x = 3 * 4
This statement creates a new variable x
in the current scope and assigns it the result of the expression after the =
. Once defined, the variable can be accessed by its name, as shown below:
device = "QuantumProcessor"
print(device) // >> QuantumProcessor
Scope
Wysb enforces block-level scope, meaning a variable is valid from the point it's defined until the end of the block where it was declared.
function demo() {
print(value) // Error: "value" is not yet defined.
value = 42
print(value) // >> 42
}
print(value) // Error: "value" is no longer in scope.
Variables declared at the global level (top-level of the script) are global, while those defined inside functions or blocks are local. It's possible to "shadow" a variable by declaring another one with the same name in a nested scope without causing an error:
name = "global"
function demo() {
name = "local"
print(name) // >> local
}
print(name) // >> global
An operator in Wysb is an element that takes one or more values (or expressions) and produces a new value, forming an expression in itself.
Operators are categorized by the number of values they accept. Unary operators require a single value, such as !
(logical not). Binary operators require two values, like the arithmetic operators +
(addition) and -
(subtraction). Most of Wysb's operators are binary.
Precedence and Associativity
Operator precedence defines how "strongly" an operator binds to its operands. For instance, in the expression 2 + 3 * 4
, the result is 14
because multiplication (*
) has higher precedence than addition (+
). You can use parentheses to override precedence, like in (2 + 3) * 4
, which evaluates to 20
.
Associativity determines how operators of the same precedence are grouped. For example, subtraction (-
) is left-associative, meaning 5 - 3 - 1
is grouped as (5 - 3) - 1
, which results in 1
. Assignment (=
), on the other hand, is right-associative, so x = y = z
is grouped as x = (y = z)
.
While operators follow a specific precedence and associativity, adding parentheses can make the code more readable and explicit about how operations are grouped.
The following table summarizes Wysb's operator precedence from highest to lowest. Operators in the same row have equal precedence:
Precedence | Operator | Description | Associativity |
---|---|---|---|
1 |
() .
|
Grouping, Method call | Left |
2 | - |
Negation | Right |
3 | * / % |
Multiply, Divide, Modulo | Left |
4 | + - |
Add, Subtract | Left |
5 | < <= > >= |
Comparisons | Left |
6 | == != |
Equals, Not equal | Left |
7 | and |
Logical and | Left |
8 | or |
Logical or | Left |
9 | = |
Assignment | Right |
Arithmetic Operators
Basic arithmetic in Wysb works just like the arithmetic you learned in school. Here are some examples:
Example | Name | Result |
---|---|---|
-a |
Negation | Opposite of a
|
a + b |
Addition | Sum of a and b
|
a - b |
Subtraction | Difference between a and b
|
a * b |
Multiplication | Product of a and b
|
a / b |
Division | Quotient of a divided by b
|
a % b |
Modulo | Remainder of a divided by b
|
The modulo operator (%
) retains the sign of the dividend. For example:
print(7 % 4) // >> 3
print(7 % -4) // >> 3
print(-7 % 4) // >> -3
print(-7 % -4) // >> -3
Assignment Operator
The assignment operator in Wysb is =
. It assigns the value of the expression on the right to the variable on the left.
greeting = "Hello, Wysb!"
print(greeting) // >> Hello, Wysb!
Comparison Operators
Comparison operators allow you to evaluate relationships between values. Here's a summary:
Example | Name | Result |
---|---|---|
a == b |
Equal |
true if a is equal to b
|
a != b |
Not equal |
true if a is not equal to b
|
a < b |
Less than |
true if a is less than b
|
a > b |
Greater than |
true if a is greater than b
|
a <= b |
Less than or equal |
true if a is less than or equal to b
|
a >= b |
Greater than or equal |
true if a is greater than or equal to b
|
Here’s the adapted version for Wysb with modified examples and syntax:
Control flow allows you to decide which parts of the code get executed and how often. Conditional statements decide whether a block of code should run, while loops repeat actions until certain conditions are met.
Truthiness
In Wysb, every value has an inherent truthiness. Values like 0
, false
, or null
are considered falsy, while most others are truthy. These evaluations are useful for logical expressions and condition checks.
Logical Operators
Wysb features two special logical operators: and
and or
. These operators use short-circuit evaluation, meaning they evaluate the left operand first and, depending on its value, may skip evaluating the right operand.
- The
and
operator evaluates the left-hand side. If it’s falsy, it returns that value immediately. If it's truthy, it evaluates and returns the right-hand value.
print(false and 10) // >> false
print(true and 5) // >> 5
- The
or
operator works the opposite way. If the left-hand side is truthy, it’s returned; otherwise, the right-hand value is evaluated and returned.
print(false or 10) // >> 10
print(true or 5) // >> true
Conditional Statements
Conditional statements allow you to execute certain blocks of code based on whether a condition is true or false.
If Statements
The simplest form of conditional control in Wysb is the if
statement. It checks if a condition is true and, if so, executes the corresponding block of code.
if (condition) {
// Execute this block if condition is true
}
You can also add an else
clause to handle cases where the condition is false.
if (condition) {
// Do this if condition is true
} else {
// Do this if condition is false
}
For multiple conditions, you can use else if
to chain them:
if (firstCondition) {
// Do this if firstCondition is true
} else if (secondCondition) {
// Do this if secondCondition is true
} else {
// Do this if none of the above are true
}
Switch Statements
The switch
statement allows you to evaluate a single expression and match its result against several cases, making it more concise than multiple if-else
statements.
switch (value) {
case 1 {
// Handle case where value is 1
}
case 2 {
// Handle case where value is 2
}
default {
// Handle all other cases
}
}
In Wysb, cases do not fall through like in some other languages. Only the first matching case is executed. You can also group multiple cases together:
switch (value) {
case 0, 1, 2 {
// Handle cases where value is 0, 1, or 2
}
case 3, 4, 5 {
// Handle cases where value is 3, 4, or 5
}
}
Looping Statements
Loops are useful for executing code multiple times. Wysb offers several ways to create loops.
While Statement
The while
loop repeatedly executes a block of code as long as a specified condition is true.
while (condition) {
// Do something as long as condition is true
}
For example, the following loop increments x
while n
is less than 5:
n = 0
x = 0
while (n < 5) {
n = n + 1
x += n
}
print(x) // >> 15
For Statement
The for
loop in Wysb works similarly to C-style loops. It consists of an initializer, a condition, and an increment.
for (initializer; condition; increment) {
// Do something
}
Here’s an example that counts from 1 to 5:
for (i = 1; i <= 5; i = i + 1) {
print(i)
}
Break Statement
The break
statement immediately exits the nearest enclosing loop, skipping any further iterations.
function testBreak(x) {
i = 0
while (i < 6) {
if (i == 3) {
break
}
i = i + 1
}
return i * x
}
print(testBreak(2)) // >> 6
Continue Statement
The continue
statement skips the rest of the current iteration and moves on to the next one.
i = 0
n = 0
while (i < 5) {
i = i + 1
if (i == 3) {
continue
}
n = n + 1
}
print(n) // >> 4
In this example, when i
equals 3, the loop skips the rest of the iteration and increments i
, so n
is not updated for that cycle.
Here is the version in English with examples and syntax adapted for Wysb:
Like in many dynamic languages, functions are first-class values in Wysb. This means they can be stored in variables, passed as arguments to other functions, and returned as results. This gives great flexibility when using functions in the language.
Defining Functions
You can define functions in Wysb using the function
keyword, followed by a list of parameters and a block of code:
function sum(a, b) {
print(a + b)
}
The body of a function is always a block of code. To return a value, you use the return
statement inside the function.
function sum(a, b) {
return a + b
}
Calling Functions
Once you have a function, calling it is simple: just pass the required parameters along with the function name.
result = sum(2, 3)
print(result) // >> 5
The value assigned to the variable result
is the result of the function execution, either from an explicit return
or from the last evaluated value in the function body.
Anonymous Functions
In Wysb, functions can also be anonymous, meaning they don’t have a name and can be directly assigned to variables. These functions can be passed as arguments to other functions, stored, or returned.
add = function(a, b) {
return a + b
}
print(add(3, 4)) // >> 7
First-Class Functions
Functions in Wysb are first-class values, meaning they can be used like any other value. They can be assigned to variables, passed as arguments, or even returned by other functions.
function identity(x) {
return x
}
function multiply(a, b) {
return a * b
}
result = identity(multiply)(3, 5)
print(result) // >> 15
In the example above, the identity
function returns the multiply
function, which is then called with the arguments (3, 5)
.
Local Functions
In Wysb, you can declare local functions within other functions. These functions are scoped locally and can only be accessed inside the function that defines them.
function outerFunction() {
function localFunction() {
print("I'm local!")
}
localFunction() // >> I'm local!
}
outerFunction()
The localFunction
exists only within the scope of outerFunction
and cannot be accessed from outside.
Functions Returning Functions
Just like you can pass functions as arguments, in Wysb, you can return a function from another function.
function returnFunction() {
word = "returned"
function innerFunction() {
print(word)
}
return innerFunction
}
newFunction = returnFunction()
newFunction() // >> returned
In the example above, the returnFunction
returns the innerFunction
, which still has access to the variable word
even after returnFunction
has executed.
Here is the version in English with examples and syntax adapted for Wysb:
Errors are an inevitable part of writing and running code, and Wysb provides mechanisms for detecting, reporting, and managing errors. In this section, we'll explore the different types of errors you might encounter and how Wysb handles them.
Syntax Errors
The first type of errors you are likely to encounter are syntax errors. These occur when the code you write doesn't follow the grammar rules of Wysb. For example:
1 + * 2
When Wysb encounters a syntax error, it will provide a helpful error message that points out where the problem is. For example:
[line 1] Error at '*': Expecting a valid expression.
Another common type of syntax error is using an undefined variable:
print(message)
Wysb will report this as:
1:7:repl.wysb: runtime error: unknown identifier: message
Unlike some other languages, Wysb catches these kinds of errors before it tries to run the code. This ensures that your code is free from syntax and scope-related issues before execution begins.
Runtime Errors
Even after your code passes the syntax check, there could still be errors that only occur while the code is running. These are called runtime errors. Runtime errors typically happen when your code tries to perform an action that Wysb cannot handle.
For instance, if you attempt to call a method that doesn’t exist on an object, Wysb will throw a runtime error. Consider the following example:
class Foo {
function bar() {
// some code
}
}
foo = Foo.new()
foo.someUndefinedMethod()
Running this will generate the following error:
1:4:repl.wysb: runtime error: undefined method someUndefinedMethod for class Foo
Wysb halts execution immediately after encountering a runtime error. Unlike some languages that try to continue executing after an error occurs, Wysb stops to ensure you’re aware of the problem.
Creating Runtime Errors
In some cases, you may want to deliberately trigger a runtime error in your code. Wysb allows you to do this using the abort()
function. This can be useful for ensuring that certain conditions are met or for debugging purposes.
abort("Something went wrong!")
This will immediately stop the program and print the following error:
runtime error: Something went wrong!
You must pass a string as the error message. If no message is provided, or if it's null
, the abort()
function will not raise an error.
Here's the section on Classes translated into Wysb with similar syntax and examples:
Classes
Classes in Wysb define the behavior and state of objects. The behavior is determined by methods within the class, and each object of the same class supports these methods. The state is maintained through properties, which are unique to each instance of the class.
Defining a Class
You create a class using the class
keyword:
class CoffeeMaker {
//
}
This creates a class named CoffeeMaker
with no methods or properties.
Methods
To add functionality to your class, you define methods:
class CoffeeMaker {
function brew() {
print("Your coffee is now brewing.")
}
}
Here, the brew
method prints a message. You can also add parameters to methods:
class CoffeeMaker {
function brew(dosage, temperature) {
print("Your %s of coffee is now brewing at %s degrees.".format(dosage, temperature))
}
}
Method Scope
In object-oriented languages like Wysb, there is another kind of scope: object scope. When you call a method on an object, you're accessing the method in the object's scope.
For example:
CoffeeMaker.brew()
This looks up the brew
method in the scope of the CoffeeMaker
class.
The this
Keyword
Within a method, you often need to refer to the current instance of the object. You can do this using this
:
class CoffeeMaker {
function setGrind(grind) {
this.grind = grind
}
function printGrind() {
this.setGrind("course")
print(this.grind)
}
}
this
refers to the instance of the class whose method is currently being executed. It allows you to access and modify properties and call other methods on the same instance.
You cannot use this
outside of a method, but inside a method, this
always refers to the current instance:
class CoffeeMaker {
function setGrind(grind) {
this.grind = grind
}
function printGrindThrice() {
this.setGrind("course")
for (i in 1 .. 3) {
print(this.grind)
}
}
}
In Wysb, this
retains its reference to the original object even within callbacks.
Constructors
To create instances of a class, you define a constructor. The constructor is a special method that initializes new objects:
class CoffeeMaker {
function constructor(grind, temperature) {
print("Grind set to: %s".format(grind))
print("Temperature set to: %s".format(temperature))
}
}
When you create a new instance, Wysb automatically calls the constructor:
drip = CoffeeMaker.new("flat", "200")
chemex = CoffeeMaker.new("coarse", "202")
The constructor initializes the instance with specified values and sets properties accordingly.
Properties
Properties store the state of an instance and are similar to variables but are bound to the instance:
class CoffeeMaker {
function constructor(grind, temperature) {
this.grind = grind
this.temperature = temperature
this.printSettings()
}
function printSettings() {
print("Grind set to: %s".format(this.grind))
print("Temperature set to: %s".format(this.temperature))
}
}
Properties like this.grind
and this.temperature
are unique to each instance.
Inheritance
A class can inherit from another class using the extends
keyword. This allows the new class to use methods and properties from the parent class:
class Bar extends Foo {
//
}
The Bar
class inherits from Foo
and can use its methods and properties.
Traits
Wysb also supports traits, which are like classes but cannot be instantiated. Traits are used to share methods and properties between classes:
trait Brewable {
function brew() {
print("Your coffee is now brewing.")
}
}
To use a trait in a class, use the use
keyword:
class CoffeeMaker {
use B
}
You can include multiple traits by separating them with commas:
class CoffeeMaker {
use C, B
}
This way, CoffeeMaker
gets methods from both C
and B
.
Here's the section on Method Calls adapted to Wysb with new examples but respecting the syntax, semantics, and rules of the language:
Wysb is highly object-oriented, meaning that most of the code you write will involve calling methods on objects. A typical method call looks like this:
cat.purr("Happy purring!")
In this example, you have a receiver expression (cat
), followed by a .
(dot), then a method name (purr
), and an argument list in parentheses (("Happy purring!")
). If there are multiple arguments, they are separated by commas:
cat.perform("jump", "high")
The argument list can also be empty:
cat.sleep()
When Wysb executes a method call, it follows these steps:
- Evaluate the Receiver and Arguments: It evaluates the receiver and arguments from left to right.
- Look Up the Method: It searches for the method on the object that the receiver refers to.
- Invoke the Method: It calls the method with the provided argument values.
Here's how to structure the documentation for modules in Wysb:
Wysb uses a straightforward module system to organize and manage code in separate files.
Each Wysb file operates as its own module with its own scope. Importing a file into another does not explicitly merge their scopes. This means two modules can define top-level variables with the same name without causing conflicts.
Wysb manages imported files efficiently. Importing a module in different places does not reload or re-evaluate that module each time. The first import of a module is the only instance where it is loaded and evaluated.
All top-level variables within a module can be exported. To import specific values, you list the identifiers in your import statement:
import Request, Response from "network"
This imports and binds the Request
and Response
variables from the network
module, making them available in your file.
You can import a variable under a different name using the as
keyword:
import str as isString from "utils"
To import every value from a module, use the *
wildcard:
import * from "utils"
Wysb supports cyclic imports to some extent, though they should be avoided if possible. Wysb tracks imported modules and can handle cyclic imports without getting stuck in an infinite loop:
// main.wys
import "a"
// a.wys
print("start a")
import "b"
print("end a")
// b.wys
print("start b")
import "c"
print("end b")
// c.wys
print("start c")
import "a"
print("end c")
Running the above code will produce:
start a
start b
start c
end c
end b
end a
The program prints the output correctly without falling into an infinite loop.
You can import a module solely for its side effects, without importing any values:
import "config"
This will execute the global code of the module but won’t bind any new variables.
Here's a basic example to illustrate how modules work in Wysb:
File Structure:
main.wys
modules/
functions.wys
variables.wys
main.wys
import * from "modules/functions"
import * from "modules/variables"
print("Program loaded.")
add(one, two)
greet(hello)
functions.wys
function add(a, b) {
print(a + b)
}
function greet(message) {
print(message)
}
variables.wys
one = 1
two = 2
hello = "Hello, world!"
This example demonstrates how to load modules easily. Modules in Wysb are just files with the .wys
extension and can be organized in any directory within your project. In this example, we used a modules
directory for clarity.
Lists in Wysb are ordered collections of elements that can be of various types, identified by a numerical index. You can create a list using square brackets and separate elements with commas. For example:
[
"Wysb",
42,
function (x) {
return x + 1
},
]
Accessing Elements
To access elements in a list, use the subscript operator with the index of the element. Note that indices start at zero:
words = ["begin", "continue", "end"]
print(words[0]) // >> begin
print(words[1]) // >> continue
print(words[2]) // >> end
Methods
Here are the methods available for lists in Wysb:
-
first()
Returns the first element in the list. If the list is empty, it returnsnull
:[5, 10, 15].first() // >> 5
-
join(separator)
Joins the items in a list into a string, using the specified separator:[5, 10, 15].join('-') // >> 5-10-15
-
last()
Returns the last element in the list. If the list is empty, it returnsnull
:[5, 10, 15].last() // >> 15
-
length()
Returns the number of elements in the list:[5, 10, 15].length() // >> 3
-
pop()
Removes the last element from the list and returns it:list = [5, 10, 15] list.pop() // >> 15 print(list) // >> [5, 10]
-
push(element)
Adds an element to the end of the list:list = [5, 10] list.push(15) print(list) // >> [5, 10, 15]
-
tail()
Returns a new list containing all elements except the first:[5, 10, 15].tail() // >> [10, 15]
-
toString()
Returns a string representation of the list:[5, 10, 15].toString() // >> [5, 10, 15]
Maps in Wysb, also known as associative arrays, hashes, or dictionaries in other languages, store collections of key-value pairs. Maps are constructed using curly braces with comma-separated key-value pairs. Each pair is separated by a colon.
{
"title": "Wysb",
"version": 1.0,
"function": function(x) { return x + 10 }
}
Accessing Elements
To access an element in a map, use the subscript operator with the key:
ages = { Alice: 30, Bob: 25, Charlie: 35 }
print(ages["Alice"]) // >> 30
print(ages["Bob"]) // >> 25
print(ages["Charlie"]) // >> 35
If you attempt to access a key that does not exist in the map, it will return null
:
print(ages["David"]) // >> null
Strings are used for representing text data in Wysb.
Creating Strings
Strings can be created using either single or double quotes:
text1 = "A sample string"
text2 = 'Another example string'
Comparing Strings
Strings can be compared using the ==
operator. This comparison is case-sensitive:
text1 = "hello"
text2 = "world"
// false
result = text1 == text2
Long Strings
To handle long strings that span multiple lines, you can concatenate strings using the +
operator:
longText = "This is a long string that needs " +
"to be split across multiple lines " +
"to keep the code readable."
Methods
Here are some useful string methods in Wysb:
-
find()
Finds the first occurrence of a pattern in the string using a regular expression:
pattern = "I need (.*)" input = "I need tea" result = pattern.find(input) // expected output: ["tea"]
-
format()
Formats a string according to specified placeholders:
name = "Alex" age = 25 message = "%s is %s years old.".format(name, age) // expected output: "Alex is 25 years old."
-
endsWith()
Checks if the string ends with a specified value:
result = "This is my hobby".endsWith("hobby") // expected value: true
-
length()
Returns the length of the string:
length = "Programming".length() // expected value: 11
-
replace()
Replaces occurrences of a substring with a new substring:
replaced = "Code 1.0".replace("1.0", "2.0") // expected value: "Code 2.0"
-
split()
Splits the string into a list based on a delimiter:
parts = "apple, banana, cherry".split(", ") // expected value: ["apple", "banana", "cherry"]
-
startsWith()
Checks if the string starts with a specified value:
result = "This is my name".startsWith("This") // expected value: true
-
toLowerCase()
Converts the string to lowercase:
lower = "HELLO".toLowerCase() // expected value: "hello"
-
toUpperCase()
Converts the string to uppercase:
upper = "hello".toUpperCase() // expected value: "HELLO"
-
toString()
Converts the value to a string (this is useful for ensuring the value is a string):
str = 123.toString() // expected value: "123"
-
toNumber()
Converts the string to a number if the string represents a valid number:
number = "42.5".toNumber() // expected value: 42.5
-
trim()
Removes whitespace from both ends of the string:
trimmed = " Wysb ".trim() // expected value: "Wysb"
-
trimEnd()
Removes whitespace from the end of the string:
trimmedEnd = " Wysb ".trimEnd() // expected value: " Wysb"
-
trimStart()
Removes whitespace from the start of the string:
trimmedStart = " Wysb ".trimStart() // expected value: "Wysb "
Wysb uses a single numeric type: arbitrary-precision fixed-point decimals. This approach simplifies usage compared to having multiple numeric types such as integers and floats, though it might have a minor impact on performance.
Number Values
Numbers in Wysb are represented as fixed-point decimals, which ensures high precision. Examples of numeric values are:
0
5678
-1234
0.01
2.71828
45.67
-0.001
Precision
Unlike binary floating-point types, which can introduce rounding errors in fractional decimal representations, Wysb maintains accuracy in its arithmetic operations. For example:
value = 0
for (i = 0; i < 1000; i = i + 1) {
value = value + 0.01
}
print(value)
// expected value: 10
This code snippet will accurately print 10
, avoiding the rounding issues seen in languages with binary floating-point types.
Wysb numbers can represent values with up to 2^31 digits after the decimal point.
Scientific Notation
You can express numeric values in scientific notation using e
, which denotes a power of 10:
1.2 // expected value: 1.2
1.2e0 // expected value: 1.2
1.2e1 // expected value: 12.0
1.2e2 // expected value: 120.0
1.2e3 // expected value: 1200.0
5e-2 // expected value: 0.05
Methods
Here are some methods available for numeric values in Wysb:
-
round()
Rounds the number to the nearest integer or specified precision:
rounded = 456.78.round() // expected value: 457 rounded = 456.789.round(2) // expected value: 456.79
-
toString()
Converts the number to its string representation:
stringValue = 3.14159.toString() // expected value: "3.14159"
The console
object provides methods for outputting different types of messages to the console. These methods help in debugging, logging, and reporting information and errors.
Methods
-
console.debug()
Outputs a debug message to the console, useful for detailed troubleshooting information.
console.debug("This is a debug message")
-
console.error()
Outputs an error message to the console, typically used for reporting errors or problems that need attention.
console.error("This is an error message")
-
console.info()
Outputs an informative message to the console, used for general information and status updates.
console.info("This is an info message")
-
console.log()
Outputs a general logging message to the console. This is the most commonly used method for general-purpose logging.
console.log("This is a log message")
-
console.warn()
Outputs a warning message to the console, indicating potential issues or warnings that are not necessarily errors but should be noted.
console.warn("This is a warning message")
The http
object provides methods for handling HTTP requests and starting an HTTP server. These methods facilitate building web servers and handling incoming requests.
Methods
-
http.handle()
Registers a new handler for a specified URL pattern. This method maps incoming requests to a handler function that processes the request and generates a response.
http.handle("/", function(request) { print("hello world") })
-
http.listen()
Starts an HTTP server at the given port and address. Executes the provided callback function when the server successfully starts.
http.listen(3000, function() { print("Server started at http://localhost:3000 🌱") })
The io
object provides methods for handling file operations, including appending, reading, and writing content to files. These methods facilitate file manipulation and data management within your Wysb programs.
Methods
-
io.append()
Appends content to the end of the specified file. This method adds new data to the existing file without altering its current contents.
io.append('./log.txt', 'message from wysb')
-
io.read()
Reads and returns the entire contents of the specified file as a string. This method allows you to access the data stored in a file.
content = io.read('./log.txt')
-
io.write()
Writes content to the specified file, completely overriding any existing data. This method replaces the file's contents with the new data provided.
io.write('New content for the file', './log.txt')
JSON
The json
module provides methods for converting values to and from JavaScript Object Notation (JSON), a widely used format for data interchange. JSON supports serialization of maps, lists, numbers, strings, booleans, and null values. It is both language-agnostic and commonly utilized in various programming environments.
Methods
-
json.encode()
Encodes a value into a JSON string. The value provided must be a map or a list.
json.encode({ name: 'Alice', age: 30 }) // '{"name":"Alice","age":30}' json.encode([10, 20, 30]) // '[10,20,30]'
-
json.decode()
Decodes a JSON string into its corresponding value. The input string must be valid JSON.
json.decode('{"name":"Alice","age":30}') // { name: 'Alice', age: 30 } json.decode('[10,20,30]') // [10, 20, 30]
The math
module provides a set of functions and constants for performing mathematical operations and calculations.
Methods
-
math.abs()
Returns the absolute (non-negative) value of a given number.
math.abs(-100) // expected value: 100
-
math.cos()
Returns the cosine of the specified radian value.
math.cos(math.pi / 2).round() // expected value: 0
-
math.isNegative()
Determines if the specified value is negative.
math.isNegative(-100) // expected value: true
-
math.isPositive()
Determines if the specified value is positive.
math.isPositive(100) // expected value: true
-
math.isZero()
Determines if the specified value is zero.
math.isZero(5) // expected value: false
-
math.max()
Returns the largest of the two given numbers.
math.max(100, 200) // expected value: 200
-
math.min()
Returns the smallest of the two given numbers.
math.min(100, 200) // expected value: 100
-
math.sin()
Returns the sine of the specified radian value.
math.sin(math.pi).round() // expected value: 0
-
math.tan()
Returns the tangent of the specified radian value.
math.tan(0).round() // expected value: 0
Properties
-
math.e
The mathematical constant e = 2.718281…, which can be rounded to a desired precision.
math.e.round(2) // expected value: 2.72
-
math.epsilon
The mathematical constant 𝜀 = 2.220446049250e-16, representing the smallest difference from zero in floating-point numbers.
math.epsilon // expected value: 0.00000000000000022204460492503130808472633361816
-
math.pi
The mathematical constant π = 3.141592…, which can be rounded to a desired precision.
math.pi.round(2) // expected value: 3.14
-
math.tau
The mathematical constant τ = 6.283185…, which can be rounded to a desired precision.
math.tau.round(2) // expected value: 6.28
The os
module provides methods and properties related to the operating system environment, including command-line arguments, time measurements, and system information.
Methods
-
os.args()
Returns a list of command-line arguments passed to a Wysb script. The first element
[0]
is the script name.os.args()
-
os.clock()
Returns the number of nanoseconds elapsed since January 1, 1970 UTC (Unix epoch).
os.clock() // expected value: 1643344902998773000
-
os.exit()
Causes the current program to exit with the given status code and optional message. Code zero typically indicates success, while non-zero codes indicate errors. The program terminates immediately.
os.exit(0, "Process completed successfully") // expected output: Process completed successfully
Properties
-
os.name
Returns the name of the current operating system. Possible values include:
-
darwin
(macOS) linux
windows
os.name // expected value: darwin
-
The random
module provides methods for generating random numbers and controlling the randomness.
Methods
-
random.seed()
Initializes the random number generator. This method accepts an optional seed value. If no seed is provided, the current time is used. To generate the same sequence of random numbers across multiple runs of your program, you should provide a specific seed value.
random.seed()
-
random.random()
Returns a random floating-point number between 0 and 1. You can optionally specify a minimum and maximum value. The returned value will be within the specified range, inclusive.
random.random() // Returns a number between 0 and 1 random.random(0, 10) // Returns a number between 0 and 10 random.random(10) // Returns a number between 0 and 10 (default minimum is 0)
Properties
-
random.seed
The current seed value of the random number generator.
random.seed
The time
module provides methods and properties related to time, allowing you to work with time intervals and obtain the current time.
Methods
-
time.now()
Returns the current time in seconds elapsed since January 1, 1970 UTC, known as the Unix epoch.
now = time.now() // Example output: 1519316240
-
time.sleep()
Pauses the execution of the program for the specified number of milliseconds.
// Sleep for 1 second time.sleep(1000)
Properties
-
time.nanosecond
The numerical value of a nanosecond in seconds.
nanosecond = time.nanosecond // Expected value: 0.000000001
-
time.microsecond
The numerical value of a microsecond in seconds.
microsecond = time.microsecond // Expected value: 0.000001
-
time.millisecond
The numerical value of a millisecond in seconds.
millisecond = time.millisecond // Expected value: 0.001
-
time.second
The numerical value of a second.
second = time.second // Expected value: 1
-
time.minute
The numerical value of a minute in seconds.
minute = time.minute // Expected value: 60
-
time.hour
The numerical value of an hour in seconds.
hour = time.hour // Expected value: 3600
-
time.day
The numerical value of a day in seconds.
day = time.day // Expected value: 86400
-
time.week
The numerical value of a week in seconds.
week = time.week // Expected value: 604800
-
time.month
The numerical value of a month in seconds.
month = time.month // Expected value: 2592000 (approximate, assuming 30 days per month)
-
time.year
The numerical value of a year in seconds.
year = time.year // Expected value: 31536000 (approximate, assuming 365 days per year)