Examples

This section provides examples of Ziv code to help you get started with the language. Each example demonstrates a specific feature or concept of Ziv and includes an explanation of the code.

Table of Contents

Hello, World!

The classic "Hello, World!" program is a simple example that demonstrates how to print text to the console. Here is the Ziv code for the "Hello, World!" program:

fn main():
    print("Hello, World!")

In this example, we define a function called main that prints the text "Hello, World!" to the console using the print function. The print function is a built-in function in Ziv that outputs text to the standard output.

To run this program, save the code to a file with a .ziv extension (e.g., hello.ziv) and use the Ziv interpreter to execute the file:

zivc hello.ziv

This will compile the Ziv code and generate an executable file that you can run to see the output "Hello, World!" printed to the console.

Basic Calculator

This example demonstrates how to create a simple calculator program in Ziv that can perform basic arithmetic operations. Here is the Ziv code for the basic calculator program:

fn add(a: int, b: int) -> int:
    return a + b

fn subtract(a: int, b: int) -> int:
    return a - b

fn multiply(a: int, b: int) -> int:
    return a * b

fn divide(a: int, b: int) -> int:
    if b == 0:
        error("Division by zero is not allowed")
    return a / b

fn main():
    num1: int = 10
    num2: int = 5

    print("Addition: ", add(num1, num2)) # Output: 15
    print("Subtraction: ", subtract(num1, num2)) # Output: 5
    print("Multiplication: ", multiply(num1, num2)) # Output: 50
    print("Division: ", divide(num1, num2)) # Output: 2

In this example, we define four functions for performing basic arithmetic operations: add, subtract, multiply, and divide. These functions take two integer arguments and return the result of the corresponding operation. The divide function also includes a check to prevent division by zero.

Fibonacci Sequence

The Fibonacci sequence is a series of numbers in which each number is the sum of the two preceding ones. This example demonstrates how to generate the Fibonacci sequence in Ziv using a recursive function. Here is the Ziv code for the Fibonacci sequence program:

fn fibonacci(n: int) -> int:
    if n <= 1:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

fn main():
    num_terms: int = 10

    for i: int in range(num_terms):
        print(fibonacci(i))

In this example, we define a recursive function called fibonacci that calculates the nth term of the Fibonacci sequence. The main function generates and prints the first num_terms terms of the Fibonacci sequence using a loop.

List Operations

This example demonstrates how to perform basic operations on lists in Ziv, such as creating a list, adding elements to a list, and iterating over a list. Here is the Ziv code for the list operations program:


fn main():
    # Create a list of integers
    numbers: list[int] = [1, 2, 3, 4, 5]

    # Add an element to the list
    numbers.append(6)

    # Iterate over the list and print each element
    for (num: int in numbers):
        print(num)

    # Output: 1 2 3 4 5 6

Error Handling

This example demonstrates how to handle errors in Ziv using the error function. Here is the Ziv code for the error handling program:


fn read_file(file_path: string) -> string:
    if file_path == "":
        error("File path cannot be empty")
    # Read the file
    return "File content"

In this example, we define a function called read_file that takes a file_path argument. If the file_path is empty, the function calls the error function with an error message. This will terminate the program and print the error message to the console.

Custom Error Types

This example demonstrates how to define custom error types in Ziv to represent different kinds of errors in your programs. Custom error types allow you to create structured error messages with additional information and context, making it easier to handle and report errors in your code. Here is an example of defining custom error types in Ziv:

error DivisionByZero:
    message: string

fn divide(a: int, b: int) -> int:
    if b == 0:
        throw DivisionByZero("Division by zero is not allowed")
    return a / b

In this example, we define a custom error type DivisionByZero with a message field to represent a division by zero error. We then define a function divide that checks for a division by zero condition and throws a DivisionByZero error with an error message if the condition is met. By using custom error types, you can provide more context and information about the error, making it easier to handle and report errors in your programs.

Modules and Extensions

This example demonstrates how to create and use modules and extensions in Ziv. Modules allow you to organize your code into reusable components, while extensions enable you to add new functionality to existing types. Here is an example of creating a module and an extension in Ziv:

  • Module: math.ziv

    # math.ziv
    
    module math version "1.0"
    
    fn add(a: int, b: int) -> int:
        return a + b
    
    fn subtract(a: int, b: int) -> int:
        return a - b
    
    end module
    
    # main.ziv
    
    import math
    
    fn main():
        result: int = math.add(1, 2)
        print(result)  # Output: 3
    
  • Extension: string_extensions.ziv

    # string_extensions.ziv
    
    extension string version "1.0"
    
    fn is_palindrome(s: string) -> bool:
        return s == s.reverse()
    
    end extension
    
    # main.ziv
    
    import string_extensions
    
    fn main():
        text: string = "radar"
        if text.is_palindrome():
            print("Palindrome")
        else:
            print("Not a palindrome")
    

In these examples, we define a module called math that contains functions for basic arithmetic operations and an extension for strings that adds a is_palindrome method. We then import and use these modules and extensions in the main function.

Classes and Objects

This example demonstrates how to define classes and create objects in Ziv. Classes allow you to define custom data types with properties and methods, while objects are instances of classes that hold specific values. Here is an example of defining a class and creating objects in Ziv:


class Point:
    private x: int
    private y: int

    public fn init(x: int, y: int):
        self.x = x
        self.y = y

    public fn + (other: Point) -> Point:
        return Point(self.x + other.x, self.y + other.y)

fn main():
    p1: Point = Point()
    p1.init(1, 2)

    p2: Point = Point()
    p2.init(3, 4)

    p3: Point = p1 + p2

    print(p3.get_x(), p3.get_y())  # Output: 4 6

In this example, we define a class called Point with private properties x and y and a public init method to initialize the object. We also define an overloaded + operator to add two Point objects together. In the main function, we create two Point objects p1 and p2, initialize them with values, add them together, and print the result.

Concurrency

This example demonstrates how to create and run concurrent tasks in Ziv using the spawn keyword. Concurrent tasks allow you to execute multiple operations simultaneously, improving performance and responsiveness. Here is an example of running concurrent tasks in Ziv:

fn task(id: int):
    print("Task " + id + " started.")
    // Simulate work with sleep
    std.time.sleep(2)
    print("Task " + id + " completed.")

fn main():
    spawn(task, 1)
    spawn(task, 2)
    spawn(task, 3)

In this example, we define a function called task that takes an id argument and simulates work by sleeping for 2 seconds. In the main function, we use the spawn keyword to run three concurrent tasks with different IDs. The tasks will execute simultaneously, and the completion messages will be printed to the console.

Parallel Processing

This example demonstrates how to perform parallel processing in Ziv using the parallel keyword. Parallel processing allows you to execute multiple operations concurrently across multiple cores, improving performance for CPU-bound tasks. Here is an example of parallel processing in Ziv:


fn calculate_sum(start: int, end: int) -> int:
    sum: int = 0
    for (i: int = start; i <= end; i = i + 1):
        sum = sum + i
    return sum

fn main():
    parallel:
        sum1: int = calculate_sum(1, 1000)
        sum2: int = calculate_sum(1001, 2000)
        sum3: int = calculate_sum(2001, 3000)
    total_sum: int = sum1 + sum2 + sum3
    print("Total sum: ", total_sum)

File I/O

This example demonstrates how to read and write files in Ziv using the std.fs module. File I/O operations allow you to interact with files on the filesystem, such as reading data from files, writing data to files, and creating new files. Here is an example of file I/O in Ziv:

import std.fs

fn read_file(file_path: string):
    file: std.fs.File =
        std.fs.open(file_path, std.fs.Mode.READ)
    if file.is_open():
        content: string = file.read_all()
        print(content)
        file.close()
        error("Failed to open file")

fn write_file(file_path: string, content: string):
    file: std.fs.File =
        std.fs.open(file_path, std.fs.Mode.WRITE)
    if file.is_open():
        file.write(content)
        file.close()
        error("Failed to open file")

fn main():
    file_path: string = "example.txt"
    content: string = "Hello, World!"

    write_file(file, content)
    read_file(file)

In this example, we define two functions read_file and write_file that read and write files, respectively. The main function demonstrates how to write the text "Hello, World!" to a file called example.txt and then read the contents of the file and print them to the console.

Networking

This example demonstrates how to perform networking operations in Ziv using the std.net module. Networking operations allow you to interact with remote servers, send and receive data over the network, and create network clients and servers. Here is an example of networking in Ziv:

import std.net

fn fetch_url(url: string):
    response: std.net.Response =
        std.net.get(url)
    if response.status_code == 200:
        print(response.body)
        error("Failed to fetch URL")

fn main():
    url: string = "https://example.com"
    fetch_url(url)

In this example, we define a function fetch_url that fetches the contents of a URL using an HTTP GET request. The main function demonstrates how to fetch the contents of the URL https://example.com and print the response body to the console.

Command-Line Arguments

This example demonstrates how to access command-line arguments in Ziv using the std.args module. Command-line arguments allow you to pass input parameters to a Ziv program when running it from the command line. Here is an example of accessing command-line arguments in Ziv:

import std.args

fn main():
    args: list[string] = std.args.get_args()
    for (arg: string in args):
        print(arg)

In this example, we define the main function that retrieves the command-line arguments using the std.args.get_args function and prints each argument to the console. When running the Ziv program from the command line, you can pass arguments that will be printed by the program.

Environment Variables

This example demonstrates how to access environment variables in Ziv using the std.env module. Environment variables allow you to store configuration settings and other information that can be accessed by Ziv programs. Here is an example of accessing environment variables in Ziv:

import std.env

fn main():
    username: string = std.env.get("USERNAME")
    if username != "":
        print("Hello, ", username)
        error("Username not found in environment variables")

In this example, we use the std.env.get function to retrieve the value of the USERNAME environment variable and print a greeting message to the console. If the USERNAME environment variable is not found, the program will print an error message.

JSON Parsing

This example demonstrates how to parse JSON data in Ziv using the std.json module. JSON parsing allows you to read and manipulate JSON data structures in Ziv programs. Here is an example of parsing JSON data in Ziv:


import std.json

fn main():
    data: std.json.Value = std.json.parse(json_data)
    name: string = data.get("name").as_string()
    age: int = data.get("age").as_int()
    print("Name: ", name)
    print("Age: ", age)

In this example, we define the main function that contains a JSON string representing a person's name and age. We use the std.json.parse function to parse the JSON data into a Value object and then extract the name and age values from the object and print them to the console.

Regular Expressions

This example demonstrates how to use regular expressions in Ziv using the std.regex module. Regular expressions allow you to search for patterns in text data and perform string manipulation based on those patterns. Here is an example of using regular expressions in Ziv:

import std.regex

fn main():
    text: string = "The quick brown fox jumps over the lazy dog"
    pattern: string = "fox|dog"
    matches: list[string] = std.regex.find_all(text, pattern)
    for (match: string in matches):
        print(match)

In this example, we define the main function that contains a text string and a regular expression pattern to search for the words "fox" or "dog" in the text. We use the std.regex.find_all function to find all matches of the pattern in the text and print each match to the console.

Unit Testing

This example demonstrates how to write unit tests in Ziv using the test keyword. Unit testing allows you to verify the correctness of individual functions or modules by writing test cases that check the expected behavior of the code. Here is an example of writing unit tests in Ziv:

fn add(a: int, b: int) -> int:
    return a + b

test "Addition":
    assert(add(1, 2) == 3)
    assert(add(0, 0) == 0)
    assert(add(-1, 1) == 0)

fn main():
    run_tests()

In this example, we define a function add that performs addition and a test case for the add function. The test case uses the assert keyword to check the expected results of adding different numbers. The main function calls the run_tests function to execute all test cases defined in the program.

Context Managers

This example demonstrates how to use context managers in Ziv to manage resources and ensure proper cleanup. Context managers allow you to define a block of code that automatically handles resource allocation and deallocation, such as opening and closing files or database connections. Here is an example of using context managers in Ziv:


fn main():
    using std.fs.open("example.txt", std.fs.Mode.WRITE) as file:
        file.write("Hello, World!")

Metaprogramming

This example demonstrates how to use metaprogramming in Ziv to generate code dynamically at compile time. Metaprogramming allows you to write code that generates other code, enabling powerful abstractions and code transformations. Here is an example of metaprogramming in Ziv:

macro repeat(n: int, body: block):
    for (i: int = 0; i < n; i = i + 1):
        body

fn main():
    repeat(3):
        print("Hello, World!")

Custom Attributes

This example demonstrates how to define and use custom attributes in Ziv to annotate code elements with metadata. Custom attributes allow you to add additional information to functions, types, or variables that can be used by the compiler or other tools, without affecting the program's behavior. Here is an example of defining and using custom attributes in Ziv:

attribute deprecated:
    message: string
    level: string = "warning" # Default value

attribute dangerous:
    message: string
    level: string = "error" # Default value

@deprecated("This function is deprecated")
fn old_function():
    # Function implementation

@dangerous("This function is dangerous", level: "error")
fn dangerous_function():
    # Function implementation

fn main():
    old_function() # Warning: This function is deprecated
    dangerous_function() # Error: This function is dangerous

Decorators

This example demonstrates how to use decorators in Ziv to add behavior to functions or methods at runtime. Decorators allow you to wrap or modify the behavior of functions without changing their source code, enabling code reuse and separation of concerns. Here is an example of using decorators in Ziv:

fn uppercase_decorator(fn: fn() -> string) -> string:
    result: string = fn()
    return result.upper()

@uppercase_decorator
fn get_greeting() -> string:
    return "hello"

fn main():
    print(get_greeting())  # Output: HELLO

Generics

This example demonstrates how to use generics in Ziv to write reusable code that works with different types. Generics allow you to define functions, classes, or data structures that can operate on any type, providing flexibility and type safety. Here is an example of using generics in Ziv:


fn print_list<T>(items: list[T]) -> void:
    for (item: T in items):
        print(item)

In this example, we define a generic function print_list that takes a list of items of any type T and prints each item to the console. The function can be used with lists of integers, strings, or any other type, making it versatile and reusable.

Enums

This example demonstrates how to define and use enums in Ziv to represent a set of named constants. Enums allow you to define custom data types with a fixed set of values, making the code more readable and maintainable. Here is an example of using enums in Ziv:

enum Color:
    RED
    GREEN
    BLUE

fn main():
    color: Color = Color.RED
    match color:
        Color.RED => print("Red")
        Color.GREEN => print("Green")
        Color.BLUE => print("Blue")

In this example, we define an enum Color with three constants RED, GREEN, and BLUE. We create a variable color of type Color and use a match expression to print the corresponding color name based on the value of the color variable.

Option Type

This example demonstrates how to use the option type in Ziv to handle nullable values and avoid null pointer exceptions. The option type allows you to represent a value that may or may not be present, providing a safer and more expressive way to handle missing values. Here is an example of using the option type in Ziv:

fn divide(a: int, b: int) -> option[int]:
    if b == 0:
        return none
    return some(a / b)

In this example, we define a function divide that takes two integers a and b and returns an option[int] value representing the result of dividing a by b. If b is zero, the function returns none to indicate a division by zero error. Otherwise, it returns some with the result of the division.

Error Handling with Option Type

This example demonstrates how to use the option type for error handling in Ziv. By combining the option type with pattern matching, you can elegantly handle errors and missing values in a functional and concise way. Here is an example of error handling with the option type in Ziv:

fn parse_int(s: string) -> option[int]:
    try:
        return some(s.to_int())
        return none

fn main():
    num_str: string = "123"
    match parse_int(num_str):
        some(num) => print("Parsed number: ", num)
        none => print("Failed to parse number")
    # Output: Parsed number: 123

    invalid_str: string = "abc"
    match parse_int(invalid_str):
        some(num) => print("Parsed number: ", num)
        none => print("Failed to parse number")
    # Output: Failed to parse number

In this example, we define a function parse_int that attempts to parse a string s into an integer using the to_int method. If the parsing is successful, the function returns some with the parsed integer value. If an error occurs during parsing, the function returns none. In the main function, we use pattern matching to handle the result of the parse_int function and print the parsed number or an error message based on the result.

Standard Library

The Ziv standard library provides a rich set of modules and functions for common programming tasks, such as working with files, networking, data serialization, and more. You can explore the Ziv Standard Library Reference to learn more about the available modules and functions in the standard library.