-
Notifications
You must be signed in to change notification settings - Fork 0
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.
- Python API
- Interpreter Architecture
- AST Node Types
- Built-in Function Interface
- Embedding Veyra
- Extension Points
- CLI Interface
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
Tokenizes Veyra source code into tokens.
from veyra.lexer import Lexer
lexer = Lexer("let x = 42;")
tokens = lexer.tokenize()
# Returns list of Token objectsToken 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
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 nodeAll AST nodes inherit from a base ASTNode class and implement an accept(visitor) method for the visitor pattern.
- Lexing: Source code → Tokens
- Parsing: Tokens → AST
- 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 resultThe 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_functionBinary operations (addition, subtraction, etc.)
class BinaryOp(ASTNode):
def __init__(self, left, op, right):
self.left = left # ASTNode
self.op = op # Token
self.right = right # ASTNodeUnary operations (negation, logical not)
class UnaryOp(ASTNode):
def __init__(self, op, expr):
self.op = op # Token
self.expr = expr # ASTNodeLiteral 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 references
class Variable(ASTNode):
def __init__(self, name):
self.name = name # strFunction calls
class Call(ASTNode):
def __init__(self, callee, args):
self.callee = callee # ASTNode (usually Variable)
self.args = args # list[ASTNode]Array literals
class ArrayLiteral(ASTNode):
def __init__(self, elements):
self.elements = elements # list[ASTNode]Dictionary literals
class DictLiteral(ASTNode):
def __init__(self, pairs):
self.pairs = pairs # list[tuple[ASTNode, ASTNode]]Variable declarations
class LetStatement(ASTNode):
def __init__(self, name, initializer):
self.name = name # str
self.initializer = initializer # ASTNode or NoneConditional 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 NoneWhile loops
class WhileStatement(ASTNode):
def __init__(self, condition, body):
self.condition = condition # ASTNode
self.body = body # list[ASTNode]For loops
class ForStatement(ASTNode):
def __init__(self, variable, iterable, body):
self.variable = variable # str
self.iterable = iterable # ASTNode
self.body = body # list[ASTNode]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]Return statements
class ReturnStatement(ASTNode):
def __init__(self, value):
self.value = value # ASTNode or NoneClass 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]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]Throw statements
class ThrowStatement(ASTNode):
def __init__(self, expression):
self.expression = expression # ASTNodefrom 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- Functions receive arguments as Python objects
- Return values should be compatible with Veyra types
- Raise
ValueErrororTypeErrorfor invalid arguments - Handle type conversion appropriately
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-insfrom 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.0import 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}")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 nodeclass 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}")# Run a Veyra file
python -m veyra script.veyra
# Start REPL
python -m veyra
# Run with verbose output
python -m veyra --verbose script.veyra# 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()# 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.