--- url: /guide/getting-started/introduction.md --- # Introduction AIScript is a unique combination of interpreter programming language and web framework, both written in Rust, designed to help developers build AI applications effortlessly. The language syntax draws inspiration from Python, JavaScript, and Rust, combining their strengths to create something that's intuitive, powerful, and easy to use. :::warning `AIScript` is in early development. Please do not use it in production yet. ::: ## Programming Language As a programming language, AIScript is built with a custom interpreter from the ground up: - Functions are first-class citizens with support for object-oriented programming paradigms - Built-in AI primitives, including prompt, AI functions, and agent capabilities - Dynamic typing system with targeted static type checking for improved safety - Built-in data validation similar to Python's [Pydantic](https://docs.pydantic.dev/latest/) - Simple yet powerful error handling, inspired by Rust, Go, and Zig - Rich standard library leveraging Rust's ecosystem underneath - Automatic garbage collection for memory management ## Web Framework AIScript isn't just a language—it's a complete web development solution: - Elegant and intuitive route DSL for defining endpoints - Automatic parameter validation with clear error messages - Automatic OpenAPI schema and documentation generation - Built on Rust's best practices, using [axum](https://github.com/tokio-rs/axum) and [sqlx](https://github.com/launchbadge/sqlx) under the hood - Combines Rust's axum performance with Python's Flask-like simplicity - Built-in database modules ([`std.db.pg`](/std/db/pg) and [`std.db.redis`](/std/db/redis)) - Built-in authentication and social login capabilities - Battery-included features easily enabled through simple configuration ## How AIScript works ```javascript $ export OPENAI_API_KEY= $ cat web.ai get / { """An api to ask LLM""" query { """the question to ask""" @string(min_len=3, max_len=100) // validate params with builtin directive @string question: str } // `ai` and `prompt` are keywords ai fn ask(question: str) -> str { let answer = prompt question; return answer; } // use query.name or query["name"] to access query parameter let answer = ask(query.question); return { answer }; } $ aiscript serve web.ai Listening on http://localhost:8080 $ curl http://localhost:8080 { "error": "Missing required field: question" } $ curl http://localhost:8080?question=Hi { "error": "Field validation failed: question: String length is less than the minimum length of 3" } $ curl http://localhost:8080?question=What is the capital of France? { "answer": "The capital of France is Paris." } ``` You can open [http://localhost:8080/redoc](http://localhost:8080/redoc) to see the automatically generated API documentation. ![](/guide/open-api.png) ### Local Models Support AIScript also supports local models through [Ollama](https://ollama.ai/), allowing you to run AI models on your own hardware: ```javascript $ ollama pull llama3.2 $ export OLLAMA_API_ENDPOINT=http://localhost:11434/v1 $ cat local.ai let a = prompt { input: "What is rust?", model: "llama3.2" }; print(a); ``` ## Use Cases AIScript excels in these scenarios: - **AI-powered APIs**: When you need to build APIs that leverage LLMs and other AI services - **Prototyping**: Rapidly build and test ideas without configuration overhead - **Microservices**: Create lightweight, focused services with minimal boilerplate - **Data validation**: When request/response validation is critical to your application - **Internal tools**: Build tools quickly with integrated documentation ## Philosophy AIScript embraces these core principles: - **Convention over configuration**: Sensible defaults that work out of the box - **Progressive complexity**: Easy to get started, powerful when you need it - **AI-native design**: Built from the ground up for the age of AI - **Developer happiness**: Focusing on the developer experience first - **Performance without sacrifice**: No need to choose between speed and productivity --- url: /guide/getting-started/installation.md --- # Installation ## Quick Install You can quickly install AIScript using our installation script directly from GitHub: ```bash curl https://aiscript.dev/install.sh | sh ``` Or if you prefer wget: ```bash wget -q -O- https://aiscript.dev/install.sh | sh ``` This script will: 1. Detect your OS and architecture 2. Download the appropriate binary 3. Verify the checksum 4. Install AIScript to `~/.aiscript/bin/` 5. Add this directory to your PATH (if needed) ## Install via Cargo ``` $ cargo install aiscript $ aiscript --version ``` ## Manual Installation If you prefer manual installation, you can download the appropriate binary for your platform from the [Releases page](https://github.com/aiscript-dev/aiscript/releases). ## Linux ```bash # Download the binary curl -L -o aiscript https://github.com/aiscript-dev/aiscript/releases/latest/download/aiscript-linux-x86_64 # Make it executable chmod +x aiscript # Move to a directory in your PATH sudo mv aiscript /usr/local/bin/ ``` ## macOS ```bash # Download the appropriate binary for your architecture (Intel or M1/M2) # For Intel Macs (x86_64): curl -L -o aiscript https://github.com/aiscript-dev/aiscript/releases/latest/download/aiscript-macos-x86_64 # For Apple Silicon (M1/M2): curl -L -o aiscript https://github.com/aiscript-dev/aiscript/releases/latest/download/aiscript-macos-arm64 # Make it executable chmod +x aiscript # Move to a directory in your PATH sudo mv aiscript /usr/local/bin/ ``` ## Windows 1. Download the [Windows executable](https://github.com/aiscript-dev/aiscript/releases/latest/download/aiscript-windows-x86_64.exe) 2. Rename it to `aiscript.exe` if desired 3. Move it to a directory in your PATH or create a directory for it and add that to your PATH ## Building from Source If you prefer to build AIScript from source, you'll need Rust installed: ```bash # Clone the repository $ git clone https://github.com/aiscript-dev/aiscript.git $ cd aiscript # Build in release mode $ cargo build --release # The binary will be in target/release/aiscript ``` --- url: /guide/language/basic.md --- # Basic Syntax AIScript syntax is inspired by Python, JavaScript and Rust. If you're familiar with any of these languages, you can grasp the basic language syntax in about 10 minutes. ## Comments AIScript uses `//` for comments. ```js // This is a comment // Print a Hello World print("Hello World"); ``` ## Semicolon AIScript is a semicolon-terminated language, which means `;` is required at the end of statements. ```js let a = 1; let flag = true; print(1 + 2); ``` ## Variables AIScript uses `let` to define variables. Type annotations are not supported yet. ```js let name = "AIScript"; let age = 18; // Type annotation is not support yet // let flag: bool = false; ``` There are two kinds of variables: `global variables` and `local variables`. Any variable defined in the root scope is a global variable; otherwise, it's a local scope variable. ```js let global_variable = "abc"; if len(global_variable) > 1 { let local_variable = "xyz"; print("I can access global variable:", global_variable); // print: I can access global variable: abc } // Local variables are inaccessible outside of their local scope. // This print statement will raise error: Undefined variable 'local_variable'. // print(local_variable); ``` ## Constants Constants are declared with the `const` keyword and are immutable. Constants can only be declared once and must be initialized with a value; they cannot be reassigned. ```js const PI = 3.14; print(PI); // print 3.14 PI = 3.1415; // Error at 'PI': Cannot assign to constant variable. ``` ## Primitive types ```js // nil let nil_value = nil; // strings let name = "AIScript"; // integers let age = 18; let negative = -1; // let hex = 0x10; // let octal = 0o10; // let binary = 0b10; // let big = 1_000_000_000_000; // floats let pi = 3.14; // booleans let flag1 = true; let flag2 = false; // arrays let numbers = [1, 2, 3, 4, 5]; // object literal let address_key = "address"; let person = { name: "AIScript", age: 18, // double quotes are optional is_male: true, hobbies: ["reading", "coding", "gaming"], [address_key]: { // key name are computable city: "Beijing", street: "No. 100, Xihuan Road", zipcode: "100000", country: "China", phone: "13800138000", }, }; // tuples let person = ("AIScript", 18, true); ``` ## Operators ```js // arithmetic operators let a = 1 + 2; let b = 1 - 2; let c = 1 * 2; let d = 1 / 2; let e = 1 % 2; // logical operators let a = true and false; let b = true or false; let c = not true; // comparison operators let a = 1 == 2; let b = 1 != 2; let c = 1 > 2; let d = 1 < 2; let e = 1 >= 2; let f = 1 <= 2; // bitwise operators let a = 1 & 2; let b = 1 | 2; let c = 1 ^ 2; let d = 1 << 2; let e = 1 >> 2; // assignment operators let a = 1; a += 2; a -= 2; a *= 2; a /= 2; a %= 2; // in operator let a = 1 in [1, 2, 3]; let b = 1 not in [1, 2, 3]; ``` :::danger Not supported AIScript doesn't support increment/decrement operators like `i++`, `++i`, `i--` or `--i`. ::: ## String ```js // string concatenation let a = "AIScript"; let b = "Hello" + a + "!"; // string length let a = "AIScript"; print(len(a)); // print 8 ``` ## Formatted String Literal AIScript supports `f-strings` (formatted string literals, like Python's [f-string](https://docs.python.org/3/tutorial/inputoutput.html#tut-f-strings) but not identical) for string interpolation. F-strings provide a concise and readable way to embed expressions inside string literals. To create an f-string, prefix the string with `f` and use curly braces `{}` to include expressions. ```js let name = "Alice"; let greeting = f"Hello, {name}!"; print(greeting); // Hello, Alice! let x = 10; let y = 5; print(f"{x} + {y} = {x + y}"); // 10 + 5 = 15 print(f"{x} - {y} = {x - y}"); // 10 - 5 = 5 print(f"{x} * {y} = {x * y}"); // 10 * 5 = 50 print(f"{x} / {y} = {x / y}"); // 10 / 5 = 2 ``` F-strings can contain any valid AIScript expression inside the curly braces. The expressions are evaluated at runtime and their results are converted to strings. This makes f-strings particularly useful for: * String interpolation with variables * Embedding arithmetic expressions * Calling functions within strings * Formatting complex data structures ## Control flow ```js let age = 20; if age > 60 { print("You are a senior"); } else if age > 18 and age <= 60 { print("You are an adult"); } else if age > 12 and age <= 18 { print("You are a teenager"); } else { print("You are a child"); } ``` ## Inline if AIScript doesn't support the `?:` ternary operator like other languages, but you can use `inline if` syntax. ```js let score = 92; let result = "A" if score > 90 else "B"; print(result); // "A" ``` ## Match Use `match` to evaluate expressions against different patterns: * `match` also support evaluate enums, learn more in [Enum chapter](/guide/language/enum-match) * Use `|` to combine multipe case in one arm * `match` arm support `if` guards ```js let language = "AIScript"; match language { "AIScript" => print("AIScript"), "Rust" | "Python" => print("Rust or Python"), "JavaScript" => print("JavaScript"), x if x.starts_with("Z") => print("Maybe Zig?"), _ => print("Unknown language"), }; ``` ## Functions * Use `fn` keyword to define function; you can use `ai fn` to define [AI function](/guide/ai/function). * Argument and return type annotations are recommend but optional. * Use `|` to append error types after return type, see [Error handling](/guide/language/error-handling). ```js fn add(a: int, b: int) -> int { return a + b; } print(add(1, 2)); // 3 ``` ## Closures Functions are first-class citizens in AIScript, so you can return a function as a closure. The key difference between closures and regular functions is that `closures capture variables` from their surrounding scope. ```js fn add(a: int) { fn add_inner(b: int) -> int { return a + b; } return add_inner; } let add5 = add(5); print(add5(10)); // 15 ``` Closure `add_inner` captures the outer variable `a`. ## Slice ```js // array slice let a = [1, 2, 3, 4, 5]; let b = a[0:3]; // [1, 2, 3] let c = a[3:]; // [4, 5] let d = a[:3]; // [1, 2, 3] // string slice let a = "AIScript"; let b = a[0:2]; // "AI" let c = a[3:]; // "Script" let d = a[:2]; // "AI" ``` ## Loops AIScript only has two keyword for loops: `for` and `while`. ### Regular for iteration ```js for let i = 0; i < 10; i = i + 1 { print(i); } ``` ### While loop :::info AIScript doesn't support `do...while`. ::: ```js let i = 0; while i < 10 { print(i); } while true { // infinite loop } ``` ### Iterate over a list ```js let numbers = [1, 2, 3, 4, 5]; for number in numbers { print(number); } ``` ### Iterate over a map ```js let person = { "name": "AIScript", "age": 18, "gender": "male", }; for (key, value) in person { print(f"{key}: {value}"); } ``` ### Iterate over range ```js for i in 1..10 { print(i); } ``` ### Break and Continue ```js for let i = 0; i < 10; i += 1 { if i % 2 == 0 { continue; } else if i == 9 { print("i == 9"); break; } print(i); } // 1 // 3 // 5 // 7 // i == 9 ``` ## Environment AIScript provide a convenient way to access environment variables, the syntax is `$ENV_NAME`. You can also use [std.env](/std/env) module too. ```rust let home = $HOME; print(home); ``` --- url: /guide/language/closure-lambda.md --- # Closures and Lambdas AIScript treats functions as first-class citizens, enabling powerful functional programming patterns through closures and lambdas. These constructs allow you to write more concise, flexible, and expressive code. ## Understanding Closures A closure is a function that captures and remembers the environment in which it was created. This means it can access variables from its enclosing scope, even after that scope has completed execution. ```js fn create_counter(start: int) { let count = start; fn increment() -> int { count += 1; return count; } return increment; } let counter = create_counter(10); print(counter()); // 11 print(counter()); // 12 print(counter()); // 13 ``` In this example, the `increment` function forms a closure by capturing the `count` variable from its outer scope. Each time `counter` is called, it remembers and updates the same `count` variable. ## Creating Lambdas Lambdas (also called anonymous functions) are concise function expressions that you can define inline without naming. AIScript provides a simple syntax for lambdas using the pipe `|` character: ```js // Basic lambda syntax let add = |a, b| { return a + b; }; print(add(5, 3)); // 8 // For single expressions, you can omit the braces and return statement let multiply = |a, b| a * b; print(multiply(4, 6)); // 24 ``` ## Higher-Order Functions Closures and lambdas shine when used with higher-order functions (functions that take other functions as arguments or return them): ```js // Map example let numbers = [1, 2, 3, 4, 5]; let squared = map(numbers, |n| n * n); print(squared); // [1, 4, 9, 16, 25] // Filter example let even_numbers = filter(numbers, |n| n % 2 == 0); print(even_numbers); // [2, 4] ``` ## Capturing Variables Closures can capture variables in three different ways: ### 1. By Reference (Default) ```js let x = 10; let add_x = |n| n + x; print(add_x(5)); // 15 x = 20; print(add_x(5)); // 25 (reflects updated value of x) ``` ### 2. Mutable Capture When a closure modifies a captured variable, it affects the original: ```js let counter = 0; let increment = || { counter += 1; return counter; }; print(increment()); // 1 print(increment()); // 2 print(counter); // 2 (affected by the closure) ``` ## Immediate Invocation You can immediately invoke a lambda after defining it: ```js let result = |x, y| { let sum = x + y; let product = x * y; return sum + product; }(3, 4); print(result); // 19 (sum: 7, product: 12) ``` ## Recursion with Closures Closures can call themselves recursively: ```js // Using a named function for recursion let factorial = |n| { fn fact(x) -> int { if x <= 1 { return 1; } return x * fact(x - 1); } return fact(n); }; print(factorial(5)); // 120 ``` ## Closures as Callbacks Closures are ideal for event-driven or asynchronous programming: ```js fn fetch_data(url: str, on_success, on_error) { // Simulating an async operation if url.starts_with("https") { on_success(f"Data from {url}"); } else { on_error(f"Invalid URL: {url}"); } } fetch_data( "https://api.example.com", |data| print("Success:", data), |error| print("Error:", error) ); ``` :::danger Notice AIScript current doesn't support `fn` as type annotation. ```rs fn fetch_data(url: str, on_success: fn(data: str), on_error: fn(error: str)) {} ``` ::: ## Performance Considerations While closures provide powerful abstractions, they have memory implications since they maintain references to their captured environment. Consider these best practices: 1. **Limit capture scope**: Only capture what you need 2. **Be mindful of memory**: Large closures with many captures may impact performance 3. **Consider alternatives**: For simple cases, regular functions might be more efficient Closures and lambdas are fundamental to many modern programming patterns, from functional programming to asynchronous workflows. By mastering these concepts in AIScript, you'll be able to write more concise, maintainable, and powerful code. --- url: /guide/language/pipe.md --- # Pipe Operator AIScript features a powerful pipe operator (`|>`) that allows for clean, readable function composition. This operator takes the value on its left side and passes it as the first argument to the function on its right side, enabling elegant data transformation pipelines. ## Basic Usage The pipe operator (`|>`) passes the result of the left expression as the first argument to the function on the right: ```js let result = "hello world" |> len; print(result); // 11 ``` This is equivalent to: ```js let result = len("hello world"); print(result); // 11 ``` :::tip `len()` is a builtin function in AIScript, see [Builtin Functions](/std/builtin/functions#len) for more details. ::: ## Chaining Transformations The real power of the pipe operator is in creating readable processing pipelines: ```js let data = [1, 2, 3, 4, 5]; let result = data |> map(|x| x * 2) // [2, 4, 6, 8, 10] |> filter(|x| x > 5) // [6, 8, 10] |> sum; // 24 print(result); // 24 ``` :::tip You can omit the `()` if the function only has one argument, for example `sum()` in the above example. ::: Without the pipe operator, this would be written as: ```js let data = [1, 2, 3, 4, 5]; let mapped = map(data, |x| x * 2); let filtered = filter(mapped, |x| x > 5); let result = sum(filtered); print(result); // 24 ``` Or even less readably as nested function calls: ```js let result = sum( filter( map( [1, 2, 3, 4, 5], |x| x * 2 ), |x| x > 5 ) ); ``` :::tip `map()`, `filter()` and `sum()` are builtin functions in AIScript, see [Builtin Functions](/std/builtin/functions#sum) for more details. ::: ## Working with Additional Arguments When piping to a function that requires multiple arguments, the piped value becomes the first argument, and you must provide the remaining arguments: ```js let v = 1; let add_n = |x, n| x + n; let mul_n = |x, n| x * n; // Without pipe print( mul_n(add_n(v, 10), 5) ); // expect: 55 // With pipe print( v |> add_n(10) |> mul_n(5) ); // expect: 55 ``` ## Creating Custom Pipelines You can define functions designed specifically for use in pipelines: ```js fn to_uppercase(text: str) -> str { return text.to_uppercase(); } fn remove_spaces(text: str) -> str { return text.replace(" ", ""); } fn count_chars(text: str) -> int { return len(text); } let result = "Hello World" |> remove_spaces // "HelloWorld" |> to_uppercase // "HELLOWORLD" |> count_chars; // 10 print(result); // 10 ``` ## Pipe with Method Calls You can pipe directly to method calls using a special syntax: ```js // The standard library's string module let processed = "hello world" |> str.trim() |> str.to_uppercase() |> str.replace("WORLD", "AISCRIPT"); print(processed); // "HELLO AISCRIPT" ``` ## Practical Examples ### Data Processing ```js let users = [ {name: "Alice", age: 28, active: true}, {name: "Bob", age: 35, active: false}, {name: "Charlie", age: 21, active: true}, {name: "Diana", age: 40, active: true} ]; let result = users |> filter(|user| user.active) |> map(|user| user.name); print(", ".join(result)); // "Alice, Charlie, Diana" ``` ### Text Analysis ```js let word_frequencies = "This is a simple example. This example shows the pipe operator." |> str.to_lowercase() |> str.replace(/[.,!?;:]/g, "") |> str.split(" ") |> reduce(|acc, word| { acc[word] = (acc[word] || 0) + 1; return acc; }, {}); print(word_frequencies); // { // "this": 2, // "is": 1, // "a": 1, // "simple": 1, // "example": 2, // "shows": 1, // "the": 1, // "pipe": 1, // "operator": 1 // } ``` ## Benefits of the Pipe Operator 1. **Readability**: Processes flow from left to right, matching how we naturally read 2. **Maintainability**: Easy to add, remove, or reorder processing steps 3. **Reduced nesting**: Eliminates deeply nested function calls 4. **Focused functions**: Encourages small, single-purpose functions 5. **Testability**: Pipeline components can be tested in isolation The pipe operator is particularly valuable when working with data transformation, functional programming patterns, and AI workflow pipelines, making it a core feature of AIScript's expressive syntax. --- url: /guide/language/enum-match.md --- # Enum and Match Enums (short for enumerations) and match expressions are powerful features in AIScript that work together to provide type-safe pattern matching. These features are inspired by Rust's enum and match system, offering an elegant way to handle variants and control flow. ## Enums An enum is a type that can hold one of several possible variants, each with its own associated data. Enums are declared using the `enum` keyword: ```js enum Color { Red, Green, Blue, } ``` In this example, `Color` can be one of three variants: `Red`, `Green`, `Blue`. :::warning AIScript does not support algebra data types like Rust, and has no plan to support it. So following enum type is not supported. ```rs enum Color { Red, Green, Blue, RGB(int, int, int), Hex(str), } ``` ::: ### Create Enum Variant You can create enum variant using the double colon (`::`) syntax: ```js let red = Color::Red; ``` ### Enum with Values Enum variants can have associated values: ```rust enum Color { Red = "#FF0000", Green = "#00FF00", Blue = "#0000FF", } let red = Color::Red; print(red); // Color::Red(#FF0000) ``` Enum's number value will be auto increment if you don't specify the value. ```rust enum Status { Draft = 1, Pending, Active = 10, // you can change the value of enum variant Finished, } print([Status::Draft]); // 1 print([Status::Pending]); // 2 print([Status::Active]); // 10 print([Status::Finished]); // 11 ``` However, some caveats should be noted: * you cannot mix different type value * for string value, no default value will be set, you must specify all values for all variants ```rust enum Status { Draft = "draft", Pending, // [line 3] Error at 'Pending': Must specify value for non-integer enum variants Active = 10, Finished, } ``` ### Evaluate Enum Values You can use `[]` to evaluate the value of enum variant. ```rust let value = [Color::Red]; print(value); // #FF0000 let color = Color::Blue; print([color]); // #0000FF ``` :::tip The `[Enum::Variant]` syntax is similar to Python Enum's `.value`. ```py from enum import Enum class Color(Enum): RED = "#FF0000" Green = "#00FF00", Blue = "#0000FF", print(Color.RED.value) # "#FF0000" ``` ::: ### Enum Methods `enum` supports methods like `class`. ```rust enum Color { Red = "#FF0000", Green = "#00FF00", Blue = "#0000FF", fn name(self) -> str { match self { Color::Red => "red", Color::Green => "green", Color::Blue => "blue", } } fn static_method() { print("static method of Color enum"); } } print(Color::Blue.name()); // blue Color.static_method(); // static method of Color enum ``` ## Match Expression The match expression allows you to compare a value against a series of patterns and execute code based on which pattern matches: ```rust fn match_string(s: str) { return match s { "hello" | "hi" => "greeting", "bye" => "farewell", s if s.starts_with("#") => "sharp", x if x.starts_with("@") => "at", x if x in ["luck", "magic"] => "lucky", s if s == "ai" => "ai", _ => "unknown", }; } match_string("hello"); // greeting match_string("bye"); // farewell match_string("#id"); // sharp match_string("@aiscript"); // at match_string("luck"); // lucky match_string("ai"); // ai match_string("unknown"); // unknown ``` * Use `|` to match multiple values * Match support `if` guards ### Pattern Matching on Number Types Match works on other data types too, not just enums: ```rust let value = 42; match value { 0 => print("Zero"), 1 | 2 | 3 => print("Small number"), n if n < 10 => print("Single digit:", n), n if n >= 10 and n < 100 => print("Double digits:", n), _ => print("Large number"), }; // Double digits: 42 ``` ### Match with Variable Binding You can bind values to variables in match patterns: ```js let point = [10, 20]; match point { [0, 0] => print("At origin"), [0, y] => print(f"On y-axis at y={y}"), [x, 0] => print(f"On x-axis at x={x}"), [x, y] => print(f"At position ({x}, {y})"), }; ``` ### Match with Guards Add conditional guards to match arms for more specific matching: ```js let num = 15; match num { n if n % 15 == 0 => print("FizzBuzz"), n if n % 3 == 0 => print("Fizz"), n if n % 5 == 0 => print("Buzz"), n => print(f"{n}"), }; ``` ## Advanced Pattern Matching ### Range Patterns Match values within ranges: ```rust let grade = 85; let letter = match grade { n if n >= 90 and n <= 100 => "A", n if n >= 80 and n < 90 => "B", n if n >= 70 and n < 80 => "C", n if n >= 60 and n < 70 => "D", n if n >= 0 and n < 60 => "F", _ => "Invalid grade", }; print("Grade: ", letter); // Grade: B fn match_range(n) { return match n { 0..60 => "F", 60..70 => "D", 70..80 => "C", 80..90 => "B", 90..=100 => "A", _ => "Invalid grade", }; } print(match_range(92)); // A print(match_range(101)); // Invalid grade ``` ### Or Patterns Use the pipe (`|`) symbol to match multiple patterns in a single arm: ```js let day = "Saturday"; let type = match day { "Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday" => "Weekday", "Saturday" | "Sunday" => "Weekend", _ => "Invalid day", }; print(day, " is a ", type); // Saturday is a Weekend ``` ### Match with Error Handling Use match with error enums for elegant error handling: ```js let result = fetch_data("example.com") |err| { match err { NetworkError!::ConnectionFailed => "Failed to connect", NetworkError!::Timeout => "Connection timed out", NetworkError!::ServerError => "Server error", } }; print(result); ``` ## Practical Examples ### State Machine ```rust enum TrafficLight { Red, Yellow, Green, } fn next_state(current: TrafficLight) -> TrafficLight { match current { TrafficLight::Red => TrafficLight::Green, TrafficLight::Yellow => TrafficLight::Red, TrafficLight::Green => TrafficLight::Yellow, } } let light = TrafficLight::Red; let next_light = match next_state(light) { TrafficLight::Red => "Stop", TrafficLight::Yellow => "Caution", TrafficLight::Green => "Go", }; print(next_light); // "Go" ``` Enums and match expressions in AIScript provide a powerful way to model domain concepts, handle errors, and create clean, maintainable code. By leveraging pattern matching, you can write code that is both safer and more expressive than traditional if-else chains. --- url: /guide/language/class-object.md --- # Class and Object AIScript offers a powerful class-based object-oriented programming model inspired by Rust and JavaScript. Classes provide a clean way to encapsulate data and behavior while offering intuitive syntax for inheritance and method definition. ## Defining a Class Use the `class` keyword to define a class with properties and methods. ```js class Person { name: str, age: int, fn new(name: str, age: int) { self.name = name; self.age = age; } } ``` The `new()` method is a special constructor that initializes a new instance of the class. ## Creating Objects Create a new instance of a class using the class name followed by parentheses containing constructor arguments: ```js let alice = Person("Alice", 30); print(alice); // Person {name: Alice, age: 30} ``` You can also use object literal syntax with type checking: ```js let bob = Person { name: "Bob", age: 25, }; print(bob); // Person {name: Bob, age: 25} ``` ## Methods Define methods within a class using the `fn` keyword, declare `self` as the first argument to make the method a instance method, otherwise, it's a static method: ```js class Person { name: str, age: int, fn new(name: str, age: int) { self.name = name; self.age = age; } fn greet(self) -> str { print("Hello, my name is", self.name, "and I'm", self.age, "years old."); } fn static_method() { print("static method of Person"); } } let alice = Person("Alice", 30); alice.greet(); // "Hello, my name is Alice and I'm 30 years old." Person.static_method(); // "static method of Person" ``` Use `class_instance.method()` to call an instance method, use `ClassName.method()` to call a static method. ## Accessing Properties and Methods Access object properties and methods using the dot notation: ```js print(alice.name); // "Alice" alice.age = 31; print(alice.age); // 31 print(alice.greet()); // "Hello, my name is Alice and I'm 31 years old." ``` ## Field Validation AIScript supports field validation using directive annotations similar to Python's [Pydantic](https://docs.pydantic.dev/latest/): ```js class User { @string(min_len=3, max_len=50) username: str, @number(min=0, max=120) age: int, } // This will throw a validation error let invalid_user = User { username: "a", age: 150, } |err| { print(err); }; ``` Above code will throw a validation error: ```json ValidationError! { errors: [ { msg: Number is greater than the maximum value of 120, loc: [age], type: validation_error, input: 150 }, { msg: String length is less than the minimum length of 3, loc: [username], type: validation_error, input: a } ] } ``` ## Inheritance Use the parentheses syntax to inherit from another class: ```js class Employee(Person) { job_title: str, fn new(name: str, age: int, job_title: str) { // Call the parent class constructor super.new(name, age); self.job_title = job_title; } fn greet(self) -> str { print("Hello, my name is", self.name,"and I'm", self.age, "years old, a", self.job_title); } } let developer = Employee("Carlos", 28, "Software Developer"); developer.greet(); // "Hello, my name is Carlos and I'm 28 years old, a Software Developer" ``` :::danger Notice AIScript only support inherit single super class. ::: ## Default Values You can provide default values for class properties: ```js class Config { host: str = "localhost", port: int = 8080, debug: bool = false, } // Use defaults let default_config = Config(); print(default_config); // Config {host: localhost, debug: false, port: 8080} // Override defaults let custom_config = Config { host: "example.com", port: 443, }; print(custom_config); // Config {host: example.com, debug: false, port: 443} ``` This overview covers the fundamentals of class and object-oriented programming in AIScript. By combining these features with AIScript's error handling, validation, and type system, you can create clean, maintainable, and robust code. --- url: /guide/language/error-handling.md --- # Error Handling AIScript provides a robust and elegant error handling system inspired by modern languages like Rust and Zig, while maintaining simplicity for developers. This approach allows you to write code that explicitly handles potential failures without resorting to exception hierarchies or complex try-catch blocks. ## Error Types In AIScript, errors are represented using enum or class with a special `!` suffix: ```js enum FileError! { NotFound, PermissionDenied, InvalidFormat, } class SyntaxError! { message: str, line: int, } ``` This syntax clearly communicates that an enum or class is meant to represent error cases. ## Declaring Functions with Error Types Functions that can fail should declare their potential error types in the return signature using the pipe (`|`) operator: ```rust fn read_file(path: str) -> str | FileError! { // Implementation that might fail... } ``` This return type specifies that the function returns either a string or a `FileError!`. ```rust fn read_file(path: str) -> str | FileError! | IoError! { } ``` For multiple error types, use the`|` to seperate them. ## Raising Errors Use the `raise` keyword to signal an error from a function: ```rust fn read_file(path: str) -> str | FileError! { if !file_exists(path) { raise FileError!::NotFound; } if !has_permission(path) { raise FileError!::PermissionDenied; } let content = read_file_contents(path); if !is_valid_format(content) { raise FileError!::InvalidFormat; } return content; } ``` ## Handling Errors AIScript provides three main ways to handle errors: ### Error Handler The error handler syntax uses the pipe (`|err|`) syntax followed by a code block: ```rust let content = read_file("config.json") |err| { match err { FileError!::NotFound => { print("File not found, using default config"); "{}" // Default empty config }, FileError!::PermissionDenied => { print("Permission denied, please check file permissions"); "{}" }, FileError!::InvalidFormat => { print("Invalid file format"); "{}" } } }; // content is guaranteed to be a string here print("Config loaded:", content"); ``` The error handler receives the error as a parameter and must return a value of the same type as the success case. ### Error Propagation For functions that might fail with similar error types, you can use the `?` operator to immediately return errors to the caller: ```rust fn load_configuration() -> str | FileError! { let file_content = read_file("config.json")?; // Propagate error let config = parse_json(file_content)?; // Propagate error return config; } // Usage let config = load_configuration() |err| { print("Error loading configuration: {err}"); {} // Default empty config }; ``` When a function call with `?` returns an error, the current function immediately returns with that error. ## Best Practices 1. **Be specific with error types**: Create domain-specific error types for different categories of errors 2. **Provide meaningful error information**: Include relevant details in error variants 3. **Handle errors close to their source**: When possible, handle errors where you have the context to do so 4. **Use the `?` operator for error propagation**: It makes code cleaner when functions call other functions that might fail 5. **Consider using default values**: For non-critical errors, providing sensible defaults can improve resilience AIScript's error handling system strikes a balance between robustness and simplicity. By making errors explicit in function signatures and providing elegant ways to handle them, it helps you write more reliable and maintainable code. --- url: /guide/language/module.md --- # Modules AIScript's module system allows you to organize your code into reusable, self-contained units. This helps maintain clean code structure, prevents naming conflicts, and enables efficient code sharing across your application. ## Importing Modules Use the `use` keyword to import modules: ```rust // Import a standard library module use std.math; use std.http; // Import a local module use my_app.utils; ``` ## Using Imported Modules After importing a module, you can access its functions, classes, and variables using dot notation: ```rust use std.math; // Use functions from the math module let result = math.sin(0.5) + math.cos(0.5); print(result); // Constants are also available print(math.PI); // 3.14159... ``` ## Creating and Export Modules In AIScript, each file is automatically a module. The name of the module corresponds to its file path relative to your project root: ```rust pub fn pluralize(word: str) -> str { // Simple pluralization logic if word.ends_with("s") { return word + "es"; } return word + "s"; } // Private helper function (not exported) fn is_vowel(char: str) -> bool { return char.to_lowercase() in ["a", "e", "i", "o", "u"]; } ``` Use `pub` keyword to make items public and available for import in other modules. ## Importing and Using Your Modules ```rust // Import your module use string_helpers; let word = "apple"; let plural = string_helpers.pluralize(word); print(plural); // "apples" // The private is_vowel function is not accessible // string_helpers.is_vowel("a"); // Error: function is not exported ``` ## Module Hierarchies AIScript supports hierarchical module structures: ``` project/ ├── main.ai ├── models/ │ ├── user.ai │ └── product.ai └── utils/ ├── validation.ai └── formatting.ai ``` You can import modules from this hierarchy: ```rust // In main.ai use models.user; use models.product; use utils.validation; use utils.formatting; let new_user = user.User("Alice", "alice@example.com"); ``` :::warning Circular Dependencies AIScript also has circular dependencies issue: ```rust // In module_a.ai use module_b; pub fn function_a() { print("Function A"); module_b.function_b(); } // In module_b.ai use module_a; pub fn function_b() { print("Function B"); } ``` AIScript cannot fix circular dependencies automatically, you should refactor your code to avoid them. ::: ## Standard Library Modules AIScript comes with a rich standard library organized into modules: ```rust // Math operations use std.math; print(math.sqrt(16)); // 4 // File system operations use std.fs; fs.write_file("output.txt", "Hello, world!"); // HTTP client and server use std.http; let response = http.get("https://api.example.com/data"); // Date and time use std.time; print(time.now()); // JSON handling use std.serde; let parsed = srde.from_str('{"name": "AIScript"}'); // Database operations use std.db.pg; let users = pg.query("SELECT * FROM users"); ``` ## Module Aliases Use the `as` keyword to create aliases for imported modules or items: ```rust use std.collections.HashMap as Map; use utils.very_long_module_name as short; let map = Map(); short.do_something(); ``` ## Dynamic Module Loading AIScript supports dynamic module loading for advanced use cases: ```rust fn load_module(module_name: str) -> object { return import(module_name); } let module = load_module("utils.helpers"); module.function(); ``` ## Best Practices 1. **Organize by feature**: Group related functionality together rather than organizing by type (e.g., models, utils) 2. **Keep modules focused**: Each module should have a single responsibility 3. **Minimize public API**: Only export what is necessary 4. **Use clear naming**: Module names should clearly indicate their purpose 5. **Avoid circular dependencies**: Restructure your code to minimize circular references 6. **Document public API**: Add comments or docstrings to explain exported functions and classes AIScript's module system strikes a balance between simplicity and power, allowing you to organize your code effectively while maintaining a clean development experience. --- url: /guide/ai/prompt.md --- # Prompt The `prompt` keyword is one of AIScript's core AI integration features, allowing you to easily interact with large language models (LLMs) directly from your code. ## Basic Usage The simplest form of the `prompt` keyword takes a string argument and returns the model's response: ```rust let answer = prompt "What is the capital of France?"; print(answer); // The capital of France is Paris. ``` In this basic form, AIScript will use the default model configuration (typically OpenAI's model) to generate the response. ## Advanced Configuration For more control over the prompt behavior, you can provide additional configuration options: ```rust let detailed_answer = prompt { input: "Explain quantum computing", model: "claude-3.7", max_tokens: 500, temperature: 0.7, system_prompt: "You are a quantum physics expert" }; ``` ### Configuration Options | Option | Type | Description | |--------|------|-------------| | `input` | String | The prompt text to send to the LLM | | `model` | String | The model identifier to use (e.g., "gpt-4", "claude-3.7") | | `max_tokens` | Number | Maximum number of tokens in the generated response | | `temperature` | Number | Controls randomness (0.0-1.0, lower is more deterministic) | | `system_prompt` | String | System message to guide the model's behavior | ## Dynamic Prompts You can construct prompts dynamically using variables and string interpolation: ```rust let topic = "quantum computing"; let detail_level = "beginner"; let response = prompt f"Explain {topic} at a {detail_level} level"; ``` ## API Keys and Configuration To use the `prompt` feature, you need to configure your API keys. AIScript supports multiple LLM providers including cloud-based services and local models. ### Environment Variables Set your API keys using environment variables: ```bash # For OpenAI export OPENAI_API_KEY="your_openai_key" # For Anthropic Claude export CLAUDE_API_KEY="your_claude_key" # For Ollama (local models) export OLLAMA_API_ENDPOINT="http://localhost:11434/v1" ``` ### Configuration File Alternatively, you can specify API keys in your project [configuration file](/reference/configuration/ai): ```toml [ai.openai] api_key = "YOUR_OPENAI_API_KEY" [ai.anthropic] api_key = "YOUR_CLAUDE_API_KEY" # For Ollama (local models) [ai.ollama] api_endpoint = "http://localhost:11434/v1" model = "llama3.2" # or any other model installed in your Ollama instance ``` ## Local Models with Ollama AIScript supports running local models through [Ollama](https://ollama.ai/), which allows you to run AI models on your own hardware without sending data to external services. ### Setting Up Ollama 1. **Install Ollama** from [ollama.ai](https://ollama.ai/) 2. **Pull models** you want to use: ```bash ollama pull llama3.2 ollama pull deepseek-r1 ollama pull codellama ``` 3. **Start Ollama** (it runs on port 11434 by default) 4. **Configure AIScript** to use Ollama ### Example with Local Models ```rust // Using a local Llama model let response = prompt { input: "What is rust?", model: "llama3.2" }; // Using a local coding model let code_explanation = prompt { input: "Review this Python function for potential issues", model: "codellama" }; ``` ## Error Handling Like other AIScript operations, you can use error handling with the `prompt` keyword: ```rust let response = prompt "Generate a complex response" |err| { print(f"Error occurred: {err}"); return "Fallback response"; }; ``` ## Best Practices * **Be specific**: The more specific your prompt, the better the response quality. * **Consider context limits**: Each model has a maximum context window. Very large prompts may be truncated. * **Handle rate limits**: API providers have rate limits. Consider implementing retry logic for production applications. * **Manage costs**: Using LLMs incurs costs based on token usage. Monitor usage to avoid unexpected charges. ## Examples ### Simple Question-Answering ```rust let question = "What is the capital of Japan?"; let answer = prompt question; print(answer); ``` ### Generating Content with Specific Parameters ```rust let poem = prompt { input: "Write a haiku about programming", temperature: 0.9, max_tokens: 100 }; print(poem); ``` ### Using Different Models ```rust // Using OpenAI let openai_response = prompt { input: f"Summarize this article: {article_text}", model: "gpt-4" }; // Using Claude let claude_response = prompt { input: f"Summarize this article: {article_text}" , model: "claude-3.7" }; // Using Ollama (local models) let local_response = prompt { input: f"Summarize this article: {article_text}", model: "llama3.2" }; ``` ### Chaining Prompts ```rust let topic = prompt "Suggest a topic for a blog post"; let outline = prompt f"Create an outline for a blog post about: {topic}"; let introduction = prompt f"Write an introduction for a blog post with this outline: {outline}"; print("Topic:", topic); print("Outline", outline); print("Introduction:", introduction); ``` ## Notes and Limitations * The response from a prompt is always a string. If you need structured data, consider asking for specific formats like JSON in your prompt or parse the result. * Network connectivity is required for the `prompt` keyword to function, as it makes API calls to LLM providers. * API costs apply based on your provider's pricing model, typically based on the number of tokens processed. --- url: /guide/ai/function.md --- # AI Functions AI Functions enable you to encapsulate AI capabilities into reusable code blocks, making your AI-powered applications more modular and maintainable. In AIScript, AI functions are defined using the `ai fn` syntax, allowing you to create functions that leverage language models directly within your code. ## Basic Syntax An AI function is defined using the `ai` keyword followed by a regular function declaration: ```rust ai fn function_name(parameter1: type, parameter2: type) -> return_type { // Function body with AI capabilities } ``` The `ai` prefix allows the function to use AI-specific features like the `prompt` keyword, and it also signals to the AIScript runtime that this function may require LLM access. :::warning You can not use `prompt` outside the non-ai function. ```rust fn analyze_sentiment(text: str) -> float { // [line 3] Error at 'prompt': Can't prompt outside of ai function or root script. let sentiment = prompt f"Analyze the sentiment of this text and return a score between -1 and 1: {text}"; return float(sentiment); } ``` ::: ## Example: Sentiment Analysis Here's a basic example of an AI function that analyzes sentiment in text: ```rust ai fn analyze_sentiment(text: str) -> float { let sentiment = prompt f"Analyze the sentiment of this text and return a score between -1 and 1: {text}"; return float(sentiment); } // Use the AI function let review = "This product is amazing! Best purchase ever!"; let score = analyze_sentiment(review); print(score); // 0.9 ``` ## Error Handling in AI Functions Since AI functions often involve network operations and third-party services, it's good practice to include error handling: ```rust ai fn generate_summary(text: str) -> str | NetworkError! { let summary = prompt f"Summarize this text in 2-3 sentences: {text}" ?; return summary; } // Using the function with error handling let article = "...long article text..."; let summary = generate_summary(article) |err| { print(f"Failed to generate summary: {err}"); return "Summary unavailable"; }; ``` ## AI Function Configuration You can customize the behavior of AI functions by configuring prompt parameters: ```rust ai fn generate_ideas(topic: str, count: int = 3) -> str { let ideas = prompt { input: f"Generate {count} creative ideas about {topic}", temperature: 0.9, // Higher creativity max_tokens: 300 }; return ideas; } ``` ## Best Practices 1. **Descriptive Function Names**: Choose names that clearly indicate the function's purpose, like `classify_text()` or `generate_summary()`. 2. **Explicit Return Types**: Always specify the return type for clarity and to enable better error handling. 3. **Input Validation**: Validate parameters before sending them to the language model to prevent errors. 4. **Prompt Engineering**: Craft clear, specific prompts within your AI functions to get consistent results. 5. **Response Processing**: Always process and validate the raw responses from the language model before returning them. 6. **Error Handling**: Implement proper error handling for network issues, API failures, and unexpected responses. 7. **Documentation**: Add comments to explain what the function does, expected inputs, and potential limitations. ## Performance Considerations - AI functions incur latency due to network calls to LLM providers - Use caching for frequently used AI function results when appropriate - Consider batching related AI operations together to minimize API calls - Be aware of token usage and associated costs ## Limitations - AI functions rely on external API services and are affected by their availability - Results from LLMs may vary over time or with different model versions - Complex parsing of LLM outputs may sometimes be unreliable due to model variability AI functions are a powerful way to incorporate AI capabilities into your AIScript applications while maintaining clean, modular code that separates AI logic from your application logic. --- url: /guide/ai/agent.md --- # Agents Agents in AIScript provide a powerful way to create autonomous, goal-directed AI assistants with access to specific tools and capabilities. The `agent` keyword allows you to define intelligent agents that can execute complex workflows, make decisions, and interact with both users and other agents. ## Basic Syntax An agent is defined using the `agent` keyword followed by a name and a set of configurations and tool functions: ```rust agent AgentName { instructions: "Detailed instructions for the agent", model: "model-name", // Optional tools: [function1, function2], // Optional fn tool_function1(param1: type, param2: type) -> return_type { // Tool implementation } fn tool_function2(param: type) -> return_type { // Another tool implementation } } ``` ## Agent Configuration Agents can be configured with several parameters: | Parameter | Type | Description | |-----------|------|-------------| | `instructions` | String | Detailed instructions for the agent's behavior and goals | | `model` | String | Optional. The LLM model to use (defaults to system default) | | `tools` | Array | Optional. List of external functions the agent can use | | `tool_choice` | String | Optional. Strategy for selecting tools ("auto" or "none") | ## Agent Tools Tools are functions that agents can call to perform specific actions. Tools can be defined in three ways: 1. **Internal tools**: Functions defined inside the agent block 2. **External tools**: Functions passed to the agent via the `tools` parameter 3. **Tool references**: References to tools defined in other files or modules Example of internal tools: ```rust agent Researcher { instructions: "You are a research assistant...", fn search_web(query: str) -> str { // Implementation return "search results"; } fn save_notes(content: str) -> bool { // Implementation return true; } } ``` Example of external tools: ```rust fn fetch_data(url: str) -> str { // Implementation } fn analyze_data(data: str) -> object { // Implementation } agent DataAnalyst { instructions: "You are a data analyst...", tools: [fetch_data, analyze_data] } ``` ### Tool Docs In order to help LLM understand the purpose of each tool, you can add docstrings to your tools. Docstrings should be placed immediately after the tool's definition and should follow the python-style doc comment format. ```rust agent Researcher { instructions: "You are a research assistant...", fn search_web(query: str) -> str { """ Search the web for information about a topic. """ // Implementation return "search results"; } fn save_notes(content: str) -> bool { """ Save notes about a topic. """ // Implementation return true; } } ``` ## Running Agents To activate an agent, use the `run` method. This starts the agent's execution with optional input and configuration: ```rust // Basic usage let result = MyAgent.run(input="Please help me with this task"); // With debug mode let debug_result = MyAgent.run( input="Analyze this data", debug=true ); ``` The `run` method accepts these parameters: | Parameter | Type | Description | |-----------|------|-------------| | `input` | String | Initial input/instruction for the agent | | `debug` | Boolean | Optional. When true, shows detailed agent thought process | ## Multi-Agent Orchestration AIScript supports sophisticated multi-agent systems where agents can collaborate and transfer control between each other. This enables complex workflows and agent swarms. :::info AIScript builtin multi-agent orchestration was inspired by [OpenAI Swarm](https://github.com/openai/swarm). ::: ### Agent Transfer Agents can transfer control to other agents using return values: ```rust agent Coordinator { instructions: "You coordinate the workflow...", fn transfer_to_specialist(topic: str) { if topic == "finance" { return FinanceSpecialist; } else { return TechSpecialist; } } } agent FinanceSpecialist { instructions: "You provide financial advice...", fn transfer_back() { return Coordinator; } } ``` ## Example: Rock Paper Scissors Game This example demonstrates a multi-agent system that implements a simple Rock Paper Scissors game: ```rust let player1_move = nil; let player2_move = nil; agent Judge { instructions: "You are the judge of the Rock Paper Scissors game. 1. Let Player1 move first then Player2 2. Use record_move to store each player's move after it finished 3. After both players finished, use announce_result to display the result 4. End the game after announcing results", fn record_move(player: str, move: str) { """Record a player's move.""" print(player, "choose", move); if player == "Player1" { player1_move = move; } else { player2_move = move; } } fn announce_result() { """Check the recorded moves and announce the winner.""" let winning_moves = {"rock": "scissors", "scissors": "paper", "paper": "rock"}; if player1_move == player2_move { print("It's a tie!"); } else if winning_moves[player1_move] == player2_move { print("Player 1 wins!"); } else { print("Player 2 wins!"); } } fn transfer_to_player1() { """Transfer control to Player 1 Agent""" return Player1; } fn transfer_to_player2() { """Transfer control to Player 2 Agent""" return Player2; } } ``` ```rust use std.random; fn make_move() -> str { """Make a move in the game. Returns rock, paper, or scissors.""" return random.choice(["rock", "paper", "scissors"]); } fn transfer_to_judge() { """Transfer control to Judge Agent""" return Judge; } agent Player1 { instructions: "You are Player 1 in the Rock Paper Scissors game. 1. Make your move using the make_move function 2. Transfer control to the judge after your move", tools: [make_move, transfer_to_judge], } agent Player2 { instructions: "You are Player 2 in the Rock Paper Scissors game. 1. Make your move using the make_move function 2. Transfer control to the judge after your move", tools: [make_move, transfer_to_judge], } ``` ```js // Start the game with the Judge agent Judge.run(input="Let's start play the game!", debug=true); // Here are the logs: // // Judge call transfer_to_player1() // Player1 call make_move(): scissors // Player1 call transfer_to_judge() // Judge call record_move(Player1, scissors) // Judge call transfer_to_player2() // Player2 call make_move(): paper // Player2 call transfer_to_judge() // Judge call record_move(Player2, paper) // Judge call announce_result(): Player 1 wins! ``` ## Advanced Agent Techniques ### Stateful Agents Agents can maintain state across interactions by using variables in their surrounding scope: ```rust let conversation_history = []; agent CustomerSupport { instructions: "You are a customer support agent...", fn add_to_history(message: str) { conversation_history.push(message); } fn summarize_conversation() -> str { let history = conversation_history.join("\n"); return prompt f"Summarize this conversation: {history}"; } } ``` ### Agent Specialization Create specialized agents for different domains or tasks: ```rust agent CodeExpert { instructions: "You are an expert in software development...", model: "gpt-4", // More capable model for coding tasks fn review_code(code: str) -> str { return prompt f"Review this code and provide feedback: {code}"; } } agent ContentWriter { instructions: "You are a creative content writer...", model: "claude-3.7", // Different model for creative tasks fn write_article(topic: str, style: str) -> str { return prompt "Write an article about {topic} in {style} style"; } } ``` ## Best Practices 1. **Clear Instructions**: Provide detailed, step-by-step instructions to guide your agent's behavior. 2. **Minimize State**: Keep shared state minimal and explicit to avoid confusion. 3. **Tool Documentation**: Add descriptive docstrings to your tools to help the agent understand their purpose. 4. **Error Handling**: Implement error handling in tools to provide informative feedback. 5. **Agent Boundaries**: Clearly define responsibilities for each agent in multi-agent systems. 6. **Test Thoroughly**: Test your agents with various inputs to ensure they behave as expected. ## Limitations * Agents operate within the constraints of the underlying LLM models * Tool execution may have latency due to API calls and network operations * Complex multi-agent systems may require careful orchestration to avoid loops or deadlocks * Agents cannot maintain state independently of your code; state must be explicitly managed ## Performance Considerations * Use the most appropriate model for each agent based on task complexity * Consider batching operations when possible to reduce API calls * Monitor token usage for cost management * For complex workflows, consider implementing checkpoints or resumable states Agents in AIScript provide a powerful framework for building autonomous AI systems that can work together to solve complex problems, all while maintaining the simplicity and clarity that AIScript is designed for. --- url: /guide/ai/local-models.md --- # Local Models with Ollama AIScript supports running AI models locally through [Ollama](https://ollama.ai/), providing privacy, cost-effectiveness, and offline capabilities. ## Setting Up Ollama ### Installation 1. **Install Ollama** from [ollama.ai](https://ollama.ai/) 2. **Start Ollama** service (runs on port 11434 by default) 3. **Pull models** you want to use: ```bash # Popular general-purpose models ollama pull llama3.2 # Coding models ollama pull codellama ollama pull deepseek-coder # Specialized models ollama pull deepseek-r1 ollama pull gemma2 ollama pull qwen2.5 ``` ### Verification Check that Ollama is running and models are available: ```bash # List installed models ollama list # Test a model ollama run llama3.2 "Hello, how are you?" ``` ## Configuration Configure AIScript to use Ollama models: ```toml [ai.ollama] api_endpoint = "http://localhost:11434/v1" model = "llama3.2" # Default model ``` ```bash export OLLAMA_API_ENDPOINT="http://localhost:11434/v1" ``` ## Basic Usage ### Simple Prompts ```rust let response = prompt { input: "Explain how neural networks work", model: "llama3.2" }; ``` ## Troubleshooting ### Common Issues **Ollama not responding:** ```bash # Check if Ollama is running curl http://localhost:11434/api/tags # Restart Ollama service ollama serve ``` **Model not found:** ```bash # List available models ollama list # Pull missing model ollama pull llama3.2 ``` **Out of memory:** * Try a smaller model (e.g., `phi3` instead of `llama3.1`) * Close other applications * Use quantized models ### Performance Issues * **Slow responses**: Try smaller models or increase hardware resources * **High memory usage**: Monitor with `htop` or Activity Monitor * **GPU not utilized**: Ensure GPU drivers are properly installed ## Comparison: Local vs Cloud Models | Aspect | Local Models | Cloud Models | |--------|-------------|--------------| | **Privacy** | ✅ Complete | ❌ Data sent externally | | **Cost** | ✅ One-time setup | ❌ Per-token billing | | **Internet** | ✅ Works offline | ❌ Requires connection | | **Latency** | ✅ Low (local) | ❌ Network dependent | | **Quality** | ⚠️ Model dependent | ✅ Usually higher | | **Maintenance** | ❌ Self-managed | ✅ Fully managed | | **Scaling** | ❌ Hardware limited | ✅ Unlimited | Local models with Ollama provide a powerful way to integrate AI capabilities into your AIScript applications while maintaining full control over your data and costs. --- url: /guide/web/route.md --- # Route and Endpoint **Route** is the main entry point that handles an HTTP request in AIScript. ## Define An Endpoint Defining an endpoint is simple and straightforward. The syntax is: ``` verb path [, verb path...] { // query parameters query { // parameter definitions } // request body body { // body field definitions } } ``` The `verb` is the HTTP method: | Verb | Description | | -------- | -------------- | | `get` | GET request | | `post` | POST request | | `put` | PUT request | | `delete` | DELETE request | | `head` | HEAD request | The `path` is the URL path. ```py get /hello { return "Hello World"; } ``` ```bash $ curl http://localhost:8080/hello Hello World ``` ## Parse Query String Use the `query` block to declare fields in the query string. You can set a default value to make a parameter optional. To access query parameters, use the `query.name` or `query["name"]` syntax. ```py get /hello { query { @string(min_len=3, max_len=10) name: str = "Alice" } return f"Hello, {query.name}!"; } ``` ```bash $ curl http://localhost:8080/hello?name=AIScript Hello, AIScript! ``` Each field declared in the `query` block is a query parameter, with `str` indicating the parameter type. ```bash $ curl http://localhost:8080/hello Hello, Alice! ``` The [@string](/reference/directives#string) is a validator. AIScript will validate the query parameter and return an error if validation fails. ```bash $ curl http://localhost:8080/hello?name=Le { "error": "Invalid query parameter: name, must be between 3 and 10 characters" } ``` :::tip For more information about validators, see [Validator](./validator.md). ::: ## Parse Request Body ```py post /hello { @json body { @string(min_len=3, max_len=10) name: str } return f"Hello, {body.name}!"; } ``` ```bash $ curl -X POST -H "Content-Type: application/json" \ -d '{"name": "AIScript"}' http://localhost:8080/hello Hello, AIScript! ``` Each field declared in the `body` block is a request body parameter, with `str` indicating the parameter type. The [@json](/reference/directives#json) directive tells AIScript to parse the request body as JSON. Alternatively, the [@form](/reference/directives#form) directive instructs AIScript to parse the request body as form data. Access body parameters using `body.name` or `body["name"]` syntax. ```py post /hello { @form body { @string(min_len=3, max_len=10) name: str } return f"Hello, {body.name}!"; } ``` ```bash $ curl -X POST -H "Content-Type: application/x-www-form-urlencoded" \ -d 'name=AIScript' http://localhost:8080/hello Hello, AIScript! ``` ## Headers and Cookies Accessing and setting headers and cookies is straightforward using the `header` and `cookie` variables, which are request-scoped. ```py get /hello { let abc = header.abc; let xyz = cookie.xyz; return f"header: abc={abc}, cookie: xyz={xyz}"; } ``` :::warning `cookie` is not supported yet. ::: ## Request Object ```py get /hello { let method = request.method; let url = request.url; let path = request.path; let scheme = request.scheme; let host = request.host; let port = request.port; return f"method: {method}, url: {url}, path: {path}, scheme: {scheme}, host: {host}, port: {port}"; } ``` ```bash $ curl http://localhost:8080/hello method: GET, url: http://localhost:8080/hello, path: /hello, scheme: http, host: localhost, port: 8000 ``` ## Redirect ```py get /hello { return temporary_redirect("/hello2"); } get /hello2 { return "Hello, World!"; } ``` ## Path Parameters ```py get /hello/:name { path { name: str } return f"Hello, {path.name}!"; } ``` ## Multiple Routes ```py get /hello, get /world { return "Hello, World!"; } post /hello2 { return "Hello, World2!"; } ``` ## Route Programming API routes typically require more than simple text responses; they often need programming logic to handle requests. In AIScript routes, you can write any programming logic just as you would in other web frameworks. ```py get /hello { query { value: int } if query.value > 10 { return "Value is greater than 10"; } else { return "Value is less than or equal to 10"; } } ``` ```bash $ curl http://localhost:8080/hello?value=11 Value is greater than 10 ``` ## Query Database with SQL ```py get /tweet/:id { path { id: str } use std.db.pg; return pg.query("SELECT * FROM tweet WHERE id = $1", path.id); } ``` --- url: /guide/web/validator.md --- # Validator Validators are directives that validate the value of a field, most commonly used in routes. ## Route Validators In most web frameworks, you need to write a lot of code to validate request bodies, query strings, and path parameters. Examples include [webargs](https://webargs.readthedocs.io/en/latest/) in Python, [express-validator](https://express-validator.github.io/docs) in Node.js, and [zod](https://github.com/colinhacks/zod) in TypeScript. AIScript provides a simple way to validate request components using directives. ### Query String Validators ```js get /hello { query { @string(min_len=3, max_len=10) name: str, @in(["male", "female"]) gender: str } } ``` ### Request Body Validators ```js post /hello { @json // or use @form to validate form data body { @regex(r"^\d{4}-\d{2}-\d{2}$") birthdate: str, @format(type="email") email: str } } ``` ## Built-in Validators AIScript provides several built-in validators for common validation scenarios, for the full list of validators, see [Validator Reference](/reference/directives#validators). ### String Validators ```js @string(min_len=3, max_len=20) username: str @regex(r"^[a-zA-Z0-9_]+$") alphanumeric: str @format(type="email") email: str @format(type="url") website: str ``` ### Numeric Validators ```js @number(min=18, max=100) age: int @number(gt=0) positive_value: float ``` ### Collection Validators ```js @array(min_len=1, max_len=5) tags: array @in(["admin", "user", "guest"]) role: str ``` ### Date Validators ```js @date(format="YYYY-MM-DD") birthdate: str @date(min="2022-01-01", max="2022-12-31") event_date: str ``` ## Custom Error Messages You can provide custom error messages for validation failures: ```js @string(min_len=8, error="Password must be at least 8 characters") password: str @in(["USD", "EUR", "GBP"], error="Currency must be one of USD, EUR, or GBP") currency: str ``` ## Combining Validators Multiple validators can be applied to a single field: ```js @string(min_len=3) @regex(r"^[a-zA-Z0-9_]+$", error="Username can only contain letters, numbers, and underscores") username: str ``` ## Validation Response When validation fails, AIScript automatically returns an appropriate HTTP response with detailed error information: ```json { "error": "Validation Error", "details": [ { "field": "username", "message": "String must be at least 3 characters long" }, { "field": "address.zipcode", "message": "Must match pattern ^\d{5}$" } ] } ``` ## Advanced Usage ### Combining Multiple Validators Multiple validators can be applied to a single field for complex validation scenarios: ```js post /api/user { @json body { @string(min_len=3, max_len=20) @regex(r"^[a-zA-Z0-9_]+$") username: str, @string(min_len=8) @not(@in(["password", "12345678"])) password: str } } ``` ### Custom Error Messages Most validators accept an `error` parameter for custom error messages: ```js post /api/register { @json body { @string(min_len=3, error="Username must be at least 3 characters") username: str, @format(type="email", error="Please provide a valid email address") email: str } } ``` ### Conditional Validation Use the `@or` directive for conditional validation: ```js post /api/payment { @json body { @or(@regex(r"^\d{16}$"), @format(type="email")) payment_type: str, payment_details: str } } ``` By using validators, AIScript helps you build robust applications with clear data validation rules, reducing boilerplate code and ensuring consistent validation throughout your application. --- url: /guide/web/response.md --- # Response In AIScript, the `Response` object provides a flexible way to control HTTP responses from your routes. This object gives you complete control over status codes, response bodies, headers, and cookies. ## Basic Usage The most basic way to create a response is to simply return a value from a route: ```js get /hello { return "Hello, World!"; } ``` AIScript automatically converts this to a proper HTTP response with status code 200 and the appropriate content type. ## Creating Custom Responses For more control, use the `Response` constructor: ```js get /api/user { return response( status_code=200, body={"id": 1, "name": "Alice"}, headers={"X-API-Version": "1.0"}, cookies={"session": "abc123"} ); } ``` The `Response` constructor accepts the following parameters: | Parameter | Type | Description | Default | |-----------|------|-------------|---------| | `status_code` | Number | HTTP status code (100-599) | 200 | | `body` | Any | Response body (can be string, object, array, etc.) | nil | | `headers` | Object | HTTP headers as key-value pairs | {} | | `cookies` | Object | Cookies as key-value pairs | {} | ## Status Codes You can set any valid HTTP status code: ```js get /not-found { return response( status_code=404, body="Resource not found" ); } get /error { return response( status_code=500, body="Internal server error" ); } ``` ## Response Body The response body can be of any type: ```js // String response get /text { return response(body="Plain text response"); } // JSON response (object automatically serialized to JSON) get /json { return response(body={ "id": 123, "name": "Product", "price": 29.99 }); } // Array response get /array { return response(body=[1, 2, 3, 4, 5]); } ``` ## Headers Set custom HTTP headers: ```js get /api/data { return response( body={"data": "value"}, headers={ "Content-Type": "application/json", "X-API-Version": "2.0", "Cache-Control": "max-age=3600" } ); } ``` ## Cookies Set cookies in the response: ```js get /login { return response( body="Login successful", cookies={ "session": "abc123", "user": "john", "preferences": "dark-mode" } ); } ``` ## Redirects AIScript provides convenient functions for creating redirect responses: ### Temporary Redirect (307) ```js get /old-page { return temporary_redirect(target="/new-page"); } ``` ### Permanent Redirect (308) ```js get /outdated { return permanent_redirect(target="/updated"); } ``` ## Advanced Examples ### Conditional Response ```js get /api/users/:id { path { id: str } use std.db.pg; let user = pg.query("SELECT * FROM users WHERE id = $1", path.id); if user == nil { return response( status_code=404, body={"error": "User not found"} ); } return response( status_code=200, body=user, headers={"X-User-Id": str(id)} ); } ``` ### File Download ```js get /download { use std.io; let file_content = io.read_file("path/to/document.pdf"); return response( body=file_content, headers={ "Content-Type": "application/pdf", "Content-Disposition": "attachment; filename=\"document.pdf\"" } ); } ``` ### CORS Support ```js get /api/data { return response( body={"message": "API data"}, headers={ "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "GET, POST, OPTIONS", "Access-Control-Allow-Headers": "Content-Type, Authorization" } ); } ``` ## Error Responses AIScript automatically generates appropriate error responses for validation failures and exceptions, but you can also create custom error responses: ```js get /api/resource { if !authorized() { return response( status_code=401, body={"error": "Unauthorized access"} ); } if !resource_exists() { return response( status_code=404, body={"error": "Resource not found"} ); } // Continue with normal processing... } ``` --- url: /guide/web/openapi-docs.md --- # OpenAPI and Docs AIScript provides automatic OpenAPI documentation generation from your route definitions with zero configuration. This feature creates comprehensive API documentation including type information, validation rules, and examples derived directly from your code. ## Basic Documentation The simplest way to document your API is through docstrings. In AIScript, you can use triple-quoted strings (`"""..."""`) to provide documentation for routes and their parameters: ```py post /api/chat { """ Chat API endpoint Sends a message to the chat service and returns a response. """ body { """The message to be sent to the chat service""" message: str, } return f"Input: {body.message}!"; } ``` ## Using @docs Directive The `@docs` directive gives you fine-grained control over how your API is documented: ```py @docs(tag="Authentication", deprecated=true) post /api/login { """Login endpoint - use the new OAuth endpoint instead""" // Route implementation } ``` The `@docs` directive supports the following parameters: - `tag`: String value that groups related endpoints in the documentation UI - `deprecated`: Boolean that marks an endpoint as deprecated - `hidden`: Boolean that excludes an endpoint from the documentation ## Documenting Routes Each route can have a detailed description using docstrings and can be enhanced with the `@docs` directive: ```py @docs(tag="Users") get /api/users { """ List all users Returns a paginated list of all users in the system. Results can be filtered by role or status. """ query { @number(min=1) """Page number for pagination""" page: int = 1, @number(min=1, max=100) """Number of items per page""" limit: int = 20 } // Route implementation } ``` ## Documenting Parameters You can document query parameters, path parameters, and request body fields: ### Query Parameters ```py get /api/products { query { """Filter products by category""" category: str, """Sort order (asc or desc)""" @in(["asc", "desc"]) sort: str = "asc", """Minimum price filter""" @number(min=0) min_price: float } } ``` ### Path Parameters Path parameters allow you to capture values from the URL path. The syntax similar to `query {}` block, you can add type validators to each parameter: ```py get /api/users/:id { """ Get user by ID "" path { """User ID (must be a valid string)""" id: str } // Route implementation } ``` You can combine multiple path parameters and add type validation: ```py get /users/:userId/posts/:postId { """ Get user's post """ path { """User ID (alphanumeric with minimum length)""" @string(min_len=3) userId: str, """Post ID (must be a positive integer)""" @number(min=1) postId: int } // Implementation example print(f"Fetching post {path.postId} for user {path.userId}"); return { "userId": path.userId, "postId": path.postId }; } ``` ### Request Body ```py post /api/products { @json body { """Product name (must be unique)""" @string(min_len=3, max_len=100) name: str, """Detailed product description""" description: str, """Product price in USD""" @number(min=0.01) price: float, """Product categories (at least one required)""" @array(min_items=1) categories: array } } ``` ## Response Documentation You can document the responses a route might return: ```py get /api/orders/:id { """ Get order details Retrieves the details of a specific order. Responses: 200: Successful operation 404: Order not found 403: Unauthorized access """ path { id: str } // Route implementation } ``` ## Auto-Generated Documentation AIScript automatically generates OpenAPI documentation based on your route definitions. This documentation includes: 1. All routes and their HTTP methods 2. Request parameters (query, path, body) 3. Validation rules applied to parameters 4. Data types for all parameters and responses 5. Docstrings as descriptions 6. Documentation specific to each parameter 7. Tags and groupings defined by `@docs` directives 8. Deprecated endpoints marked accordingly ## Accessing Documentation By default, AIScript makes your API documentation available at `/redoc` and the OpenAPI specification at `/openapi.json`. You can customize these paths in your project configuration: ```toml # project.toml [apidoc] enabled = true type = "redoc" path = "/redoc" ``` AIScript's documentation UI support **Swagger UI** and **Redoc**. ## Tags and Grouping You can use the `tag` parameter in the `@docs` directive to group related endpoints in the documentation: ```py @docs(tag="Users") get /api/users { """List all users""" } @docs(tag="Users") get /api/users/:id { """Get user by ID""" } @docs(tag="Products") get /api/products { """List all products""" } ``` In the generated documentation UI, endpoints will be grouped by their tags, making it easier to navigate complex APIs. ## Marking Deprecated Endpoints Use the `deprecated` parameter to mark endpoints that should no longer be used: ```py @docs(deprecated=true) get /api/v1/users { """This endpoint is deprecated. Use /api/v2/users instead.""" } ``` Deprecated endpoints will be clearly marked in the documentation UI, but will still be included so that users who need to migrate can understand the old API. ## Hiding Endpoints You can exclude specific endpoints from documentation with the `hidden` parameter: ```py @docs(hidden=true) get /internal/metrics { """This endpoint won't appear in documentation""" } ``` This is useful for internal endpoints that aren't meant for public consumption. ## Combining Multiple Directives You can combine the `@docs` directive with other directives to create comprehensive documentation: ```py @auth(jwt=true) @docs(tag="Users") get /api/users/me { """Get the currently authenticated user's profile""" // Route implementation } ``` ## Benefits Automatic OpenAPI documentation in AIScript offers several benefits: 1. **Zero configuration**: Documentation is generated automatically from your code 2. **Always up-to-date**: Documentation updates whenever your code changes 3. **Complete type information**: All parameter types are accurately reflected 4. **Validation rules included**: All validators are documented 5. **Interactive testing**: Test endpoints directly from the documentation UI 6. **Client generation**: Use the OpenAPI spec to generate client libraries in various languages 7. **Organized documentation**: Group endpoints logically with tags 8. **Deprecation notices**: Clearly mark endpoints that are being phased out AIScript's built-in documentation system ensures your API is well-documented with minimal effort, making it easier for both you and your API consumers to understand and use your services. --- url: /guide/web/project.md --- # Project Building web applications is rarely a single-file endeavor. AIScript provides a structured project system to help you organize your code efficiently. ## Creating a New Project Let's create a new project with the AIScript CLI: ```bash $ aiscript new my-project ``` This command creates a new project named `my-project` in the current directory. ```bash $ cd my-project $ tree my-project ├── lib/ ├── migrations/ ├── routes/ └── project.toml ``` ## Project Structure AIScript follows the **"convention over configuration"** principle. This means that by following standard directory structures, you can minimize the amount of configuration needed. The default project structure is as follows: ### project.toml The `project.toml` file is a **TOML** configuration file for your project: ```toml [project] name = "my-project" description = "My project" version = "0.1.0" [apidoc] enabled = true type = "redoc" path = "/redoc" [network] host = "0.0.0.0" port = 8000 ``` You can configure project metadata, API documentation settings, network options, and more. For detailed configuration options, see [Configuration Reference](/reference/configuration/general). ### routes/ The `routes` directory contains route definition files. Each file in this directory can define multiple routes: ``` routes/ ├── auth.ai # Authentication routes ├── users.ai # User management routes └── products.ai # Product routes ``` Example route file (`routes/users.ai`): ```js get /api/users { // Return all users } get /api/users/:id { // Return user by ID } post /api/users { // Create a new user } ``` ### lib/ The `lib` directory contains reusable library code. You can organize your application logic into modules that can be imported by your routes: ``` lib/ ├── auth.ai # Authentication utilities ├── db.ai # Database helpers └── validation.ai # Validation functions ``` Example library file (`lib/auth.ai`): ```js fn validate_token(token: str) -> bool { // Token validation logic return true; } fn get_user_from_token(token: str) -> object { // Extract user information from token return { id: 1, name: "John" }; } ``` Using library code in routes: ```js // routes/auth.ai use lib.auth; post /api/login { // Login logic } get /api/profile { let token = header.Authorization; if !auth.validate_token(token) { return response(status_code=401, body={"error": "Invalid token"}); } let user = auth.get_user_from_token(token); return user; } ``` ### migrations/ The `migrations` directory contains SQL migration files for database schema changes: ``` migrations/ ├── 001_create_users.sql ├── 002_create_products.sql └── 003_add_user_roles.sql ``` Example migration file (`migrations/001_create_users.sql`): ```sql -- Up migration CREATE TABLE users ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, email VARCHAR(255) UNIQUE NOT NULL, password VARCHAR(255) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ``` ## Project Commands The AIScript CLI provides several commands to manage your project: ```bash # Start the development server $ aiscript serve # Run migrations $ aiscript migrate up # Roll back migrations $ aiscript migrate down ``` For more information, refer to the [Command Reference](/reference/cli). ## Environment Variables AIScript supports environment variables for configuration. Create a `.env` file in your project root: ``` # .env DATABASE_URL=postgres://user:password@localhost/mydb JWT_SECRET=your-secret-key PORT=8080 ``` Access environment variables in your code: ```js let port = $PORT; let db_url = $DATABASE_URL; ``` ## Project Configuration The `project.toml` file supports various configuration options: ### General Configuration ```toml [project] name = "my-project" description = "My awesome project" version = "1.0.0" ``` ### Network Configuration ```toml [network] host = "0.0.0.0" port = 8080 ``` ### Database Configuration ```toml [database] url = "postgres://user:password@localhost/mydb" pool_size = 10 ``` ### API Documentation ```toml [apidoc] enabled = true type = "swagger" path = "/docs" title = "My API" version = "1.0.0" ``` ### Middleware Configuration ```toml [middleware] global = ["lib/middleware.ai:logger", "lib/middleware.ai:error_handler"] [middleware.cors] allowed_origins = ["http://localhost:3000"] allowed_methods = ["GET", "POST", "PUT", "DELETE"] ``` ## Project Structure Best Practices 1. **Route Organization**: Group related routes in the same file (e.g., `users.ai` for all user-related routes). 2. **Modular Code**: Extract reusable logic to the `lib` directory. 3. **Environment Configuration**: Use environment variables for configuration that changes between environments. 4. **Migration Versioning**: Number migrations sequentially to ensure they're applied in the correct order. 5. **Middleware Separation**: Define middleware in dedicated files for clarity. By following these conventions and best practices, you can build well-organized, maintainable AIScript applications that scale with your needs. --- url: /guide/web/database.md --- # Database AIScript provides native database integration with type-safe queries and comprehensive drivers for several database systems. This chapter covers how to use the database modules to interact with PostgreSQL, SQLite, and Redis. ## Connection Configuration Database connections are configured in your project's `project.toml` file: ```toml [database.postgresql] url = "postgresql://username:password@localhost:5432/mydb" [database.sqlite] url = "sqlite://my_database.db" [database.redis] url = "redis://localhost:6379" password = "optional_password" ``` ## PostgreSQL Integration The PostgreSQL module provides a robust interface for working with PostgreSQL databases. ### Basic Queries ```rust // Import postgres module from std library use std.db.pg; // Simple query with a parameter let result = pg.query("SELECT * FROM users WHERE id = $1;", 42); print(result); // Insert data and return the inserted row let new_language = pg.query( "INSERT INTO languages (name, created_year) VALUES($1, $2) RETURNING id, name;", "AIScript", 2024 ); print(new_language); // [{id: 1, name: "AIScript"}] ``` ### Type-Safe Queries with Classes AIScript allows mapping query results to typed objects for type-safe data access: ```rust use std.db.pg; // Define a class representing the database table class Language { id: int, name: str, created_year: int } // Query results are mapped to Language instances let languages = pg.query_as(Language, "SELECT id, name, created_year FROM languages WHERE created_year > $1;", 2000); // Now languages is an array of Language objects print(languages); ``` ### Transactions AIScript supports database transactions for executing multiple operations atomically: ```rust use std.db.pg; // Begin a transaction let tx = pg.begin_transaction(); // Execute multiple operations within the transaction tx.query("INSERT INTO users (name, email) VALUES($1, $2);", "Alice", "alice@example.com") |err| { print(f"Transaction failed: {err}"); tx.rollback(); return; }; tx.query("INSERT INTO user_roles (user_id, role) VALUES(LASTVAL(), 'admin');") |err| { print(f"Transaction failed: {err}"); tx.rollback(); return; }; // Commit the transaction if all operations succeed tx.commit(); print("Transaction successed"); ``` ## SQLite Integration SQLite provides a lightweight database solution that doesn't require a separate server. ### Basic SQLite Operations ```rust use std.db.sqlite; // Create a table sqlite.query("CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY, title TEXT, content TEXT);"); // Insert data sqlite.query("INSERT INTO notes (title, content) VALUES($1, $2);", "Meeting Notes", "Discuss new features"); // Query data let notes = sqlite.query("SELECT * FROM notes;"); print(notes); ``` ### SQLite Transactions ```rust use std.db.sqlite; let tx = sqlite.begin_transaction(); tx.query("INSERT INTO notes (title, content) VALUES($1, $2);", "Note 1", "Content 1") |err| { print(f"Transaction failed: {err}"); tx.rollback(); return; }; tx.query("INSERT INTO notes (title, content) VALUES($1, $2);", "Note 2", "Content 2") |err| { print(f"Transaction failed: {err}"); tx.rollback(); return; }; tx.commit(); print("Transaction failed"); ``` ## Redis Integration The Redis module provides access to Redis data structures and operations. ### Basic Redis Commands ```rust use std.db.redis; // Set a key redis.cmd("SET user:1 Alice"); // Get a key let user = redis.cmd("GET user:1"); print(user); // "Alice" // Working with hash data redis.cmd("HSET user:2 name Bob age 30 email bob@example.com"); let name = redis.cmd("HGET user:2 name"); let all_fields = redis.cmd("HGETALL user:2"); print(name); // "Bob" print(all_fields); // {name: "Bob", age: "30", email: "bob@example.com"} ``` ### Redis Pipelines Redis pipelines allow sending multiple commands in a single round-trip, improving performance: ```rust use std.db.redis; // Create a pipeline let p = redis.pipeline(); // Add commands to the pipeline p.cmd("SET counter 1"); p.cmd("INCR counter"); p.cmd("INCR counter"); p.cmd("GET counter"); // Execute all commands in one round-trip let results = p.exec(); print(results); // ["OK", 2, 3, "3"] ``` ### Redis Lists ```rust use std.db.redis; // Work with lists redis.cmd("LPUSH mylist A B C"); redis.cmd("RPUSH mylist D E F"); let list = redis.cmd("LRANGE mylist 0 -1"); print(list); // ["C", "B", "A", "D", "E", "F"] ``` ### Redis Sets ```rust use std.db.redis; // Work with sets redis.cmd("SADD myset apple banana orange"); redis.cmd("SADD anotherset banana kiwi"); let intersection = redis.cmd("SINTER myset anotherset"); print(intersection); // ["banana"] ``` --- url: /guide/web/auth.md --- # Auth and Social Login Authentication is a critical component of modern web applications, yet implementing it correctly can be challenging and error-prone. AIScript simplifies this process with built-in authentication directives that handle complex security flows while maintaining type safety throughout the authentication process. ## Authentication Options AIScript provides several authentication mechanisms out of the box: * **JWT Authentication**: Secure token-based authentication * **Basic Authentication**: Simple username/password authentication * **SSO (Single Sign-On)**: Integration with social login providers like Google, GitHub, and more ## JWT JSON Web Tokens (JWT) provide a stateless authentication mechanism that's perfect for APIs and single-page applications. AIScript makes JWT implementation straightforward with the `@auth` directive. ```js post /signin { body { username: str, password: str, } // Verify username and password correctly use std.auth.jwt; // Create payload with claims let payload = { sub: body.username, }; // Create access token that expires in 1 hour (3600 seconds) let token = jwt.create_access_token(payload, 3600, "your-secret"); return { token }; } @auth get /ask { query { @string(min_len=5, max_len=50) question: str } ai fn ask_llm(question: str) -> str { return prompt question; } let answer = ask_llm(query.question); return { answer }; } ``` ```bash $ curl --location 'http://localhost:8080/signin' \ --header 'Content-Type: application/json' \ --data '{"username": "abc", "password": "123455"}' { "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhYmMiLCJleHAiOjE3NDEzMjA0NzUsIm5iZiI6MTc0MTMxNjg3NSwiaWF0IjoxNzQxMzE2ODc1fQ.542CSBvnWbmQZAO8Ba-IjxxsZ4U-JSTmSalJFN574SY" } $ curl --location --request GET 'http://localhost:8080/ask?question=What%20is%20the%20capital%20of%20China%3F' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhYmMiLCJleHAiOjE3NDEzMjA0NzUsIm5iZiI6MTc0MTMxNjg3NSwiaWF0IjoxNzQxMzE2ODc1fQ.542CSBvnWbmQZAO8Ba-IjxxsZ4U-JSTmSalJFN574SY' { "answer": "The capital of France is Paris." } ``` ```toml [auth.jwt] secret = "your-secret" expiration = 3600 # Token expiration time in seconds ``` When a route is decorated with `@auth`, AIScript automatically: 1. Verifies the JWT token from the `Authorization` header 2. Rejects requests with invalid or expired tokens 3. Makes the decoded token payload available within the route handler Configure JWT settings in your `project.toml`: ```toml [auth.jwt] secret = "your-secret" expiration = 3600 # Token expiration time in seconds ``` For enhanced security, use environment variables for sensitive values like the JWT secret. ## Basic Auth For simpler use cases or admin interfaces, AIScript provides basic authentication with the `@basic_auth` directive: ```js @basic_auth post /guess { body { @number(min=1, max=100) magic: int = 1 } let result = "Try again!"; if body.magic == 42 { result = "You guessed it!"; } return { result }; } ``` The `@basic_auth` directive validates username and password credentials according to your configuration. Configure basic authentication in your `project.toml`: ```toml [auth.basic] username = "admin" password = "123456" # Use environment variables for production ``` ## Social Login with SSO AIScript makes implementing social login seamless with the `@sso` directive. This handles the complex OAuth flows required by providers like Google, GitHub, or Microsoft. ### Setting Up Google Authentication 1. Configure the provider in your `project.toml`: ```toml [sso.google] client_id = "123" # Your Google OAuth client ID client_secret = "abc" # Your Google OAuth client secret redirect_url = "http://localhost:8080/auth/google/callback" scopes = ["email", "profile"] ``` 2. Create an authorization endpoint: ```js @sso(provider="google") get /auth/google { let url = sso.authority_url(); print(url); return temporary_redirect(target=url); } ``` 3. Implement the callback endpoint: ```js @sso(provider="google") get /auth/google/callback { query { code: str, state: str, } print("code", query.code); print("state", query.state); let user_info = sso.verify(code=query.code); print(user_info); return { user_info }; } ``` When a user visits `/auth/google`, they'll be redirected to Google's authentication page. After successful authentication, Google redirects back to your `/auth/google/callback` endpoint with an authorization code. The `sso.verify()` function exchanges this code for an access token and retrieves the user's profile information. ### Supporting Multiple Providers AIScript supports multiple SSO providers simultaneously. Simply add additional configurations in your `project.toml`: ```toml [sso.github] client_id = "456" client_secret = "def" redirect_url = "http://localhost:8080/auth/github/callback" scopes = ["user:email"] [sso.microsoft] client_id = "789" client_secret = "ghi" redirect_url = "http://localhost:8080/auth/microsoft/callback" scopes = ["User.Read"] ``` Then create corresponding route handlers for each provider: ```js @sso(provider="github") get /auth/github { return temporary_redirect(target=sso.authority_url()); } @sso(provider="github") get /auth/github/callback { query { code: str, state: str } let user_info = sso.verify(code=query.code); return { user_info }; } ``` ## Conclusion AIScript's authentication directives simplify the implementation of secure authentication flows without sacrificing flexibility. Whether you need simple password-based authentication or complex multi-provider SSO, AIScript provides the tools to implement authentication with minimal boilerplate while maintaining type safety throughout the process. By abstracting away the complex security details, AIScript lets you focus on building your application logic while ensuring your authentication implementation follows security best practices. --- url: /guide/contribution/interpreter-architecture.md --- # Interpreter Architecture AIScript's interpreter architecture follows a traditional compilation pipeline with modern enhancements for flexibility, performance, and AI integration capabilities. This chapter aims to help new contributors understand the system's architecture, key components, and how they interact. ## Overview The AIScript interpreter follows these main stages: **Lexical Analysis** → **Parsing** → **Type Checking** → **Code Generation** → **Virtual Machine Execution** ![](/guide/interpreter-stages.png) This design allows for clear separation of concerns while maintaining flexibility for language evolution. Let's explore each component in detail. ## Lexical Analysis (Lexer) The [lexer](https://github.com/aiscriptdev/aiscript/tree/main/aiscript-lexer) is the first stage of compilation, responsible for converting source code text into a stream of tokens. Each token represents a meaningful unit in the language (like keywords, identifiers, operators, and literals). ```rust // Example of how the lexer works: // Input: "let x = 10 + 20;" // Output: [Token(Let), Token(Identifier, "x"), Token(Equal), Token(Number, "10"), // Token(Plus), Token(Number, "20"), Token(Semicolon)] ``` Key responsibilities: - Breaking source code into tokens - Handling string/numeric literals - Managing line numbers for error reporting - Skipping whitespace and comments - Recognizing keywords and operators Important structures in the lexer: - `TokenType` enum: Defines all possible token types - `Token` struct: Contains the token type, lexeme (original text), and line number - `Scanner` struct: Manages the scanning state and provides methods for token consumption ## Parsing The [parser](https://github.com/aiscriptdev/aiscript/tree/main/aiscript-parser) converts the token stream into an Abstract Syntax Tree (AST), which represents the hierarchical structure of the program. AIScript uses a recursive descent parser with Pratt parsing for expressions. Key components: - AST node definitions - Parsing functions for statements and expressions - Precedence handling for operators - Error recovery mechanisms - Type annotation handling The parser also performs some early validation, such as: - Checking for valid syntax - Validating enum variants - Ensuring valid function declarations - Validating match patterns ## Type Checking and Resolution AIScript includes a type checking system ([ty/resolver.rs](https://github.com/aiscriptdev/aiscript/tree/main/aiscript-vm/src/ty)) that validates types at compile time when possible. This improves error detection before runtime and enables better performance optimizations. Main features: - Type annotation validation - Class and enum type checking - Validation of object literals against class definitions - Function parameter type checking - Error type validation The type resolver is introduced early in the parsing phase to catch type errors as soon as possible. ## Code Generation The [code generator](https://github.com/aiscriptdev/aiscript/blob/main/aiscript-vm/src/compiler/codegen.rs) transforms the AST into bytecode that can be executed by the virtual machine. This phase also performs several optimizations. Key aspects: - Generation of VM opcodes from AST nodes - Handling variable scope and closures - Managing function parameters and defaults - Implementing control flow (if/else, loops, etc.) - Error handling code generation - Enum and class compilation The code generator produces a set of functions with associated bytecode chunks, which are then executed by the VM. ## Virtual Machine The [virtual machine](https://github.com/aiscriptdev/aiscript/blob/main/aiscript-vm/) is a stack-based interpreter that executes the generated bytecode. It maintains execution state and provides runtime facilities like garbage collection. Important components: - Call frames for function invocation - Value stack for computations - Global and local variable storage - Upvalue handling for closures - Garbage collection (via `gc_arena`) - Runtime error handling The VM also handles built-in functions, modules, and AI operations. ## Value Representation Values in AIScript are represented using a tagged union approach, allowing efficient storage and manipulation of different data types: ```rust pub enum Value<'gc> { Number(f64), Boolean(bool), String(InternedString<'gc>), IoString(Gc<'gc, String>), Closure(Gc<'gc, Closure<'gc>>), NativeFunction(NativeFn<'gc>), Array(GcRefLock<'gc, Vec>>), Object(GcRefLock<'gc, Object<'gc>>), Enum(GcRefLock<'gc, Enum<'gc>>), EnumVariant(Gc<'gc, EnumVariant<'gc>>), Class(GcRefLock<'gc, Class<'gc>>), Instance(GcRefLock<'gc, Instance<'gc>>), BoundMethod(Gc<'gc, BoundMethod<'gc>>), Module(InternedString<'gc>), Agent(Gc<'gc, Agent<'gc>>), Nil, } ``` This design allows for efficient operations while supporting garbage collection and reference semantics. ## Memory Management AIScript uses the [gc_arena](https://github.com/kyren/gc-arena) crate for memory management, which provides: - Tracing garbage collection - Memory safety through lifetime parameters - Efficient allocation and collection - Cycle detection All heap-allocated objects are wrapped in `Gc` or `GcRefLock` pointers, allowing the garbage collector to track and manage memory. ## AI Integration AIScript has special handling for AI operations: - `prompt` for sending requests to AI models - `Agent` system for complex AI interactions - AI function compilation and execution ## OpCode System AIScript uses a bytecode instruction set defined in [chunk.rs](https://github.com/aiscriptdev/aiscript/blob/main/aiscript-vm/src/chunk.rs): ```rust pub enum OpCode { Constant(u8), // Load constant value Return, // Return from function Add, Subtract, // Arithmetic operations GetLocal(u8), // Get local variable SetLocal(u8), // Set local variable // ... many more instructions } ``` Each instruction operates on the VM's stack and affects program execution flow. ## How to Contribute Now that you understand the architecture, here are some ways to contribute: 1. **Start Small**: Look for issues labeled "good first issue" in our GitHub repository. 2. **Improve Error Messages**: Clear error messages help users debug their code. The parser and VM error systems are good places to contribute improvements. 3. **Add Language Features**: New syntax features typically require changes to the lexer, parser, code generator, and VM. 4. **Optimize Performance**: Look for opportunities to improve bytecode generation or VM execution. 5. **Enhance Type System**: Contribute to the type resolver to improve static analysis capabilities. 6. **Fix Bugs**: Bug fixes are always valuable contributions. Before making significant changes, please open a GitHub issue to discuss your approach with the community. This ensures your efforts align with the project's goals and direction. ## Development Workflow 1. Fork the repository on GitHub 2. Create a feature branch 3. Make your changes, following our code style 4. Add tests for your changes 5. Run the existing test suite to ensure nothing breaks 6. Submit a pull request with a clear description of your changes ## Code Organization Conventions - Each major component has its own module - Opt for composition over inheritance - Prefer immutable data where possible - Use descriptive naming - Document public APIs with comments - Follow Rust's naming conventions The AIScript interpreter is designed to be modular and extensible, making it possible for contributors to work on different parts independently. We're excited to see what you'll build with us! --- url: /guide/contribution/roadmap.md --- # Roadmap Currently, **AIScript** is in its early stages of development. This roadmap outlines our vision and planned features for future releases, helping contributors understand where the language is headed and identify areas where they can make an impact. ## Language Features - [x] **String Interpolation** - Improve the current string interpolation with formatting options - Add support for more complex expressions within interpolated strings - [ ] `for in` Loop - Add support for iterating over arrays and objects - [ ] **Type System Improvements** - Enhance type annotations and static checking - [ ] **WebAssembly Integration** - Compile AIScript to WebAssembly for high-performance execution - Enable importing WebAssembly modules into AIScript ## Web Runtime - [ ] **Validation System Enhancements** - Enable customizable validator error messages - Add more built-in validators for common patterns - [ ] **HTML Template Rendering** - [ ] **WebSocket Support** ## AI Capabilities - [ ] **Enhanced AI Model Integration** - Support more AI models and providers - [ ] **Agent Framework Extensions** - Develop more sophisticated agent capabilities - Add tools for agent debugging and introspection - Implement memory management for long-running agents ## Performance Optimization - [ ] **Code Generation Improvements** - Optimize bytecode generation - Add constant folding and other compiler optimizations - [ ] **AOT/JIT Compilation** - Implement Ahead-Of-Time compilation for deployment scenarios - Develop Just-In-Time compilation for dynamic code execution - [ ] **Memory Management** - Improve garbage collection algorithms - Add memory profiling tools - Optimize for reduced memory footprint ## Developer Experience - [ ] **Language Server Protocol** - Implement full LSP support for IDE integration - [ ] **Package Management** We welcome contributions in any of these areas. If you're interested in helping with a particular feature, please check the GitHub issues or start a discussion on our community forum to coordinate efforts. --- url: /reference/language-keyword.md --- # Language Keywords ## AI ### ai The `ai` keyword defines an AI-powered function that can interact with AI models. ```rust ai fn summarize(text: str) -> str { prompt "Summarize the following text in 3 sentences: {text}"; } let summary = summarize("AIScript is a programming language designed for AI interactions..."); print(summary); ``` ### prompt The `prompt` keyword sends a text prompt to an AI model and returns the response. ```rust // Basic usage let response = prompt "What is the capital of France?"; // With model specification let detailed_answer = prompt { input: "Explain quantum computing", model: "gpt-4", max_tokens: 500, temperature: 0.7, system_prompt: "You are a quantum physics expert" }; ``` ### agent The `agent` keyword defines an AI agent with specific capabilities and instructions. ```rust agent SearchAssistant { instructions: "You are a search assistant. Help users find information.", model: "claude-3-5-sonnet-latest", fn search(query: str) -> str { // Implementation of search functionality prompt f"Search for: {query}"; } fn summarize(text: str) -> str { prompt f"Summarize: {text}"; } } // Using the agent let result = SearchAssistant.run(input: "Find information about climate change"); ``` ## Control Flow ### if Executes a block of code conditionally. ```rust let x = 10; if x > 5 { print("x is greater than 5"); } else if x < 5 { print("x is less than 5"); } else { print("x equals 5"); } ``` ### else Used with `if` to provide alternative code blocks. ### for Creates a loop that iterates over collections or ranges. ```rust for i in 1..5 { print(i); // Prints 1, 2, 3, 4 } let names = ["Alice", "Bob", "Charlie"]; for name in names { print(name); } ``` ### while Creates a loop that continues as long as a condition is true. ```rust let i = 0; while i < 5 { print(i); i = i + 1; } ``` ### break Exits the current loop. ```rust for i in 1..10 { if i == 5 { break; } print(i); // Prints 1, 2, 3, 4 } ``` ### continue Skips to the next iteration of a loop. ```rust for i in 1..10 { if i % 2 == 0 { continue; } print(i); // Prints 1, 3, 5, 7, 9 } ``` ### match Pattern matching for conditional logic. ```rust let value = 2; match value { 1 => print("One"), 2 => print("Two"), 3 => print("Three"), _ => print("Other number") } // Match with enum variants enum Status { Pending, Started, Finished, } let result = Result::Pending; match result { Result::Pending => print("status: pending"), Result::Started => print("status: started"), Result::Finished => print("status: finished"), } ``` ## Function ### fn Defines a function. ```rust fn add(a: int, b: int) -> int { return a + b; } ``` ### return Returns a value from a function. ```rust fn is_even(num: int) -> bool { if num % 2 == 0 { return true; } return false; } ``` ### raise Returns an error from a function. ```rust enum ArithError! { DivideByZero } fn divide(a: int, b: int) -> float, ArithError! { if b == 0 { raise ArithError!::DivideByZero; } return a / b; } // Using the function with error handling let result = divide(10, 0) |err| { print("Division by zero"); return 0; }; ``` ## Variable Declaration ### let Declares a mutable variable. ```rust let name = "AIScript"; name = "AI Script Language"; // Valid - can be reassigned ``` ### const Declares an immutable constant. ```rust const PI = 3.14159; const MAX_USERS = 100; // PI = 3.14; // Invalid - constants cannot be reassigned ``` ## Visibility ### pub Makes a declaration public and accessible from other modules. ```rust pub fn add(a: int, b: int) -> int { return a + b; } pub const VERSION = "1.0.0"; ``` ## Type Definition ### enum Defines an enumeration type. ```rust enum Color { Red, Green, Blue } // Enum with values enum HttpStatus { OK = 200, NotFound = 404, ServerError = 500 } // Enum for errors enum NetworkError! { ConnectionFailed, Timeout, InvalidResponse, } ``` ### class Defines a class with fields and methods. ```rust class Person { name: str, age: int, fn new(name: str, age: int) { self.name = name; self.age = age; } } let person = Person("John", 30); ``` ## Logical Operators ### and Logical AND operator. ```rust if x > 0 and x < 10 { print("x is between 0 and 10"); } ``` ### or Logical OR operator. ```rust if x < 0 or x > 10 { print("x is not between 0 and 10"); } ``` ### not Logical NOT operator. ```rust if not (x == 5) { print("x is not 5"); } ``` ## Boolean Literals ### true Boolean literal representing a true value. ### false Boolean literal representing a false value. ## Other ### in Tests for membership or is used with `for` loops. ```rust // Membership test let numbers = [1, 2, 3, 4, 5]; print(3 in numbers); // true // Used in for loops for element in numbers { print(element); } ``` ### self References the current instance in a class method. ```rust class Counter { count: int = 0, fn increment(self) { self.count = self.count + 1; } fn get_count(self) -> int { return self.count; } } ``` ### super References the parent class in a class inheritance context. ```rust class Animal { fn make_sound() { print("Generic animal sound"); } } class Dog(Animal) { fn make_sound() { super.make_sound(); print("Woof!"); } } ``` ### nil Represents the absence of a value. ```rust let optional = nil; if optional == nil { print("No value"); } ``` ### use Imports modules or specific items from modules. ```rust // Import a module use std.math; print(math.sin(0.5)); // Import multiple modules use std.io; use std.time; ``` ### \_ Used as a wildcard in match expressions. ```rust match value { 1 => print("One"), 2 => print("Two"), _ => print("Other number") } ``` --- url: /reference/route/keyword.md --- # Route Keywords In AIScript, the route handlers are defined using HTTP method keywords followed by a URL path pattern. This approach provides a clean and intuitive way to build web APIs and handle HTTP requests. ## Get The `get` keyword defines a route handler for HTTP GET requests, which are used to retrieve data. ```rust get /hello { return "Hello World!"; } ``` ## Post The `post` keyword defines a route handler for HTTP POST requests, which are typically used to create new resources. ```rust post /products { body { name: str, price: float, category: str, } // Validate input if body.price <= 0 { return response(status_code = 400, body = {error: "Price must be positive"}); } // Create new product let id = db.create_product(body.name, body.price, body.category); return response(body = {id: id, message: "Product created"}); } ``` ## Put The `put` keyword defines a route handler for HTTP PUT requests, which are used to update existing resources. ```rust put /products/:id { path { id: int } body { name: str price: float category: str } // Check if product exists if !db.product_exists(path.id) { return response(status_code = 404, body = {error: "Product not found"}); } // Update product db.update_product(path.id, body.name, body.price, body.category); return {message: "Product updated"}; } ``` ## Delete The `delete` keyword defines a route handler for HTTP DELETE requests, which are used to remove resources. ```rust delete /products/:id { path { id: int } // Check if product exists if !db.product_exists(path.id) { return response(status_code = 404, body = {error: "Product not found"}); } // Delete product db.delete_product(path.id); return "Ok"; } ``` ## Route The `route` keyword allows grouping related routes under a common path prefix, creating a more organized API structure. This helps maintain cleaner code and logical resource hierarchies. ```js route /user { // This handles GET /user/info get /info { return { version: "1.0", endpoints: ["GET /info", "PUT /:id"] }; } // This handles PUT /user/:id put /:id { path { id: int } body { name: str, email: str, } // Update user with the given ID db.update_user(path.id, body.name, body.email); return {message: "User updated"}; } } ``` ## Query Provides access to query parameters in the URL. ```rust get /search { query { term: str, page: int = 1, } let term = query.term; let page = query.page; return f"Searching for '{term}' on page {page}"; } ``` ## Path Contains route parameters defined with `:` in the route path. ```rust get /users/:id/posts/:postId { path { id: str, postId: int, } let userId = path.id; let postId = path.postId; return f"Accessing post {postId} for user {userId}"; } ``` ## Body Contains the parsed request body. Automatically parsed based on Content-Type. ```rust post /users { body { name: str, email: str, } let name = body.name; let email = body.email; // Create a new user with name and email return {id: create_user(name, email)}; } ``` ## Header Access request headers through the `header` object. ```rust get /headers { let userAgent = header["User-Agent"]; let contentType = header["Content-Type"]; return f"User-Agent: {userAgent}, Content-Type: {contentType}"; } ``` ## Cookie Access cookies through the `cookie` object. ```rust get /welcome { let username = cookie.username; if username { return "Welcome back, {username}!"; } else { return "Welcome, guest!"; } } ``` --- url: /reference/route/request.md --- # Request Each route handler receives a `request` object that contains information about the incoming HTTP request. ## request.method Returns the HTTP method of the request (GET, POST, PUT, DELETE, etc.) ```rust get /echo { return f"Method: {request.method}"; // Returns "Method: GET" } ``` ## request.url Returns the complete URL of the request. ```rust get /info { return f"URL: {request.url}"; // Might return "URL: https://example.com/info?key=value" } ``` ## request.path Returns the path portion of the URL. ```rust get /path/info { return f"Path: {request.path}"; // Returns "Path: /path/info" } ``` ## request.scheme Returns the scheme of the request (http or https). ```rust get /scheme { return f"Scheme: {request.scheme}"; // Returns "Scheme: https" for secure requests } ``` ## request.host Returns the host from the request. ```rust get /host { return f"Host: {request.host}"; // Might return "Host: example.com" } ``` ## request.port Returns the port number of the request. ```rust get /port { return f"Port: {request.port}"; // Might return "Port: 443" for HTTPS } ``` --- url: /reference/route/response.md --- # Response The `Response` object in AIScript represents an HTTP response and provides control over status codes, headers, cookies, and the response body. ## Constructor ### response() Creates a new Response object with customizable properties. #### Syntax ```js response(status_code=, body=, headers=, cookies=) ``` #### Parameters | Parameter | Type | Description | Default | |-----------|------|-------------|---------| | `status_code` | Number | HTTP status code (100-599) | 200 | | `body` | Any | Response body (can be string, object, array, etc.) | nil | | `headers` | Object | HTTP headers as key-value pairs | {} | | `cookies` | Object | Cookies as key-value pairs | {} | #### Returns A new `Response` object. #### Example ```js let response = response( status_code=200, body={"message": "Success"}, headers={"Content-Type": "application/json"}, cookies={"session": "abc123"} ); ``` #### Exceptions - Throws an error if `status_code` is not between 100 and 599 - Throws an error if `headers` is not an object - Throws an error if `cookies` is not an object ## Properties ### response.status_code The HTTP status code of the response. #### Type Number #### Example ```js let response = response(status_code=404); print(response.status_code); // 404 ``` ### response.body The body content of the response. #### Type Any (string, object, array, number, boolean, nil) #### Example ```js let response = response(body="Hello World"); print(response.body); // "Hello World" ``` ### response.headers HTTP headers to be sent with the response. #### Type Object (key-value pairs) #### Example ```js let response = response(headers={"Content-Type": "text/plain"}); print(response.headers["Content-Type"]); // "text/plain" ``` ### response.cookies Cookies to be set in the response. #### Type Object (key-value pairs) #### Example ```js let response = response(cookies={"user": "john"}); print(response.cookies["user"]); // "john" ``` ## Helper Functions ### temporary_redirect() Creates a response with a 307 Temporary Redirect status and the specified target URL. #### Syntax ```js temporary_redirect(target=) ``` #### Parameters | Parameter | Type | Description | |-----------|------|-------------| | `target` | String | The URL to redirect to | #### Returns A `Response` object with status code 307 and appropriate headers. #### Example ```js return temporary_redirect(target="/new-location"); ``` ### permanent_redirect() Creates a response with a 308 Permanent Redirect status and the specified target URL. #### Syntax ```js permanent_redirect(target=) ``` #### Parameters | Parameter | Type | Description | |-----------|------|-------------| | `target` | String | The URL to redirect to | #### Returns A `Response` object with status code 308 and appropriate headers. #### Example ```js return permanent_redirect(target="/new-permanent-location"); ``` ## Common Status Codes For convenience, here are common HTTP status codes you might use: | Code | Description | |------|-------------| | 200 | OK | | 201 | Created | | 204 | No Content | | 400 | Bad Request | | 401 | Unauthorized | | 403 | Forbidden | | 404 | Not Found | | 405 | Method Not Allowed | | 409 | Conflict | | 422 | Unprocessable Entity | | 429 | Too Many Requests | | 500 | Internal Server Error | | 503 | Service Unavailable | ## Usage Examples ### JSON Response ```js get /api/data { return response( status_code=200, body={"id": 123, "name": "Example"}, headers={"Content-Type": "application/json"} ); } ``` ### Error Response ```js get /protected { if !is_authenticated() { return response( status_code=401, body={"error": "Authentication required"}, headers={"WWW-Authenticate": "Basic"} ); } // Continue with authenticated access } ``` ### File Download ```js get /download { return response( body=file_content, headers={ "Content-Type": "application/pdf", "Content-Disposition": "attachment; filename=\"document.pdf\"", "Content-Length": str(file_size) } ); } ``` ### Setting Cookies ```js post /login { return response( status_code=200, body={"status": "logged_in"}, cookies={ "session": "abc123", } ); } ``` ### No Content Response ```js delete /api/resource/:id { path { id: str } delete_resource(id); return response(status_code=204); // No content } ``` --- url: /reference/directives/route.md --- # Route Directives These directives are applied to route declarations to modify their behavior. ## @json Specifies that the route expects and parses JSON request bodies. ```js post /api/users { @json body { name: str, email: str, } // Process body as JSON return f"Created user: {body.name}"; } ``` ## @form Specifies that the route expects and parses form data (`application/x-www-form-urlencoded` or `multipart/form-data`). ```js post /api/contact { @form body { name: str, message: str, } // Process body as form data return f"Received message from: {body.name}"; } ``` ## @auth Requires authentication for the route. Can specify roles or permissions. ```js @auth get /api/profile { // Only authenticated users can access return "Your profile data"; } @auth(roles=["admin"]) get /api/users { // Only admins can access return "All users data"; } ``` ## @basic_auth Enables HTTP Basic Authentication for the route. ```js @basic_auth get /admin/dashboard { // Only users with valid basic auth credentials can access return "Admin dashboard"; } ``` ## @sso Enables Single Sign-On authentication for the route. ```js @sso(provider="google") get /dashboard { // User must be authenticated via Google SSO return f"Welcome, {user.name}"; } ``` ## @docs Provides documentation and metadata for OpenAPI generation. Parameters: - **deprecated**: Marks a route as deprecated - **tag**: Assigns the route to a specific tag group in documentation - **hidden**: Excludes the route from documentation ```js @docs(deprecated=true) get /api/users { // This route is deprecated return "Users list"; } @docs(tag="Authentication") post /api/login { // This route will be grouped under "Authentication" in docs return "Login successful"; } @docs(hidden=true) get /internal/metrics { // This route won't appear in documentation return "Internal metrics"; } ``` --- url: /reference/directives/validator.md --- # Validator Directives These directives validate field values in route parameters, request bodies, or model definitions. ## @string Validates string values with various constraints. Parameters: - `min_len`: Minimum length of the string - `max_len`: Maximum length of the string - `exact_len`: Exact length the string must have - `start_with`: String must start with this prefix - `end_with`: String must end with this suffix ```js post /api/register { @json body { @string(min_len=3, max_len=20) username: str, @string(min_len=8, start_with="A") password: str } } ``` ## @number Validates numeric values against specified constraints. Parameters: - `min`: Minimum value - `max`: Maximum value - `equal`: Value must equal this number ```js post /api/product { @json body { @number(min=1, max=1000) price: float, @number(min=0) quantity: int } } ``` ## @in Validates that a value is one of a set of allowed values. ```js post /api/order { @json body { @in(["small", "medium", "large"]) size: str, @in([1, 2, 3, 4, 5]) rating: int } } ``` ## @regex Validates that a string matches a given regular expression pattern. ```js post /api/register { @json body { @regex(pattern=r"^[a-zA-Z0-9_]+$") username: str, @regex(pattern=r"^\d{3}-\d{2}-\d{4}$") ssn: str } } ``` ## @format Validates that a string conforms to a specific format. Supported formats: - `email`: Email address - `url`: URL - `uuid`: UUID - `ipv4`: IPv4 address - `ipv6`: IPv6 address - `date`: Date (YYYY-MM-DD) - `datetime`: Datetime (ISO 8601) - `time`: Time (HH:MM:SS) - `month`: Month (YYYY-MM) - `week`: Week (YYYY-Www) - `color`: HTML color code - `uscc`: Unified Social Credit Code ```js post /api/user { @json body { @format(type="email") email: str, @format(type="url") website: str, @format(type="date") birthdate: str } } ``` ## @array Validates arrays against specified constraints. Parameters: - `min_len`: Minimum number of items - `max_len`: Maximum number of items - `unique`: Whether items must be unique ```js post /api/tags { @json body { @array(min_len=1, max_len=5, unique=true) tags: array } } ``` ## @date Validates that a string is a valid date in the specified format and range. Parameters: - `format`: Date format string (default: "YYYY-MM-DD") - `min`: Minimum date - `max`: Maximum date ```js post /api/event { @json body { @date(format="YYYY-MM-DD", min="2023-01-01", max="2023-12-31") event_date: str } } ``` ## @not Negates another validator, passing if the inner validator fails. ```js post /api/product { @json body { @not(@in(["admin", "system"])) role: str } } ``` ## @or Combines multiple validators with logical OR, passing if any of the inner validators pass. ```js post /api/identifier { @json body { @or([ @format(type="email"), @regex(pattern=r"^\d{10}$") ]) identifier: str // Can be either email or 10-digit number } } ``` --- url: /reference/cli.md --- # Commands Reference ## aiscript Launch an interactive AIScript interactive environment (REPL) where you can test AIScript code snippets and explore language features. ```bash $ aiscript AI Script REPL v0.1.0 Type '.help' for more information. > ``` ### aiscript new Create a new AIScript project with a standard directory structure. ```bash $ aiscript new [PROJECT_NAME] ``` **Example:** ```bash $ aiscript new test-project Successfully created new AIScript project: test-project Project structure: test-project ├── lib/ ├── routes/ │ └── index.ai └── project.toml Run `aiscript serve` to start the server. ``` ### aiscript serve Run the AIScript project as a web server. ```bash $ aiscript serve [--port=8000] [--host=0.0.0.0] ``` **Options:** * `--port`: Port to listen on (default: 8000) * `--host`: Host to bind to (default: 0.0.0.0) * `--reload`: Run in development mode with hot reloading ## aim The **aim** (AIScript Manager) is a command-line tool for AIScript, similar to [cargo](https://github.com/rust-lang/cargo) for Rust. It provides a comprehensive set of commands to help you build, test, and deploy web applications quickly. ### aim ai Generate AI applications built in AIScript from a prompt description. ```bash $ aim ai [PROMPT_FILE] ``` This command analyzes a prompt and generates a complete AIScript application based on the description. **Example:** ```bash $ cat prompts.txt Please write a chatbot app that can answer questions about the following topics: - history - geography $ aim ai prompts.txt ``` The generated application will include all necessary files, routes, and AI integration to build the described chatbot. ### aim compile Compile an AIScript project into executable code. ```bash $ aim compile [--release] ``` **Options:** * `--release`: Compile in release mode with optimizations This command validates your code and compiles it to an executable format. ### aim deploy Deploy an AIScript project to the aiscript.dev server. ```bash $ aim deploy [--env=production] ``` **Options:** * `--env`: Target environment (default: production) This command packages your application and deploys it to the AIScript cloud hosting service. ### aim debug Open an interactive, Postman-like debug UI in the browser for testing your API routes. ```bash $ aim debug [--port=8001] ``` **Options:** * `--port`: Port to use for the debug UI (default: 8001) The debug UI automatically imports your API routes, allowing you to test endpoints with different parameters and view responses. ### aim fmt Format AIScript source code according to the language style guidelines. ```bash $ aim fmt [FILES...] ``` **Options:** * `--check`: Check if files are formatted without modifying them * `--all`: Format all files in the project ### aim lint Lint AIScript source code to identify potential problems. ```bash $ aim lint [FILES...] ``` **Options:** * `--fix`: Automatically fix issues when possible * `--all`: Lint all files in the project ### aim migrate Manage database schema migrations. #### aim migrate up Apply pending migrations to update the database schema. ```bash $ aim migrate up [--steps=N] ``` **Options:** * `--steps`: Number of migrations to apply (default: all pending) #### aim migrate down Revert migrations to roll back database schema changes. ```bash $ aim migrate down [--steps=N] ``` **Options:** * `--steps`: Number of migrations to revert (default: 1) ### aim mock Generate mock data for development and testing. ```bash $ aim mock [ENTITY] [COUNT] ``` **Example:** ```bash $ aim mock users 100 ``` This generates 100 mock user records based on your data models. ### aim test Run tests for an AIScript project. ```bash $ aim test [TEST_PATH] ``` **Options:** * `--watch`: Watch for file changes and rerun tests * `--coverage`: Generate test coverage report ### aim version Display the version information for AIScript and its components. ```bash $ aim version ``` **Output example:** ``` AIScript CLI v1.0.0 AIScript Runtime v1.0.0 AIScript Compiler v1.0.0 ``` *** These commands provide a complete toolkit for AIScript development, from project creation and testing to deployment. The `aim` command-line interface streamlines the development workflow, making it easy to build, test, and deploy AIScript applications. --- url: /reference/configuration/ai.md --- # AI > For AI observability, please refer to [observability](./observability.md). AIScript supports multiple AI providers: - **OpenAI**: Cloud-based models including GPT-4 and GPT-3.5 - **DeepSeek**: Efficient and powerful models - **Anthropic**: Claude models for advanced reasoning - **Ollama**: 100+ local models from various providers running on your own hardware ## Configuration ```toml [ai.openai] api_key = "YOUR_API_KEY" # optional, default is the official OpenAI API endpoint api_endpoint = "YOUR_API_ENDPOINT" model = "gpt-4" [ai.deepseek] api_key = "YOUR_API_KEY" # optional, default is the official DeepSeek API endpoint api_endpoint = "YOUR_API_ENDPOINT" model = "deepseek-chat" [ai.anthropic] api_key = "YOUR_API_KEY" # optional, default is the official Anthropic API endpoint api_endpoint = "YOUR_API_ENDPOINT" model = "claude-3-5-sonnet-latest" # Ollama - Local models [ai.ollama] api_endpoint = "http://localhost:11434/v1" # Default Ollama endpoint model = "llama3.2" # or any other model installed in your Ollama instance ``` ## Using Ollama [Ollama](https://ollama.ai/) allows you to run local AI models on your own hardware. To use Ollama with AIScript: 1. **Install Ollama** from [ollama.ai](https://ollama.ai/) 2. **Pull your desired models** (e.g., `ollama pull llama3.2`) 3. **Make sure Ollama is running** locally 4. **Configure AIScript** to use Ollama as shown above or by setting the `OLLAMA_API_ENDPOINT` environment variable ### Available Models Ollama provides access to 100+ models ranging from small 135M parameter models to massive 671B parameter models, including: - **Llama family**: `llama3.2`, `llama3.1`, `codellama` - **DeepSeek models**: `deepseek-r1`, `deepseek-v3`, `deepseek-coder` - **Specialized models**: `phi3`, `gemma2`, `qwen2.5`, `mistral` - **And many more**: See the [full list of available models](https://ollama.com/search) ### Environment Variables You can also configure Ollama using environment variables: ```bash export OLLAMA_API_ENDPOINT="http://localhost:11434/v1" ``` --- url: /reference/configuration/auth.md --- # Auth ```toml [auth.jwt] secret = "$JWT_SECRET" expiration = 3600 [auth.basic] username = "admin" password = "123456" ``` --- url: /reference/configuration/database.md --- # Database ```toml [database.sqlite] path = "example.db" [database.postgresql] host = "localhost" port = 5432 user = "postgres" password = "postgres" database = "example" [database.mysql] host = "localhost" port = 3306 user = "mysql" password = "mysql" database = "example" [database.mongodb] host = "localhost" port = 27017 user = "mongodb" password = "mongodb" [database.redis] host = "localhost" port = 6379 password = "redis" ``` ## Vector Database ## Rational Database ### SQLite ### PostgreSQL ### MySQL ## NoSQL Database ## MongoDB ## Cache Database ### Redis --- url: /reference/configuration/general.md --- # General ```toml [project] name = "example" description = "An twitter clone" version = "0.1.0" authors = ["Folyd"] [network] host = "0.0.0.0" port = 8000 [apidoc] enabled = true type = "swagger" path = "/docs" ``` ## Project ## Network ## API doc --- url: /reference/configuration/middleware.md --- # Middleware Middleware in AIScript provides a powerful way to process HTTP requests and responses. Middleware functions are executed in a sequence, allowing you to modify requests, validate authentication, log activities, and more before a route handler is invoked. ## CORS Controls Cross-Origin Resource Sharing settings. ```toml # project.toml [middleware.cors] allowed_origins = ["http://localhost:3000", "https://example.com"] allowed_methods = ["GET", "POST", "PUT", "DELETE"] allowed_headers = ["Content-Type", "Authorization"] allow_credentials = true max_age = 86400 ``` ### Options | Option | Type | Description | Default | | ------------------- | ------- | --------------------------------------------------------- | --------------------------------------------- | | `allowed_origins` | Array | List of origins that are allowed to access the resource | `["*"]` | | `allowed_methods` | Array | HTTP methods allowed | `["GET", "POST", "PUT", "DELETE", "OPTIONS"]` | | `allowed_headers` | Array | HTTP headers allowed | `["Content-Type", "Authorization"]` | | `allow_credentials` | Boolean | Indicates if cookies can be included in requests | `false` | | `max_age` | Number | How long the results of a preflight request can be cached | `86400` (24 hours) | ## Rate Limit Limits the number of requests a client can make within a specified time period. ```toml # project.toml [middleware.rate_limit] limit = 100 window = 60 # in seconds message = "Too many requests, please try again later." ``` ### Options | Option | Type | Description | Default | | --------------- | ------ | ----------------------------------------------------------------------- | --------------------- | | `limit` | Number | Maximum number of requests allowed | `100` | | `window` | Number | Time window in seconds | `60` | | `message` | String | Message to return when rate limit is exceeded | `"Too many requests"` | | `key_extractor` | String | Function to extract the rate limit key (e.g., "ip", "header:X-API-Key") | `"ip"` | ## Body Limit Limits the size of request bodies. ```toml # project.toml [middleware.body_limit] limit = "1mb" ``` ### Options | Option | Type | Description | Default | | ------- | ------ | --------------------------------------------------- | ------- | | `limit` | String | Maximum size of request body (e.g., "1mb", "500kb") | `"1mb"` | ## Timeout Sets a timeout for handling requests. ```toml # project.toml [middleware.timeout] duration = 5000 # in milliseconds message = "Request timeout" ``` ### Options | Option | Type | Description | Default | | ---------- | ------ | ------------------------------------- | ------------------- | | `duration` | Number | Timeout in milliseconds | `5000` | | `message` | String | Message to return when timeout occurs | `"Request timeout"` | ## Compression Compresses response bodies. ```toml # project.toml [middleware.compression] level = 6 # compression level (1-9) threshold = 1024 # minimum size to compress ``` ### Options | Option | Type | Description | Default | | ----------- | ------ | --------------------------------- | -------------------------------------------------------------------- | | `level` | Number | Compression level (1-9) | `6` | | `threshold` | Number | Minimum size in bytes to compress | `1024` | | `types` | Array | Content types to compress | `["text/plain", "text/html", "application/json", "application/xml"]` | ## Security Headers Adds security-related HTTP headers to responses. ```toml # project.toml [middleware.security_headers] xss_protection = "1; mode=block" content_security_policy = "default-src 'self'" ``` ### Options | Option | Type | Description | Default | | ------------------------- | ------ | ------------------------------------ | ------------------------------ | | `xss_protection` | String | X-XSS-Protection header value | `"1; mode=block"` | | `content_type_options` | String | X-Content-Type-Options header value | `"nosniff"` | | `frame_options` | String | X-Frame-Options header value | `"SAMEORIGIN"` | | `content_security_policy` | String | Content-Security-Policy header value | `"default-src 'self'"` | | `referrer_policy` | String | Referrer-Policy header value | `"no-referrer-when-downgrade"` | --- url: /reference/configuration/observability.md --- # Observability ```toml [observability.llm] prompt_log = true response_log = true token_cost = true [observability.logging] level = "info" format = "json" [observability.mertrics] enabled = true host = "localhost" port = 9090 path = "/metrics" [observability.tracing] ``` ## LLM ## Logging ## Metrics ## Tracing --- url: /reference/configuration/security.md --- # Security ```toml [security.jwt] secret = "secret" expiration = 3600 header = "Authorization" [security.csrf] enabled = true header = "X-CSRF-Token" [security.ssl] enabled = false cert = "cert.pem" key = "key.pem" ``` ## JSON Web Token ## CSRF ## SSL --- url: /reference/configuration/sso.md --- # SSO ```toml [sso.google] client_id = "123" client_secret = "abc" redirect_url = "http://localhost:8080/callback" scopes = ["email"] [sso.github] client_id = "123" client_secret = "abc" redirect_url = "http://localhost:8080/callback" scopes = ["email"] ``` --- url: /std/overview.md --- --- url: /std/builtin/functions.md --- # Built-in Functions This document provides an overview of the built-in functions available in the AIScript VM. ## abs() Returns the absolute value of a number. **Parameters**: - A single number. **Return Type**: `Number` **Example**: ```rust let result = abs(-5); // result is 5 ``` ## all() Returns `true` if all elements of the array are true. **Parameters**: - An array of boolean values. **Return Type**: `Boolean` **Example**: ```rust let result = all([true, true, false]); // result is false ``` ## any() Returns `true` if any element of the array is true. **Parameters**: - An array of boolean values. **Return Type**: `Boolean` **Example**: ```rust let result = any([false, false, true]); // result is true ``` ## ascii() Converts a value to its ASCII representation. **Parameters**: - A single value. **Return Type**: `String` **Example**: ```rust let result = ascii(65); // result is "A" ``` ## bin() Converts a number to its binary representation. **Parameters**: - A single number. **Return Type**: `String` **Example**: ```rust let result = bin(5); // result is "101" ``` ## bool() Converts a value to a boolean. **Parameters**: - A single value. **Return Type**: `Boolean` **Example**: ```rust let result = bool(0); // result is false ``` ## callable() Checks if a value is callable. **Parameters**: - A single value. **Return Type**: `Boolean` **Example**: ```rust let result = callable(someFunction); // result is true ``` ## chr() Converts an ASCII code to its character representation. **Parameters**: - A single number representing an ASCII code. **Return Type**: `String` **Example**: ```rust let result = chr(65); // result is "A" ``` ## filter() Filters elements of an array based on a predicate function. **Parameters**: - An array and a predicate function. **Return Type**: `Array` **Example**: ```rust let result = filter([1, 2, 3], x => x > 1); // result is [2, 3] ``` ## float() Converts a value to a floating-point number. **Parameters**: - A single value. **Return Type**: `Number` **Example**: ```rust let result = float("3.14"); // result is 3.14 ``` ## format() Formats a string using placeholders. **Parameters**: - A format string and values to replace placeholders. **Return Type**: `String` **Example**: ```rust let result = format("Hello, {}!", "world"); // result is "Hello, world!" ``` ## hex() Converts a number to its hexadecimal representation. **Parameters**: - A single number. **Return Type**: `String` **Example**: ```rust let result = hex(255); // result is "ff" ``` ## input() Reads a line of input from the user. **Parameters**: - An optional prompt string. **Return Type**: `String` **Example**: ```rust let name = input("Enter your name: "); ``` ## int() Converts a value to an integer. **Parameters**: - A single value. **Return Type**: `Number` **Example**: ```rust let result = int("42"); // result is 42 ``` ## len() Returns the length of a string, array, or object. **Parameters**: - A string, array, or object. **Return Type**: `Number` **Example**: ```rust let length = len([1, 2, 3]); // length is 3 ``` ## map() Applies a function to each element of an array. **Parameters**: - An array and a function. **Return Type**: `Array` **Example**: ```rust let result = map([1, 2, 3], x => x * 2); // result is [2, 4, 6] ``` ## max() Returns the maximum value from a list of numbers or an array. **Parameters**: - A list of numbers or a single array. **Return Type**: `Number` **Example**: ```rust let result = max(1, 2, 3); // result is 3 ``` ## min() Returns the minimum value from a list of numbers or an array. **Parameters**: - A list of numbers or a single array. **Return Type**: `Number` **Example**: ```rust let result = min(1, 2, 3); // result is 1 ``` ## oct() Converts a number to its octal representation. **Parameters**: - A single number. **Return Type**: `String` **Example**: ```rust let result = oct(8); // result is "10" ``` ## ord() Returns the ASCII code of a character. **Parameters**: - A single character. **Return Type**: `Number` **Example**: ```rust let result = ord("A"); // result is 65 ``` ## print() Prints a value to the console. **Parameters**: - A single value. **Return Type**: `nil` **Example**: ```rust print("Hello, world!"); ``` ## round() Rounds a number to the nearest integer. **Parameters**: - A single number. **Return Type**: `Number` **Example**: ```rust let result = round(3.5); // result is 4 ``` ## str() Converts a value to a string. **Parameters**: - A single value. **Return Type**: `String` **Example**: ```rust let result = str(123); // result is "123" ``` ## sum() Returns the sum of elements in an array. **Parameters**: - An array of numbers. **Return Type**: `Number` **Example**: ```rust let result = sum([1, 2, 3]); // result is 6 ``` ## zip() Combines multiple arrays into a single array of tuples. **Parameters**: - Multiple arrays. **Return Type**: `Array` **Example**: ```rust let result = zip([1, 2], [3, 4]); // result is [(1, 3), (2, 4)] ``` --- url: /std/builtin/array.md --- # Array The `Array` type provides various methods for array manipulation, searching, and ordering. ## append(item) Adds an item to the end of the array and returns the modified array. **Parameters**: - `item`: The item to add to the end of the array. **Return Type**: `Array` **Example**: ```js let numbers = [1, 2, 3]; numbers.append(4); // [1, 2, 3, 4] ``` ## extend(array) Extends the array by appending all items from another array and returns the modified array. **Parameters**: - `array`: The array containing items to append. **Return Type**: `Array` **Example**: ```js let numbers = [1, 2, 3]; numbers.extend([4, 5]); // [1, 2, 3, 4, 5] ``` ## insert(index, item) Inserts an item at the specified position in the array and returns the modified array. **Parameters**: - `index`: The index before which to insert the item. - `item`: The item to insert. **Return Type**: `Array` **Example**: ```js let numbers = [1, 2, 4]; numbers.insert(2, 3); // [1, 2, 3, 4] ``` ## remove(item) Removes the first occurrence of the specified item from the array and returns the modified array. **Parameters**: - `item`: The item to remove. **Return Type**: `Array` **Example**: ```js let numbers = [1, 2, 3, 2]; numbers.remove(2); // [1, 3, 2] ``` ## pop(index?) Removes and returns the item at the specified position. If no index is provided, removes and returns the last item. **Parameters**: - `index`: The index of the item to remove (optional). **Return Type**: The removed item. **Example**: ```js let numbers = [1, 2, 3]; let last = numbers.pop(); // 3, numbers becomes [1, 2] let first = numbers.pop(0); // 1, numbers becomes [2] ``` ## clear() Removes all items from the array and returns the modified (empty) array. **Return Type**: `Array` **Example**: ```js let numbers = [1, 2, 3]; numbers.clear(); // [] ``` ## index(item, start?, end?) Returns the index of the first occurrence of the specified item within the optional start and end range. **Parameters**: - `item`: The item to search for. - `start`: The index to start the search from (optional). - `end`: The index to end the search at (optional). **Return Type**: `Number` (Returns -1 if the item is not found) **Example**: ```js let numbers = [10, 20, 30, 20]; numbers.index(20); // 1 numbers.index(20, 2); // 3 ``` ## count(item) Returns the number of occurrences of the specified item in the array. **Parameters**: - `item`: The item to count. **Return Type**: `Number` **Example**: ```js let numbers = [1, 2, 3, 2, 1]; numbers.count(2); // 2 ``` ## sort(reverse?) Sorts the items of the array in place and returns the modified array. **Parameters**: - `reverse`: Whether to sort in descending order (optional, default: false). **Return Type**: `Array` **Example**: ```js let numbers = [3, 1, 4, 2]; numbers.sort(); // [1, 2, 3, 4] numbers.sort(true); // [4, 3, 2, 1] ``` ## reverse() Reverses the elements of the array in place and returns the modified array. **Return Type**: `Array` **Example**: ```js let numbers = [1, 2, 3]; numbers.reverse(); // [3, 2, 1] ``` ## slice(start, end?) Returns a shallow copy of a portion of the array into a new array, without modifying the original array. **Parameters**: - `start`: The beginning index (can be negative to index from the end). - `end`: The end index (optional, can be negative to index from the end). **Return Type**: `Array` **Example**: ```js let numbers = [1, 2, 3, 4, 5]; numbers.slice(1, 4); // [2, 3, 4] numbers.slice(-2); // [4, 5] ``` --- url: /std/builtin/string.md --- # String The `String` type provides a variety of methods for string manipulation and analysis. ## is_empty() Checks if the string is empty. **Return Type**: `Boolean` **Example**: ```rust let empty = "".is_empty(); // true let not_empty = "hello".is_empty(); // false ``` ## to_uppercase() Converts all characters in the string to uppercase. **Return Type**: `String` **Example**: ```rust let upper = "hello".to_uppercase(); // "HELLO" ``` ## to_lowercase() Converts all characters in the string to lowercase. **Return Type**: `String` **Example**: ```rust let lower = "HELLO".to_lowercase(); // "hello" ``` ## trim() Removes whitespace from both ends of the string. **Return Type**: `String` **Example**: ```rust let trimmed = " hello ".trim(); // "hello" ``` ## trim_start() Removes whitespace from the start of the string. **Return Type**: `String` **Example**: ```rust let trimmed = " hello".trim_start(); // "hello" ``` ## trim_end() Removes whitespace from the end of the string. **Return Type**: `String` **Example**: ```rust let trimmed = "hello ".trim_end(); // "hello" ``` ## contains(substr: string) Checks if the string contains the specified substring. **Parameters**: - `substr`: The substring to search for. **Return Type**: `Boolean` **Example**: ```rust let result = "hello world".contains("world"); // true ``` ## starts_with(prefix: string) Checks if the string starts with the specified prefix. **Parameters**: - `prefix`: The prefix to check. **Return Type**: `Boolean` **Example**: ```rust let result = "hello world".starts_with("hello"); // true ``` ## ends_with(suffix: string) Checks if the string ends with the specified suffix. **Parameters**: - `suffix`: The suffix to check. **Return Type**: `Boolean` **Example**: ```rust let result = "hello world".ends_with("world"); // true ``` ## index_of(substr: string, start?: number) Finds the index of the first occurrence of the substring, starting from the specified position. **Parameters**: - `substr`: The substring to search for. - `start`: The position to start the search from (optional). **Return Type**: `Number` **Example**: ```rust let index = "hello world".index_of("world"); // 6 ``` ## last_index_of(substr: string) Finds the index of the last occurrence of the substring. **Parameters**: - `substr`: The substring to search for. **Return Type**: `Number` **Example**: ```rust let index = "hello world world".last_index_of("world"); // 12 ``` ## substring(start: number, end?: number) Extracts a substring from the string, starting and ending at the specified indices. **Parameters**: - `start`: The starting index. - `end`: The ending index (optional). **Return Type**: `String` **Example**: ```rust let sub = "hello world".substring(0, 5); // "hello" ``` ## slice(start: number, end?: number) Extracts a section of the string and returns it as a new string, without modifying the original string. **Parameters**: - `start`: The starting index. - `end`: The ending index (optional). **Return Type**: `String` **Example**: ```rust let slice = "hello world".slice(0, 5); // "hello" ``` ## split(delimiter: string) Splits the string into an array of substrings using the specified delimiter. **Parameters**: - `delimiter`: The delimiter to split the string by. **Return Type**: `Array` **Example**: ```rust let parts = "hello world".split(" "); // ["hello", "world"] ``` ## join(array: Array) Joins an array of strings into a single string, with the original string as the separator. **Parameters**: - `array`: The array of strings to join. **Return Type**: `String` **Example**: ```rust let joined = ",".join(["a", "b", "c"]); // "a,b,c" ``` ## regex_match(pattern: string) Checks if the string matches the specified regular expression pattern. **Parameters**: - `pattern`: The regex pattern to match. **Return Type**: `Boolean` **Example**: ```rust let match = "hello".regex_match("^h"); // true ``` ## regex_replace(pattern: string, replacement: string) Replaces occurrences of the regex pattern with the replacement string. **Parameters**: - `pattern`: The regex pattern to replace. - `replacement`: The string to replace matches with. **Return Type**: `String` **Example**: ```rust let replaced = "hello world".regex_replace("world", "there"); // "hello there" ``` ## repeat(count: number) Repeats the string the specified number of times. **Parameters**: - `count`: The number of times to repeat the string. **Return Type**: `String` **Example**: ```rust let repeated = "ha".repeat(3); // "hahaha" ``` ## reverse() Reverses the characters in the string. **Return Type**: `String` **Example**: ```rust let reversed = "hello".reverse(); // "olleh" ``` ## replace(from: string, to: string) Replaces occurrences of a substring with another substring. **Parameters**: - `from`: The substring to replace. - `to`: The substring to replace with. **Return Type**: `String` **Example**: ```rust let replaced = "hello world".replace("world", "there"); // "hello there" ``` --- url: /std/builtin/response.md --- # response The builtin `response` functions to create HTTP response objects with various configurations. ## response(status_code: number, body: any, headers: object, cookies: object) Creates a new Response instance with the given status code, body, headers, and cookies. **Parameters**: - `status_code`: The HTTP status code (default is 200). - `body`: The response body, which can be any value. - `headers`: An object representing the HTTP headers. - `cookies`: An object representing the cookies. **Return Type**: `Response` **Example**: ```rust let res = response(status_code=200, body="Hello, World!", headers={}, cookies={}); ``` ## temporary_redirect(target: string) Creates a temporary redirect (307) response with the specified target URL. **Parameters**: - `target`: The URL to redirect to. **Return Type**: `Response` **Example**: ```rust let res = temporary_redirect(target="/new-url"); ``` ## permanent_redirect(target: string) Creates a permanent redirect (308) response with the specified target URL. **Parameters**: - `target`: The URL to redirect to. **Return Type**: `Response` **Example**: ```rust let res = permanent_redirect(target="/new-url"); ``` --- url: /std/db/pg.md --- # std.db.pg The `std.db.pg` module provides functions for interacting with PostgreSQL databases, including executing queries, handling transactions, and mapping results to objects. ## Core Functions ## query(sql: string, ...bindings: any) Executes a SQL query and returns the results as an array of objects. **Parameters**: - `sql`: The SQL query string - `bindings`: Optional parameters to bind to the query **Return Type**: `Array` **Example**: ```rust use std.db.pg; let results = pg.query("SELECT * FROM users WHERE age > $1", 18); // Returns an array of objects representing the query results ``` ## query_as(class: Class, sql: string, ...bindings: any) Executes a SQL query and maps the results to instances of the specified class. **Parameters**: - `class`: The class to map results to - `sql`: The SQL query string - `bindings`: Optional parameters to bind to the query **Return Type**: `Array` **Example**: ```rust use std.db.pg; class User { id: number; name: string; age: number; } let users = pg.query_as(User, "SELECT * FROM users WHERE age > $1", 18); // Returns an array of User instances ``` ## begin_transaction() Starts a new database transaction. Returns a Transaction object that provides methods for executing queries within the transaction. **Return Type**: `Transaction` **Example**: ```rust use std.db.pg; let tx = pg.begin_transaction(); // Returns a Transaction object ``` ## Transaction Methods The following methods are available on Transaction objects returned by `begin_transaction()`. ### query(sql: string, ...bindings: any) Executes a SQL query within the transaction and returns the results as an array of objects. **Parameters**: - `sql`: The SQL query string - `bindings`: Optional parameters to bind to the query **Return Type**: `Array` **Example**: ```rust use std.db.pg; let tx = pg.begin_transaction(); let results = tx.query("SELECT * FROM users WHERE age > $1", 18); ``` ### query_as(class: Class, sql: string, ...bindings: any) Executes a SQL query within the transaction and maps the results to instances of the specified class. **Parameters**: - `class`: The class to map results to - `sql`: The SQL query string - `bindings`: Optional parameters to bind to the query **Return Type**: `Array` **Example**: ```rust use std.db.pg; class User { id: number; name: string; age: number; } let tx = pg.begin_transaction(); let users = tx.query_as(User, "SELECT * FROM users WHERE age > $1", 18); ``` ### commit() Commits the transaction, saving all changes made within the transaction. **Return Type**: `nil` **Example**: ```rust use std.db.pg; let tx = pg.begin_transaction(); // Execute queries... tx.commit(); ``` ### rollback() Rolls back the transaction, discarding all changes made within the transaction. **Return Type**: `nil` **Example**: ```rust use std.db.pg; let tx = pg.begin_transaction(); // Execute queries... tx.rollback(); ``` ## Usage Examples ### Basic Query Example ```rust use std.db.pg; // Simple query let users = pg.query("SELECT * FROM users WHERE age > $1", 18); // Iterate through results for user in users { print(user.name); } ``` ### Transaction Example ```rust use std.db.pg; // Start transaction let tx = pg.begin_transaction(); // Update user balance tx.query("UPDATE users SET balance = balance - $1 WHERE id = $2", 100, 123); // Insert transaction record tx.query("INSERT INTO transactions (user_id, amount) VALUES ($1, $2)", 123, -100); // Commit if successful tx.commit(); ``` ### Typed Query Example ```rust use std.db.pg; // Define user class class User { id: number; name: string; email: string; } // Query users and map to class let users = pg.query_as(User, "SELECT * FROM users WHERE active = true"); // Access typed properties for user in users { print(`${user.name} <${user.email}>`); } ``` ### Parameter Binding Example ```rust use std.db.pg; // Query with different parameter types let results = pg.query( "SELECT * FROM users WHERE name = $1 AND age > $2 AND active = $3", "John", // string 25, // number true // boolean ); ``` ### Array Parameter Example ```rust use std.db.pg; // Query with array parameter let user_ids = [1, 2, 3, 4]; let users = pg.query("SELECT * FROM users WHERE id = ANY($1)", user_ids); ``` ### Date Parameter Example ```rust use std.db.pg; // Query with date parameter let users = pg.query( "SELECT * FROM users WHERE created_at > $1", "2023-01-01" // Date string in YYYY-MM-DD format ); ``` --- url: /std/db/mysql.md --- # std.db.mysql --- url: /std/db/sqlite.md --- # std.db.sqlite The `std.db.sqlite` module provides functions for interacting with SQLite databases, including executing queries, handling transactions, and mapping results to objects. ## Core Functions ## query(sql: string, ...bindings: any) Executes a SQL query and returns the results as an array of objects. **Parameters**: - `sql`: The SQL query string - `bindings`: Optional parameters to bind to the query **Return Type**: `Array` **Example**: ```rust use std.db.sqlite; let results = pg.query("SELECT * FROM users WHERE age > $1", 18); // Returns an array of objects representing the query results ``` ## query_as(class: Class, sql: string, ...bindings: any) Executes a SQL query and maps the results to instances of the specified class. **Parameters**: - `class`: The class to map results to - `sql`: The SQL query string - `bindings`: Optional parameters to bind to the query **Return Type**: `Array` **Example**: ```rust use std.db.sqlite; class User { id: number; name: string; age: number; } let users = pg.query_as(User, "SELECT * FROM users WHERE age > $1", 18); // Returns an array of User instances ``` ## begin_transaction() Starts a new database transaction. Returns a Transaction object that provides methods for executing queries within the transaction. **Return Type**: `Transaction` **Example**: ```rust use std.db.sqlite; let tx = pg.begin_transaction(); // Returns a Transaction object ``` ## Transaction Methods The following methods are available on Transaction objects returned by `begin_transaction()`. ### query(sql: string, ...bindings: any) Executes a SQL query within the transaction and returns the results as an array of objects. **Parameters**: - `sql`: The SQL query string - `bindings`: Optional parameters to bind to the query **Return Type**: `Array` **Example**: ```rust use std.db.sqlite; let tx = pg.begin_transaction(); let results = tx.query("SELECT * FROM users WHERE age > $1", 18); ``` ### query_as(class: Class, sql: string, ...bindings: any) Executes a SQL query within the transaction and maps the results to instances of the specified class. **Parameters**: - `class`: The class to map results to - `sql`: The SQL query string - `bindings`: Optional parameters to bind to the query **Return Type**: `Array` **Example**: ```rust use std.db.sqlite; class User { id: number; name: string; age: number; } let tx = pg.begin_transaction(); let users = tx.query_as(User, "SELECT * FROM users WHERE age > $1", 18); ``` ### commit() Commits the transaction, saving all changes made within the transaction. **Return Type**: `nil` **Example**: ```rust use std.db.sqlite; let tx = pg.begin_transaction(); // Execute queries... tx.commit(); ``` ### rollback() Rolls back the transaction, discarding all changes made within the transaction. **Return Type**: `nil` **Example**: ```rust use std.db.sqlite; let tx = pg.begin_transaction(); // Execute queries... tx.rollback(); ``` ## Usage Examples ### Basic Query Example ```rust use std.db.sqlite; // Simple query let users = pg.query("SELECT * FROM users WHERE age > $1", 18); // Iterate through results for user in users { print(user.name); } ``` ### Transaction Example ```rust use std.db.sqlite; // Start transaction let tx = pg.begin_transaction(); // Update user balance tx.query("UPDATE users SET balance = balance - $1 WHERE id = $2", 100, 123); // Insert transaction record tx.query("INSERT INTO transactions (user_id, amount) VALUES ($1, $2)", 123, -100); // Commit if successful tx.commit(); ``` ### Typed Query Example ```rust use std.db.sqlite; // Define user class class User { id: number; name: string; email: string; } // Query users and map to class let users = pg.query_as(User, "SELECT * FROM users WHERE active = true"); // Access typed properties for user in users { print(`${user.name} <${user.email}>`); } ``` ### Parameter Binding Example ```rust use std.db.sqlite; // Query with different parameter types let results = pg.query( "SELECT * FROM users WHERE name = $1 AND age > $2 AND active = $3", "John", // string 25, // number true // boolean ); ``` ### Array Parameter Example ```rust use std.db.sqlite; // Query with array parameter let user_ids = [1, 2, 3, 4]; let users = pg.query("SELECT * FROM users WHERE id = ANY($1)", user_ids); ``` ### Date Parameter Example ```rust use std.db.sqlite; // Query with date parameter let users = pg.query( "SELECT * FROM users WHERE created_at > $1", "2023-01-01" // Date string in YYYY-MM-DD format ); ``` --- url: /std/db/redis.md --- # std.db.redis The `std.db.redis` module provides functions for interacting with Redis databases, including direct command execution and pipeline operations. ## Core Functions ### cmd(command: string, ...args: any) Executes a Redis command and returns the result. **Parameters**: - `command`: The Redis command string (e.g., "SET mykey value") - `args`: Optional additional arguments for the command **Return Type**: `any` (depends on Redis command) **Example**: ```rust use std.db.redis; // Set a key redis.cmd("SET mykey somevalue"); // Get a key let value = redis.cmd("GET mykey"); print(value); // "somevalue" // Get list elements let lists = redis.cmd("LRANGE mylist 0 -1"); print(lists); // Array of list elements ``` ### pipeline() Creates a new Redis pipeline for executing multiple commands atomically. **Return Type**: `Pipeline` object **Pipeline Methods**: - `cmd(command: string, ...args: any)` - Adds a command to the pipeline - `exec()` - Executes all commands in the pipeline - `discard()` - Discards the pipeline without executing **Example**: ```rust use std.db.redis; // Create pipeline let p = redis.pipeline(); // Add commands p.cmd("SET k1 v1"); p.cmd("SET k2 v2"); p.cmd("GET k1"); // Execute pipeline let results = p.exec(); print(results); // ["OK", "OK", "v1"] // Alternatively, discard pipeline // p.discard(); ``` ## Usage Examples ### Basic Key Operations ```rust use std.db.redis; // Set and get values redis.cmd("SET username alice"); let name = redis.cmd("GET username"); print(name); // "alice" // Increment counter redis.cmd("INCR counter"); let count = redis.cmd("GET counter"); print(count); // "1" ``` ### List Operations ```rust use std.db.redis; // Push to list redis.cmd("RPUSH mylist item1"); redis.cmd("RPUSH mylist item2"); // Get all list items let items = redis.cmd("LRANGE mylist 0 -1"); print(items); // ["item1", "item2"] ``` ### Hash Operations ```rust use std.db.redis; // Set hash fields redis.cmd("HSET user:1000 name Alice age 30"); // Get hash field let name = redis.cmd("HGET user:1000 name"); print(name); // "Alice" // Get all hash fields let user = redis.cmd("HGETALL user:1000"); print(user); // {"name": "Alice", "age": "30"} ``` ### Set Operations ```rust use std.db.redis; // Add to set redis.cmd("SADD myset item1"); redis.cmd("SADD myset item2"); // Get set members let members = redis.cmd("SMEMBERS myset"); print(members); // ["item1", "item2"] ``` ### Pipeline Example ```rust use std.db.redis; // Create pipeline let p = redis.pipeline(); // Add multiple commands p.cmd("SET user:1:name Alice"); p.cmd("SET user:1:age 30"); p.cmd("GET user:1:name"); // Execute and get results let results = p.exec(); print(results); // ["OK", "OK", "Alice"] ``` ### Error Handling ```rust use std.db.redis; try { // Attempt invalid command redis.cmd("INVALIDCOMMAND"); } catch (e) { print(`Redis error: ${e.message}`); } ``` ### Transaction Example ```rust use std.db.redis; // Start transaction redis.cmd("MULTI"); // Add commands redis.cmd("SET key1 value1"); redis.cmd("SET key2 value2"); // Execute transaction let results = redis.cmd("EXEC"); print(results); // ["OK", "OK"] ``` ### Expiration Example ```rust use std.db.redis; // Set key with expiration redis.cmd("SET mykey value EX 60"); // Expires in 60 seconds // Check TTL let ttl = redis.cmd("TTL mykey"); print(ttl); // 60 (or less, depending on when checked) ``` ### Complex Pipeline ```rust use std.db.redis; // Create pipeline for user registration let p = redis.pipeline(); // Add user data p.cmd("HSET user:1000 name Alice age 30 email alice@example.com"); // Add to users set p.cmd("SADD users 1000"); // Get all user data p.cmd("HGETALL user:1000"); // Execute pipeline let results = p.exec(); print(results); // [1, 1, {"name": "Alice", "age": "30", "email": "alice@example.com"}] ``` ### Sorted Set Operations ```rust use std.db.redis; // Add to sorted set redis.cmd("ZADD myzset 1 item1"); redis.cmd("ZADD myzset 2 item2"); // Get sorted set members let members = redis.cmd("ZRANGE myzset 0 -1"); print(members); // ["item1", "item2"] ``` ### Pub/Sub Example ```rust use std.db.redis; // Publish message redis.cmd("PUBLISH mychannel 'Hello World'"); // Note: Subscribe would be handled in a separate process // redis.cmd("SUBSCRIBE mychannel"); ``` --- url: /std/env.md --- # std.env The `std.env` module provides functions for interacting with environment variables and command-line arguments. ## args() Returns command-line arguments as an array of strings. **Return Type**: `Array` **Example**: ```rust use std.env; // If you run: aiscript script.ai arg1 arg2 let args = env.args(); // args will be: ["aiscript", "script.ai", "arg1", "arg2"] ``` ## vars() Returns all environment variables as an object where keys are variable names and values are their corresponding values. **Return Type**: `Object` **Example**: ```rust use std.env; let variables = env.vars(); // Example output: // { // "PATH": "/usr/local/bin:/usr/bin", // "HOME": "/home/user", // "USER": "username" // } ``` ## get\_env(name: string) Gets the value of an environment variable. **Parameters**: * `name`: The name of the environment variable to retrieve **Return Type**: `string | nil` **Example**: ```rust use std.env; let home = env.get_env("HOME"); // If HOME is set: "/home/user" // If HOME is not set: nil ``` ## set\_env(name: string, value: string) Sets the value of an environment variable. **Parameters**: * `name`: The name of the environment variable to set * `value`: The value to set **Return Type**: `nil` **Example**: ```rust use std.env; env.set_env("MY_VAR", "my_value"); let value = env.get_env("MY_VAR") // Returns "my_value" ``` --- url: /std/http.md --- # std.http The `std.http` module provides functions for making HTTP requests to external services and APIs. ## get(url: string, headers?: object) Makes an HTTP GET request to the specified URL. **Parameters**: * `url`: The URL to send the request to * `headers` (optional): An object containing HTTP headers **Return Type**: `object` with the following properties: * `status`: HTTP status code (number) * `statusText`: HTTP status message (string) * `ok`: Whether the request was successful (boolean) * `headers`: Response headers (object) * `text`: Response body as text (string) * `json`: Response body parsed as JSON (if applicable) **Example**: ```rust use std.http; // Simple GET request let response = http.get("https://api.example.com/users"); if (response.ok) { print(response.json); } else { print(`Error: ${response.status} ${response.statusText}`); } // GET request with headers let response = http.get("https://api.example.com/users", { "Authorization": "Bearer token123", "Accept": "application/json" }); ``` ## post(url: string, body: string, headers?: object) Makes an HTTP POST request to the specified URL with the given body. **Parameters**: * `url`: The URL to send the request to * `body`: The request body as a string * `headers` (optional): An object containing HTTP headers **Return Type**: `object` (same as get) **Example**: ```rust use std.http; // POST request with JSON body let response = http.post( "https://api.example.com/users", '{"name": "John", "email": "john@example.com"}', { "Content-Type": "application/json", "Authorization": "Bearer token123" } ); if (response.ok) { print(`User created with ID: ${response.json.id}`); } else { print(`Error: ${response.status} ${response.statusText}`); } ``` ## put(url: string, body: string, headers?: object) Makes an HTTP PUT request to the specified URL with the given body. **Parameters**: * `url`: The URL to send the request to * `body`: The request body as a string * `headers` (optional): An object containing HTTP headers **Return Type**: `object` (same as get) **Example**: ```rust use std.http; // PUT request to update a resource let response = http.put( "https://api.example.com/users/123", '{"name": "John Updated", "email": "john.updated@example.com"}', { "Content-Type": "application/json", "Authorization": "Bearer token123" } ); if (response.ok) { print("User updated successfully"); } else { print(`Error: ${response.status} ${response.statusText}`); } ``` ## delete(url: string, headers?: object) Makes an HTTP DELETE request to the specified URL. **Parameters**: * `url`: The URL to send the request to * `headers` (optional): An object containing HTTP headers **Return Type**: `object` (same as get) **Example**: ```rust use std.http; // DELETE request to remove a resource let response = http.delete( "https://api.example.com/users/123", { "Authorization": "Bearer token123" } ); if (response.ok) { print("User deleted successfully"); } else { print(`Error: ${response.status} ${response.statusText}`); } ``` ## patch(url: string, body: string, headers?: object) Makes an HTTP PATCH request to the specified URL with the given body. **Parameters**: * `url`: The URL to send the request to * `body`: The request body as a string * `headers` (optional): An object containing HTTP headers **Return Type**: `object` (same as get) **Example**: ```rust use std.http; // PATCH request to partially update a resource let response = http.patch( "https://api.example.com/users/123", '{"email": "new.email@example.com"}', { "Content-Type": "application/json", "Authorization": "Bearer token123" } ); if (response.ok) { print("User partially updated successfully"); } else { print(`Error: ${response.status} ${response.statusText}`); } ``` ## head(url: string, headers?: object) Makes an HTTP HEAD request to the specified URL. **Parameters**: * `url`: The URL to send the request to * `headers` (optional): An object containing HTTP headers **Return Type**: `object` (same as get, but typically without body content) **Example**: ```rust use std.http; // HEAD request to check if a resource exists let response = http.head("https://api.example.com/users/123"); if (response.ok) { print("Resource exists"); print(`Last-Modified: ${response.headers["last-modified"]}`); } else { print("Resource not found"); } ``` ## Usage Examples ## Fetching JSON Data ```rust use std.http; // Get JSON data from an API let response = http.get("https://api.example.com/data"); if (response.ok) { // Access parsed JSON directly let items = response.json.items; for (item in items) { print(`${item.id}: ${item.name}`); } } else { print(`Failed to fetch data: ${response.status}`); } ``` ## Handling Authentication ```rust use std.http; // Function to make authenticated requests fn authenticated_request(url, method, body = nil) { let token = "your-auth-token"; let headers = { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" }; if (method == "GET") { return http.get(url, headers); } else if (method == "POST") { return http.post(url, body, headers); } else if (method == "PUT") { return http.put(url, body, headers); } else if (method == "DELETE") { return http.delete(url, headers); } return nil; } // Use the function let user_data = authenticated_request("https://api.example.com/me", "GET"); print(user_data.json); ``` ## Submitting Form Data ```rust use std.http; // Submit form data let form_data = 'name=John&email=john@example.com&message=Hello'; let response = http.post( "https://example.com/contact", form_data, { "Content-Type": "application/x-www-form-urlencoded" } ); if (response.ok) { print("Form submitted successfully"); } else { print(`Form submission failed: ${response.text}`); } ``` --- url: /std/io.md --- # std.io The `std.io` module provides functions for file system operations and standard input/output. ## read\_file(path: string) Reads the entire contents of a file into a string. **Parameters**: * `path`: The path to the file to read **Return Type**: `string` **Example**: ```rust use std.io; let content = io.read_file("example.txt"); // Returns the entire content of example.txt as a string ``` ## read\_lines(path: string) Reads all lines from a file and returns them as a single string with newlines. **Parameters**: * `path`: The path to the file to read **Return Type**: `string` **Example**: ```rust use std.io; let content = io.read_lines("example.txt"); // Returns all lines from example.txt joined with newlines ``` ## write\_file(path: string, content: string) Writes a string to a file, creating the file if it doesn't exist or overwriting it if it does. **Parameters**: * `path`: The path to the file to write * `content`: The string content to write to the file **Return Type**: `boolean` **Example**: ```rust use std.io; let success = io.write_file("output.txt", "Hello, world!"); // Creates or overwrites output.txt with "Hello, world!" // Returns true if successful ``` ## append\_file(path: string, content: string) Appends a string to the end of a file, creating the file if it doesn't exist. **Parameters**: * `path`: The path to the file to append to * `content`: The string content to append to the file **Return Type**: `boolean` **Example**: ```rust use std.io; let success = io.append_file("log.txt", "New log entry\n"); // Appends "New log entry" followed by a newline to log.txt // Returns true if successful ``` ## input(prompt?: string) Reads a line of input from standard input. If a prompt is provided, it will be displayed before reading input. **Parameters**: * `prompt` (optional): A string to display as a prompt **Return Type**: `string` **Example**: ```rust use std.io; let name = io.input("Enter your name: "); // Displays "Enter your name: " and waits for user input // Returns the entered text without the trailing newline ``` ## exists(path: string) Checks if a file or directory exists at the specified path. **Parameters**: * `path`: The path to check **Return Type**: `boolean` **Example**: ```rust use std.io; if (io.exists("config.json")) { // File or directory exists } ``` ## is\_file(path: string) Checks if the path points to a file. **Parameters**: * `path`: The path to check **Return Type**: `boolean` **Example**: ```rust use std.io; if (io.is_file("document.txt")) { // Path exists and is a file } ``` ## is\_dir(path: string) Checks if the path points to a directory. **Parameters**: * `path`: The path to check **Return Type**: `boolean` **Example**: ```rust use std.io; if (io.is_dir("images")) { // Path exists and is a directory } ``` ## create\_dir(path: string) Creates a directory and all its parent directories if they don't exist. **Parameters**: * `path`: The path of the directory to create **Return Type**: `boolean` **Example**: ```rust use std.io; let success = io.create_dir("data/logs/2023"); // Creates the directory structure data/logs/2023 // Returns true if successful ``` ## remove\_file(path: string) Removes a file from the file system. **Parameters**: * `path`: The path of the file to remove **Return Type**: `boolean` **Example**: ```rust use std.io; let success = io.remove_file("temp.txt"); // Removes the file temp.txt // Returns true if successful ``` ## remove\_dir(path: string, recursive?: boolean) Removes a directory from the file system. **Parameters**: * `path`: The path of the directory to remove * `recursive` (optional): If true, removes the directory and all its contents recursively. Default is false. **Return Type**: `boolean` **Example**: ```rust use std.io; // Remove an empty directory let success1 = io.remove_dir("empty_folder"); // Remove a directory and all its contents let success2 = io.remove_dir("project_backup", true); ``` ## rename(from: string, to: string) Renames a file or directory. **Parameters**: * `from`: The current path * `to`: The new path **Return Type**: `boolean` **Example**: ```rust use std.io; let success = io.rename("old_name.txt", "new_name.txt"); // Renames old_name.txt to new_name.txt // Returns true if successful ``` --- url: /std/math.md --- # std.math The `std.math` module provides mathematical constants and functions for numerical operations. ## Constants * `PI`: The mathematical constant π (pi), approximately 3.14159 * `E`: The mathematical constant e (Euler's number), approximately 2.71828 * `TAU`: The mathematical constant τ (tau), equal to 2π * `INFINITY`: Represents positive infinity * `NEG_INFINITY`: Represents negative infinity * `NAN`: Represents "Not a Number" ## Functions ## add(a: number, b: number) Adds two numbers. **Parameters**: * `a`: First number to add * `b`: Second number to add **Return Type**: `number` **Example**: ```rust use std.math; math.add(5, 3) // Returns: 8 ``` ## sub(a: number, b: number) Subtracts the second number from the first. **Parameters**: * `a`: Number to subtract from * `b`: Number to subtract **Return Type**: `number` **Example**: ```rust use std.math; math.sub(5, 3) // Returns: 2 ``` ## mul(a: number, b: number) Multiplies two numbers. **Parameters**: * `a`: First number to multiply * `b`: Second number to multiply **Return Type**: `number` **Example**: ```rust use std.math; math.mul(5, 3) // Returns: 15 ``` ## div(a: number, b: number) Divides the first number by the second. **Parameters**: * `a`: Number to divide * `b`: Number to divide by **Return Type**: `number` **Example**: ```rust use std.math; math.div(6, 2) // Returns: 3 ``` ## floor\_div(a: number, b: number) Performs integer division, rounding down to the nearest integer. **Parameters**: * `a`: Number to divide * `b`: Number to divide by **Return Type**: `number` **Example**: ```rust use std.math; math.floor_div(7, 2) // Returns: 3 ``` ## sqrt(x: number) Calculates the square root of a number. **Parameters**: * `x`: Number to calculate square root of **Return Type**: `number` **Example**: ```rust use std.math; math.sqrt(16) // Returns: 4 ``` ## pow(base: number, exponent: number) Raises a number to the specified power. **Parameters**: * `base`: The base number * `exponent`: The power to raise the base to **Return Type**: `number` **Example**: ```rust use std.math; math.pow(2, 3) // Returns: 8 ``` ## log(x: number) Calculates the natural logarithm (base e) of a number. **Parameters**: * `x`: Number to calculate logarithm of **Return Type**: `number` **Example**: ```rust use std.math; math.log(math.E) // Returns: 1 ``` ## sin(x: number) Calculates the sine of an angle (in radians). **Parameters**: * `x`: Angle in radians **Return Type**: `number` **Example**: ```rust use std.math; math.sin(math.PI / 2) // Returns: 1 ``` ## cos(x: number) Calculates the cosine of an angle (in radians). **Parameters**: * `x`: Angle in radians **Return Type**: `number` **Example**: ```rust use std.math; math.cos(0) // Returns: 1 ``` ## tan(x: number) Calculates the tangent of an angle (in radians). **Parameters**: * `x`: Angle in radians **Return Type**: `number` **Example**: ```rust use std.math; math.tan(math.PI / 4) // Returns: 1 ``` ## asin(x: number) Calculates the arcsine (inverse sine) of a number. **Parameters**: * `x`: Number between -1 and 1 **Return Type**: `number` **Example**: ```rust use std.math; math.asin(1) // Returns: PI/2 ``` ## acos(x: number) Calculates the arccosine (inverse cosine) of a number. **Parameters**: * `x`: Number between -1 and 1 **Return Type**: `number` **Example**: ```rust use std.math; math.acos(1) // Returns: 0 ``` ## floor(x: number) Rounds a number down to the nearest integer. **Parameters**: * `x`: Number to round down **Return Type**: `number` **Example**: ```rust use std.math; math.floor(3.7) // Returns: 3 ``` ## ceil(x: number) Rounds a number up to the nearest integer. **Parameters**: * `x`: Number to round up **Return Type**: `number` **Example**: ```rust use std.math; math.ceil(3.2) // Returns: 4 ``` ## round(x: number) Rounds a number to the nearest integer. **Parameters**: * `x`: Number to round **Return Type**: `number` **Example**: ```rust use std.math; math.round(3.5) // Returns: 4 ``` ## abs(x: number) \{#abs} Returns the absolute value of a number. **Parameters**: * `x`: Number to get absolute value of **Return Type**: `number` **Example**: ```rust use std.math; math.abs(-5) // Returns: 5 ``` ## min(a: number, b: number) Returns the smaller of two numbers. **Parameters**: * `a`: First number to compare * `b`: Second number to compare **Return Type**: `number` **Example**: ```rust use std.math; math.min(2, 5) // Returns: 2 ``` ## max(a: number, b: number) Returns the larger of two numbers. **Parameters**: * `a`: First number to compare * `b`: Second number to compare **Return Type**: `number` **Example**: ```rust use std.math; math.max(2, 5) // Returns: 5 ``` --- url: /std/random.md --- # std.random The `std.random` module provides a comprehensive set of functions for random number generation, including basic random numbers, range-based selections, and various probability distributions. ## seed(value: int) -> void Sets the seed for the random number generator to produce reproducible sequences of random numbers. **Parameters:** * `value`: An integer value to use as the seed **Example:** ```rust use std.random; random.seed(42); // Now all subsequent random operations will produce the same sequence ``` ## random() -> float Generates a random floating-point number between 0.0 (inclusive) and 1.0 (exclusive). **Returns:** * A random float in the range \[0.0, 1.0) **Example:** ```rust use std.random; let value = random.random(); print(value); // Outputs: 0.7123889... ``` ## randint(min: int, max: int) -> int Generates a random integer between min and max (inclusive). **Parameters:** * `min`: The lower bound (inclusive) * `max`: The upper bound (inclusive) **Returns:** * A random integer in the range \[min, max] **Example:** ```rust use std.random; let dice_roll = random.randint(1, 6); print(dice_roll); // Outputs a number between 1 and 6 ``` ## uniform(min: float, max: float) -> float Generates a random floating-point number from a uniform distribution between min and max. **Parameters:** * `min`: The lower bound (inclusive) * `max`: The upper bound (exclusive) **Returns:** * A random float in the range \[min, max) **Example:** ```rust use std.random; let temperature = random.uniform(20.0, 30.0); print(temperature); // Outputs: 25.7392... ``` ## range(start: int, end: int, step?: int) -> int Generates a random integer from a range with optional step size. **Parameters:** * `start`: The start of the range (inclusive) * `end`: The end of the range (exclusive) * `step`: (Optional) The step size between numbers **Returns:** * A random integer from the specified range **Example:** ```rust use std.random; // Random even number between 0 and 10 let even = random.range(0, 10, 2); print(even); // Outputs: 0, 2, 4, 6, or 8 ``` ## choice(list: array) -> any Randomly selects an element from a list. **Parameters:** * `list`: An array of elements to choose from **Returns:** * A random element from the list **Example:** ```rust use std.random; let colors = ["red", "green", "blue"]; let color = random.choice(colors); print(color); // Outputs: one of the colors ``` ## normal(mean: float, std\_dev: float) -> float Generates a random floating-point number from a normal (Gaussian) distribution. **Parameters:** * `mean`: The mean (average) of the distribution * `std_dev`: The standard deviation of the distribution **Returns:** * A random float from the normal distribution **Example:** ```rust use std.random; // Generate IQ scores (mean=100, std_dev=15) let iq = random.normal(100.0, 15.0); print(iq); // Outputs: 97.234... ``` ## exponential(lambda: float) -> float Generates a random floating-point number from an exponential distribution. **Parameters:** * `lambda`: The rate parameter (λ) of the exponential distribution **Returns:** * A random float from the exponential distribution **Example:** ```rust use std.random; // Generate waiting times between events let wait_time = random.exponential(0.5); print(wait_time); // Outputs: 1.386... ``` ## bool() -> boolean Generates a random boolean value. **Returns:** * A random boolean (true or false) **Example:** ```rust use std.random; let coin_flip = random.bool(); print(coin_flip); // Outputs: true or false ``` --- url: /std/serde.md --- # std.serde The `std.serde` module provides functionality for JSON serialization and deserialization. It allows you to convert between AIScript values and JSON format, with support for both string and file operations. ## from\_str(json\_str: string) -> any Parses a JSON string and converts it into an AIScript value. **Parameters:** * `json_str`: A string containing valid JSON data **Returns:** * The parsed AIScript value **Throws:** * Runtime error if the input is not valid JSON * Runtime error if wrong number of arguments provided **Example:** ```rust use std.serde; let json = "{\"name\": \"John\", \"age\": 30}"; let data = serde.from_str(json); print(data.name); // Outputs: John ``` ## to\_str(value: any, pretty?: boolean) -> string Converts an AIScript value to a JSON string. **Parameters:** * `value`: The value to serialize * `pretty`: (Optional) If true, formats the output with proper indentation and line breaks **Returns:** * A JSON string representation of the value **Throws:** * Runtime error if serialization fails * Runtime error if wrong number of arguments provided * Runtime error if pretty argument is not a boolean **Example:** ```rust use std.serde; let obj = { name: "John", age: 30, hobbies: ["reading", "coding"] }; // Compact output let str1 = serde.to_str(obj); print(str1); // {"name":"John","age":30,"hobbies":["reading","coding"]} // Pretty-printed output let str2 = serde.to_str(obj, pretty=true); print(str2); // { // "name": "John", // "age": 30, // "hobbies": [ // "reading", // "coding" // ] // } ``` ## from\_file(path: string) -> any Reads and parses a JSON file into an AIScript value. **Parameters:** * `path`: Path to the JSON file **Returns:** * The parsed AIScript value **Throws:** * Runtime error if file cannot be read * Runtime error if file content is not valid JSON * Runtime error if wrong number of arguments provided **Example:** ```rust use std.serde; // Assuming config.json contains: {"port": 8080, "host": "localhost"} let config = serde.from_file("config.json"); print(config.port); // Outputs: 8080 ``` ## to\_file(path: string, value: any, pretty?: boolean) -> boolean Writes an AIScript value to a file in JSON format. **Parameters:** * `path`: Path where the JSON file should be written * `value`: The value to serialize * `pretty`: (Optional) If true, formats the output with proper indentation and line breaks **Returns:** * `true` if the operation was successful **Throws:** * Runtime error if file cannot be written * Runtime error if serialization fails * Runtime error if wrong number of arguments provided * Runtime error if pretty argument is not a boolean **Example:** ```rust use std.serde; let data = { users: [ { id: 1, name: "Alice" }, { id: 2, name: "Bob" } ] }; // Write with pretty printing serde.to_file("users.json", data, pretty=true); ``` --- url: /std/time.md --- # std.time The `std.time` module provides functions for working with time, including getting the current time, formatting and parsing datetime strings, and handling time durations. ## now() Returns the current time as a Unix timestamp in seconds with fractional part. **Return Type**: `number` **Example**: ```rust use std.time; let current_time = time.now(); // Returns the current Unix timestamp, e.g., 1709389245.789 ``` ## unix\_timestamp(time\_str: string) Returns the Unix timestamp in seconds for a given time string in ISO 8601 format. **Parameters**: * `time_str`: A string representing a datetime in ISO 8601 format (RFC 3339) **Return Type**: `number` **Example**: ```rust use std.time; let timestamp = time.unix_timestamp("2023-01-15T12:30:45Z"); // Returns the Unix timestamp for the specified datetime ``` ## sleep(seconds: number) Pauses the execution for the specified number of seconds. **Parameters**: * `seconds`: The number of seconds to sleep (can include fractional part for milliseconds) **Return Type**: `nil` **Example**: ```rust use std.time; // Sleep for 2.5 seconds time.sleep(2.5); ``` ## to\_utc(timestamp: number) Converts a Unix timestamp to a UTC datetime string in RFC 3339 format. **Parameters**: * `timestamp`: A Unix timestamp in seconds **Return Type**: `string` **Example**: ```rust use std.time; let datetime = time.to_utc(1609459200); // Returns "2021-01-01T00:00:00+00:00" ``` ## to\_local(timestamp: number) Converts a Unix timestamp to a local datetime string in RFC 3339 format. **Parameters**: * `timestamp`: A Unix timestamp in seconds **Return Type**: `string` **Example**: ```rust use std.time; let local_datetime = time.to_local(1609459200); // Returns the datetime in local timezone, e.g., "2021-01-01T08:00:00+08:00" ``` ## format\_datetime(timestamp: number, format: string) Formats a timestamp using the specified format string according to the chrono crate's format syntax. **Parameters**: * `timestamp`: A Unix timestamp in seconds * `format`: A format string following chrono's syntax **Return Type**: `string` **Example**: ```rust use std.time; let formatted = time.format_datetime(1609459200, "%Y-%m-%d %H:%M:%S"); // Returns "2021-01-01 00:00:00" (in UTC) or adjusted for local timezone ``` ## parse\_datetime(datetime\_str: string, format: string) Parses a datetime string using the specified format and returns the corresponding Unix timestamp. **Parameters**: * `datetime_str`: A string representing a datetime * `format`: A format string following chrono's syntax **Return Type**: `number` **Example**: ```rust use std.time; let timestamp = time.parse_datetime("2021-01-01 12:30:45", "%Y-%m-%d %H:%M:%S"); // Returns the Unix timestamp for the specified datetime ``` ## seconds(value: number) Converts a value in seconds to seconds (identity function). **Parameters**: * `value`: A number of seconds **Return Type**: `number` **Example**: ```rust use std.time; let duration = time.seconds(30); // Returns 30.0 ``` ## minutes(value: number) Converts a value in minutes to seconds. **Parameters**: * `value`: A number of minutes **Return Type**: `number` **Example**: ```rust use std.time; let duration = time.minutes(2); // Returns 120.0 (2 * 60) ``` ## hours(value: number) Converts a value in hours to seconds. **Parameters**: * `value`: A number of hours **Return Type**: `number` **Example**: ```rust use std.time; let duration = time.hours(1); // Returns 3600.0 (1 * 60 * 60) ``` ## days(value: number) Converts a value in days to seconds. **Parameters**: * `value`: A number of days **Return Type**: `number` **Example**: ```rust use std.time; let duration = time.days(1); // Returns 86400.0 (1 * 24 * 60 * 60) ``` ## Usage Examples ### Working with timestamps and formatting ```rust use std.time; // Get current timestamp let now = time.now(); // Convert to human-readable format let utc_time = time.to_utc(now); let local_time = time.to_local(now); // Format with custom format string let formatted = time.format_datetime(now, "%Y-%m-%d %H:%M:%S"); ``` ### Creating and using durations ```rust use std.time; // Create a duration of 2 hours and 30 minutes in seconds let duration = time.hours(2) + time.minutes(30); // Use with sleep time.sleep(time.seconds(0.5)); // Sleep for half a second // Calculate a future timestamp let future_time = time.now() + time.days(7); // One week from now ``` ### Parsing and manipulating dates ```rust use std.time; // Parse a date string let birthday = time.parse_datetime("1990-05-15", "%Y-%m-%d"); // Format it differently let formatted_birthday = time.format_datetime(birthday, "%B %d, %Y"); ``` --- url: /blog/index.md --- # AIScript blogs Check here for the latest articles and release announcements about AIScript. ## [Why AIScript](/blog/why-aiscript) > March 11, 2025 ## [Announcing AIScript and How I Built It](/blog/announcing-aiscript) > March 11, 2025 --- url: /blog/why-aiscript.md --- # Why AIScript AIScript is an experimental project exploring a new approach to building web applications in the AI era. This isn't just another language created by someone learning interpreter design—my ambition is much greater. Regardless of which language you use to build web APIs, you need a web framework. These frameworks essentially perform the same functions: parsing HTTP requests, executing database queries, and returning results as JSON or rendered HTML. Some frameworks offer additional features like request validation or OpenAPI spec generation. However, frameworks across different languages provide vastly different development experiences. Switching between them inevitably requires learning new syntax and documentation. That's why I wanted to create an intuitive approach that combines language with web framework to deliver a modern experience for building web applications. ![](/guide/web-framework.png) ## Intuitive First My vision is to write code that runs a web API with a single command—no need to learn a new framework, struggle with choosing which one to use, or install dependencies to run it. Just execute `aiscript serve` and you're up and running. ```javascript get /guess { query { @number(min=0, max=100) value: int } let message = "You got it!" if query.value == 42 else "Try again"; return { message }; } ``` ```bash $ aiscript serve route.ai Listening on http://localhost:8080 ``` ```bash $ curl -X GET http://localhost:8080/guess?n=10 {"message": "Try again"} $ curl -X GET http://localhost:8080/guess?n=42 {"message": "You got it!"} $ curl -X GET http://localhost:8080/guess?n=999 {"error": "Field validation failed: value: Number is greater than the maximum value of 100."} ``` This is just a small part of the ideas that have come to mind. I've searched to see if someone has already built something similar, but finding nothing, I decided to build it myself. ## Out-of-box First When building a web app, most components beyond the core business logic are standard across projects. How do you implement JWT authentication? How do you integrate Google or GitHub login? How do you query databases or validate data? For these rarely changing standards, I believe they should be built directly into the language or framework, providing an out-of-box experience. ```rust @auth // JWT auth post /chat { query { @string(min_len=5, max_len=200) message: str } use std.db.pg; let message = pg.query("INSERT INTO messages (message) VALUES ($1)", query.message); return message; } ``` ```javascript @sso(provider="google") get /auth/google { let url = sso.authority_url(); print(url); return temporary_redirect(target=url); } @sso(provider="google") get /auth/google/callback { query { code: str, state: str, } print("code", query.code); print("state", query.state); let user_info = sso.verify(code=query.code); print(user_info); return { user_info }; } ``` ```javascript class User { @string(min_len=3, max=20) name: str, @number(min=18, strict_int=true, strict_float=true) age: int, @in(["male", "female"]) gender: str = "male", } let u1 = User("Le", 20, "male") |err| { print("Validate error:", err); }; // Validate error: { // loc: [name], input: Le, type: validation_error, // msg: String length is less than the minimum length of 3 // } let u2 = User("Lee", 17, "male") |err| { print("Validate error:", err); }; // Validate error: { // loc: [age], input: 17, type: validation_error, // msg: Number is less than the minimum value of 18 // } let u3 = User("Lee", 20, "boy") |err| { print("Validate error:", err); }; // Validate error: { // loc: [gender], input: boy, type: validation_error, // msg: Value is not in the list of allowed values // } let u4 = User("Lee") |err| { print("Validate error:", err); }; // Validate error: { // loc: [age], input: nil, type: missing, // msg: Field required // } ``` ```toml [auth.jwt] secret = "$JWT_SECRET" expiration = 3600 [sso.google] client_id = "your_google_client_id" client_secret = "your_google_client_secret" redirect_url = "http://localhost:8080/auth/google/callback" scopes = ["email"] ``` ## AI First :::info People often ask "Do we really need a new programming language in an age when AI can write code for us?". ::: In my opinion, for web applications at least, the answer is yes. Most mainstream languages were designed decades ago—features like `prompt` and `agent` aren't primitives in these languages, but they are in AIScript. You don't need to install packages to call LLM APIs or orchestrate multiple agents. ```rust let answer = prompt "Why the sky is blue?"; print(answer); ``` ```python agent WeatherAgent { instructions: "Providing a weather forecast at the locations the user provides.", model: "gpt-4o", fn get_forecast(location: str, forecast_date: date) -> str { """ Call this tools to forecast weather based on `location` and `forecast_date`. """ // In real code: call weather API, DB queries, etc. return f'The forecast in {location} on {forecast_date} is 24°C and sunny.'; } } let user_prompt = "What will the weather be like in Paris on Tuesday?"; WeatherAgent.run(input=user_prompt); ``` ```toml [ai.openai] api_key = "YOUR_API_KEY" [ai.anthropic] api_key = "YOUR_API_KEY" # Local models with Ollama (no API key needed) [ai.ollama] api_endpoint = "http://localhost:11434/v1" model = "llama3.2" api_key = "" ``` Additionally, AIScript includes many out-of-box features in its standard library, which is particularly helpful for AI code generation. :::info Thoughts on the AI Code Generation Era #### Building Web Apps Is Not That Easy * Solid coding experience is still required, from programming languages to web frameworks and libraries. You need substantial hands-on experience to build software with confidence. * Even with AI code generation, you still need expertise if you're building anything beyond a toy project. * Finishing the code is just the beginning; deployment, observability, and security present additional challenges that current AI tools don't fully address. #### Code Generation: Just the Tip of the Iceberg * For any serious production software, 80% or more of the cost comes after initial development—in what's called "Day-2 operations." * Day-2 operations present the biggest challenges. * Tools like Cursor, WindSurf, or even Devin can't fully solve deployment problems or make Day-2 operations effortless. ::: ## Best Practice First Developers shouldn't need to learn several approaches to the same problem just to determine which is best. Ideally, there should be one solution representing best practices, freeing you from decision paralysis. AIScript adopts Rust's web development best practices and encapsulates them directly into the language and framework, letting you focus on what matters most—your application's unique value. --- url: /blog/announcing-aiscript.md --- # Announcing AIScript and How I Built It ![](/aiscript-social-image.png) I'm excited to share **AIScript**, a new programming language I've been developing over the past few months. AIScript is designed specifically for web development in the AI era, with AI capabilities as first-class language features, and an intuitive route DSL and directive design. In my [Why AIScript](/blog/why-aiscript) post, I shared why I developed this new language; this post focuses on what AIScript is and how I developed it. ## What is AIScript? AIScript is a unique combination of an interpreted programming language and web framework, both written in Rust, designed to help developers build AI applications effortlessly. The language syntax draws inspiration from Python, JavaScript, and Rust, combining their strengths to create something that's intuitive, powerful, and easy to use. ```js $ export OPENAI_API_KEY= $ cat web.ai get / { """An api to ask LLM""" query { """the question to ask""" @string(min_len=3, max_len=100) // validate params with builtin directive @string question: str } // `ai` and `prompt` are keywords ai fn ask(question: str) -> str { let answer = prompt question; return answer; } // use query.name or query["name"] to access query parameter let answer = ask(query.question); return { answer }; } $ aiscript serve web.ai Listening on http://localhost:8080 $ curl http://localhost:8080 { "error": "Missing required field: question" } $ curl http://localhost:8080?question=Hi { "error": "Field validation failed: question: String length is less than the minimum length of 3" } $ curl http://localhost:8080?question=What is the capital of France? { "answer": "The capital of France is Paris." } ``` The [Introduction](/guide/getting-started/introduction) page provides more details about AIScript. ## How I Built AIScript My initial impression was that creating a programming language would be difficult and require relentless effort and energy. I learned some compiler concepts in university CS courses but still had no concrete idea how to create a language from scratch. I believe most people share this feeling. When I decided to develop AIScript, I began by searching for compiler books and YouTube videos. The [Dragon Book](https://en.wikipedia.org/wiki/Compilers:_Principles,_Techniques,_and_Tools) is commonly recommended, but after reading it, I found it too academic for beginners. I rarely recommend it if you're new to language development like I was. Then I discovered an excellent book called [Crafting Interpreters](https://craftinginterpreters.com/) by Robert Nystrom. This book guided me into the interpreter world, and I learned to write a Rust version of the [Lox language](https://craftinginterpreters.com/the-lox-language.html) which I open-sourced on [GitHub](https://github.com/Folyd/lox-lang). I also learned about *Pratt parsing* from Crafting Interpreters to handle expressions, an elegant and easy-to-understand method for expression parsing. Matklad has a great [article](https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html) explaining this approach. ![](https://craftinginterpreters.com/image/header.png) Since many concepts in Crafting Interpreters are derived from the Lua interpreter, I also purchased a book to learn about Lua's implementation. During this time, I discovered two outstanding projects: [Piccolo](https://github.com/kyren/piccolo), a Rust implementation of the Lua VM, and [gc-arena](https://github.com/kyren/gc-arena), an incremental garbage collector written in safe Rust. I spent considerable time diving into both projects and learned a great deal. This gave me a fundamental understanding of interpreter development within about a month. I then began writing AIScript from scratch, basing some code on my Lox implementation and using gc-arena for garbage collection. However, I soon realized that Crafting Interpreters' single-pass architecture, while offering good performance for parsing and compilation, made static type checking and advanced optimization difficult. To address this, I collaborated with Claude to help me [migrate from a single-pass to a two-pass architecture](https://github.com/aiscriptdev/aiscript/pull/1). Through this process, I discovered that Claude excels at writing interpreters, which led me to instruct Claude to help implement the remaining components of the AIScript interpreter. I've had a fantastic collaboration with Claude in implementing the AIScript interpreter. Not only did Claude help me write boilerplate code, but it also served as an exceptional interpreter teacher. When I was uncertain about the best implementation method, I would ask how CPython, Ruby, or other languages implement similar features, and Claude would provide detailed explanations that helped me make informed decisions. Without Claude's assistance, I definitely would have struggled to complete the first version of AIScript in just four months! ## Final Thoughts I'm deeply grateful to Claude for being my interpreter teacher. I believe anyone following a similar path can also create their own interpreter language with relative ease. Building a programming language has been one of the most educational experiences of my development career. I hope AIScript can help bridge the gap between traditional web development and AI capabilities, making powerful AI features accessible to more developers. Lastly and most importantly, AIScript is open source and available on [GitHub](https://github.com/aiscriptdev/aiscript). Hope you give it a try and welcome contributions from the community, whether it's submitting bug reports, suggesting features, improving documentation, or contributing code. Thanks for reading! --- url: /index.md ---