Sn2 Language Documentation

Welcome to the official documentation for Sn2, a modern, simple scripting language that runs on a powerful and well-established runtime. Sn2 files use the .sn2 extension.

The name 'Sn2' is derived from its developer, Souvik Nandi (Sn), with the '2' representing the second version of this language concept.

Syntax Basics

Comments: Sn2 supports single-line comments with // and multi-line comments with /* ... */.

// This is a single-line comment

/* This is a
multi-line comment. */

Variables: Declare variables using the let keyword.

let my_variable = "Hello, World!"
let a_number = 123

Printing: Use the show command to print to the console.

let version = "1.0"
// You can use + for concatenation or use string formatting (see below)
show "Current version: " + version

User Input: Get input from the user with the input() function.

Null Value: Use the null keyword to represent the absence of a value. Example: let data = null

Booleans

Sn2 supports boolean logic with the literal values true and false.

let is_active = true
if (is_active) { show "System is active." }

Operators

Sn2 supports standard arithmetic and logical operators. It also includes a ternary operator for concise conditional assignments.

Ternary Operator:

let age = 20
let status = (age >= 18) ? "Adult" : "Minor"
show "Status: \${status}" // Output: Status: Adult

Compound Assignment Operators:

let score = 100
score -= 10 // Equivalent to: let score = score - 10
show "New score: \${score}" // Output: New score: 90

Membership Operator:

let my_list = [10, 20, 30]
if (20 in my_list) { show "Found it!" }

Negative Membership Operator:

let my_list = [10, 20, 30]
if (40 not in my_list) { show "40 is not in the list." }

Strict Equality Operators:

let value = 30
if (value === 30) { show "Value is strictly 30." }
if ("30" !== 30) { show "String '30' is not strictly equal to number 30." }

Data Structures

Lists (Arrays): Ordered collections created with square brackets [].

let my_list = [10, "hello", true]
show my_list[1] // Output: hello

Dictionaries (Maps): Key-value pairs created with curly braces .

let person = {"name": "Alex", "age": 25}
show person["name"] // Output: Alex

Tuples: Ordered, unchangeable collections created with parentheses (). They are useful for fixed collections of data.

let point = (10, 20)
show "X coordinate: \${point[0]}" // Output: X coordinate: 10

Sets: Unordered collections of unique items. They are created with curly braces , but without key-value pairs.

let my_set = {1, 2, 2, 3, "hello"}
show my_set // Output: {1, 2, 3, 'hello'} (order may vary)

List Comprehensions

Sn2 supports a concise and powerful syntax for creating new lists based on existing ones. This is often more readable than using a traditional loop.

let numbers = [1, 2, 3, 4, 5]

// Create a new list with each number squared
let squares = [n*n for n in numbers]
show squares // Output: [1, 4, 9, 16, 25]

// Create a new list with only the even numbers, squared
let even_squares = [n*n for n in numbers if n % 2 == 0]
show even_squares // Output: [4, 16]

List Destructuring

Sn2 allows you to unpack values from a list into distinct variables in a single line, which can make your code cleaner and more readable.

let point = [10, 20, 30]
let [x, y, z] = point
show "x=\${x}, y=\${y}, z=\${z}" // Output: x=10, y=20, z=30

Type Casting

Sn2 provides built-in functions to explicitly convert values between different types.

  • toInt(value): Converts a value to an integer.
  • toFloat(value): Converts a value to a floating-point number.
  • toString(value): Converts a value to a string.
let num_string = "123.45"
let my_float = toFloat(num_string)
let my_int = toInt(my_float) // Note: This will truncate, resulting in 123
show "Integer: \${my_int}, String: \${toString(my_float)}"

Built-in Functions

Sn2 provides several globally available functions for common tasks.

  • input(prompt): Reads a line of input from the user.
  • len(collection): Returns the number of items in a collection.
  • iter(collection): Returns an iterator object for a collection.
  • next(iterator): Retrieves the next item from an iterator.
let my_list = [1, 2, 3, 4]
show "The list has \${len(my_list)} items."

let name = input("Enter your name: ")
show "Your name has \${len(name)} characters."

Control Flow

Conditionals: Use if/else if/else with conditions in parentheses and blocks in curly braces.

let score = 85
if (score > 90) {
    show "Grade: A"
} else if (score > 80) {
    show "Grade: B"
} else {
    show "Grade: C or lower"
}

Match Statement: Use a match statement for more complex conditional logic. Use case _: for a default case.

let status_code = 404

match (status_code) {
    case 200: { show "OK" }
    case 404: { show "Not Found" }
    case 500: { show "Server Error" }
    case _: { show "Unknown status" }
}
// Output: Not Found

While Loop:

let count = 3
while (count > 0) {
    show count
    let count = count - 1
}

For-Each Loop:

let my_items = ["one", "two", "three"]
loop item in my_items {
    show "Item: " + item
}

Range Loop: Use the .. operator for ranges.

// This will print numbers 0 through 9
loop i in 0..10 {
    show "Current number: " + i
}

Iterators

For more granular control over looping, create an iterator from a collection using iter() and get the next item with next().

let numbers = [10, 20, 30]
let num_iter = iter(numbers)

show next(num_iter) // Output: 10
show next(num_iter) // Output: 20

Functions

Define functions with the func keyword.

func add(x, y) {
    return x + y
}
let sum = add(10, 5)
show "The sum is: " + sum // Output: The sum is: 15

Decorators

Modify a function's behavior using decorators with the @ symbol.

func simple_logger(fn) {
    func wrapper() {
        show "Calling function..."
        fn()
        show "Function call finished."
    }
    return wrapper
}

@simple_logger
func say_hello() {
    show "Hello, Sn2!"
}

say_hello()

Classes and Objects (OOP)

Sn2 supports OOP. The constructor is named init, and instance members are accessed with this.

class Dog {
    func init(this, name) {
        let this.name = name
    }

    func bark(this) {
        show this.name + " says: Woof!"
    }
}

let my_dog = Dog("Rex")
my_dog.bark() // Output: Rex says: Woof!

Inheritance

Inherit from a parent class using <. Call parent methods using super.

class Animal {
    func init(this, name) {
        let this.name = name
    }
    func speak(this) {
        return this.name + " makes a sound."
    }
}

class Dog < Animal {
    func init(this, name, breed) {
        super.init(name)
        let this.breed = breed
    }

    func speak(this) {
        let parent_sound = super.speak()
        show parent_sound + " Specifically, it barks."
    }
}

let my_dog = Dog("Buddy", "Golden Retriever")
my_dog.speak()
// Output: Buddy makes a sound. Specifically, it barks.

Lambda Functions

Create small, anonymous functions using the arrow syntax =>.

let multiply = (a, b) => a * b
let product = multiply(7, 6)
show "The product is: " + product // Output: The product is: 42

Math Library

Sn2 includes a built-in Math object for mathematical functions and constants.

let num = 64
let square_root = Math.sqrt(num)
show "The square root of " + num + " is " + square_root

String Formatting

Embed variables into strings using the \${...} syntax.

let user = "Souvik"
let score = 95
let message = "User \${user} has a score of \${score}."
show message // Output: User Souvik has a score of 95.

String Methods

Strings come with a variety of built-in methods for manipulation.

let greeting = "   Hello, World!   "

show greeting.upper() // "   HELLO, WORLD!   "
show greeting.lower() // "   hello, world!   "
show greeting.strip() // "Hello, World!"

let filename = "document.pdf"
if (filename.endsWith(".pdf")) { show "It's a PDF file." }
if (greeting.strip().startsWith("Hello")) { show "It starts with Hello." }

JSON Handling

Use the built-in JSON object to parse and stringify JSON data.

let json_string = '{"name": "Alice", "id": 123}'
let data = JSON.parse(json_string)
show "Parsed name: \${data["name"]}"

let user_obj = { "user": "Bob", "active": true }
let new_json_string = JSON.stringify(user_obj)
show new_json_string

let pretty_json = JSON.stringify(user_obj, 4)
show pretty_json

Regular Expressions

Use the built-in RegEx object for pattern matching.

let text = "My email is example@sn2.dev, please write to me."
let emails = RegEx.findall(r"[\\w\\.-]+@[\\w\\.-]+", text)
show "Found emails: \${emails}"

let sanitized_text = RegEx.sub(r"[\\w\\.-]+@[\\w\\.-]+", "[REDACTED]", text)
show sanitized_text

Date & Time

Use the built-in DateTime object for working with dates and times.

let now = DateTime.now()
let formatted_date = now.strftime("%Y-%m-%d %H:%M:%S")
show "The current date and time is: \${formatted_date}"

Modules

Organize code into multiple files and use import to use them.

math_utils.sn2:

// File: math_utils.sn2
func add(a, b) {
    return a + b
}
let E = 2.71828

main.sn2:

// File: main.sn2
import "math_utils" as mu

let result = mu.add(5, 3)
show "The result is: \${result}"

Error Handling

Handle potential errors using a try/catch block.

try {
    let result = 10 / 0
} catch (e) {
    show "An error occurred: " + e
}

Running Code

To run an Sn2 file, use the runsn command:

runsn example.sn2