ferriorm-parser 0.1.5

Schema parser for ferriorm ORM - parses .ferriorm files into AST
Documentation
// ────────────────────────────────────────────────────────────────────────
// ferriorm schema grammar (PEG)
//
// This file defines the complete grammar for .ferriorm schema files using the
// pest parser-generator syntax.  It covers:
//   - datasource and generator blocks
//   - enum definitions
//   - model definitions with fields, field types, and attributes
//   - block-level attributes (@@index, @@unique, @@map, @@id)
//   - field-level attributes (@id, @unique, @default, @relation, @map, @db.*)
//   - literals, identifiers, comments, and whitespace
//
// The grammar intentionally mirrors the Prisma schema language so that users
// familiar with Prisma can adopt ferriorm with minimal friction.
// ────────────────────────────────────────────────────────────────────────

schema = { SOI ~ (block | NEWLINE)* ~ EOI }

block = _{ datasource_block | generator_block | enum_block | model_block }

// ─── Datasource ────────────────────────────────────────────
datasource_block = { "datasource" ~ identifier ~ "{" ~ NEWLINE* ~ (kv_pair ~ NEWLINE*)* ~ "}" }

// ─── Generator ─────────────────────────────────────────────
generator_block = { "generator" ~ identifier ~ "{" ~ NEWLINE* ~ (kv_pair ~ NEWLINE*)* ~ "}" }

// ─── Key-value pairs ──────────────────────────────────────
kv_pair = { identifier ~ "=" ~ value }
value   = _{ func_call | string_literal | number_literal | boolean_literal | identifier_value }
func_call      = { identifier ~ "(" ~ (value ~ ("," ~ value)*)? ~ ")" }
identifier_value = { identifier }

// ─── Enum ──────────────────────────────────────────────────
enum_block   = { "enum" ~ identifier ~ "{" ~ NEWLINE* ~ (enum_variant ~ NEWLINE*)* ~ "}" }
enum_variant = { identifier }

// ─── Model ─────────────────────────────────────────────────
model_block = { "model" ~ identifier ~ "{" ~ NEWLINE* ~ (model_member ~ NEWLINE*)* ~ "}" }
model_member = _{ block_attribute | field_def }

// ─── Field ─────────────────────────────────────────────────
field_def  = { identifier ~ field_type ~ field_attribute* }
field_type = { identifier ~ (list_modifier | optional_modifier)? }
list_modifier     = { "[]" }
optional_modifier = { "?" }

// ─── Field attributes ─────────────────────────────────────
field_attribute = _{ attr_id | attr_unique | attr_updated_at | attr_default | attr_relation | attr_map | attr_db_type }

attr_id         = { "@id" }
attr_unique     = { "@unique" }
attr_updated_at = { "@updatedAt" }
attr_default    = { "@default" ~ "(" ~ default_value ~ ")" }
attr_relation   = { "@relation" ~ "(" ~ relation_args ~ ")" }
attr_map        = { "@map" ~ "(" ~ string_literal ~ ")" }
attr_db_type    = { "@db." ~ identifier ~ ("(" ~ (value ~ ("," ~ value)*)? ~ ")")? }

// ─── Default values ───────────────────────────────────────
default_value = _{ func_call | string_literal | number_literal | boolean_literal | identifier_value }

// ─── Relation args ────────────────────────────────────────
relation_args = { relation_arg ~ ("," ~ relation_arg)* }
relation_arg  = { named_arg }
named_arg     = { identifier ~ ":" ~ (field_list | value) }
field_list    = { "[" ~ identifier ~ ("," ~ identifier)* ~ "]" }

// ─── Block attributes ─────────────────────────────────────
block_attribute = _{ block_attr_index | block_attr_unique | block_attr_map | block_attr_id }

block_attr_index  = { "@@index" ~ "(" ~ field_list ~ ")" }
block_attr_unique = { "@@unique" ~ "(" ~ field_list ~ ")" }
block_attr_map    = { "@@map" ~ "(" ~ string_literal ~ ")" }
block_attr_id     = { "@@id" ~ "(" ~ field_list ~ ")" }

// ─── Terminals ─────────────────────────────────────────────
identifier     = @{ ASCII_ALPHA ~ (ASCII_ALPHANUMERIC | "_")* }
string_literal = @{ "\"" ~ string_inner ~ "\"" }
string_inner   = @{ (!"\"" ~ !"\\" ~ ANY | "\\" ~ ANY)* }
number_literal = @{ "-"? ~ ASCII_DIGIT+ ~ ("." ~ ASCII_DIGIT+)? }
boolean_literal = { "true" | "false" }

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