use std::collections::HashMap;
use crate::datum::{Datum, DatumKind, Delim, Prefix};
use crate::options::{Dialect, Options};
use crate::reader::parse;
use crate::walk::code_nodes;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Role {
Keyword,
Name,
Qualifier,
DispatchValue,
SpecializedArglist,
Arglist,
Docstring,
Declare,
Interactive,
Body,
Other,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Category {
Function,
Macro,
Variable,
Constant,
Class,
Struct,
Generic,
Method,
Type,
Test,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Confidence {
Builtin,
Declared,
Inferred,
Weak,
Consumer,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Dispatch {
Qualifiers,
Value,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Docstring {
None,
Leading,
LeadingOrLone,
}
#[derive(Debug, Clone, PartialEq)]
pub struct FormSpec {
pub head: String,
pub leading: Vec<Role>,
pub docstring: Docstring,
pub body: bool,
pub category: Option<Category>,
pub dispatch: Option<Dispatch>,
pub confidence: Confidence,
}
impl FormSpec {
fn new(
head: impl Into<String>,
leading: Vec<Role>,
docstring: Docstring,
body: bool,
confidence: Confidence,
) -> Self {
FormSpec {
head: head.into(),
leading,
docstring,
body,
category: None,
dispatch: None,
confidence,
}
}
pub fn define(
head: impl Into<String>,
leading: Vec<Role>,
docstring: Docstring,
body: bool,
) -> Self {
FormSpec::new(head, leading, docstring, body, Confidence::Consumer)
}
pub fn with_category(mut self, category: Category) -> Self {
self.category = Some(category);
self
}
pub fn with_dispatch(mut self, dispatch: Dispatch) -> Self {
self.dispatch = Some(dispatch);
self
}
}
#[derive(Debug, Clone, Default)]
pub struct Registry {
specs: HashMap<String, FormSpec>,
}
impl Registry {
pub fn new() -> Self {
Registry::default()
}
pub fn insert(&mut self, spec: FormSpec) {
self.specs.insert(spec.head.clone(), spec);
}
pub fn get(&self, head: &str) -> Option<&FormSpec> {
self.specs.get(head)
}
pub fn remove(&mut self, head: &str) -> Option<FormSpec> {
self.specs.remove(head)
}
pub fn iter(&self) -> impl Iterator<Item = &FormSpec> {
self.specs.values()
}
pub fn merge(&mut self, other: Registry) {
self.specs.extend(other.specs);
}
pub fn len(&self) -> usize {
self.specs.len()
}
pub fn is_empty(&self) -> bool {
self.specs.is_empty()
}
}
impl Extend<FormSpec> for Registry {
fn extend<T: IntoIterator<Item = FormSpec>>(&mut self, iter: T) {
for spec in iter {
self.insert(spec);
}
}
}
impl FromIterator<FormSpec> for Registry {
fn from_iter<T: IntoIterator<Item = FormSpec>>(iter: T) -> Self {
let mut reg = Registry::new();
reg.extend(iter);
reg
}
}
#[derive(Debug)]
pub struct Part<'a, 't> {
pub role: Role,
pub datum: &'a Datum<'t>,
}
#[derive(Debug)]
pub struct Annotated<'a, 't> {
pub form: &'a Datum<'t>,
pub head: &'t str,
pub parts: Vec<Part<'a, 't>>,
pub category: Option<Category>,
pub confidence: Confidence,
}
impl<'a, 't> Annotated<'a, 't> {
pub fn first(&self, role: Role) -> Option<&'a Datum<'t>> {
self.parts.iter().find(|p| p.role == role).map(|p| p.datum)
}
pub fn all(&self, role: Role) -> impl Iterator<Item = &'a Datum<'t>> + '_ {
self.parts
.iter()
.filter(move |p| p.role == role)
.map(|p| p.datum)
}
pub fn specialized_params(&self) -> Vec<SpecializedParam<'a, 't>> {
match self.first(Role::SpecializedArglist) {
Some(arglist) => split_specialized_arglist(arglist),
None => Vec::new(),
}
}
}
#[derive(Debug)]
pub struct SpecializedParam<'a, 't> {
pub variable: &'a Datum<'t>,
pub specializer: Option<&'a Datum<'t>>,
}
pub fn split_specialized_arglist<'a, 't>(arglist: &'a Datum<'t>) -> Vec<SpecializedParam<'a, 't>> {
let DatumKind::List { items, .. } = &arglist.kind else {
return Vec::new();
};
let mut out = Vec::new();
for item in items {
match &item.kind {
DatumKind::Symbol(s) if s.starts_with('&') => break,
DatumKind::Symbol(_) => out.push(SpecializedParam {
variable: item,
specializer: None,
}),
DatumKind::List { items: pair, .. } => {
if let Some(var) = pair.first() {
out.push(SpecializedParam {
variable: var,
specializer: pair.get(1),
});
}
}
_ => {}
}
}
out
}
fn is_delimited_list(datum: &Datum<'_>) -> bool {
matches!(datum.kind, DatumKind::List { .. })
}
fn list_head<'a, 't>(datum: &'a Datum<'t>) -> Option<(&'t str, &'a [Datum<'t>])> {
let items = datum.items()?;
let head = datum.head_symbol()?;
Some((head, items))
}
#[must_use]
pub fn annotate_form<'a, 't>(form: &'a Datum<'t>, reg: &Registry) -> Option<Annotated<'a, 't>> {
let (head, items) = list_head(form)?;
let spec = reg.get(head)?;
let mut parts = Vec::with_capacity(items.len());
parts.push(Part {
role: Role::Keyword,
datum: &items[0],
});
let mut i = 1;
for &role in &spec.leading {
if i >= items.len() {
break;
}
if role == Role::Name && !is_name_shaped(&items[i]) {
return None;
}
parts.push(Part {
role,
datum: &items[i],
});
i += 1;
}
match spec.dispatch {
Some(Dispatch::Qualifiers) => {
while i < items.len() && !is_delimited_list(&items[i]) {
parts.push(Part {
role: Role::Qualifier,
datum: &items[i],
});
i += 1;
}
if i < items.len() && is_delimited_list(&items[i]) {
parts.push(Part {
role: Role::SpecializedArglist,
datum: &items[i],
});
i += 1;
}
}
Some(Dispatch::Value) => {
if i < items.len() {
parts.push(Part {
role: Role::DispatchValue,
datum: &items[i],
});
i += 1;
}
if i < items.len()
&& matches!(
items[i].kind,
DatumKind::List {
delim: Delim::Square,
..
}
)
{
parts.push(Part {
role: Role::Arglist,
datum: &items[i],
});
i += 1;
}
}
None => {}
}
if i < items.len() {
let is_str = matches!(items[i].kind, DatumKind::Str(_));
let accepted = match spec.docstring {
Docstring::None => false,
Docstring::Leading => is_str && i + 1 < items.len(),
Docstring::LeadingOrLone => is_str,
};
if accepted {
parts.push(Part {
role: Role::Docstring,
datum: &items[i],
});
i += 1;
}
}
if spec.body {
for item in &items[i..] {
let role = match list_head(item) {
Some(("declare", _)) => Role::Declare,
Some(("interactive", _)) => Role::Interactive,
_ => Role::Body,
};
parts.push(Part { role, datum: item });
}
}
Some(Annotated {
form,
head,
parts,
category: spec.category,
confidence: spec.confidence,
})
}
fn is_name_shaped(datum: &Datum<'_>) -> bool {
match &datum.kind {
DatumKind::Symbol(_) => true,
DatumKind::List {
delim: Delim::Round,
items,
..
} => matches!(items.first().map(|d| &d.kind), Some(DatumKind::Symbol(_))),
_ => false,
}
}
#[must_use]
pub fn annotate_tree<'a, 't>(data: &'a [Datum<'t>], reg: &Registry) -> Vec<Annotated<'a, 't>> {
code_nodes(data)
.filter_map(|datum| annotate_form(datum, reg))
.collect()
}
fn strip_earmuffs(s: &str) -> &str {
s.trim_matches(|c| c == '*' || c == '_')
}
fn classify_param(name: &str) -> Option<Role> {
match strip_earmuffs(&name.to_ascii_lowercase()) {
"name" | "names" | "symbol" | "sym" | "fsym" | "fn-name" | "var" | "variable" | "place"
| "target" | "def" => Some(Role::Name),
"arglist" | "args" | "arguments" | "lambda-list" | "key-args" | "params" | "parameters"
| "ll" => Some(Role::Arglist),
"docstring" | "doc" | "doc-string" => Some(Role::Docstring),
"body" | "forms" | "bodyform" | "def-body" | "rest" | "heads" | "clauses" => {
Some(Role::Body)
}
_ => None,
}
}
struct HarvestProfile {
macro_heads: &'static [&'static str],
doc_policy: Docstring,
use_declare: bool,
clojure_meta: bool,
}
fn harvest_profile(dialect: Dialect) -> Option<HarvestProfile> {
use Docstring::{Leading, LeadingOrLone, None as NoDoc};
let profile = |macro_heads, doc_policy, use_declare, clojure_meta| HarvestProfile {
macro_heads,
doc_policy,
use_declare,
clojure_meta,
};
Some(match dialect {
Dialect::EmacsLisp => profile(&["defmacro", "cl-defmacro"], LeadingOrLone, true, false),
Dialect::CommonLisp => profile(&["defmacro"], Leading, false, false),
Dialect::Clojure | Dialect::Phel => profile(&["defmacro"], Leading, false, true),
Dialect::Fennel => profile(&["macro"], Leading, false, false),
Dialect::Janet => profile(&["defmacro", "defmacro-"], Leading, false, false),
Dialect::Hy => profile(&["defmacro"], LeadingOrLone, false, false),
Dialect::Lfe => profile(&["defmacro"], Leading, false, false),
Dialect::Islisp => profile(&["defmacro"], NoDoc, false, false),
_ => return None,
})
}
pub fn harvest_source(source: &str, reg: &mut Registry) -> usize {
harvest_source_for(source, Dialect::EmacsLisp, reg)
}
fn is_syntax_rules_dialect(dialect: Dialect) -> bool {
matches!(
dialect,
Dialect::Scheme
| Dialect::Guile
| Dialect::Gauche
| Dialect::Mosh
| Dialect::Gambit
| Dialect::SchemeSuperset
| Dialect::Racket
)
}
pub fn harvest_source_for(source: &str, dialect: Dialect, reg: &mut Registry) -> usize {
let parsed = parse(source, &Options::for_dialect(dialect));
let mut added = 0;
if is_syntax_rules_dialect(dialect) {
for datum in &parsed.data {
if let Some(spec) = harvest_scheme_macro(datum) {
reg.insert(spec);
added += 1;
}
}
} else if let Some(profile) = harvest_profile(dialect) {
for datum in &parsed.data {
if let Some(spec) = harvest_defmacro(datum, &profile) {
reg.insert(spec);
added += 1;
}
}
}
added
}
fn harvest_defmacro(form: &Datum<'_>, profile: &HarvestProfile) -> Option<FormSpec> {
let (head, items) = list_head(form)?;
if !profile.macro_heads.contains(&head) {
return None;
}
let name_datum = items.get(1)?;
let (name, name_meta) = match &name_datum.kind {
DatumKind::Symbol(s) => (*s, None),
DatumKind::Prefixed {
prefix: Prefix::Meta,
inner,
arg,
..
} => match inner.kind {
DatumKind::Symbol(s) => (s, arg.as_deref()),
_ => return None,
},
_ => return None,
};
let mut idx = 2;
let mut attr_map = None;
while let Some(item) = items.get(idx) {
match &item.kind {
DatumKind::Str(_) => idx += 1, DatumKind::List {
delim: Delim::Curly,
..
} => {
attr_map = Some(item);
idx += 1; }
_ => break,
}
}
let DatumKind::List { items: params, .. } = &items.get(idx)?.kind else {
return None;
};
let (mut leading, mut docstring, mut body, mut matched_any) = classify_arglist_params(params);
let mut declared = false;
if profile.use_declare {
for item in &items[(idx + 1).min(items.len())..] {
if let Some(("declare", decl_items)) = list_head(item) {
for spec in &decl_items[1..] {
if let Some((key, _)) = list_head(spec) {
match key {
"doc-string" => {
docstring = true;
declared = true;
}
"debug" => declared = true, _ => {}
}
}
}
}
}
}
if profile.clojure_meta {
let arglist = name_meta
.and_then(clojure_arglists)
.or_else(|| attr_map.and_then(clojure_arglists));
let mut arglists_applied = false;
if let Some(arglist) = arglist {
let (l, doc, b, matched) = classify_arglist_params(arglist);
if matched {
leading = l;
docstring = doc;
body = b;
matched_any = true;
declared = true;
arglists_applied = true;
}
}
if !arglists_applied {
if let Some(indent) = name_meta
.and_then(clojure_style_indent)
.or_else(|| attr_map.and_then(clojure_style_indent))
{
if let StyleIndent::Count(n) = indent {
if leading.len() < n {
leading.resize(n, Role::Other);
}
}
body = true;
declared = true;
matched_any = true;
}
}
}
let confidence = if declared {
Confidence::Declared
} else if matched_any {
Confidence::Inferred
} else {
Confidence::Weak
};
let docstring = if docstring {
profile.doc_policy
} else {
Docstring::None
};
Some(FormSpec::new(name, leading, docstring, body, confidence))
}
fn classify_arglist_params(params: &[Datum<'_>]) -> (Vec<Role>, bool, bool, bool) {
let mut leading = Vec::new();
let mut docstring = false;
let mut body = false;
let mut matched_any = false;
let mut rest = false;
for p in params {
if let DatumKind::HashLiteral {
tag: "*" | "**",
inner: Some(inner),
} = &p.kind
{
if let DatumKind::Symbol(nm) = inner.kind {
if classify_param(nm).is_some() {
matched_any = true;
}
}
body = true;
rest = true;
continue;
}
let DatumKind::Symbol(pname) = p.kind else {
continue;
};
if matches!(pname, "&rest" | "&body" | "&") {
rest = true;
continue;
}
if pname.starts_with('&') || pname == ":rest" {
continue;
}
if rest {
if classify_param(pname).is_some() {
matched_any = true;
}
body = true;
break;
}
match classify_param(pname) {
Some(Role::Body) => {
body = true;
matched_any = true;
break;
}
Some(Role::Docstring) => {
docstring = true;
matched_any = true;
}
Some(role) => {
leading.push(role);
matched_any = true;
}
None => leading.push(Role::Other),
}
}
(leading, docstring, body, matched_any)
}
fn clojure_meta_value<'a, 't>(meta: &'a Datum<'t>, key: &str) -> Option<&'a Datum<'t>> {
let DatumKind::List {
delim: Delim::Curly,
items,
..
} = &meta.kind
else {
return None;
};
for pair in items.chunks(2) {
if let [k, v] = pair {
if matches!(k.kind, DatumKind::Keyword(kw) if kw == key) {
return Some(v);
}
}
}
None
}
fn clojure_arglists<'a, 't>(meta: &'a Datum<'t>) -> Option<&'a [Datum<'t>]> {
let value = clojure_meta_value(meta, ":arglists")?;
let list = match &value.kind {
DatumKind::Prefixed {
prefix: Prefix::Quote,
inner,
..
} => inner.as_ref(),
_ => value,
};
list.items()?.first()?.items()
}
enum StyleIndent {
Count(usize),
Body,
}
fn clojure_style_indent(meta: &Datum<'_>) -> Option<StyleIndent> {
interpret_style_indent(clojure_meta_value(meta, ":style/indent")?)
}
fn interpret_style_indent(spec: &Datum<'_>) -> Option<StyleIndent> {
match &spec.kind {
DatumKind::Number(n) => n.parse::<usize>().ok().map(StyleIndent::Count),
DatumKind::Keyword(":defn" | ":form") => Some(StyleIndent::Body),
DatumKind::List { .. } => interpret_style_indent(spec.items()?.first()?),
_ => None,
}
}
fn harvest_scheme_macro(form: &Datum<'_>) -> Option<FormSpec> {
let (head, items) = list_head(form)?;
if head == "define-macro" {
return harvest_define_macro(items);
}
let (name, patterns): (&str, Vec<&Datum<'_>>) = match head {
"define-syntax-rule" => {
let pattern = items.get(1)?;
let name = pattern.items()?.first()?.as_symbol()?;
(name, vec![pattern])
}
"define-syntax" | "define-syntax-parameter" => {
let name = scheme_def_name(items.get(1)?)?;
let clauses = items.get(2..)?.iter().find_map(find_transformer_clauses)?;
let patterns = clauses
.iter()
.filter_map(|c| c.items().and_then(|x| x.first()))
.filter(|p| matches!(&p.kind, DatumKind::List { .. }))
.collect();
(name, patterns)
}
_ => return None,
};
let mut best: Option<(Vec<Role>, bool, usize)> = None;
for pattern in patterns {
let Some((leading, body, matched)) = classify_pattern(pattern) else {
continue;
};
let better = best.as_ref().map_or(true, |(bl, _, bm)| {
(matched, leading.len()) > (*bm, bl.len())
});
if better {
best = Some((leading, body, matched));
}
}
let (leading, body, matched) = best?;
let confidence = if matched > 0 {
Confidence::Inferred
} else {
Confidence::Weak
};
Some(FormSpec::new(
name,
leading,
Docstring::None,
body,
confidence,
))
}
fn harvest_define_macro(items: &[Datum<'_>]) -> Option<FormSpec> {
let DatumKind::List {
items: sig, tail, ..
} = &items.get(1)?.kind
else {
return None;
};
let name = sig.first()?.as_symbol()?;
let (leading, _doc, mut body, matched) = classify_arglist_params(sig.get(1..)?);
if tail.is_some() {
body = true; }
let confidence = if matched {
Confidence::Inferred
} else {
Confidence::Weak
};
Some(FormSpec::new(
name,
leading,
Docstring::None,
body,
confidence,
))
}
fn scheme_def_name<'t>(datum: &Datum<'t>) -> Option<&'t str> {
match &datum.kind {
DatumKind::Symbol(s) => Some(s),
DatumKind::List { .. } => datum.items()?.first()?.as_symbol(),
_ => None,
}
}
fn find_transformer_clauses<'a, 't>(d: &'a Datum<'t>) -> Option<&'a [Datum<'t>]> {
let DatumKind::List { items, .. } = &d.kind else {
return None;
};
if let Some(h) = items.first().and_then(Datum::as_symbol) {
if matches!(h, "syntax-rules" | "syntax-case" | "syntax-parse") {
return Some(items);
}
}
items.iter().find_map(find_transformer_clauses)
}
fn classify_pattern(pattern: &Datum<'_>) -> Option<(Vec<Role>, bool, usize)> {
let params = pattern.items()?;
let mut leading = Vec::new();
let mut body = false;
let mut matched = 0usize;
for p in params.iter().skip(1) {
match &p.kind {
DatumKind::Symbol("...") => {
leading.pop();
body = true;
matched += 1;
break;
}
DatumKind::Symbol(s) => {
let base = s.split(':').next().unwrap_or(s);
match classify_param(base) {
Some(Role::Body) => {
body = true;
matched += 1;
break;
}
Some(role) => {
leading.push(role);
matched += 1;
}
None => leading.push(Role::Other),
}
}
DatumKind::List { .. } => {
leading.push(Role::Arglist);
matched += 1;
}
_ => leading.push(Role::Other),
}
}
Some((leading, body, matched))
}
struct Builtins {
reg: Registry,
}
impl Builtins {
fn new() -> Self {
Builtins {
reg: Registry::new(),
}
}
fn def(
&mut self,
head: &str,
leading: Vec<Role>,
doc: Docstring,
body: bool,
category: Option<Category>,
) {
let mut spec = FormSpec::new(head, leading, doc, body, Confidence::Builtin);
spec.category = category;
self.reg.insert(spec);
}
fn method(&mut self, head: &str, dispatch: Dispatch, doc: Docstring, category: Category) {
let mut spec = FormSpec::new(head, vec![Role::Name], doc, true, Confidence::Builtin);
spec.category = Some(category);
spec.dispatch = Some(dispatch);
self.reg.insert(spec);
}
}
#[must_use]
pub fn bundled_registry(dialect: Dialect) -> Registry {
match dialect {
Dialect::Scheme => scheme_builtins(),
Dialect::Guile
| Dialect::Gauche
| Dialect::Mosh
| Dialect::Gambit
| Dialect::SchemeSuperset => scheme_extended_builtins(),
Dialect::Racket => racket_builtins(),
Dialect::CommonLisp => common_lisp_builtins(),
Dialect::EmacsLisp => emacs_lisp_builtins(),
Dialect::Clojure => clojure_builtins(),
Dialect::Phel => clojure_builtins(),
Dialect::Fennel => fennel_builtins(),
Dialect::Janet => janet_builtins(),
Dialect::Hy => hy_builtins(),
Dialect::Lfe => lfe_builtins(),
Dialect::Islisp => islisp_builtins(),
Dialect::AutoLisp => autolisp_builtins(),
Dialect::Edn => Registry::new(),
}
}
fn emacs_lisp_builtins() -> Registry {
use Category::{Constant, Function, Generic, Macro, Method, Struct, Test, Variable};
use Docstring::{LeadingOrLone, None as NoDoc};
use Role::{Arglist, Name, Other};
let mut b = Builtins::new();
let fnlike = vec![Name, Arglist];
b.def("defun", fnlike.clone(), LeadingOrLone, true, Some(Function));
b.def(
"defsubst",
fnlike.clone(),
LeadingOrLone,
true,
Some(Function),
);
b.def(
"cl-defun",
fnlike.clone(),
LeadingOrLone,
true,
Some(Function),
);
b.def(
"cl-defsubst",
fnlike.clone(),
LeadingOrLone,
true,
Some(Function),
);
b.def(
"define-inline",
fnlike.clone(),
LeadingOrLone,
true,
Some(Function),
);
b.def("defmacro", fnlike.clone(), LeadingOrLone, true, Some(Macro));
b.def(
"cl-defmacro",
fnlike.clone(),
LeadingOrLone,
true,
Some(Macro),
);
b.def("cl-defgeneric", fnlike, LeadingOrLone, true, Some(Generic));
b.method("cl-defmethod", Dispatch::Qualifiers, LeadingOrLone, Method);
let varlike = vec![Name, Other];
b.def(
"defvar",
varlike.clone(),
LeadingOrLone,
false,
Some(Variable),
);
b.def(
"defvar-local",
varlike.clone(),
LeadingOrLone,
false,
Some(Variable),
);
b.def(
"defconst",
varlike.clone(),
LeadingOrLone,
false,
Some(Constant),
);
b.def(
"defcustom",
varlike.clone(),
LeadingOrLone,
true,
Some(Variable),
);
b.def(
"defface",
varlike.clone(),
LeadingOrLone,
true,
Some(Variable),
);
b.def("defgroup", varlike, LeadingOrLone, true, None);
b.def("defvar-keymap", vec![Name], NoDoc, true, Some(Variable));
b.def("define-minor-mode", vec![Name], LeadingOrLone, true, None);
b.def(
"define-derived-mode",
vec![Name, Other, Other],
LeadingOrLone,
true,
None,
);
b.def(
"define-global-minor-mode",
vec![Name],
LeadingOrLone,
true,
None,
);
b.def(
"cl-defstruct",
vec![Name],
LeadingOrLone,
true,
Some(Struct),
);
b.def(
"ert-deftest",
vec![Name, Arglist],
LeadingOrLone,
true,
Some(Test),
);
b.reg
}
fn scheme_builtins() -> Registry {
use Category::{Macro, Type};
use Docstring::None as NoDoc;
use Role::Name;
let mut b = Builtins::new();
b.def("define", vec![Name], NoDoc, true, None);
b.def("define-values", vec![Name], NoDoc, true, None);
b.def("define-syntax", vec![Name], NoDoc, true, Some(Macro));
b.def("define-record-type", vec![Name], NoDoc, true, Some(Type));
b.def("define-library", vec![Name], NoDoc, true, None);
b.reg
}
fn scheme_extended_builtins() -> Registry {
use Category::{Class, Constant, Generic, Macro};
use Docstring::None as NoDoc;
use Role::{Name, Other};
let mut reg = scheme_builtins();
let mut b = Builtins::new();
b.def("define-class", vec![Name], NoDoc, true, Some(Class));
b.def("define-generic", vec![Name], NoDoc, false, Some(Generic));
b.def(
"define-constant",
vec![Name, Other],
NoDoc,
false,
Some(Constant),
);
b.def("define-inline", vec![Name], NoDoc, true, None);
b.def("define-syntax-rule", vec![Name], NoDoc, true, Some(Macro));
b.def("define*", vec![Name], NoDoc, true, None);
b.def("define-public", vec![Name], NoDoc, true, None);
reg.merge(b.reg);
reg
}
fn racket_builtins() -> Registry {
let mut reg = scheme_builtins();
reg.insert(
FormSpec::new(
"struct",
vec![Role::Name],
Docstring::None,
true,
Confidence::Builtin,
)
.with_category(Category::Struct),
);
reg.insert(
FormSpec::new(
"define-syntax-rule",
vec![Role::Name],
Docstring::None,
true,
Confidence::Builtin,
)
.with_category(Category::Macro),
);
for head in ["define/public", "define/private", "define/override"] {
reg.insert(
FormSpec::new(
head,
vec![Role::Name],
Docstring::None,
true,
Confidence::Builtin,
)
.with_category(Category::Method),
);
}
reg
}
fn common_lisp_builtins() -> Registry {
use Category::{Class, Constant, Function, Generic, Macro, Method, Struct, Type, Variable};
use Docstring::{Leading, LeadingOrLone, None as NoDoc};
use Role::{Arglist, Name, Other};
let mut b = Builtins::new();
b.def("defun", vec![Name, Arglist], Leading, true, Some(Function));
b.def("defmacro", vec![Name, Arglist], Leading, true, Some(Macro));
b.def("deftype", vec![Name, Arglist], Leading, true, Some(Type));
b.def(
"defgeneric",
vec![Name, Arglist],
NoDoc,
true,
Some(Generic),
);
b.method("defmethod", Dispatch::Qualifiers, Leading, Method);
b.def(
"defvar",
vec![Name, Other],
LeadingOrLone,
false,
Some(Variable),
);
b.def(
"defparameter",
vec![Name, Other],
LeadingOrLone,
false,
Some(Variable),
);
b.def(
"defconstant",
vec![Name, Other],
LeadingOrLone,
false,
Some(Constant),
);
b.def("defclass", vec![Name], NoDoc, true, Some(Class));
b.def("define-condition", vec![Name], NoDoc, true, Some(Class));
b.def("defstruct", vec![Name], Leading, true, Some(Struct));
b.def("defpackage", vec![Name], NoDoc, true, None);
b.reg
}
fn clojure_builtins() -> Registry {
use Category::{Function, Generic, Macro, Method, Struct, Test, Type};
use Docstring::{Leading, None as NoDoc};
use Role::Name;
let mut b = Builtins::new();
b.def("defn", vec![Name], Leading, true, Some(Function));
b.def("defn-", vec![Name], Leading, true, Some(Function));
b.def("defmacro", vec![Name], Leading, true, Some(Macro));
b.def("def", vec![Name], Leading, true, None); b.def("defonce", vec![Name], NoDoc, true, None);
b.def("defmulti", vec![Name], Leading, true, Some(Generic));
b.method("defmethod", Dispatch::Value, NoDoc, Method);
b.def("defprotocol", vec![Name], Leading, true, Some(Generic));
b.def("definterface", vec![Name], NoDoc, true, Some(Type));
b.def("defrecord", vec![Name], NoDoc, true, Some(Struct));
b.def("deftype", vec![Name], NoDoc, true, None);
b.def("deftest", vec![Name], NoDoc, true, Some(Test));
b.def("definline", vec![Name], Leading, true, Some(Function));
b.def("ns", vec![Name], Leading, true, None);
b.reg
}
fn fennel_builtins() -> Registry {
use Category::{Function, Macro, Variable};
use Docstring::{Leading, None as NoDoc};
use Role::{Arglist, Name};
let mut b = Builtins::new();
b.def("fn", vec![Name, Arglist], Leading, true, Some(Function));
b.def("lambda", vec![Name, Arglist], Leading, true, Some(Function));
b.def("λ", vec![Name, Arglist], Leading, true, Some(Function));
b.def("macro", vec![Name, Arglist], Leading, true, Some(Macro));
b.def("macros", vec![Name], NoDoc, true, Some(Macro));
b.def("local", vec![Name], NoDoc, true, Some(Variable));
b.def("var", vec![Name], NoDoc, true, Some(Variable));
b.def("global", vec![Name], NoDoc, true, Some(Variable));
b.reg
}
fn janet_builtins() -> Registry {
use Category::{Function, Macro, Variable};
use Docstring::Leading;
use Role::Name;
let mut b = Builtins::new();
b.def("defn", vec![Name], Leading, true, Some(Function));
b.def("defn-", vec![Name], Leading, true, Some(Function));
b.def("defmacro", vec![Name], Leading, true, Some(Macro));
b.def("defmacro-", vec![Name], Leading, true, Some(Macro));
b.def("def", vec![Name], Leading, true, None);
b.def("def-", vec![Name], Leading, true, None);
b.def("var", vec![Name], Leading, true, Some(Variable));
b.def("var-", vec![Name], Leading, true, Some(Variable));
b.reg
}
fn hy_builtins() -> Registry {
use Category::{Class, Function, Macro};
use Docstring::{LeadingOrLone, None as NoDoc};
use Role::{Arglist, Name};
let mut b = Builtins::new();
b.def(
"defn",
vec![Name, Arglist],
LeadingOrLone,
true,
Some(Function),
);
b.def(
"defmacro",
vec![Name, Arglist],
LeadingOrLone,
true,
Some(Macro),
);
b.def("defclass", vec![Name], LeadingOrLone, true, Some(Class));
b.def("setv", vec![Name], NoDoc, true, None);
b.reg
}
fn lfe_builtins() -> Registry {
use Category::{Function, Macro, Struct};
use Docstring::{Leading, None as NoDoc};
use Role::{Arglist, Name};
let mut b = Builtins::new();
b.def("defun", vec![Name, Arglist], Leading, true, Some(Function));
b.def("defmacro", vec![Name, Arglist], Leading, true, Some(Macro));
b.def("defrecord", vec![Name], NoDoc, true, Some(Struct));
b.def("defmodule", vec![Name], NoDoc, true, None);
b.reg
}
fn islisp_builtins() -> Registry {
use Category::{Class, Constant, Function, Generic, Macro, Method, Variable};
use Docstring::None as NoDoc;
use Role::{Arglist, Name};
let mut b = Builtins::new();
b.def("defun", vec![Name, Arglist], NoDoc, true, Some(Function));
b.def("defmacro", vec![Name, Arglist], NoDoc, true, Some(Macro));
b.def(
"defgeneric",
vec![Name, Arglist],
NoDoc,
true,
Some(Generic),
);
b.method("defmethod", Dispatch::Qualifiers, NoDoc, Method);
b.def("defclass", vec![Name], NoDoc, true, Some(Class));
b.def("defconstant", vec![Name], NoDoc, true, Some(Constant));
b.def("defglobal", vec![Name], NoDoc, true, Some(Variable));
b.def("defdynamic", vec![Name], NoDoc, true, Some(Variable));
b.reg
}
fn autolisp_builtins() -> Registry {
use Role::{Arglist, Name};
let mut b = Builtins::new();
b.def(
"defun",
vec![Name, Arglist],
Docstring::None,
true,
Some(Category::Function),
);
b.reg
}