// wyre script grammar
/*
//sample wyre script
enum UserRole {
ADMIN,
USER,
GUEST
}
enum ListingStatus {
ACTIVE,
INACTIVE,
SOLD
}
// Models with optional fields and default values
model User {
id: uuid
name: string
email: string & unique
password: string
role: UserRole = UserRole.USER
bio: string | null
created_at: timestamp = now()
updated_at: timestamp | null
}
model Listing {
id: uuid
title: string
description: string | null
price: decimal
status: ListingStatus = ListingStatus.ACTIVE
created_at: timestamp = now()
updated_at: timestamp | null
created_by: one<User>
watching: many<User>
tags: many<Tag>
views: int = 0
}
model Tag {
id: uuid
name: string & unique
description: string | null
parent: one<Tag> | null
}
// Custom function with optional and default parameters
function generateSlug(title: string, separator: string = "-", maxLength: int? = 100): string {
let slug = title.toLowerCase().replace(/\s+/g, separator);
return maxLength ? slug.slice(0, maxLength) : slug;
}: js
// Services with optional and default parameters
service UserService {
#[allow(admin)]
create_user(user: User): User
#[allow(admin, owner)]
get_user(id: uuid): User
#[allow(admin, owner)]
update_user(id: uuid, user: User): User
#[allow(admin)]
delete_user(id: uuid): boolean
#[allow(all)]
search_users(query: string = "", role: UserRole = UserRole.USER, page: int = 1, limit: int = 20): many<User>
}
service AuthService {
#[allow(all)]
login(email: string, password: string): string
#[allow(authenticated)]
logout(token: string): boolean
#[allow(all)]
register(name: string, email: string, password: string, role: UserRole = UserRole.USER): User
}
service ListingService {
#[allow(authenticated)]
create_listing(listing: Listing): Listing
#[allow(owner, admin)]
update_listing(id: uuid, listing: Listing): Listing
#[allow(owner, admin)]
delete_listing(id: uuid): boolean
#[allow(all)]
get_listing(id: uuid): Listing
#[allow(all)]
search_listings(query: string = "", category: string = "", status: ListingStatus = ListingStatus.ACTIVE, page: int = 1, limit: int = 20): many<Listing>
}
service TagService {
#[allow(admin)]
create_tag(tag: Tag): Tag
#[allow(all)]
get_tag(id: uuid): Tag
#[allow(admin)]
update_tag(id: uuid, tag: Tag): Tag
#[allow(admin)]
delete_tag(id: uuid): boolean
#[allow(all)]
search_tags(query: string | null, page: int = 1, limit: int = 20): many<Tag>
}
// Hooks with explicit types
hook User.before_create(user: User) {
user.password = bcrypt.hash(user.password, 10);
user.email = user.email.toLowerCase();
}: js
hook User.before_update(user: User) {
if (user.password) {
user.password = bcrypt.hash(user.password, 10);
}
user.updated_at = new Date();
}: js
hook Listing.before_create(listing: Listing) {
listing.slug = generateSlug(listing.title);
listing.views = 0;
}: js
hook Listing.after_update(listing: Listing) {
if (listing.status === ListingStatus.SOLD) {
for (const watcher of listing.watching) {
sendNotification(watcher.id, `The listing "${listing.title}" has been sold.`);
}
}
}: js
// Realtime with optional parameter and explicit return type
realtime listen_new_listings(category: string?): Stream<Listing> {
let stream = Listing.stream().filter(listing => listing.status === ListingStatus.ACTIVE);
if (category) {
stream = stream.filter(listing => listing.tags.some(tag => tag.name === category));
}
return stream.map(listing => ({
id: listing.id,
title: listing.title,
price: listing.price,
slug: listing.slug,
views: listing.views
}));
}: js
realtime listen_user_activity(userId: uuid): Stream<User> {
return User.stream()
.filter(user => user.id === userId)
.map(user => ({
id: user.id,
name: user.name,
email: user.email,
role: user.role,
lastActive: new Date()
}));
}: js
*/
WHITESPACE = _{ " " | "\t" | "\r" | "\n" }
LINE_COMMENT = _{ "//" ~ (!"\n" ~ ANY)* ~ "\n" }
BLOCK_COMMENT = _{ "/*" ~ (!"*/" ~ ANY)* ~ "*/" }
COMMENT = _{ LINE_COMMENT | BLOCK_COMMENT }
identifier = @{ ASCII_ALPHA ~ (ASCII_ALPHANUMERIC | "_")* }
number = @{ ASCII_DIGIT+ ~ ("." ~ ASCII_DIGIT+)? }
string = @{ "\"" ~ (!"\"" ~ ANY)* ~ "\"" }
member_access = { identifier ~ ("." ~ identifier)+ }
function_call = { identifier ~ "(" ~ function_parameters? ~ ")" }
method_call = { member_access ~ "(" ~ function_parameters? ~ ")" }
medium = { ":js" | ":ts" | ":dart" }
wyre_script = { SOI ~ (COMMENT | block)* ~ EOI }
block = { enum_definition | model_definition | function_definition | service_definition | hook_definition | realtime_definition }
enum_definition = { "enum" ~ identifier ~ "{" ~ (identifier ~ ("," ~ identifier)* ~ ","?)? ~ "}" }
model_definition = { "model" ~ identifier ~ "{" ~ model_content ~ "}" }
function_definition = { "function" ~ identifier ~ "(" ~ function_parameters? ~ ")" ~ ":" ~ return_type ~ "{" ~ function_content ~ "}" ~ medium? }
service_definition = { "service" ~ identifier ~ "{" ~ service_content ~ "}" }
hook_definition = { "hook" ~ member_access ~ "(" ~ function_parameters? ~ ")" ~ "{" ~ hook_content ~ "}" ~ medium? }
realtime_definition = { "realtime" ~ identifier ~ "(" ~ function_parameters? ~ ")" ~ ":" ~ return_type ~ "{" ~ realtime_content ~ "}" ~ medium? }
model_content = { field_definition* }
service_content = { method_definition* }
hook_content = { (!("}:") ~ ANY)* }
realtime_content = { (!("}:") ~ ANY)* }
function_content = { (!("}:") ~ ANY)* }
field_definition = { annotation* ~ identifier ~ ":" ~ field_type ~ field_options? }
method_definition = { annotation* ~ identifier ~ "(" ~ function_parameters? ~ ")" ~ ":" ~ return_type }
function_parameters = { function_parameter ~ ("," ~ function_parameter)* ~ ","? }
function_parameter = { identifier ~ ":" ~ parameter_type ~ default_value? }
annotation = { "#[" ~ identifier ~ ("(" ~ annotation_parameters? ~ ")")? ~ "]" }
annotation_parameters = { identifier ~ ("," ~ identifier)* }
field_type = {
identifier ~
("<" ~ identifier ~ (("," ~ identifier)* ~ ">")?)? ~
(("&" ~ identifier) | ("|" ~ "null"))?
}
field_options = { default_value | ( and_option | or_option | null_option)* }
and_option = { "&" ~ identifier }
or_option = { "|" ~ identifier }
null_option = { "|" ~ "null" }
default_value = { "=" ~ (method_call | member_access | function_call | identifier | number | string | "null") }
parameter_type = { field_type ~ ("?")? }
return_type = { "many" ~ "<" ~ identifier ~ ">" | field_type | "Stream" ~ "<" ~ identifier ~ ">" }