Extending Sifzz with Python Modules
NEXT PAGE - SUBMITTING A MODULE
Sifzz has a modular architecture that allows developers to extend the language with custom Python modules. This makes contributing to Sifzz easier and enables domain-specific extensions without modifying the core interpreter.
modules/ directoryTo install a module, download its .py file FROM THE OFFICIAL Sifzz REPOSITORY ONLY and put it in modules/. Sifzz should automatically detect this. If the module that you are installing isnβt from the official repository, make sure that they have been approved in the Approved Repositories List.
β
sifzz-project/
βββ sifzz.py # Main interpreter
βββ modules/ # Module directory
β βββ mdg.md # Module Development Guide (This file)
modules/ directoryCreate modules/hello_module.py:
"""
Hello Module for Sifzz
A simple example module
"""
import sys
import os
# Add the parent directory to sys.path so we can import sifzz
parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if parent_dir not in sys.path:
sys.path.insert(0, parent_dir)
# Now import SifzzModule
try:
from sifzz import SifzzModule
except ImportError as e:
print(f"[ERROR] Could not import SifzzModule: {e}")
print(f"[ERROR] sys.path: {sys.path}")
raise
import re
class HelloModule(SifzzModule):
"""Simple greeting module"""
def register_commands(self):
"""Register commands this module provides"""
# Register: greet "Name"
self.register(
r'greet "([^"]+)"',
self.greet_person,
"Greet a person by name"
)
# Register: shout "Message"
self.register(
r'shout "([^"]+)"',
self.shout_message,
"Shout a message in uppercase"
)
def greet_person(self, match):
"""Handler for 'greet' command"""
name = match.group(1)
print(f"Hello, {name}! Nice to meet you!")
def shout_message(self, match):
"""Handler for 'shout' command"""
message = match.group(1)
print(f"{message.upper()}!!!")
Create test.sfzz:
# Test the hello module
greet "Alice"
shout "this is amazing"
python sifzz.py test.sfzz
Output:
[INFO] Loaded module: hello_module
Hello, Alice! Nice to meet you!
THIS IS AMAZING!!!
All modules inherit from SifzzModule:
class MyModule(SifzzModule):
def register_commands(self):
# Register your commands here
pass
self.register(pattern, handler, description)
Parameters:
pattern (str) - Regex pattern to match commandhandler (function) - Function to call when matcheddescription (str) - Human-readable description# Simple command
r'hello'
# Command with quoted string
r'say "([^"]+)"'
# Command with variable name
r'set (\w+) to (.+)'
# Command with number
r'wait (\d+\.?\d*) seconds?'
# Complex command
r'download "([^"]+)" as "([^"]+)"'
Handlers receive a match object from regex:
def my_handler(self, match):
# Extract captured groups
arg1 = match.group(1)
arg2 = match.group(2)
# Do something with the interpreter
self.interpreter.variables['result'] = arg1 + arg2
Your module has access to the interpreter through self.interpreter:
# Get a variable
value = self.interpreter.variables.get('myVar', default_value)
# Set a variable
self.interpreter.variables['myVar'] = 42
# Check if variable exists
if 'myVar' in self.interpreter.variables:
# ...
# Get a list
my_list = self.interpreter.lists.get('myList', [])
# Set a list
self.interpreter.lists['myList'] = [1, 2, 3]
# Modify a list
self.interpreter.lists['myList'].append(item)
# Check if function exists
if 'myFunction' in self.interpreter.functions:
# Get function start and end lines
func_start, func_end = self.interpreter.functions['myFunction']
# Evaluate a Sifzz expression
result = self.interpreter.eval_expression('"hello" + " world"')
# Evaluate a condition
is_true = self.interpreter.eval_condition('x greater than 5')
"""Advanced Math Operations Module"""
from sifzz import SifzzModule
import math
class MathModule(SifzzModule):
def register_commands(self):
self.register(
r'set (\w+) to sin\((.+)\)',
self.calc_sin,
"Calculate sine"
)
self.register(
r'set (\w+) to cos\((.+)\)',
self.calc_cos,
"Calculate cosine"
)
self.register(
r'set (\w+) to power\((.+), (.+)\)',
self.calc_power,
"Calculate power (base, exponent)"
)
def calc_sin(self, match):
var_name = match.group(1)
value = self.interpreter.eval_expression(match.group(2))
self.interpreter.variables[var_name] = math.sin(float(value))
def calc_cos(self, match):
var_name = match.group(1)
value = self.interpreter.eval_expression(match.group(2))
self.interpreter.variables[var_name] = math.cos(float(value))
def calc_power(self, match):
var_name = match.group(1)
base = self.interpreter.eval_expression(match.group(2))
exp = self.interpreter.eval_expression(match.group(3))
self.interpreter.variables[var_name] = math.pow(float(base), float(exp))
Usage:
set angle to 1.57
set result to sin(angle)
say result # ~1.0
set x to power(2, 8)
say x # 256.0
"""JSON Operations Module"""
from sifzz import SifzzModule
import json
class JsonModule(SifzzModule):
def register_commands(self):
self.register(
r'parse json "([^"]+)" and store in (\w+)',
self.parse_json,
"Parse JSON string into variable"
)
self.register(
r'get json key "([^"]+)" from (\w+) and store in (\w+)',
self.get_json_key,
"Extract value from JSON by key"
)
def parse_json(self, match):
json_str = match.group(1)
var_name = match.group(2)
try:
data = json.loads(json_str)
self.interpreter.variables[var_name] = data
except json.JSONDecodeError as e:
print(f"JSON Error: {e}")
self.interpreter.variables[var_name] = None
def get_json_key(self, match):
key = match.group(1)
source_var = match.group(2)
target_var = match.group(3)
if source_var in self.interpreter.variables:
data = self.interpreter.variables[source_var]
if isinstance(data, dict):
self.interpreter.variables[target_var] = data.get(key, None)
Usage:
parse json "{\"name\": \"Alice\", \"age\": 30}" and store in person
get json key "name" from person and store in personName
say personName # Alice
"""Date and Time Operations Module"""
from sifzz import SifzzModule
from datetime import datetime, timedelta
class DateTimeModule(SifzzModule):
def register_commands(self):
self.register(
r'set (\w+) to current time',
self.get_current_time,
"Get current time"
)
self.register(
r'set (\w+) to current date',
self.get_current_date,
"Get current date"
)
self.register(
r'format (\w+) as "([^"]+)" and store in (\w+)',
self.format_datetime,
"Format datetime with custom format"
)
def get_current_time(self, match):
var_name = match.group(1)
self.interpreter.variables[var_name] = datetime.now().strftime("%H:%M:%S")
def get_current_date(self, match):
var_name = match.group(1)
self.interpreter.variables[var_name] = datetime.now().strftime("%Y-%m-%d")
def format_datetime(self, match):
source_var = match.group(1)
format_str = match.group(2)
target_var = match.group(3)
# This is simplified - would need proper datetime object handling
self.interpreter.variables[target_var] = format_str
Usage:
set now to current time
say now # 14:30:45
set today to current date
say today # 2025-10-03
"""Simple Database/Storage Module"""
from sifzz import SifzzModule
import json
import os
class DatabaseModule(SifzzModule):
def __init__(self, interpreter):
super().__init__(interpreter)
self.db_file = "sifzz_db.json"
self.load_db()
def load_db(self):
"""Load database from file"""
if os.path.exists(self.db_file):
with open(self.db_file, 'r') as f:
self.db = json.load(f)
else:
self.db = {}
def save_db(self):
"""Save database to file"""
with open(self.db_file, 'w') as f:
json.dump(self.db, f, indent=2)
def register_commands(self):
self.register(
r'save "([^"]+)" as (\w+) in database',
self.db_save,
"Save value to database with key"
)
self.register(
r'load (\w+) from database and store in (\w+)',
self.db_load,
"Load value from database by key"
)
self.register(
r'delete (\w+) from database',
self.db_delete,
"Delete key from database"
)
def db_save(self, match):
value = match.group(1)
key = match.group(2)
self.db[key] = value
self.save_db()
def db_load(self, match):
key = match.group(1)
var_name = match.group(2)
self.interpreter.variables[var_name] = self.db.get(key, None)
def db_delete(self, match):
key = match.group(1)
if key in self.db:
del self.db[key]
self.save_db()
Usage:
save "John Doe" as username in database
load username from database and store in name
say name # John Doe
Use natural, readable command syntax:
β Good:
r'download "([^"]+)" as "([^"]+)"'
# Usage: download "http://example.com" as "file.zip"
β Bad:
r'dl ([^ ]+) ([^ ]+)'
# Usage: dl http://example.com file.zip
Always handle errors gracefully:
def my_handler(self, match):
try:
# Your logic here
pass
except FileNotFoundError:
print("Error: File not found")
except Exception as e:
print(f"Error: {e}")
Check for optional dependencies:
def __init__(self, interpreter):
super().__init__(interpreter)
try:
import requests
self.requests = requests
self.available = True
except ImportError:
print("[WARNING] Module requires 'requests'")
print(" Install with: pip install requests")
self.available = False
Document your module well:
"""
My Awesome Module
This module provides:
- command1: Does something cool
- command2: Does something else
Dependencies:
- requests (pip install requests)
"""
snake_case.pyPascalCasesnake_casenatural language syntaxCreate test scripts for your module:
# test_mymodule.sfzz
say "Testing My Module"
# Test command 1
my command "test"
# Test command 2
another command with params
say "Tests complete!"
These are built into the interpreter:
set, increase, decrease)say, write)ask)create list, add to, remove from)if, loop, repeat, for each)function, call)add, subtract, multiply, divide)Place these in modules/:
file_operations.py - File I/Oweb_operations.py - HTTP requestsjson_module.py - JSON parsingdatetime_module.py - Date/time operationsdatabase_module.py - Simple databasemodules/"""
Module Name
Brief description
Commands:
- command1: Description
- command2: Description
Dependencies:
- library1 (optional)
"""
import sys
import os
# Add the parent directory to sys.path so we can import sifzz
parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if parent_dir not in sys.path:
sys.path.insert(0, parent_dir)
# Now import SifzzModule
try:
from sifzz import SifzzModule
except ImportError as e:
print(f"[ERROR] Could not import SifzzModule: {e}")
print(f"[ERROR] sys.path: {sys.path}")
raise
class MyModule(SifzzModule):
"""Module description"""
def register_commands(self):
"""Register all commands"""
self.register(
r'pattern here',
self.handler_method,
"Description"
)
def handler_method(self, match):
"""Handle the command"""
# Implementation here
pass
Modules can maintain their own state:
class StatefulModule(SifzzModule):
def __init__(self, interpreter):
super().__init__(interpreter)
self.counter = 0
self.cache = {}
def increment_counter(self, match):
self.counter += 1
print(f"Counter: {self.counter}")
Access other modules through the interpreter:
# In one module
self.interpreter.variables['shared_data'] = data
# In another module
data = self.interpreter.variables.get('shared_data')
Add custom expression evaluators:
def register_commands(self):
# This would require core interpreter modifications
# But you can work with eval_expression
pass
Q: Can modules modify the core interpreter?
A: Yes, through self.interpreter, but be careful. It will make code reviews take longer!
Q: Can modules depend on other modules?
A: Not directly, but they can share data through variables.
Q: How do I debug my module?
A: Use print statements and test with simple .sfzz scripts.
Q: Can I distribute my module?
A: Yes! Share your .py file and documentation.
Q: What if my regex pattern conflicts with core commands?
A: Core commands are checked first. Use specific patterns.
Added in v0.3, PackageAPI is a way for you to use pip packages easily and have them be automatically installed with the module. To do this, just add this to your from sifzz import SifzzModule like so:
from sifzz import SifzzModule, PackageAPI
Then you can automatically install the package with
PackageAPI.getDependency('PACKAGE_NAME')
Then import it after
import PACKAGE_NAME
And you have sucessfully used PackageAPI
For debugging, we require you to use the DEBUG_MODE global. You can do this by adding it to your from sifzz import SifzzModule line like so:
from sifzz import SifzzModule, DEBUG_MODE
Now do:
if DEBUG_MODE:
print('[DEBUG] Debug message') # The [DEBUG] tag is required
To enable debug mode when running, run with -d (or use the launcher).
Happy Module Development! π