An AI generated, agnostic prompt language for describing software logic to AI systems.
LogicScript sits between plain English (too vague) and production code (too specific). Describe your system's logic once — any AI turns it into TypeScript, Python, SQL, Java, Rust, C++ — or whatever language!
SHAPE User id : UUID required auto email : String required unique role : Enum[admin, user] default=user FUNC createUser(email, password, name) --- Creates a new user account. --- VALIDATE email matches email pattern password length >= 8 email not exists in User DO hash = bcrypt(password) user = User.create(email, hash, name) EMIT UserCreated WITH user RETURN user ON FAIL THROW ValidationError ON UserCreated TRIGGER EmailService.sendWelcome(user.email) LOG "New signup: {user.email}"
Write one LogicScript prompt. Generate TypeScript, Python, SQL, Java, Rust, or C++ from the same source. The AI handles the translation.
LogicScript describes intent, not implementation. Plain English is valid inside any block. The AI fills gaps with sensible defaults for your target stack.
Power users write prompts without programming experience. Developers use the same format to generate production-grade code with precise contracts.
Use SHAPE to describe records — fields, types, and constraints like required,
unique, and default.
Use FUNC with VALIDATE and DO blocks to describe actions, rules,
and what happens when things go wrong.
Pick any language or framework. TypeScript, Python, SQL, Java, Rust, C++. The same prompt works for all of them.
Paste your LogicScript prompt with the instruction: "Implement this in [language]." Review and deploy.
Implement the following LogicScript prompt in [TARGET LANGUAGE]. Rules: - Honor every VALIDATE condition as a precondition check. - Map ON FAIL clauses to the language's error/exception mechanism. - Translate EMIT to the appropriate event or message-bus call. - Do not add behavior not described in the prompt. [LOGICSCRIPT PROMPT] REFERENCE: https://github.com/logicscript/logicscript/blob/main/logicscript-spec.md
User Guide
A practical guide for power users and developers. No programming experience required.
LogicScript is a way of writing down the rules of a piece of software in plain, structured English — precise enough that an AI can turn it into working code, but readable enough that a non-programmer can write and understand it.
Think of it like writing a Standard Operating Procedure. You describe what information exists, what actions are allowed, what the rules are, and what happens automatically. You do not describe how the computer carries out those actions — that is the AI's job.
The key idea: Write what, not how.
LogicScript is a prompt language, not an execution runtime. It has no interpreter. Its output is always AI-generated code in a target language.
A LogicScript prompt follows a few simple rules:
SHAPE, FUNC, VALIDATE,
etc.--SHAPE Product id : UUID required auto name : String required price : Float required min=0 stock : Int required default=0 FUNC buyProduct(productId, quantity, userId) --- Lets a user purchase a product. --- VALIDATE quantity > 0 product exists with id = productId product.stock >= quantity user has valid payment method DO charge user for quantity * product.price reduce product.stock by quantity create an Order record EMIT OrderPlaced WITH order RETURN order ON FAIL THROW PurchaseError
LogicScript uses indentation (two spaces) to express nesting. No braces, brackets, or end keywords.
If a condition or step is hard to express structurally, write it out in plain English. The AI infers the most idiomatic implementation.
A SHAPE describes a type of record — its fields, types, and constraints.
| Type | Stores |
|---|---|
| String | Text of any length |
| Int | Whole number |
| Float | Decimal number |
| Bool | True or false |
| UUID | Unique identifier (auto-generated) |
| Timestamp | Date and time |
| Enum[a,b,c] | One of a fixed list of values |
| List<Type> | Ordered collection |
| Constraint | Meaning |
|---|---|
| required | Must always be present |
| optional | May be blank or null |
| unique | No two records share this value |
| auto | System-generated automatically |
| default=X | Use X when not supplied |
| min=N / max=N | Value or length limits |
| immutable | Cannot change after creation |
| indexed | Optimise lookups on this field |
A FUNC defines something the system can do. It has four optional blocks:
FUNC submitExpenseReport(reportId, userId) --- Submits a draft expense report for approval. --- VALIDATE report exists with id = reportId report.status IS draft report.ownerId IS userId report.totalAmount > 0 DO report.status = pending report.submittedAt = NOW EMIT ReportSubmitted WITH { reportId, userId } RETURN report ON FAIL LOG error, THROW SubmissionError
A FLOW is for processes with distinct named stages. Use PARALLEL to run steps
concurrently.
FLOW OnboardNewEmployee(employeeId) STEP createAccounts create email, Slack, and HR system accounts STEP scheduleOrientation find next available orientation session send calendar invite to employee and manager STEP notify EMIT EmployeeOnboarded WITH { employeeId } send welcome email to employee
Each line in a VALIDATE block is one check. If any fails, execution stops before any side
effects run.
VALIDATE email not empty email matches email pattern password length >= 8 age between 18 and 120 productId exists in Product email not exists in User startDate is before endDate cart.items not empty MESSAGE "Cart cannot be empty"
Define reusable access rules once and apply them anywhere.
GUARD AdminOnly REQUIRE user.role IS admin ON FAIL THROW ForbiddenError FUNC deleteRecord(recordId) GUARD AdminOnly DO Record.delete(recordId) ALLOW publish WHEN user.role IS admin OR user.role IS editor DENY delete WHEN document.locked IS true
POLICY RateLimit APPLIES TO all API endpoints ALLOW 100 requests PER user PER minute ON EXCEED return error "Too many requests" POLICY DataRetention APPLIES TO CustomerActivityLog KEEP 2 years THEN archive to cold storage DELETE after 7 years
QUERY overdueInvoices FROM Invoice WHERE status IS sent AND dueDate < TODAY ORDER BY dueDate ASC QUERY recentOrdersForUser(userId) FROM Order WHERE userId IS userId AND createdAt WITHIN 90 days INCLUDE items, shippingAddress ORDER BY createdAt DESC
Events decouple producers from consumers. EMIT fires an event; ON reacts to it.
EMIT OrderPlaced WITH { orderId, userId, total } ON OrderPlaced send order confirmation to user.email notify fulfillment team LOG "Order {orderId} placed" ON PaymentFailed 3 times suspend user account ALERT billing-team
STATE Invoice STATES draft, sent, paid, overdue, cancelled TRANSITION draft -> sent ON sendInvoice TRANSITION sent -> paid ON paymentReceived TRANSITION sent -> overdue ON dueDatePassed TRANSITION ANY -> cancelled ON cancelInvoice WHEN status NOT IN [paid] ON ENTER overdue send reminder to invoice.customerEmail ALERT accounts-receivable-team
SCHEDULE cleanExpiredSessions EVERY 1 hour DO deleted = Session.deleteExpired() LOG "Purged {deleted} sessions" SCHEDULE monthlyBilling AT "first day of month 00:01 UTC" DO FOR EACH active subscription charge subscriber for their plan
| Annotation | Meaning |
|---|---|
| @transaction | All steps succeed or all roll back |
| @retryable attempts=3 | Retry on failure up to N times |
| @cached ttl=5m | Cache the result for 5 minutes |
| @idempotent key="..." | Only run once per unique input |
| @rateLimit 10/minute | Limit calls per user per minute |
| @deprecated since="2.0" | Mark as replaced by a newer function |
submitExpenseReport is better than submit.
ON FAIL so the AI knows what
error contract to implement.@transaction for money. Any function that moves funds or adjusts
inventory should be atomic.REFERENCE: https://github.com/logicscript/logicscript/blob/main/logicscript-spec.md to your
prompt so the AI gets up to speed quickly.
SHAPE Name — define a data record FUNC name(inputs) — define an action FLOW Name(inputs) — multi-step process STEP stepName — one stage of a flow PARALLEL … WAIT all — concurrent steps MODULE Name — group related functions ENTRY functionName — mark a function as public
VALIDATE — pre-flight checks (stop if any fail) DO — sequential side-effect steps RETURN value — output value ON FAIL THROW Error — error handling REQUIRE x ELSE THROW — inline check inside DO IF condition … ELSE — branching FOR EACH item — iteration
GUARD Name — define a reusable access rule ALLOW action WHEN x — grant permission DENY action WHEN x — block permission EMIT Event WITH data — fire an event ON EventName — react to an event TRIGGER fn(args) — call a function ALERT team-name — notify a team
Language Specification
Complete keyword reference, type system, constraints, and formal grammar.
Groups related functions and declares the public API surface. ENTRY marks a function as
publicly callable.
MODULE ServiceName [IMPORT Symbol1, Symbol2 FROM source] [USE Symbol.method AS alias] ENTRY functionName(params)
Defines a data structure with typed, constrained fields. Types and constraints are implementation hints — the AI maps them to the most appropriate constructs in the target language.
SHAPE ShapeName fieldName : Type constraint [constraint ...]
Defines a unit of logic. All four blocks are optional, but at least DO or
RETURN must be present.
FUNC functionName(param1, param2, ...)
[--- doc comment ---]
[VALIDATE
condition ...]
[DO
step ...]
[RETURN value]
[ON FAIL strategy]
ON FAIL THROW ErrorName ON FAIL LOG error ON FAIL RETURN defaultValue ON FAIL retry N times THEN THROW ErrorName ON FAIL LOG error, THROW ErrorName
A block of precondition checks inside a FUNC. Each line is one condition, evaluated top to
bottom. The first failure stops execution and triggers the ON FAIL handler. Nothing in
DO runs if any check fails.
VALIDATE email not empty email matches email pattern password length >= 8 age between 18 and 120 price > 0 productId exists in Product email not exists in User startDate is before endDate cart.items not empty MESSAGE "Cart cannot be empty"
Plain English is valid for complex conditions the AI will infer the implementation for.
A named, multi-step operation. Use when a process has distinct named stages. PARALLEL runs
steps concurrently; WAIT all or WAIT any synchronises.
FLOW FlowName(params)
STEP stepName
logic ...
PARALLEL
STEP stepA ...
STEP stepB ...
WAIT all
GUARD GuardName REQUIRE condition [OR condition] ON FAIL THROW ErrorName
POLICY PolicyName APPLIES TO target [, target ...] ALLOW N requests PER unit PER period ON EXCEED action
QUERY queryName(params) FROM ShapeName [WHERE condition [AND condition ...]] [INCLUDE relatedShape, ...] [ORDER BY field ASC|DESC] [LIMIT N] [OFFSET N]
EMIT EventName WITH { field1, field2 }
ON EventName
TRIGGER functionName(args)
SEND MessageType TO recipient
LOG "message {variable}"
ALERT team-name
ON EventName N times -- fires after N occurrences
...
STATE ShapeName
STATES state1, state2, ...
TRANSITION a -> b ON eventName
TRANSITION ANY -> b ON eventName
WHEN status NOT IN [excludedState]
ON ENTER stateName
steps ...
ON EXIT stateName
steps ...
SCHEDULE jobName
EVERY N unit -- minutes | hours | days
DO
steps ...
SCHEDULE jobName
AT "schedule expression"
DO
steps ...
Examples: "9:00 AM daily",
"Monday 8:00 AM", "first day of month 08:00 UTC"
| Annotation | Meaning | Hint |
|---|---|---|
| @transaction | Atomic block | DB transaction + rollback on error |
| @retryable attempts=N backoff=X | Retry on failure | X: linear, exponential, or duration |
| @cached ttl=Ns key="..." | Cache result | In-memory, Redis, or equivalent |
| @idempotent key="..." | Deduplicate calls | Return stored result for same key |
| @deprecated since="X" use=fn | Mark as replaced | Add deprecation warning |
| @observable metrics=[m1,m2] | Instrument with metrics | Latency, error_rate, throughput |
| @rateLimit N/period per=field | Per-caller rate limit | Middleware or decorator |
| Type | Description |
|---|---|
| String | UTF-8 text of any length |
| Int | 64-bit signed integer |
| Float | 64-bit floating-point |
| Bool | Boolean true or false |
| UUID | RFC 4122 UUID |
| Timestamp | ISO 8601 date-time with timezone |
| List<T> | Ordered collection of T |
| Map<K,V> | Key-value map |
| Enum[a,b,c] | Closed set of named values |
| JSON | Arbitrary JSON-serializable value |
| Constraint | Applies to | Meaning |
|---|---|---|
| required | All | Must be present and non-null |
| optional | All | May be null or absent |
| unique | All | Value must be unique across all records |
| auto | UUID, Timestamp | System-generated automatically |
| default=X | All | Default when not supplied |
| min=N | Int, Float, String, List | Minimum value or length |
| max=N | Int, Float, String, List | Maximum value or length |
| pattern="regex" | String | Must match the regular expression |
| immutable | All | Cannot change after creation |
| indexed | All | Add a database index |
| Keyword | Category | Role |
|---|---|---|
| MODULE | Structure | Named service/component boundary |
| ENTRY | Structure | Public function within MODULE |
| SHAPE | Structure | Data structure definition |
| FUNC | Structure | Function definition |
| FLOW | Structure | Multi-step process |
| STEP | Structure | Named stage within a FLOW |
| PARALLEL / WAIT | Structure | Concurrent steps |
| VALIDATE | Logic | Precondition block |
| DO | Logic | Ordered steps |
| RETURN | Logic | Output value |
| ON FAIL | Logic | Error handling |
| REQUIRE … ELSE | Logic | Inline assertion in DO |
| IF / ELSE | Logic | Conditional branching |
| FOR EACH | Logic | Iteration |
| ALLOW / DENY | Access | Permission rules |
| GUARD | Access | Reusable access block |
| POLICY | Access | Cross-cutting rule |
| QUERY | Data | Named data retrieval |
| EMIT … WITH | Events | Publish a named event |
| ON [event] | Events | Subscribe to an event |
| TRIGGER | Events | Invoke from event handler |
| ALERT | Events | Notify a team or channel |
| STATE | State | State machine definition |
| TRANSITION | State | Valid state change |
| ON ENTER / ON EXIT | State | State lifecycle hooks |
| SCHEDULE | Jobs | Recurring job |
| EVERY / AT | Jobs | Job timing |
program ::= (declaration | comment)*
declaration ::= module | shape | func | flow | guard
| policy | query | on_handler | state | schedule
IDENT ::= [a-zA-Z][a-zA-Z0-9_]*
PASCAL ::= [A-Z][a-zA-Z0-9]*
CAMEL ::= [a-z][a-zA-Z0-9]*
module ::= "MODULE" PASCAL NL
(INDENT import_stmt)*
(INDENT "ENTRY" CAMEL "(" params? ")")*
shape ::= "SHAPE" PASCAL NL
(INDENT field_decl)*
field_decl ::= CAMEL ":" type constraint*
type ::= "String" | "Int" | "Float" | "Bool"
| "UUID" | "Timestamp" | "JSON"
| "List<" type ">" | "Map<" type "," type ">"
| "Enum[" CAMEL ("," CAMEL)* "]"
func ::= annotation* "FUNC" CAMEL "(" params? ")" NL
doc_comment?
validate_block?
do_block?
return_stmt?
on_fail?
NL ::= newline
INDENT ::= " " (two spaces)
Examples
Ready-to-use prompts from simple functions to complete service definitions, plus generated code in six target languages.
Implement this LogicScript prompt in Python.
FUNC greet(name) --- Returns a personalised greeting. --- VALIDATE name not empty DO message = "Hello, " + name + "." PRINT message RETURN message ON FAIL THROW ValidationError
Implement this LogicScript prompt in your preferred language or framework.
SHAPE Task id : UUID required auto title : String required max=200 done : Bool default=false createdAt : Timestamp auto FUNC addTask(title) VALIDATE title not empty DO task = Task.create(title) RETURN task FUNC completeTask(taskId) VALIDATE taskId exists in Task task.done IS false DO task.done = true RETURN task QUERY openTasks FROM Task WHERE done IS false ORDER BY createdAt ASC
Implement this LogicScript prompt in your preferred language or framework.
SHAPE ContactMessage id : UUID required auto name : String required min=2 max=100 email : String required subject : String required max=200 body : String required min=10 max=5000 category : Enum[general, sales, support, billing] default=general submittedAt : Timestamp auto FUNC submitContactForm(name, email, subject, body, category) VALIDATE name not empty email matches email pattern body length >= 10 DO message = ContactMessage.create(name, email, subject, body, category) EMIT ContactFormSubmitted WITH message ON ContactFormSubmitted send auto-reply to message.email confirming receipt forward message to the team inbox for message.category
Implement this LogicScript prompt in your preferred language or framework.
SHAPE RoomBooking id : UUID required auto roomId : UUID required userId : UUID required title : String required startTime : Timestamp required endTime : Timestamp required attendees : Int required min=1 createdAt : Timestamp auto FUNC bookRoom(roomId, userId, title, startTime, endTime, attendees) VALIDATE roomId exists in Room startTime is before endTime attendees <= room.capacity no existing booking for roomId overlaps with startTime to endTime DO booking = RoomBooking.create(roomId, userId, title, startTime, endTime, attendees) EMIT RoomBooked WITH booking ON RoomBooked send confirmation email to booking.userId add booking to the room calendar
Implement this LogicScript prompt in your preferred language or framework.
SHAPE ExpenseReport id : UUID required auto employeeId : UUID required title : String required totalAmount : Float required min=0 status : Enum[draft, submitted, approved, rejected] default=draft submittedAt : Timestamp optional createdAt : Timestamp auto FUNC submitExpenseReport(reportId, employeeId) VALIDATE reportId exists in ExpenseReport report.status IS draft report.employeeId IS employeeId report.totalAmount > 0 DO report.status = submitted report.submittedAt = NOW managerId = look up employeeId's direct manager EMIT ExpenseSubmitted WITH { reportId, employeeId, managerId } ON ExpenseSubmitted send approval request to managerId send submission confirmation to employeeId
Implement this LogicScript prompt in your preferred language or framework.
SHAPE BlogPost id : UUID required auto title : String required min=5 max=200 body : String required min=100 authorId : UUID required slug : String required unique status : Enum[draft, review, published, archived] default=draft publishedAt : Timestamp optional FUNC submitForReview(postId, authorId) VALIDATE post.status IS draft post.authorId IS authorId post.body length >= 100 DO post.status = review EMIT PostSubmittedForReview WITH post FUNC publishPost(postId, editorId) VALIDATE post.status IS review editorId has role editor OR admin DO post.status = published post.publishedAt = NOW EMIT PostPublished WITH post ON PostPublished notify post.authorId of publication update site RSS feed
Implement this LogicScript prompt in your preferred language or framework.
SHAPE Product id : UUID required auto sku : String required unique stockCount : Int required default=0 reorderAt : Int required default=10 reorderQty : Int required default=50 supplierId : UUID required FUNC recordStockMovement(productId, quantityChange, reason) VALIDATE productId exists in Product quantityChange not equals 0 new stock level would not go below 0 DO product.stockCount = product.stockCount + quantityChange LOG "Stock for {product.sku}: {quantityChange} ({reason})" IF product.stockCount <= product.reorderAt EMIT LowStock WITH { productId, currentStock: product.stockCount } ON LowStock TRIGGER PurchasingService.createPurchaseOrder(productId, supplierId, product.reorderQty) ALERT purchasing-team
Implement this LogicScript prompt in your preferred language or framework.
STATE Invoice STATES draft, sent, paid, overdue, cancelled TRANSITION draft -> sent ON sendInvoice TRANSITION sent -> paid ON markAsPaid TRANSITION sent -> overdue ON dueDatePassed TRANSITION overdue -> paid ON markAsPaid TRANSITION ANY -> cancelled ON cancelInvoice WHEN status NOT IN [paid] ON ENTER sent EMIT InvoiceSent WITH invoice ON ENTER paid EMIT InvoicePaid WITH invoice ON ENTER overdue send reminder to invoice.customerEmail ALERT accounts-receivable-team ON InvoicePaid TRIGGER AccountingService.recordPayment(invoice) send receipt to invoice.customerEmail SCHEDULE checkOverdueInvoices AT "9:00 AM daily" DO overdue = all invoices WHERE status IS sent AND dueDate < TODAY FOR EACH overdue invoice trigger dueDatePassed transition
Implement this LogicScript prompt in TypeScript (Node.js). Use Prisma for database access, bcryptjs for password hashing, and EventEmitter for events.
MODULE AuthService ENTRY login(email, password) ENTRY signup(email, password, name) ENTRY logout(sessionToken) SHAPE User id : UUID required auto email : String required unique passwordHash : String required role : Enum[admin, user, guest] default=user status : Enum[active, suspended] default=active createdAt : Timestamp auto SHAPE Session id : UUID required auto userId : UUID required indexed token : String required unique auto expiresAt : Timestamp required createdAt : Timestamp auto FUNC login(email, password) --- Authenticates a user and returns a new session. --- VALIDATE email not empty password not empty DO user = UserRepo.findByEmail(email) REQUIRE user exists ELSE THROW NotFoundError REQUIRE Crypto.verify(password, user.passwordHash) ELSE THROW AuthError REQUIRE user.status IS active ELSE THROW AccountSuspendedError session = SessionRepo.create(user.id, expiresIn=7 days) RETURN session ON FAIL LOG error, THROW AuthError FUNC signup(email, password, name) VALIDATE email matches email pattern password length >= 8 name length >= 2 email not exists in User DO hash = Crypto.hash(password) user = UserRepo.create(email, hash, name) EMIT UserCreated WITH user RETURN user FUNC logout(sessionToken) DO SessionRepo.invalidate(sessionToken) ON UserCreated TRIGGER EmailService.sendWelcome(user.email, user.name) POLICY LoginProtection APPLIES TO login ALLOW 5 attempts PER ip PER 15 minutes ON EXCEED lockout for 30 minutes, ALERT security-team SCHEDULE sessionCleanup EVERY 6 hours DO deleted = SessionRepo.deleteExpired() LOG "Purged {deleted} expired sessions"
Implement this LogicScript prompt in your preferred language or framework.
SCHEDULE monthlyBilling AT "first day of month 00:01 UTC" DO subscriptions = all Subscriptions WHERE status IS active AND nextBillingDate <= TODAY FOR EACH subscription charge subscription.customerId for subscription.pricePerMonth IF charge succeeded send receipt to subscription.billingEmail IF charge failed EMIT BillingFailed WITH { subscription, reason } ON BillingFailed send payment failure notice to subscription.billingEmail retry the charge after 3 days IF charge fails 3 times in a row set subscription.status = paused ALERT billing-team
JavaScript output targets Node.js with ES modules and async/await.
SHAPE becomes a JSDoc-annotated object type, ON FAIL THROW becomes a named
Error subclass, and EMIT maps to an EventEmitter call.
| LogicScript | JavaScript (Node.js) |
|---|---|
| SHAPE | JSDoc @typedef or TypeScript interface |
| FUNC | async function export |
| VALIDATE | Guard if statements before any side effects |
| ON FAIL THROW | Named class extending Error |
| EMIT … WITH | eventBus.emit('EventName', payload) |
| PARALLEL + WAIT all | await Promise.all([...]) |
| @transaction | db.$transaction() callback |
FUNC createUser(email, password, name) VALIDATE email matches email pattern password length >= 8 name length >= 2 email not exists in User DO hash = bcrypt(password) user = User.create(email, hash, name) EMIT UserCreated WITH user RETURN user ON FAIL THROW ValidationError
// Generated from LogicScript — createUser
import bcrypt from 'bcryptjs';
import { v4 as uuidv4 } from 'uuid';
import { db } from './db.js';
import { eventBus } from './events.js';
export class ValidationError extends Error {
constructor(message) {
super(message);
this.name = 'ValidationError';
this.statusCode = 422;
}
}
const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
/** @throws {ValidationError} */
export async function createUser(email, password, name) {
// VALIDATE
if (!EMAIL_RE.test(email))
throw new ValidationError('Invalid email format');
if (password.length < 8)
throw new ValidationError('Password must be at least 8 characters');
if (name.length < 2)
throw new ValidationError('Name must be at least 2 characters');
const existing = await db.user.findUnique({ where: { email } });
if (existing)
throw new ValidationError('Email is already registered');
// DO
const hash = await bcrypt.hash(password, 12);
const user = await db.user.create({
data: { id: uuidv4(), email, name, passwordHash: hash,
role: 'user', createdAt: new Date() },
});
// EMIT UserCreated
eventBus.emit('UserCreated', user);
return user;
}
// PARALLEL + WAIT all → Promise.all
export async function generateDashboard(userId) {
const [stats, activity, notifications] = await Promise.all([
Analytics.getUserStats(userId),
ActivityLog.recent(userId, { limit: 20 }),
Inbox.unread(userId),
]);
return { stats, activity, notifications };
}
Python 3.11+ output. SHAPE becomes a @dataclass, Enum fields become
enum.Enum, and ON FAIL THROW becomes a custom exception. Async functions use
asyncio.
| LogicScript | Python |
|---|---|
| SHAPE | @dataclass with typed fields |
| FUNC | def or async def with type hints |
| VALIDATE | if … raise guards before side effects |
| ON FAIL THROW | Custom exception extending Exception |
| EMIT … WITH | event_bus.emit('event_name', payload) |
| PARALLEL + WAIT all | await asyncio.gather(...) |
| @transaction | with db.begin(): context manager |
import re, uuid, bcrypt
from dataclasses import dataclass, field
from datetime import datetime, timezone
from enum import Enum
class Role(Enum):
ADMIN = "admin"
USER = "user"
GUEST = "guest"
@dataclass
class User:
id: str = field(default_factory=lambda: str(uuid.uuid4()))
email: str = ""
name: str = ""
password_hash: str = ""
role: Role = Role.USER
created_at: datetime = field(
default_factory=lambda: datetime.now(timezone.utc))
class ValidationError(Exception):
def __init__(self, message: str):
super().__init__(message)
self.status_code = 422
EMAIL_RE = re.compile(r"^[^\s@]+@[^\s@]+\.[^\s@]+$")
_users: dict[str, User] = {}
def create_user(email: str, password: str, name: str) -> User:
"""Creates a new user account. Raises ValidationError on failure."""
if not EMAIL_RE.match(email):
raise ValidationError("Invalid email format")
if len(password) < 8:
raise ValidationError("Password must be at least 8 characters")
if len(name) < 2:
raise ValidationError("Name must be at least 2 characters")
if any(u.email == email for u in _users.values()):
raise ValidationError("Email is already registered")
hashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode()
user = User(email=email, password_hash=hashed, name=name)
_users[user.id] = user
event_bus.emit("UserCreated", user)
return user
import asyncio
async def generate_dashboard(user_id: str) -> dict:
# PARALLEL + WAIT all → asyncio.gather
stats, activity, notifications = await asyncio.gather(
analytics.get_user_stats(user_id),
activity_log.recent(user_id, limit=20),
inbox.unread(user_id),
)
return {"stats": stats, "activity": activity, "notifications": notifications}
def transfer_funds(from_id: str, to_id: str, amount: float, db, event_bus) -> dict:
if amount <= 0:
raise TransferError("Amount must be positive")
if from_id == to_id:
raise TransferError("Cannot transfer to the same account")
from_acct = db.query(Account).filter_by(id=from_id).one()
if from_acct.balance < amount:
raise TransferError("Insufficient funds")
try:
with db.begin(): # @transaction → SQLAlchemy context manager
from_acct.balance -= amount
to_acct = db.query(Account).filter_by(id=to_id).one()
to_acct.balance += amount
except Exception as exc:
raise TransferError(f"Transaction failed: {exc}") from exc
event_bus.emit("FundsTransferred", {"from": from_id, "to": to_id, "amount": amount})
return {"success": True}
SQL output covers schema DDL from SHAPE declarations and stored functions/procedures from
FUNC blocks. All examples target PostgreSQL 15+.
-- Generated from LogicScript SHAPE User
CREATE TYPE user_role AS ENUM ('admin', 'user', 'guest');
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email TEXT NOT NULL UNIQUE,
name VARCHAR(100) NOT NULL CHECK (length(name) >= 2),
role user_role NOT NULL DEFAULT 'user',
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX idx_users_email ON users (email);
CREATE INDEX idx_users_role ON users (role);
CREATE EXTENSION IF NOT EXISTS pgcrypto;
CREATE OR REPLACE FUNCTION create_user(
p_email TEXT,
p_password TEXT,
p_name TEXT
) RETURNS users LANGUAGE plpgsql AS $$
DECLARE
v_hash TEXT; v_user users;
BEGIN
IF p_email !~ '^[^\s@]+@[^\s@]+\.[^\s@]+$' THEN
RAISE EXCEPTION 'ValidationError: invalid email'; END IF;
IF length(p_password) < 8 THEN
RAISE EXCEPTION 'ValidationError: password too short'; END IF;
IF length(p_name) < 2 THEN
RAISE EXCEPTION 'ValidationError: name too short'; END IF;
IF EXISTS(SELECT 1 FROM users WHERE email = p_email) THEN
RAISE EXCEPTION 'ValidationError: email taken'; END IF;
v_hash := crypt(p_password, gen_salt('bf', 12));
INSERT INTO users(email, password_hash, name)
VALUES(p_email, v_hash, p_name) RETURNING * INTO v_user;
PERFORM pg_notify('user_created', row_to_json(v_user)::text);
RETURN v_user;
END; $$;
CREATE OR REPLACE PROCEDURE transfer_funds(
p_from_id UUID, p_to_id UUID, p_amount NUMERIC(18,2)
) LANGUAGE plpgsql AS $$
DECLARE v_balance NUMERIC(18,2);
BEGIN
IF p_amount <= 0 THEN
RAISE EXCEPTION 'TransferError: amount must be positive'; END IF;
IF p_from_id = p_to_id THEN
RAISE EXCEPTION 'TransferError: same account'; END IF;
SELECT balance INTO v_balance FROM accounts WHERE id = p_from_id FOR UPDATE;
IF v_balance < p_amount THEN
RAISE EXCEPTION 'TransferError: insufficient funds'; END IF;
UPDATE accounts SET balance = balance - p_amount WHERE id = p_from_id;
UPDATE accounts SET balance = balance + p_amount WHERE id = p_to_id;
PERFORM pg_notify('funds_transferred',
json_build_object('from',p_from_id,'to',p_to_id,'amount',p_amount)::text);
END; $$;
Java 21+ with Spring Boot. SHAPE becomes a record, FUNC becomes a
@Service method, and @Transactional handles @transaction. Events use
Spring's ApplicationEventPublisher.
// Generated from LogicScript — SHAPE User + FUNC createUser
public record User(UUID id, String email, String name,
String passwordHash, Role role, Instant createdAt) {
public enum Role { ADMIN, USER, GUEST }
}
public class ValidationException extends RuntimeException {
private final int statusCode;
public ValidationException(String msg) { super(msg); this.statusCode = 422; }
public int getStatusCode() { return statusCode; }
}
@Service
public class UserService {
private static final Pattern EMAIL_PATTERN =
Pattern.compile("^[^\s@]+@[^\s@]+\.[^\s@]+$");
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final ApplicationEventPublisher eventPublisher;
public User createUser(String email, String password, String name) {
// VALIDATE
if (!EMAIL_PATTERN.matcher(email).matches())
throw new ValidationException("Invalid email format");
if (password.length() < 8)
throw new ValidationException("Password must be at least 8 characters");
if (name.length() < 2)
throw new ValidationException("Name must be at least 2 characters");
if (userRepository.existsByEmail(email))
throw new ValidationException("Email already registered");
// DO: hash and persist
String hash = passwordEncoder.encode(password);
User user = new User(UUID.randomUUID(), email, name, hash,
User.Role.USER, Instant.now());
user = userRepository.save(user);
// EMIT UserCreated
eventPublisher.publishEvent(new UserCreatedEvent(this, user));
return user;
}
}
public enum OrderStatus { DRAFT, PENDING, PAID, SHIPPED, DELIVERED, CANCELLED }
@Service
public class OrderStateMachine {
private record Transition(Set<OrderStatus> from, OrderStatus to) {}
private static final Map<String, Transition> TRANSITIONS = Map.of(
"submitOrder", new Transition(Set.of(DRAFT), PENDING),
"paymentReceived", new Transition(Set.of(PENDING), PAID),
"fulfillOrder", new Transition(Set.of(PAID), SHIPPED),
"deliveryConfirmed", new Transition(Set.of(SHIPPED), DELIVERED),
"cancelOrder", new Transition(Set.of(DRAFT,PENDING,PAID,SHIPPED), CANCELLED)
);
public Order transition(Order order, String event) {
Transition t = TRANSITIONS.get(event);
if (t == null) throw new IllegalArgumentException("Unknown event: " + event);
if (!t.from().contains(order.getStatus()))
throw new IllegalStateException("Invalid transition");
order.setStatus(t.to());
order = orderRepository.save(order);
if (t.to() == SHIPPED) {
eventPublisher.publishEvent(new OrderShippedEvent(this, order));
notificationService.notifyUser(order.getUserId(), order.getTrackingInfo());
}
if (t.to() == CANCELLED) refundService.refundIfPaid(order);
return order;
}
}
Rust 2021 edition with tokio and sqlx. SHAPE becomes a
struct with #[derive], errors use thiserror, and
Result<T, E> replaces ON FAIL THROW.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct User {
pub id: Uuid,
pub email: String,
pub name: String,
pub password_hash: String,
pub role: Role,
pub created_at: DateTime<Utc>,
}
#[derive(Debug, thiserror::Error)]
pub enum ValidationError {
#[error("Invalid email format")] InvalidEmail,
#[error("Password too short")] PasswordTooShort,
#[error("Name too short")] NameTooShort,
#[error("Email already registered")] EmailTaken,
#[error("Database error: {0}")]
Database(#[from] sqlx::Error),
}
static EMAIL_RE: OnceLock<Regex> = OnceLock::new();
pub async fn create_user(
pool: &PgPool, bus: &impl EventBus,
email: String, password: String, name: String,
) -> Result<User, ValidationError> {
let re = EMAIL_RE.get_or_init(|| Regex::new(r"^[^\s@]+@[^\s@]+\.[^\s@]+$").unwrap());
if !re.is_match(&email) { return Err(ValidationError::InvalidEmail); }
if password.len() < 8 { return Err(ValidationError::PasswordTooShort); }
if name.len() < 2 { return Err(ValidationError::NameTooShort); }
let exists: bool = sqlx::query_scalar(
"SELECT EXISTS(SELECT 1 FROM users WHERE email=$1)")
.bind(&email).fetch_one(pool).await?;
if exists { return Err(ValidationError::EmailTaken); }
let hash = bcrypt::hash(&password, bcrypt::DEFAULT_COST).unwrap();
let user: User = sqlx::query_as(
"INSERT INTO users(id,email,name,password_hash,role,created_at)
VALUES($1,$2,$3,$4,'user',NOW()) RETURNING *")
.bind(Uuid::new_v4()).bind(&email).bind(&name).bind(&hash)
.fetch_one(pool).await?;
bus.emit("UserCreated", &user).await;
Ok(user)
}
pub async fn transfer_funds(
pool: &PgPool, bus: &impl EventBus,
from_id: Uuid, to_id: Uuid, amount: Decimal,
) -> Result<(), TransferError> {
if amount <= Decimal::ZERO { return Err(TransferError::InvalidAmount); }
if from_id == to_id { return Err(TransferError::SameAccount); }
let mut tx = pool.begin().await?;
let balance: Decimal = sqlx::query_scalar(
"SELECT balance FROM accounts WHERE id=$1 FOR UPDATE")
.bind(from_id).fetch_one(&mut *tx).await?;
if balance < amount { tx.rollback().await?; return Err(TransferError::InsufficientFunds); }
sqlx::query("UPDATE accounts SET balance=balance-$1 WHERE id=$2")
.bind(amount).bind(from_id).execute(&mut *tx).await?;
sqlx::query("UPDATE accounts SET balance=balance+$1 WHERE id=$2")
.bind(amount).bind(to_id).execute(&mut *tx).await?;
tx.commit().await?;
bus.emit("FundsTransferred", &json!({"from":from_id,"to":to_id,"amount":amount})).await;
Ok(())
}
C++20 output using RAII, std::regex, std::async for parallel flows, and exceptions
derived from std::runtime_error.
// Generated from LogicScript — SHAPE User + FUNC createUser
enum class Role { Admin, User, Guest };
struct User {
std::string email, name, password_hash, id;
Role role{Role::User};
std::chrono::system_clock::time_point created_at;
};
class ValidationError : public std::runtime_error {
public:
explicit ValidationError(const std::string& msg)
: std::runtime_error(msg), status_code_(422) {}
int status_code() const { return status_code_; }
private:
int status_code_;
};
class UserService {
public:
UserService(Database& db, EventBus& bus)
: db_(db), bus_(bus)
, email_re_(R"(^[^\s@]+@[^\s@]+\.[^\s@]+$)", std::regex::ECMAScript) {}
User create_user(std::string_view email, std::string_view password, std::string_view name) {
if (!std::regex_match(email.begin(), email.end(), email_re_))
throw ValidationError("Invalid email format");
if (password.size() < 8u)
throw ValidationError("Password must be at least 8 characters");
if (name.size() < 2u)
throw ValidationError("Name must be at least 2 characters");
if (db_.user_exists_by_email(email))
throw ValidationError("Email already registered");
std::string hash = bcrypt::generate_hash(password, 12);
User user{.id=uuid::v4(), .email=std::string(email),
.name=std::string(name), .password_hash=std::move(hash),
.role=Role::User, .created_at=std::chrono::system_clock::now()};
user = db_.insert_user(user);
bus_.emit("UserCreated", user);
return user;
}
private:
Database& db_;
EventBus& bus_;
std::regex email_re_;
};
#include <future>
struct DashboardResult { Stats stats; std::vector<Activity> activity; std::vector<Notification> notifications; };
DashboardResult DashboardService::generate_dashboard(const std::string& user_id) {
// PARALLEL + WAIT all → std::async + .get()
auto stats_fut = std::async(std::launch::async, [&] { return analytics_.get_user_stats(user_id); });
auto activity_fut = std::async(std::launch::async, [&] { return activity_log_.recent(user_id, 20); });
auto notif_fut = std::async(std::launch::async, [&] { return inbox_.unread(user_id); });
return { stats_fut.get(), activity_fut.get(), notif_fut.get() };
}