nreplops-tool 0.3.1

Non-interactive nREPL client for shell scripting and command-line
Documentation
// clojure.pest
// Copyright 2024 Matti Hänninen
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

COMMENT = @{ comment_prefix ~ comment_char* }
comment_prefix = { ";" | "#!" }
comment_char = { ! NEWLINE ~ ANY}

WHITESPACE = @{
  ( '\u{09}'..'\u{0D}'  // whitespace control chars
  | " "
  | ","
  )+
}

//
// Top-level, forms, and expressions
//

top_level = { SOI ~ form* ~ discarded_form? ~ EOI }

form = {
    expr
  | quote_unquote_form
  | preform+ ~ form
}

quote_unquote_form = { quote_unquote_prefix ~ form }
quote_unquote_prefix = { "'" | "#'" | "`" | "~@" | "~" }

preform = { meta_form | discarded_form }

// XXX(soija) Given that the "form" reulf now takes optional preforms as prefix
//            itself, could  we drop the preform form the "discarded_form" rule?
//            FIXME: Test and drop.

discarded_form = { discard_prefix ~ preform* ~ form }
discard_prefix = { "#_" }

meta_form = { meta_prefix ~ form }
meta_prefix = { "^" | "#^" }

expr = {
    nil
  | boolean
  | number
  | char
  | string
  | regex
  | symbolic_value
  | symbol
  | keyword
  | list
  | vector
  | set
  | anonymous_fn
  | map
  | reader_conditional
  | tagged_literal
}

//
// Nils
//

nil = ${ "nil" ~ ! symbol_char }

//
// Booleans
//

boolean = ${ ( "true" | "false" ) ~ ! symbol_char }

//
// Numeric literals
//

number = ${ sign? ~ unsigned_number ~ ! ASCII_ALPHANUMERIC }
sign = { "-" | "+" }
unsigned_number = _{
    unsigned_bigfloat
  | unsigned_float
  | unsigned_ratio
  | unsigned_radix_int
  | unsigned_int
}

unsigned_bigfloat = @{ whole ~ fractional? ~ base10_exp? ~ "M" }

unsigned_float = @{ whole ~ ( fractional ~ base10_exp? | base10_exp ) }
whole = @{ ASCII_DIGIT+ }
fractional = @{ "." ~ ASCII_DIGIT* }
base10_exp = ${ ^"e" ~ sign? ~ exponent }
exponent = @{ ASCII_DIGIT+ }

unsigned_ratio = ${ numerator ~ "/" ~ denominator }
numerator = @{ ASCII_DIGIT+ }
denominator = @{ ASCII_DIGIT+ }

unsigned_radix_int = ${ radix ~ ^"r" ~ radix_digits }
radix = @{ unsigned_dec }
radix_digits = @{ ASCII_ALPHANUMERIC+ }

unsigned_int = ${
    ( unsigned_oct
    | unsigned_hex
    | unsigned_dec
    )
  ~ bigint_suffix? }
bigint_suffix = { "N" }

unsigned_hex = _{ hex_prefix ~ hex_digits }
hex_prefix = _{ ^"0x" }
hex_digits = @{ ASCII_HEX_DIGIT + }

unsigned_oct = _{ oct_prefix ~ oct_digits }
oct_prefix = _{ "0" }
oct_digits = @{ ASCII_OCT_DIGIT + }

unsigned_dec = @{ ASCII_NONZERO_DIGIT ~ ASCII_DIGIT* | "0" }

//
// Character literals
//

char = ${
    "\\"
  ~ ( char_name
    | "o" ~ char_octal
    | "u" ~ char_code_point
    | char_simple
    )
  ~ ! continuation_char
}
char_name = {
    "newline"
  | "space"
  | "tab"
  | "formfeed"
  | "backspace"
}
char_octal = @{ ASCII_OCT_DIGIT{1,3} }
char_code_point = @{ ASCII_HEX_DIGIT{4} }
char_simple = @{ ! ( '\u{00}'..'\u{1F}' | " " ) ~ ANY }

// XXX(soija) TODO: consolidate with `symbol_char` and `symbol_first_char`
continuation_char = @{
    ! ( '\u{00}'..'\u{1F}'
      | " " | ","
      | "^" | "`" | "~"
      | "(" | ")" | "[" | "]" | "{" | "}"
      | "\"" | "\\"
      )
  ~ ANY
}

//
// String literals
//

string = ${
    "\""
  ~ ( unescaped
    | esc_char
    | esc_octet
    | esc_code_point
    )*
  ~ "\""
}
unescaped = @{ unescaped_char+ }
unescaped_char = {
  ! ( '\u{00}'..'\u{08}'
    | '\u{0E}'..'\u{1F}'
    | "\u{7F}"
    | "\\"
    | "\""
    )
  ~ ANY
}
esc_char = @{ "\\" ~ ( "b" | "t" | "n" | "f" | "r" | "\"" | "\\" ) }
esc_octet = @{
  "\\" ~ ( '0'..'3' ~ '0'..'7'{2}
         | '0'..'7' {1,2} ~ & ( "\"" | "\\" )
         )
}
esc_code_point = @{ "\\u" ~ ASCII_HEX_DIGIT{4} }

//
// Regex pattern literals
//

regex = ${ "#\"" ~ regex_content ~ "\"" }
regex_content = @{
  ( "\\" ~ ! control_char ~ ANY
  | ! ( control_char | "\"" ) ~ ANY
  )*
}
control_char = {
    '\u{00}'..'\u{08}'
  | '\u{0E}'..'\u{1F}'
  | "\u{7F}"
}

//
// Symbolic values
//

symbolic_value = { symbolic_value_prefix ~ unqualified_symbol }
symbolic_value_prefix = { "##" }

//
// Symbols
//

symbol = ${
    ! "##"
  ~ ( namespace ~ "/" ~ qualified_symbol
    | unqualified_symbol
    )
}
namespace = @{
    symbol_first_char
  ~ symbol_char*
  ~ ( ":" ~ symbol_char+ )*
}
unqualified_symbol = @{
    namespace
  | "/" ~ ! ( symbol_char | "/" | ":" )
}
qualified_symbol = @{
    ( "/"* ~ ":"? ~ symbol_char+ )+
  | "/" ~ ! ":"
}
symbol_char = @{ symbol_first_char | "'" | "#" | ASCII_DIGIT }
symbol_first_char = @{
    ! ( '\u{00}'..'\u{1F}'
      | " " | ","
      | ASCII_DIGIT
      | ":" | "^" | "'" | "`" | "~" | "/" | "#"
      | "(" | ")" | "[" | "]" | "{" | "}"
      | "\"" | "\\"
      )
  ~ ANY
}

//
// Keywords
//

keyword = ${
    keyword_prefix
  ~ ( namespace ~ "/" ~ qualified_symbol
    | unqualified_keyword
    )
}
keyword_prefix = @{ "::" | ":" }
unqualified_keyword = @{
    symbol_char+ ~ ( ":" ~ symbol_char+ )*
  | "/" ~ ! ( symbol_char | "/" | ":" )
}

//
// Lists, vectors, and the like
//

list = { "(" ~ seq_body ~ ")" }
vector = { "[" ~ seq_body ~ "]" }
anonymous_fn = { "#(" ~ seq_body ~ ")"}
set = { "#{" ~ seq_body ~ "}" }

seq_body = _{ form* ~ discarded_form? }

//
// Maps
//

map = { map_qualifier? ~ unqualified_map }
map_qualifier = ${ map_qualifier_prefix ~ namespace }
map_qualifier_prefix = { "#::" | "#:" }
unqualified_map = { "{" ~ map_body ~ "}" }

reader_conditional = { reader_conditional_prefix ~ reader_conditional_body }
reader_conditional_prefix = { "#?@" | "#?" }
reader_conditional_body = { "(" ~ map_body ~ ")" }

map_body = _{ map_entry* ~ discarded_form? }
map_entry = _{ form ~ form }

//
// Tagged literals
//

tagged_literal = {
    ! "#_"
  ~ "#"
  ~ tagged_literal_tag
  ~ form
}
tagged_literal_tag = { preform* ~ symbol }