prax-schema 0.6.5

Schema parser and AST for the Prax ORM
Documentation
// Prax Schema Definition Language Grammar
// =========================================
// A Pest grammar for parsing .prax schema files

// Main entry point
schema = {
    SOI ~
    (documentation | datasource_def | generator_def | model_def | enum_def | type_def | view_def | server_group_def | policy_def | raw_sql_def | NEWLINE)* ~
    EOI
}

// ============================================================================
// DATASOURCE DEFINITION
// ============================================================================

// Datasource block: datasource db { provider = "postgresql" extensions = [vector, pg_trgm] }
datasource_def = {
    "datasource" ~ identifier ~ "{" ~ NEWLINE* ~
    (datasource_property ~ NEWLINE*)* ~
    "}"
}

// Datasource property: key = value
datasource_property = {
    identifier ~ "=" ~ datasource_value
}

// Datasource value types
datasource_value = {
    env_function |
    extension_array |
    string_literal |
    identifier
}

// Environment function: env("DATABASE_URL")
env_function = {
    "env" ~ "(" ~ string_literal ~ ")"
}

// Extension array: [vector, pg_trgm, postgis]
extension_array = {
    "[" ~ (extension_item ~ ("," ~ extension_item)*)? ~ "]"
}

// Extension item: can be identifier or function call for schema/version
extension_item = {
    identifier ~ extension_args? |
    identifier
}

// Extension arguments: (schema: "public", version: "0.5.0")
extension_args = {
    "(" ~ (extension_arg ~ ("," ~ extension_arg)*)? ~ ")"
}

// Extension argument: schema: "public"
extension_arg = {
    identifier ~ ":" ~ string_literal
}

// ============================================================================
// GENERATOR DEFINITION (for code generation configuration)
// ============================================================================

generator_def = {
    "generator" ~ identifier ~ "{" ~ NEWLINE* ~
    (datasource_property ~ NEWLINE*)* ~
    "}"
}

// ============================================================================
// WHITESPACE AND COMMENTS
// ============================================================================

WHITESPACE = _{ " " | "\t" }
NEWLINE = _{ "\r\n" | "\n" | "\r" }
COMMENT = _{ "//" ~ (!NEWLINE ~ ANY)* }

// Documentation comments (triple slash)
documentation = { (doc_line ~ NEWLINE?)+ }
doc_line = @{ "///" ~ (!NEWLINE ~ ANY)* }

// ============================================================================
// LITERALS
// ============================================================================

// String literal (double quotes)
string_literal = @{ "\"" ~ string_content ~ "\"" }
string_content = @{ (!"\"" ~ ANY)* }

// Multi-line string (triple quotes)
multiline_string = @{ "\"\"\"" ~ multiline_content ~ "\"\"\"" }
multiline_content = @{ (!"\"\"\"" ~ ANY)* }

// Number literals
number_literal = @{ "-"? ~ ASCII_DIGIT+ ~ ("." ~ ASCII_DIGIT+)? }

// Boolean literals
boolean_literal = @{ "true" | "false" }

// ============================================================================
// IDENTIFIERS AND TYPES
// ============================================================================

// Basic identifier (PascalCase or snake_case)
identifier = @{ ASCII_ALPHA ~ (ASCII_ALPHANUMERIC | "_")* }

// Type name (same as identifier but semantic)
type_name = @{ ASCII_ALPHA ~ (ASCII_ALPHANUMERIC | "_")* }

// Field type with optional modifiers
field_type = {
    type_name ~ list_marker? ~ optional_marker? |
    type_name ~ optional_marker? ~ list_marker?
}

optional_marker = { "?" }
list_marker = { "[]" }

// ============================================================================
// ATTRIBUTES
// ============================================================================

// Field-level attribute: @name or @name(args)
field_attribute = {
    "@" ~ attribute_name ~ attribute_args?
}

// Model-level attribute: @@name or @@name(args)
model_attribute = {
    "@@" ~ attribute_name ~ attribute_args?
}

// Attribute name (can include dots for namespacing like @db.VarChar)
attribute_name = @{ identifier ~ ("." ~ identifier)? }

// Attribute arguments
attribute_args = {
    "(" ~ (attribute_arg ~ ("," ~ attribute_arg)*)? ~ ")"
}

// Single attribute argument (named or positional)
attribute_arg = {
    identifier ~ ":" ~ attribute_value |
    attribute_value
}

// Attribute value types
attribute_value = {
    function_call |
    field_ref_list |
    array_literal |
    string_literal |
    number_literal |
    boolean_literal |
    identifier
}

// Function call: now(), uuid(), etc.
function_call = {
    identifier ~ "(" ~ (attribute_value ~ ("," ~ attribute_value)*)? ~ ")"
}

// Field reference list: [field1, field2]
field_ref_list = {
    "[" ~ identifier ~ ("," ~ identifier)* ~ "]"
}

// Array literal: [value1, value2]
array_literal = {
    "[" ~ (attribute_value ~ ("," ~ attribute_value)*)? ~ "]"
}

// ============================================================================
// MODEL DEFINITION
// ============================================================================

model_def = {
    "model" ~ identifier ~ "{" ~ NEWLINE* ~
    (model_body_item ~ NEWLINE*)* ~
    "}"
}

model_body_item = {
    field_def |
    model_attribute
}

// Field definition: name Type @attr1 @attr2
field_def = {
    identifier ~ field_type ~ field_attribute*
}

// ============================================================================
// ENUM DEFINITION
// ============================================================================

enum_def = {
    "enum" ~ identifier ~ "{" ~ NEWLINE* ~
    (enum_body_item ~ NEWLINE*)* ~
    "}"
}

enum_body_item = {
    enum_variant |
    model_attribute
}

// Enum variant: Name or Name @map("value")
enum_variant = {
    identifier ~ field_attribute*
}

// ============================================================================
// COMPOSITE TYPE DEFINITION
// ============================================================================

type_def = {
    "type" ~ identifier ~ "{" ~ NEWLINE* ~
    (field_def ~ NEWLINE*)* ~
    "}"
}

// ============================================================================
// VIEW DEFINITION
// ============================================================================

view_def = {
    "view" ~ identifier ~ "{" ~ NEWLINE* ~
    (model_body_item ~ NEWLINE*)* ~
    "}"
}

// ============================================================================
// SERVER GROUP DEFINITION
// ============================================================================

// Server group for multi-server configurations (replicas, shards, regions)
server_group_def = {
    "serverGroup" ~ identifier ~ "{" ~ NEWLINE* ~
    (server_group_item ~ NEWLINE*)* ~
    "}"
}

server_group_item = {
    server_def |
    model_attribute
}

// Individual server definition within a group
server_def = {
    "server" ~ identifier ~ "{" ~ NEWLINE* ~
    (server_property ~ NEWLINE*)* ~
    "}"
}

// Server property: key = value
server_property = {
    identifier ~ "=" ~ attribute_value
}

// ============================================================================
// RAW SQL DEFINITION
// ============================================================================

raw_sql_def = {
    "@@sql" ~ "(" ~ string_literal ~ "," ~ multiline_string ~ ")"
}

// ============================================================================
// POLICY DEFINITION (PostgreSQL Row-Level Security)
// ============================================================================

// Policy block: policy PolicyName on ModelName { ... }
policy_def = {
    "policy" ~ identifier ~ "on" ~ identifier ~ "{" ~ NEWLINE* ~
    (policy_item ~ NEWLINE*)* ~
    "}"
}

// Policy item: one of the policy properties
policy_item = {
    policy_for |
    policy_to |
    policy_as |
    policy_using |
    policy_check |
    policy_mssql_schema |
    policy_mssql_block
}

// FOR clause: for SELECT | for [SELECT, UPDATE, DELETE]
policy_for = {
    "for" ~ (policy_command_list | policy_command)
}

// Single command: SELECT, INSERT, UPDATE, DELETE, ALL
policy_command = @{
    "ALL" | "All" | "all" |
    "SELECT" | "Select" | "select" |
    "INSERT" | "Insert" | "insert" |
    "UPDATE" | "Update" | "update" |
    "DELETE" | "Delete" | "delete"
}

// Command list: [SELECT, UPDATE, DELETE]
policy_command_list = {
    "[" ~ policy_command ~ ("," ~ policy_command)* ~ "]"
}

// TO clause: to authenticated | to [authenticated, admin]
policy_to = {
    "to" ~ (policy_role_list | identifier)
}

// Role list: [authenticated, admin]
policy_role_list = {
    "[" ~ identifier ~ ("," ~ identifier)* ~ "]"
}

// AS clause: as PERMISSIVE | as RESTRICTIVE
policy_as = {
    "as" ~ policy_type
}

// Policy type: PERMISSIVE or RESTRICTIVE
policy_type = @{
    "PERMISSIVE" | "Permissive" | "permissive" |
    "RESTRICTIVE" | "Restrictive" | "restrictive"
}

// USING clause: using "expression"
policy_using = {
    "using" ~ (multiline_string | string_literal)
}

// CHECK clause: check "expression"
policy_check = {
    "check" ~ (multiline_string | string_literal)
}

// MSSQL schema clause: mssqlSchema "Security"
policy_mssql_schema = {
    "mssqlSchema" ~ string_literal
}

// MSSQL block operations clause: mssqlBlock [AFTER_INSERT, BEFORE_DELETE]
policy_mssql_block = {
    "mssqlBlock" ~ (mssql_block_op_list | mssql_block_op)
}

// Single MSSQL block operation
mssql_block_op = @{
    "AFTER_INSERT" | "AfterInsert" | "after_insert" |
    "AFTER_UPDATE" | "AfterUpdate" | "after_update" |
    "BEFORE_UPDATE" | "BeforeUpdate" | "before_update" |
    "BEFORE_DELETE" | "BeforeDelete" | "before_delete"
}

// MSSQL block operation list: [AFTER_INSERT, BEFORE_DELETE]
mssql_block_op_list = {
    "[" ~ mssql_block_op ~ ("," ~ mssql_block_op)* ~ "]"
}