Skip to content

API Reference

Nishal K edited this page Sep 29, 2025 · 1 revision

API Reference

This page provides technical documentation for Veyra's internal APIs, embedding capabilities, and extension points for developers who want to integrate with or extend Veyra.

Table of Contents

Python API

Core Classes

Interpreter

The main execution engine for Veyra code.

from veyra.interpreter import Interpreter

# Create an interpreter instance
interp = Interpreter()

# Execute code
result = interp.execute("println('Hello, World!')")

# Execute with custom globals
globals_dict = {"custom_var": 42}
result = interp.execute("println(custom_var)", globals_dict)

Methods:

  • execute(code: str, globals_dict: dict = None) -> Any: Execute Veyra code
  • call_function(name: str, args: list) -> Any: Call a built-in function directly
  • get_global(name: str) -> Any: Get a global variable
  • set_global(name: str, value: Any): Set a global variable

Lexer

Tokenizes Veyra source code into tokens.

from veyra.lexer import Lexer

lexer = Lexer("let x = 42;")
tokens = lexer.tokenize()
# Returns list of Token objects

Token Types:

  • IDENTIFIER: Variable/function names
  • NUMBER: Numeric literals
  • STRING: String literals
  • KEYWORD: Language keywords (let, if, fn, etc.)
  • OPERATOR: Operators (+, -, *, /, etc.)
  • PUNCTUATION: Brackets, commas, semicolons

Parser

Converts tokens into an Abstract Syntax Tree (AST).

from veyra.parser import Parser
from veyra.lexer import Lexer

lexer = Lexer("let x = 1 + 2;")
tokens = lexer.tokenize()
parser = Parser(tokens)
ast = parser.parse()
# Returns root AST node

AST Node Classes

All AST nodes inherit from a base ASTNode class and implement an accept(visitor) method for the visitor pattern.

Interpreter Architecture

Execution Flow

  1. Lexing: Source code → Tokens
  2. Parsing: Tokens → AST
  3. Interpretation: AST → Execution
# Complete execution pipeline
from veyra.lexer import Lexer
from veyra.parser import Parser
from veyra.interpreter import Interpreter

def execute_veyra_code(code):
    # Phase 1: Lexing
    lexer = Lexer(code)
    tokens = lexer.tokenize()

    # Phase 2: Parsing
    parser = Parser(tokens)
    ast = parser.parse()

    # Phase 3: Interpretation
    interpreter = Interpreter()
    result = interpreter.visit(ast)

    return result

Global State Management

The interpreter maintains a global symbol table and built-in function registry:

# Access interpreter internals
interp = Interpreter()

# Get all global variables
globals_dict = interp.globals

# Get all built-in functions
builtins = interp.builtins

# Add custom built-in function
def my_custom_function(*args):
    return "Custom result"

interp.builtins['my_func'] = my_custom_function

AST Node Types

Expression Nodes

BinaryOp

Binary operations (addition, subtraction, etc.)

class BinaryOp(ASTNode):
    def __init__(self, left, op, right):
        self.left = left  # ASTNode
        self.op = op      # Token
        self.right = right # ASTNode

UnaryOp

Unary operations (negation, logical not)

class UnaryOp(ASTNode):
    def __init__(self, op, expr):
        self.op = op      # Token
        self.expr = expr  # ASTNode

Literal

Literal values (numbers, strings, booleans)

class Literal(ASTNode):
    def __init__(self, value, value_type):
        self.value = value        # The actual value
        self.value_type = value_type # 'number', 'string', 'boolean'

Variable

Variable references

class Variable(ASTNode):
    def __init__(self, name):
        self.name = name  # str

Call

Function calls

class Call(ASTNode):
    def __init__(self, callee, args):
        self.callee = callee  # ASTNode (usually Variable)
        self.args = args      # list[ASTNode]

ArrayLiteral

Array literals

class ArrayLiteral(ASTNode):
    def __init__(self, elements):
        self.elements = elements  # list[ASTNode]

DictLiteral

Dictionary literals

class DictLiteral(ASTNode):
    def __init__(self, pairs):
        self.pairs = pairs  # list[tuple[ASTNode, ASTNode]]

Statement Nodes

LetStatement

Variable declarations

class LetStatement(ASTNode):
    def __init__(self, name, initializer):
        self.name = name              # str
        self.initializer = initializer # ASTNode or None

IfStatement

Conditional statements

class IfStatement(ASTNode):
    def __init__(self, condition, then_branch, else_branch):
        self.condition = condition      # ASTNode
        self.then_branch = then_branch  # list[ASTNode]
        self.else_branch = else_branch  # list[ASTNode] or None

WhileStatement

While loops

class WhileStatement(ASTNode):
    def __init__(self, condition, body):
        self.condition = condition  # ASTNode
        self.body = body           # list[ASTNode]

ForStatement

For loops

class ForStatement(ASTNode):
    def __init__(self, variable, iterable, body):
        self.variable = variable  # str
        self.iterable = iterable  # ASTNode
        self.body = body          # list[ASTNode]

FunctionDef

Function definitions

class FunctionDef(ASTNode):
    def __init__(self, name, params, body):
        self.name = name      # str
        self.params = params  # list[str]
        self.body = body      # list[ASTNode]

ReturnStatement

Return statements

class ReturnStatement(ASTNode):
    def __init__(self, value):
        self.value = value  # ASTNode or None

ClassDef

Class definitions

class ClassDef(ASTNode):
    def __init__(self, name, superclass, methods):
        self.name = name            # str
        self.superclass = superclass # str or None
        self.methods = methods      # list[FunctionDef]

TryStatement

Try-catch blocks

class TryStatement(ASTNode):
    def __init__(self, try_body, catch_var, catch_body):
        self.try_body = try_body      # list[ASTNode]
        self.catch_var = catch_var     # str
        self.catch_body = catch_body   # list[ASTNode]

ThrowStatement

Throw statements

class ThrowStatement(ASTNode):
    def __init__(self, expression):
        self.expression = expression  # ASTNode

Built-in Function Interface

Adding Custom Built-ins

from veyra.interpreter import Interpreter

def my_custom_function(*args):
    """Custom built-in function implementation"""
    if len(args) != 2:
        raise ValueError("my_custom_function expects 2 arguments")

    arg1, arg2 = args
    # Implement your logic here
    return arg1 + arg2  # Example: addition

# Add to interpreter
interp = Interpreter()
interp.builtins['my_add'] = my_custom_function

# Now available in Veyra code
result = interp.execute("my_add(5, 3)")  # Returns 8

Built-in Function Requirements

  • Functions receive arguments as Python objects
  • Return values should be compatible with Veyra types
  • Raise ValueError or TypeError for invalid arguments
  • Handle type conversion appropriately

Standard Library Integration

Built-ins are organized in the call_function method:

def call_function(self, name, args):
    if name == 'println':
        print(*[str(self.evaluate(arg)) for arg in args])
        return None
    elif name == 'len':
        return self._builtin_len(args)
    elif name == 'read_file':
        return self._builtin_read_file(args)
    # ... more built-ins

Embedding Veyra

Basic Embedding

from veyra.interpreter import Interpreter

# Create interpreter
veyra = Interpreter()

# Execute scripts
veyra.execute("""
let data = [1, 2, 3, 4, 5];
let sum = 0;
for num in data {
    sum = sum + num;
}
println("Sum: " + to_string(sum));
""")

# Call functions and get results
result = veyra.execute("pow(2, 10)")
print(f"2^10 = {result}")  # 1024.0

Advanced Embedding with Host Interop

import datetime
from veyra.interpreter import Interpreter

class HostBridge:
    def __init__(self):
        self.veyra = Interpreter()
        self._setup_bridge()

    def _setup_bridge(self):
        # Add host functions to Veyra
        self.veyra.builtins['get_current_time'] = self._get_current_time
        self.veyra.builtins['call_python_function'] = self._call_python_function

    def _get_current_time(self, *args):
        return datetime.datetime.now().isoformat()

    def _call_python_function(self, *args):
        if len(args) != 2:
            raise ValueError("call_python_function expects function_name and argument")

        func_name, arg = args
        # Dynamic function calling (with proper security considerations)
        if func_name == 'len':
            return len(arg)
        elif func_name == 'upper':
            return str(arg).upper()
        else:
            raise ValueError(f"Unknown function: {func_name}")

    def execute_script(self, script):
        return self.veyra.execute(script)

# Usage
bridge = HostBridge()
result = bridge.execute_script("get_current_time()")
print(f"Current time: {result}")

Extension Points

Custom AST Visitors

from veyra.parser import ASTNode

class CustomVisitor:
    def visit_binary_op(self, node):
        left = self.visit(node.left)
        right = self.visit(node.right)
        # Custom logic for binary operations
        return self.custom_binary_op(left, node.op.value, right)

    def visit_function_def(self, node):
        # Custom function definition handling
        function_name = node.name
        param_count = len(node.params)
        # Register function or perform analysis
        return f"Function {function_name} with {param_count} parameters"

    def visit(self, node):
        method_name = f'visit_{node.__class__.__name__.lower()}'
        visitor_method = getattr(self, method_name, self.generic_visit)
        return visitor_method(node)

    def generic_visit(self, node):
        # Default traversal for unhandled node types
        return node

Plugin System

class VeyraPlugin:
    def __init__(self, interpreter):
        self.interpreter = interpreter
        self.setup()

    def setup(self):
        """Override to add custom built-ins or modify interpreter"""
        pass

    def on_execute_start(self, code):
        """Called before code execution"""
        pass

    def on_execute_end(self, result):
        """Called after code execution"""
        pass

# Example plugin: Logging
class LoggingPlugin(VeyraPlugin):
    def setup(self):
        self.interpreter.builtins['log_info'] = self._log_info
        self.interpreter.builtins['log_error'] = self._log_error

    def _log_info(self, *args):
        message = ' '.join(str(arg) for arg in args)
        print(f"[INFO] {message}")

    def _log_error(self, *args):
        message = ' '.join(str(arg) for arg in args)
        print(f"[ERROR] {message}")

    def on_execute_start(self, code):
        print(f"[LOG] Executing: {code[:50]}...")

    def on_execute_end(self, result):
        print(f"[LOG] Execution completed with result: {result}")

CLI Interface

Command Line Usage

# Run a Veyra file
python -m veyra script.veyra

# Start REPL
python -m veyra

# Run with verbose output
python -m veyra --verbose script.veyra

CLI Module Structure

# veyra/cli.py
import argparse
import sys
from .interpreter import Interpreter

def main():
    parser = argparse.ArgumentParser(description='Veyra Programming Language')
    parser.add_argument('file', nargs='?', help='Veyra file to execute')
    parser.add_argument('--verbose', '-v', action='store_true', help='Verbose output')

    args = parser.parse_args()

    interpreter = Interpreter()

    if args.file:
        # Execute file
        try:
            with open(args.file, 'r') as f:
                code = f.read()
            result = interpreter.execute(code)
            if args.verbose:
                print(f"Execution result: {result}")
        except FileNotFoundError:
            print(f"Error: File '{args.file}' not found")
            sys.exit(1)
        except Exception as e:
            print(f"Error: {e}")
            sys.exit(1)
    else:
        # Start REPL
        repl(interpreter, verbose=args.verbose)

def repl(interpreter, verbose=False):
    print("Veyra REPL. Type 'exit' to quit.")
    while True:
        try:
            code = input("veyra> ")
            if code.lower() in ['exit', 'quit']:
                break
            result = interpreter.execute(code)
            if verbose or result is not None:
                print(result)
        except KeyboardInterrupt:
            break
        except Exception as e:
            print(f"Error: {e}")

if __name__ == '__main__':
    main()

Extending CLI

# Custom CLI with additional options
def custom_cli():
    parser = argparse.ArgumentParser(description='Extended Veyra CLI')
    parser.add_argument('file', nargs='?', help='Veyra file to execute')
    parser.add_argument('--output', '-o', help='Output file for results')
    parser.add_argument('--profile', action='store_true', help='Enable profiling')

    args = parser.parse_args()

    interpreter = Interpreter()

    if args.profile:
        import cProfile
        profiler = cProfile.Profile()
        profiler.enable()

    # Execute code...

    if args.profile:
        profiler.disable()
        profiler.print_stats(sort='cumulative')

This API reference provides the technical details needed to understand, extend, and integrate with Veyra. For user-facing documentation, see the Language Guide and Standard Library. For contribution guidelines, see the Contributing Guide.