The Ziv Programming Language
by the Ziv Development Team and Contributors
Welcome to the Ziv Programming Language! This is the official documentation for Ziv, a new programming language designed to be simple, fast, and easy to use. This documentation will guide you through the basics of the language, and help you get started with writing your own Ziv programs.
-
User Manual: Learn how to use Ziv, from installing the compiler to writing your first program.
-
Developer Guide: Information for developers who want to contribute to the Ziv project, including how to build the compiler from source and run the test suite.
-
Language Reference: A detailed reference guide to the Ziv language, including syntax, keywords, and standard library functions.
We hope you enjoy using Ziv, and we welcome your feedback and contributions to the project!
User Manual
Welcome to the Ziv Programming Language User Manual.This section is designed to help you get started with Ziv, understand its core features, and provide examples to illustrate its capabilities.
Table of Contents
Introduction
Ziv is a modern programming language designed to be simple, expressive, and powerful. Drawing inspiration from various programming paradigms, Ziv aims to provide developers a with a versatile toolset while maintaining an easy-to-learn syntax.
Design Goals
-
Simplicity: Ziv is designed to be easy to learn and use, with a minimalistic syntax that is both expressive and readable.
-
Expressiveness: Ziv aims to provide a rich set of features that allow developers to write clean, concise code that is easy to understand.
-
Versatility: Ziv is a multi-paradigm language that supports both functional and object-oriented programming styles, allowing developers to choose the best approach for their needs.
-
Performance: Ziv is designed to be fast and efficient, with a focus on optimizing code execution and memory usage.
-
Extensibility: Ziv is designed to be extensible, with support for modules and extensions that allow developers to add new features and functionality to the language.
Why Ziv?
Ziv combines the best aspects of existing programming languages while introducing unique features that enhance developer productivity and application performance. Whether you're building web applications, system tools, or complex algorithms, Ziv provides the tools you need to succeed.
Getting Started
This section will guide you through the initial steps of using the Ziv programming language, from installation to writing your first program.
Prerequisites
-
Operating System: Ziv is currently supported on Linux and macOS. Windows support is planned for a future release.
-
Dependencies: Ziv requires the following dependencies to be installed on your system:
clang
(version 10 or later)llvm
(version 10 or later)cmake
(version 3.10 or later)ninja
(version 1.8 or later)
Ensure that these dependencies are installed before proceeding with the installation process.
Installation
Using Pre-built Binaries
Ziv provides pre-built binaries for major operating systems. Follow the steps below to install Ziv using the pre-built binaries:
-
Download: Visit the Ziv Releases page and download the latest release for your operating system.
-
Extract: Unzip the downloaded archive to a directory of your choice.
-
Add to Path: Add the Ziv binary directory to your system's
PATH
environment variable to make it accessible from the command line.
Building from Source
If you prefer to build Ziv from source, follow the steps below:
-
Clone the Repository: Clone the Ziv repository from GitHub using the following command:
git clone https://github.com/ziv-language/ziv.git
-
Build Ziv: Navigate to the cloned repository and run the following commands to build Ziv:
cd ziv cmake -B build -G Ninja -S . cmake --build build
-
Add to Path: Add the
build/bin
directory to your system'sPATH
environment variable to make the Ziv binary accessible from the command line.
Writing Your First Program
Now that you have installed Ziv, let's write a simple "Hello, World!" program to test your installation.
-
Create a new file named
hello.ziv
with the following content:# hello.ziv fn main() -> void: print("Hello, World!")
-
Save the file and run the following command in the terminal to compile and execute the program:
zivc hello.ziv
You should see the output Hello, World!
printed to the console. Congratulations! You have successfully written and executed your first Ziv program.
Tutorial
This tutorial will guide you through the basics of the Ziv programming language. By the end of this tutorial, you should have a good understanding of the language's syntax, features, and best practices.
Table of Contents
Hello, World!
Language Features
Ziv is designed to provide a robust set of features that make it easy to write code that is both efficient and easy to read. This document provides an overview of the language features that Ziv provides.
Table of Contents
- Simple and Readable Syntax
- Strong Static Typing with Type Inference
- Pattern Matching
- Modularity
- Concurrency and Parallelism
- Functional Programming
- Error Handling
- Generics
- Object-Oriented Programming
- Metaprogramming
- Interoperability
- Testing Support
- Asynchronous Programming
- Advanced Features
Simple and Readable Syntax
Ziv emphasizes readability and simplicity, making it easy to write and understand code. The syntax is clean and minimalistic, reducing the likelihood of errors and enhancing the readability of the code.
Example:
fn greet(name: string) -> void:
print("Hello, " + name + "!")
if name == "Alice":
print("You are awesome!")
else:
print("Nice to meet you!")
greet("Alice")
Strong Static Typing with Type Inference
Ziv has a robust static type system that catches errors at compile time. It also supports type inference, allowing you to omit explicit type declarations when unnecessary.
Example:
# Explicit typing
let age: int = 25
name: string = "Alice"
# Inferred typing
let age = 25 # Inferred as int
# Compile-time error example
age = "Alice"
Pattern Matching
Ziv provides powerful pattern matching capabilities that allow you to match values against patterns and extract data from them.
Example:
fn is_even(n: int) -> bool:
when n:
0 => true
1 => false
_ => is_even(n - 2)
Modularity
Ziv supports modularity through the use of modules, allowing you to organize your code into reusable components.
Example:
# math.ziv
module math
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() -> void:
result: int = math.add(10, 5)
print(result)
Concurrency and Parallelism
Ziv provides built-in support for concurrency and parallelism, allowing you to write code that can run concurrently and in parallel.
-
Concurrency: Ziv supports lightweight threads that can run concurrently when spawned.
fn task(id: int) -> void: print("Task " + id + " started") fn main() -> void: spawn(task, 1) # Start task 1 spawn(task, 2) # Start task 2
-
Parallelism: Ziv supports parallelism through the use of the
parallel
keyword, allowing you to execute code in parallel.fn task(id: int) -> void: print("Task " + id + " started") fn main() -> void: # Start task 1 and task 2 in parallel parallel: task(1) task(2)
Functional Programming
Ziv supports functional programming paradigms, including first-class functions, higher-order functions, lambdas, recursion, closures, and immutability.
Example:
-
First-Class Function
# First-class function fn greet(name: string) -> void: print("Hello, " + name + "!") fn main() -> void: f: (string) -> void = greet f("Alice") # Output: Hello, Alice!
-
Higher-Order Function
# Higher-order function fn apply(f: (int) -> int, x: int) -> int: return f(x) fn square(x: int) -> int: return x * x fn main() -> void: result: int = apply(square, 5) print(result) # Output: 25
-
Lambda Expression
# Lambda expression fn main() -> void: result: int = apply(|x| x * x, 5) print(result) # Output: 25
-
Immutability
# Immutable data structure !my_list: list[int] = [1, 2, 3] fn main() -> void: print(my_list) # Output: [1, 2, 3] my_list[0] = 4 # This will cause a compile-time error
-
Monads
# Monad using >> operator fn add_one(x: int) -> int: return x + 1 fn multiply_by_two(x: int) -> int : return x * 2 fn main() -> void: result: int = 1 >> add_one >> multiply_by_two print(result) # Output: 4
-
Arrow Functions
# Arrow function fn main() -> void: f: (int) -> int = |x| x * x result: int = f(5) print(result) # Output: 25
-
Currying
# Currying fn add(a: int, b: int) -> int: return a + b fn main() -> void: add_five: (int) -> int = add(5) result: int = add_five(10) print(result) # Output: 15
-
Partial Application
# Partial application fn add(a: int, b: int) -> int: return a + b fn main() -> void: add_five: (int) -> int = add(5, _) # Partial application result: int = add_five(10) print(result) # Output: 15
-
Map, Filter, Reduce
# Map, filter, reduce numbers: list[int] = [1, 2, 3, 4, 5] # Map doubled: list[int] = map(numbers, |x| x * 2) print(doubled) # Output: [2, 4, 6, 8, 10] # Filter evens: list[int] = filter(numbers, |x| x % 2 == 0) print(evens) # Output: [2, 4] # Reduce sum: int = reduce(numbers, 0, |acc, x| acc + x) print(sum) # Output: 15
-
Lazy Evaluation
# Lazy evaluation fn main() -> void: # infinite list of numbers numbers: list[int] = lazy: for i in 0.. : yield i # take first 5 numbers for n in take(numbers, 5): print(n)
Error Handling
Ziv provides robust error handling mechanisms that allow you to handle errors gracefully and recover from them.
Example:
fn divide(a: int, b: int) -> int:
if b == 0:
error("Division by zero")
return a / b
fn main() -> void:
result: int = divide(10, 0)
Generics
Ziv supports generics, allowing you to write code that is generic over types.
Example:
fn swap<T>(a: T, b: T) -> (T, T):
return (b, a)
fn main() -> void:
result: (int, int) = swap(10, 20)
print(result)
Object-Oriented Programming
Ziv supports object-oriented programming features, such as classes, inheritance, and encapsulation.
Example:
-
Class Definition
# Define a class with private, public, and protected members class BankAccount: public owner: string protected balance: float private account_number: string # Constructor fn init(account_number: string, owner: string): self.account_number = account_number self.owner = owner self.balance = 0.0 # Destructor fn deinit() -> void: print("Account " + self.account_number + " closed") public fn deposit(amount: float): self.balance += amount # Methods are public by default fn withdraw(amount: float): self.balance -= amount protected fn get_balance() -> float: return self.balance private fn get_account_number() -> string: return self.account_number
-
Inheritance and Protected Access
# Inheritance class SavingsAccount(BankAccount): public interest_rate: float fn init(account_number: string, owner: string, interest_rate: float): parent.init(account_number, owner) self.interest_rate = interest_rate public fn calculate_interest() -> float: return self.get_balance() * self.interest_rate
-
Instance Creation and Method Invocation
# Instance creation and method invocation fn main() -> void: account: SavingsAccount = SavingsAccount("12345", "Alice", 0.05) account.deposit(1000.0) interest: float = account.calculate_interest() print(interest) # Output: 50.0
-
Polymorphism
# Base Class class Animal: public fn speak() -> string: return "Animal speaks" # Derived Class class Dog(Animal): public fn speak() -> string: return "Dog barks" # Another Derived Class class Cat(Animal): public fn speak() -> string: return "Cat meows" fn main() -> void: dog: Animal = Dog() cat: Animal = Cat() print(dog.speak()) # Output: Dog barks print(cat.speak()) # Output: Cat meows
-
Abstract Classes
# Abstract Class abstract class Shape: public abstract fn area() -> float # Concrete Class class Circle(Shape): public radius: float fn init(radius: float): self.radius = radius # Implement abstract method public fn area() -> float: return 3.14 * self.radius * self.radius
-
Interfaces
# Interface interface Printable: public fn print() -> void # Class implementing the interface class Document(Printable): public fn print(): print("Document printed")
-
Static Members
# Static Members class Counter: public static count: int = 0 public static fn increment(): Counter.count += 1 public static fn get_count() -> int: return Counter.count fn main(): Counter.increment() Counter.increment() count: int = Counter.get_count() print(count) # Output: 2
-
Operator Overloading
# Operator Overloading class Vector: public x: float public y: float fn init(x: float, y: float): self.x = x self.y = y public fn +(other: Vector) -> Vector: return Vector(self.x + other.x, self.y + other.y)
Metaprogramming
Ziv provides metaprogramming capabilities that allow you to generate code at compile time.
Example:
macro repeat(n: int, body: block):
for i in 0..n :
body
fn main():
repeat(5):
print("Hello")
Interoperability
Ziv provides seamless interoperability with other languages, allowing you to call functions written in other languages from Ziv code.
Example:
# Import a C function
extern fn c_function() -> int
fn main() -> void:
result: int = c_function()
print(result)
Testing Support
Ziv has built-in support for testing, enabling you to write and execute test cases directly in your code.
Example:
# This function adds two numbers
fn add(a: int, b: int) -> int:
return a + b
# Test cases
test "Addition" :
assert(add(1, 2) == 3)
assert(add(0, 0) == 0)
Asynchronous Programming
Ziv provides built-in support for asynchronous programming, allowing you to write asynchronous code easily and efficiently.
Example:
async fn fetch_data(url: string) -> string;
response: string = await http.get(url)
return response
Advanced Features
Ziv provides a range of advanced features, such as decorators, and more, that allow you to write powerful and expressive code.
-
Decorators:
Ziv supports decorators, which are functions that modify the behavior of other functions or methods.
# Decorators @log fn add(a: int, b: int) -> int: return a + b fn main(): result: int = add(1, 2) print(result) # Output: 3
-
Class Decorators:
Ziv also supports class decorators, which are functions that modify the behavior of classes.
# Class Decorators fn singleton(cls: class) -> class: instance: cls = null fn get_instance() -> cls: if instance == null: instance = new cls() return instance return cls @singleton class Database: fn init(): print("Database initialized") fn main() -> void: db1: Database = Database.get_instance() db2: Database = Database.get_instance() print(db1 == db2) # Output: true
-
Module Versioning:
Ziv supports module versioning, allowing you to specify the version of a module that your code depends on.
# math.ziv module math version "1.0.0" fn add(a: int, b: int) -> int: return a + b end module
# main.ziv import math version "1.0.0" fn main(): result: int = math.add(10, 5) print(result)
-
Type Aliases:
Ziv supports type aliases, allowing you to define custom names for existing types.
# Type Aliases type Point = (int, int) fn distance(p1: Point, p2: Point) -> float: x1, y1 = p1 x2, y2 = p2 return sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
-
Import Aliases:
Ziv supports import aliases, allowing you to import modules with custom names.
# Import Aliases import math as m fn main(): result: int = m.add(10, 5) print(result)``
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!
- Basic Calculator
- Fibonacci Sequence
- List Operations
- Error Handling
- Custom Error Types
- Modules and Extensions
- Classes and Objects
- Concurrency
- Parallel Processing
- File I/O
- Networking
- Command-Line Arguments
- Environment Variables
- JSON Parsing
- Regular Expressions
- Unit Testing
- Context Managers
- Metaprogramming
- Custom Attributes
- Decorators
- Generics
- Enums
- Option Type
- Error Handling with Option Type
- Standard Library
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.
Modules and Extensions
Ziv's modular system is designed to promote code reusability and maintainability. This section covers how to create, import, and extend modules in Ziv.
Table of Contents
Modules
Ziv's modular system is designed to promote code reusability and maintainability. This section covers how to create, import, and extend modules in Ziv.
Creating a Module
A module is a collection of functions, types, and variables that can be imported and used in other files. To create a module, you need to define a file with the .ziv
extension and declare the module using the module
keyword.
Here is an example of a simple module for mathematical operations:
# math.ziv
module math
fn add(a: int, b: int) -> int:
return a + b
fn subtract(a: int, b: int) -> int:
return a - b
end module
In this example, we define a module called math
that contains two functions: add
and subtract
. These functions can be imported and used in other files.
Exporting and Importing Modules
By default, all functions, types, and variables defined in a module are public and can be accessed from other files. To import a module, you can use the import
keyword followed by the module name.
If you want to restrict access to certain functions, types, or variables, you can use the private
keyword to mark them as private.
private fn multiply(a: int, b: int) -> int:
return a * b
Here is an example of importing the math
module and using its functions:
# main.ziv
import math
fn main() -> void:
result: int = math.add(1, 2)
print(result) # Output: 3
In this example, we import the math
module and use its add
function to add two numbers.
Importing Specific Functions
If you only need to import specific functions, types, or variables from a module, you can use the from
keyword followed by the module name and the functions you want to import.
from math import add, subtract
Or you can import a specific function and rename it using the as
keyword:
import math.add as sum
Creating Libraries
You can organize related modules into a library by creating a directory with the library name and placing the module files inside it. To import a module from a library, you can use the library name followed by the module name.
lib/
math/
math.ziv
string/
string.ziv
# string/string.ziv
module lib.string
fn capitalize(s: string) -> string:
return s.capitalize()
end module
# main.ziv
import lib.string
fn main():
result: string = lib.string.capitalize("hello")
print(result) # Output: Hello
In this example, we create a library with two modules: math
and string
. We import the string
module from the library and use its capitalize
function to capitalize a string.
Extensions
Extensions allow you to add new functionality to existing types without modifying their source code. This is useful when you want to extend the behavior of a type defined in a library or module.
Creating an Extension
To create an extension, you need to define a file with the .ziv
extension and declare the extension using the extension
keyword followed by the type you want to extend.
Here is an example of extending the string
type with a new method:
# string_extension.ziv
extension string
fn uppercase() -> string:
return self.upper()
end extension
In this example, we define an extension for the string
type that adds a capitalize
method to capitalize the string.
Importing Extensions
To use an extension, you need to import it in the file where you want to extend the type. You can import an extension using the import
keyword followed by the extension file name.
# main.ziv
import string_extension
fn main():
s: string = "hello"
result: string = s.uppercase()
print(result) # Output: HELLO
In this example, we import the string_extension
extension and use the uppercase
method to convert a string to uppercase.
Extension Methods
Extension methods can access the properties and methods of the extended type using the self
keyword. You can define extension methods for any type, including built-in types, user-defined types, and library types.
extension int
fn is_even() -> bool:
return self % 2 == 0
end extension
In this example, we define an extension for the int
type that adds an is_even
method to check if the integer is even.
import int_extension
fn main():
n: int = 10
result: bool = n.is_even()
print(result) # Output: true