use crate::datum::Prefix;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DelimRole {
List,
Vector,
Map,
Ordinary,
}
impl DelimRole {
#[must_use]
pub fn is_delimiter(self) -> bool {
self != DelimRole::Ordinary
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BlockComment {
pub open: &'static str,
pub close: &'static str,
pub nestable: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CharSyntax {
HashBackslash,
Backslash,
Question,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum HashParen {
Vector,
HashFn,
None,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum HashBracket {
CharSet,
BracketString,
None,
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub struct CharRoles {
pub quote: Option<char>,
pub quasiquote: Option<char>,
pub unquote: Option<char>,
pub splicing_suffix: char,
pub deref: Option<char>,
pub meta: Option<char>,
pub splice: Option<char>,
pub mutable: Option<char>,
pub short_fn: Option<char>,
}
impl CharRoles {
pub fn scheme() -> Self {
CharRoles {
quote: Some('\''),
quasiquote: Some('`'),
unquote: Some(','),
splicing_suffix: '@',
deref: None,
meta: None,
splice: None,
mutable: None,
short_fn: None,
}
}
pub fn clojure() -> Self {
CharRoles {
unquote: Some('~'),
deref: Some('@'),
meta: Some('^'),
..CharRoles::scheme()
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Dialect {
Scheme,
Guile,
Racket,
Gauche,
Mosh,
Gambit,
SchemeSuperset,
Clojure,
Phel,
Edn,
CommonLisp,
EmacsLisp,
Islisp,
AutoLisp,
Janet,
Hy,
Fennel,
Lfe,
}
impl Dialect {
pub const ALL: &'static [Dialect] = &[
Dialect::Scheme,
Dialect::Guile,
Dialect::Racket,
Dialect::Gauche,
Dialect::Mosh,
Dialect::Gambit,
Dialect::SchemeSuperset,
Dialect::Clojure,
Dialect::Phel,
Dialect::Edn,
Dialect::CommonLisp,
Dialect::EmacsLisp,
Dialect::Islisp,
Dialect::AutoLisp,
Dialect::Janet,
Dialect::Hy,
Dialect::Fennel,
Dialect::Lfe,
];
#[must_use]
pub fn options(&self) -> Options {
Options::for_dialect(*self)
}
}
impl std::fmt::Display for Dialect {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let name = match self {
Dialect::Scheme => "scheme",
Dialect::Guile => "guile",
Dialect::Racket => "racket",
Dialect::Gauche => "gauche",
Dialect::Mosh => "mosh",
Dialect::Gambit => "gambit",
Dialect::SchemeSuperset => "scheme-superset",
Dialect::Clojure => "clojure",
Dialect::Phel => "phel",
Dialect::Edn => "edn",
Dialect::CommonLisp => "common-lisp",
Dialect::EmacsLisp => "emacs-lisp",
Dialect::Islisp => "islisp",
Dialect::AutoLisp => "autolisp",
Dialect::Janet => "janet",
Dialect::Hy => "hy",
Dialect::Fennel => "fennel",
Dialect::Lfe => "lfe",
};
f.write_str(name)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParseDialectError {
input: String,
}
impl std::fmt::Display for ParseDialectError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "unknown dialect: `{}`", self.input)
}
}
impl std::error::Error for ParseDialectError {}
impl std::str::FromStr for Dialect {
type Err = ParseDialectError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Dialect::ALL
.iter()
.copied()
.find(|d| d.to_string() == s)
.ok_or_else(|| ParseDialectError {
input: s.to_owned(),
})
}
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub struct Options {
pub line_comment: char,
pub comma_is_whitespace: bool,
pub block_comment: Option<BlockComment>,
pub datum_comment: bool,
pub discard_underscore: bool,
pub hash_syntax: bool,
pub square: DelimRole,
pub curly: DelimRole,
pub set_literal: bool,
pub regex_literal: bool,
pub tagged_literals: bool,
pub hash_apostrophe: Option<Prefix>,
pub reader_conditional: bool,
pub feature_conditional: bool,
pub read_eval: bool,
pub symbol_escape: bool,
pub booleans: bool,
pub char_syntax: Option<CharSyntax>,
pub hash_paren: HashParen,
pub keyword_colon: bool,
pub hash_keyword: bool,
pub lang_line: bool,
pub shebang_line: bool,
pub piped_symbols: bool,
pub datum_labels: bool,
pub dotted_pairs: bool,
pub roles: CharRoles,
pub long_string_backtick: bool,
pub hash_bracket: HashBracket,
pub regex_slash: bool,
pub bytevector_vu8: bool,
pub keyword_trailing_colon: bool,
pub fold_longhand: bool,
pub fold_case_insensitive: bool,
pub hash_curly_symbol: bool,
pub dotted_pairs_infix: bool,
}
impl Options {
#[must_use]
pub fn scheme() -> Self {
Options {
line_comment: ';',
comma_is_whitespace: false,
block_comment: Some(BlockComment {
open: "#|",
close: "|#",
nestable: true,
}),
datum_comment: true,
discard_underscore: false,
hash_syntax: true,
square: DelimRole::List,
curly: DelimRole::Ordinary,
set_literal: false,
regex_literal: false,
tagged_literals: false,
hash_apostrophe: None,
reader_conditional: false,
feature_conditional: false,
read_eval: false,
symbol_escape: false,
booleans: true,
char_syntax: Some(CharSyntax::HashBackslash),
hash_paren: HashParen::Vector,
keyword_colon: false,
piped_symbols: true,
datum_labels: true,
dotted_pairs: true,
hash_keyword: false,
lang_line: false,
shebang_line: false,
roles: CharRoles::scheme(),
long_string_backtick: false,
hash_bracket: HashBracket::None,
regex_slash: false,
bytevector_vu8: false,
keyword_trailing_colon: false,
fold_longhand: true,
fold_case_insensitive: false,
hash_curly_symbol: false,
dotted_pairs_infix: false,
}
}
#[must_use]
pub fn clojure() -> Self {
Options {
line_comment: ';',
comma_is_whitespace: true,
block_comment: None,
datum_comment: false,
discard_underscore: true,
hash_syntax: true,
square: DelimRole::Vector,
curly: DelimRole::Map,
set_literal: true,
regex_literal: true,
tagged_literals: true,
hash_apostrophe: Some(Prefix::VarQuote),
reader_conditional: true,
feature_conditional: false,
read_eval: false,
symbol_escape: false,
booleans: false, char_syntax: Some(CharSyntax::Backslash),
hash_paren: HashParen::HashFn,
keyword_colon: true,
piped_symbols: false,
datum_labels: false,
dotted_pairs: false,
hash_keyword: false,
lang_line: false,
shebang_line: false,
roles: CharRoles::clojure(),
long_string_backtick: false,
hash_bracket: HashBracket::None,
regex_slash: false,
bytevector_vu8: false,
keyword_trailing_colon: false,
fold_longhand: false,
fold_case_insensitive: false,
hash_curly_symbol: false,
dotted_pairs_infix: false,
}
}
#[must_use]
pub fn common_lisp() -> Self {
Options {
line_comment: ';',
comma_is_whitespace: false,
block_comment: Some(BlockComment {
open: "#|",
close: "|#",
nestable: true,
}),
datum_comment: false,
discard_underscore: false,
hash_syntax: true,
square: DelimRole::Ordinary,
curly: DelimRole::Ordinary,
set_literal: false,
regex_literal: false,
tagged_literals: false,
hash_apostrophe: Some(Prefix::FunctionQuote), reader_conditional: false,
feature_conditional: true, read_eval: true, symbol_escape: true, booleans: false, char_syntax: Some(CharSyntax::HashBackslash),
hash_paren: HashParen::Vector, keyword_colon: true, piped_symbols: true, datum_labels: true, dotted_pairs: true,
hash_keyword: false,
lang_line: false,
shebang_line: false,
roles: CharRoles::scheme(),
long_string_backtick: false,
hash_bracket: HashBracket::None,
regex_slash: false,
bytevector_vu8: false,
keyword_trailing_colon: false,
fold_longhand: true,
fold_case_insensitive: true,
hash_curly_symbol: false,
dotted_pairs_infix: false,
}
}
#[must_use]
pub fn emacs_lisp() -> Self {
Options {
line_comment: ';',
comma_is_whitespace: false,
block_comment: None, datum_comment: false,
discard_underscore: false,
hash_syntax: true,
square: DelimRole::Vector, curly: DelimRole::Ordinary,
set_literal: false,
regex_literal: false,
tagged_literals: false,
hash_apostrophe: Some(Prefix::FunctionQuote), reader_conditional: false,
feature_conditional: false,
read_eval: false,
symbol_escape: true,
booleans: false, char_syntax: Some(CharSyntax::Question), hash_paren: HashParen::Vector, keyword_colon: true, piped_symbols: false,
datum_labels: true, dotted_pairs: true,
hash_keyword: false,
lang_line: false,
shebang_line: false,
roles: CharRoles::scheme(),
long_string_backtick: false,
hash_bracket: HashBracket::None,
regex_slash: false,
bytevector_vu8: false,
keyword_trailing_colon: false,
fold_longhand: true,
fold_case_insensitive: false,
hash_curly_symbol: false,
dotted_pairs_infix: false,
}
}
#[must_use]
pub fn racket() -> Self {
Options {
square: DelimRole::List,
curly: DelimRole::List, hash_apostrophe: Some(Prefix::VarQuote), symbol_escape: true,
keyword_colon: false, hash_keyword: true,
lang_line: true,
shebang_line: true,
dotted_pairs_infix: true, ..Options::scheme()
}
}
#[must_use]
pub fn janet() -> Self {
Options {
line_comment: '#',
block_comment: None,
datum_comment: false,
hash_syntax: false, square: DelimRole::List, curly: DelimRole::Map, booleans: false,
char_syntax: None,
hash_paren: HashParen::None,
keyword_colon: true,
piped_symbols: false,
datum_labels: false,
dotted_pairs: false,
roles: CharRoles {
quasiquote: Some('~'),
unquote: Some(','),
splice: Some(';'),
mutable: Some('@'), short_fn: Some('|'),
..CharRoles::scheme()
},
long_string_backtick: true, fold_longhand: false, ..Options::scheme()
}
}
#[must_use]
pub fn hy() -> Self {
Options {
block_comment: None,
datum_comment: false,
discard_underscore: true, square: DelimRole::List, curly: DelimRole::Map, set_literal: true, tagged_literals: true, booleans: false, char_syntax: None,
hash_paren: HashParen::None,
keyword_colon: true,
piped_symbols: false,
datum_labels: false,
dotted_pairs: false, roles: CharRoles {
unquote: Some('~'), ..CharRoles::scheme()
},
hash_bracket: HashBracket::BracketString, fold_longhand: false, ..Options::scheme()
}
}
#[must_use]
pub fn autolisp() -> Self {
Options {
block_comment: Some(BlockComment {
open: ";|",
close: "|;",
nestable: false,
}),
datum_comment: false,
hash_syntax: false,
square: DelimRole::Ordinary,
booleans: false, char_syntax: None,
hash_paren: HashParen::None,
piped_symbols: false,
datum_labels: false,
roles: CharRoles {
quasiquote: None, unquote: None,
..CharRoles::scheme()
},
fold_case_insensitive: true, ..Options::scheme()
}
}
#[must_use]
pub fn guile() -> Self {
Options {
hash_keyword: true, hash_apostrophe: Some(Prefix::VarQuote), hash_curly_symbol: true, ..Options::scheme()
}
}
#[must_use]
pub fn phel() -> Self {
Options::clojure()
}
#[must_use]
pub fn edn() -> Self {
Options {
hash_paren: HashParen::None, hash_apostrophe: None, reader_conditional: false, regex_literal: false, roles: CharRoles {
deref: None, quote: None, quasiquote: None, unquote: None, meta: None, ..CharRoles::clojure()
},
..Options::clojure()
}
}
#[must_use]
pub fn fennel() -> Self {
Options {
block_comment: None,
datum_comment: false,
square: DelimRole::List, curly: DelimRole::Map, booleans: false, char_syntax: None,
hash_paren: HashParen::HashFn, keyword_colon: false, piped_symbols: false,
datum_labels: false,
dotted_pairs: false, fold_longhand: false, ..Options::scheme()
}
}
#[must_use]
pub fn lfe() -> Self {
Options {
block_comment: Some(BlockComment {
open: "#|",
close: "|#",
nestable: false, }),
regex_literal: true, hash_apostrophe: Some(Prefix::FunctionQuote), booleans: false, datum_labels: false,
..Options::scheme()
}
}
#[must_use]
pub fn islisp() -> Self {
Options {
square: DelimRole::Ordinary, keyword_colon: true, hash_apostrophe: Some(Prefix::FunctionQuote), booleans: false, datum_labels: false,
fold_case_insensitive: true, ..Options::scheme()
}
}
#[must_use]
pub fn scheme_superset() -> Self {
Options {
hash_bracket: HashBracket::CharSet, regex_slash: true, regex_literal: true, bytevector_vu8: true, keyword_colon: true, keyword_trailing_colon: true, dotted_pairs_infix: true, ..Options::scheme()
}
}
#[must_use]
pub fn for_dialect(dialect: Dialect) -> Self {
match dialect {
Dialect::Scheme => Options::scheme(),
Dialect::Guile => Options::guile(),
Dialect::Racket => Options::racket(),
Dialect::Gauche | Dialect::Mosh | Dialect::Gambit | Dialect::SchemeSuperset => {
Options::scheme_superset()
}
Dialect::Clojure => Options::clojure(),
Dialect::Phel => Options::phel(),
Dialect::Edn => Options::edn(),
Dialect::CommonLisp => Options::common_lisp(),
Dialect::EmacsLisp => Options::emacs_lisp(),
Dialect::Islisp => Options::islisp(),
Dialect::AutoLisp => Options::autolisp(),
Dialect::Janet => Options::janet(),
Dialect::Hy => Options::hy(),
Dialect::Fennel => Options::fennel(),
Dialect::Lfe => Options::lfe(),
}
}
}
impl Default for Options {
fn default() -> Self {
Options::scheme()
}
}