mooncite

    CSL-style citation and bibliography rendering for MoonBit

    citation
    bibliography
    csl
    citeproc
    moonbit
    Download zip
    Version
    0.1.0
    License
    Apache-2.0
    Last updated
    14 hours ago
    Downloads
    1

    #MoonCite

    MoonCite 是一个面向 MoonBit 的 CSL(Citation Style Language)文献引用与参考文献排版库。它把 CSL 样式、CSL-JSON 数据和本地化术语连接起来,在不访问网络的情况下生成正文引用、参考文献表和 HTML 片段。

    项目名称中的 Cite 指 citation,不是在线文献数据库。库的重点是让 MoonBit 程序可以把“文献数据 + 样式”变成稳定、可测试的排版结果,适合文档生成、科研笔记、静态站点和教学工具。

    #当前范围

    • 解析常用 CSL XML:style、macro、citation、bibliography、layout、text、names、name、date、date-part、number、group、choose 和排序键。
    • 读取 CSL-JSON 的标题、作者、日期、页码、URL、DOI、出版物等字段。
    • 输出纯文本与 HTML;支持前后缀、大小写、字体样式、作者姓名、日期、数字、条件分支和分组分隔符。
    • 提供英文、中文等本地化术语,页码范围折叠、数字引用范围折叠、作者键和文献表统计等辅助功能。
    • 样式与数据都在本地输入,库本身不联网、不调用外部服务。

    这是一个有明确边界的首版移植:覆盖常见 CSL 排版路径和可复用的数据模型,但不是对 CSL 1.0.2 全部扩展点的认证实现。复杂的脚注布局、完整的 disambiguation、BibTeX/MARC 导入、在线 DOI 查询和 PDF 排版不在当前版本范围内。

    #安装与快速开始

    MoonCite 当前按 MoonBit 模块发布源码,要求 moonc >= 0.10.14。验收前不依赖 MoonCakes,直接克隆仓库即可复现:

    git clone https://github.com/yujieMamba/mooncite.git cd mooncite moon version --all moonc -v moon check --target wasm-gc --deny-warn moon test --target wasm-gc

    在自己的 MoonBit 模块中使用时,可先把仓库作为本地源码依赖,或复制 moon.mod、moon.pkg 和库源码到工作区;本仓库的 cmd/main 和 examples/* 已经给出完整的可运行接入方式。MoonCite 暂未上传到 mooncakes.io,因此 README 不把尚未提供的包版本写成可安装事实。

    let style = """ <style xmlns="http://purl.org/net/xbiblio/csl" version="1.0"> <info><title>Demo</title><id>http://example.org/demo</id></info> <citation><layout prefix="(" suffix=")"><text variable="title"/></layout></citation> <bibliography><layout><names variable="author" suffix=". "><name/></names><date variable="issued" prefix="(" suffix="). "><date-part name="year"/></date><text variable="title"/></layout></bibliography> </style> """ let processor = @mooncite.Processor::from_style(style).unwrap() let item = @mooncite.Item::new("paper") item.set_string("title", "A short paper") item.set_name("author", [@mooncite.CslName::new(family="Doe", given="Jane")]) item.set_date("issued", @mooncite.CslDate::new(2024)) let result = processor.render_bibliography([item]) println(result.unwrap())

    仓库内有四个可直接运行的例子:

    moon run cmd/main moon run examples/beginners moon run examples/numeric moon run examples/text-extract

    #质量与可复现性

    质量报告记录了本地实际运行结果,而不是估算值:实现代码超过 3,000 行,包含 9 个测试用例和 4 个 runnable examples;在 moonc v0.10.14 下,wasm-gc、wasm、js、native 检查及 wasm/js/wasm-gc 测试均通过。CI 会固定检查 moonc >= 0.10.14,并执行 MoonBit 的格式检查、四个目标检查和测试。

    #设计取舍

    MoonCite 借鉴 CSL 规范、citeproc 实现和 CSL-JSON 数据形状,但代码以 MoonBit 数据类型和纯函数式渲染流程重写。解析器不依赖网络,数据结构公开,便于在命令行、服务端、Wasm 或静态生成器中复用。

    #许可证

    本项目采用 Apache License 2.0。规范与参考实现的来源、版本和边界见 THIRD_PARTY.md。

    Affix

    pub struct Affix {
    prefix : String
    suffix : String
    delimiter : String
    text_case : String
    font_style : String
    font_weight : String
    vertical_align : String
    }

    ChooseBranch

    pub struct ChooseBranch {
    conditions : Array[Condition]
    match_mode : String
    children : Array[Node]
    }

    Cite

    pub(all) struct Cite {
    id : String
    locator : String
    label : String
    prefix : String
    suffix : String
    suppress_author : Bool
    } derive(Eq,
    Debug
    )

    Cite::equal

    fn Cite::equal(Cite, Cite) -> Bool

    Cite::new

    fn Cite::new(id : String) -> Cite

    Cite::not_equal

    fn Cite::not_equal(x : Cite, y : Cite) -> Bool

    Cite::to_repr

    Condition

    pub enum Condition {
    Variable(String)
    Type(String)
    Numeric(String)
    IsUncertain(String)
    IsPlural(String)
    Position(String)
    }

    CslDate

    pub(all) struct CslDate {
    year : Int
    month : Int
    day : Int
    end_year : Int
    end_month : Int
    end_day : Int
    circa : Bool
    literal : String
    } derive(Eq,
    Debug
    )

    CslDate::equal

    fn CslDate::equal(CslDate, CslDate) -> Bool

    CslDate::new

    fn CslDate::new(year : Int, month? : Int, day? : Int) -> CslDate

    CslDate::not_equal

    fn CslDate::not_equal(x : CslDate, y : CslDate) -> Bool

    CslDate::to_repr

    CslError

    pub(all) enum CslError {
    BadXml(String)
    BadStyle(String)
    BadJson(String)
    BadItem(String)
    MissingStyle
    } derive(Eq,
    Debug
    )

    Recoverable processor errors. Messages stay stable for tests.

    CslError::equal

    fn CslError::equal(CslError, CslError) -> Bool

    CslError::message

    fn CslError::message(self : CslError) -> String

    CslError::not_equal

    fn CslError::not_equal(x : CslError, y : CslError) -> Bool

    CslError::to_repr

    CslName

    pub(all) struct CslName {
    family : String
    given : String
    suffix : String
    dropping_particle : String
    non_dropping_particle : String
    } derive(Eq,
    Debug
    )

    CslName::equal

    fn CslName::equal(CslName, CslName) -> Bool

    CslName::new

    fn CslName::new(family : String, given? : String) -> CslName

    CslName::not_equal

    fn CslName::not_equal(x : CslName, y : CslName) -> Bool

    CslName::to_repr

    DateNode

    pub struct DateNode {
    variable : String
    parts : Array[DatePart]
    delimiter : String
    }

    DatePart

    pub struct DatePart {
    name : String
    form : String
    prefix : String
    suffix : String
    range_delimiter : String
    text_case : String
    }

    Item

    pub(all) struct Item {
    id : String
    title : String
    container_title : String
    publisher : String
    publisher_place : String
    volume : String
    issue : String
    page : String
    edition : String
    url : String
    doi : String
    item_type : String
    abstract_text : String
    names : Map[String, Array[CslName]]
    dates : Map[String, CslDate]
    strings : Map[String, String]
    numbers : Map[String, Int]
    booleans : Map[String, Bool]
    } derive(
    Debug
    )

    Item::date

    fn Item::date(self : Item, variable : String) -> CslDate?

    Item::has

    fn Item::has(self : Item, variable : String) -> Bool

    Item::name

    fn Item::name(self : Item, variable : String) -> Array[CslName]

    Item::new

    fn Item::new(id : String) -> Item

    Item::number

    fn Item::number(self : Item, variable : String) -> Int?

    Item::set_bool

    fn Item::set_bool(self : Item, variable : String, value : Bool) -> Item

    Item::set_date

    fn Item::set_date(self : Item, variable : String, date : CslDate) -> Item

    Item::set_name

    fn Item::set_name(self : Item, variable : String, names : Array[CslName]) -> Item

    Item::set_number

    fn Item::set_number(self : Item, variable : String, value : Int) -> Item

    Item::set_string

    fn Item::set_string(self : Item, variable : String, value : String) -> Item

    Item::string

    fn Item::string(self : Item, variable : String) -> String

    Item::to_repr

    Layout

    pub struct Layout {
    affix : Affix
    children : Array[Node]
    }

    Locale

    pub(all) struct Locale {
    language : String
    terms : Map[String, String]
    months : Map[Int, String]
    }

    Localized CSL terms. The table is intentionally small but extensible: user supplied locale XML can replace any term without changing style code.

    Locale::chinese

    fn Locale::chinese() -> Locale

    Locale::english

    fn Locale::english() -> Locale

    Locale::month

    fn Locale::month(self : Locale, number : Int, form? : String) -> String

    Locale::term

    fn Locale::term(self : Locale, name : String, form? : String) -> String

    NameNode

    pub struct NameNode {
    variable : String
    form : String
    and_word : String
    delimiter : String
    initialize_with : String
    initialize : Bool
    name_as_sort_order : String
    delimiter_precedes_last : String
    et_al_min : Int
    et_al_use_first : Int
    label_variable : String
    }

    Node

    pub enum Node {
    Text(Affix, TextNode)
    Names(Affix, NameNode)
    Number(Affix, NumberNode)
    Date(Affix, DateNode)
    Group(Affix, Array[Node])
    Choose(Affix, Array[ChooseBranch])
    }

    NumberNode

    pub struct NumberNode {
    variable : String
    form : String
    prefix : String
    suffix : String
    delimiter : String
    text_case : String
    }

    Processor

    pub(all) struct Processor {
    style : Style
    }

    Processor::from_style

    fn Processor::from_style(style_xml : String) -> Result[Processor, CslError]

    Processor::render_bibliography

    fn Processor::render_bibliography(self : Processor, items : Array[Item], format? : String) -> Result[String, CslError]

    Processor::render_bibliography_html

    fn Processor::render_bibliography_html(self : Processor, items : Array[Item]) -> Result[String, CslError]

    Processor::render_bibliography_json

    fn Processor::render_bibliography_json(self : Processor, items_json : String, format? : String) -> Result[String, CslError]

    Processor::render_citation

    fn Processor::render_citation(self : Processor, items : Array[Item], cites : Array[Cite], format? : String) -> Result[String, CslError]

    Processor::render_citation_html

    fn Processor::render_citation_html(self : Processor, items : Array[Item], cites : Array[Cite]) -> Result[String, CslError]

    Processor::render_citation_json

    fn Processor::render_citation_json(self : Processor, items_json : String, cites_json : String, format? : String) -> Result[String, CslError]

    SortKey

    pub struct SortKey {
    variable : String
    macro_name : String
    names_min : Int
    names_use_first : Int
    sort_order : String
    }

    Style

    pub struct Style {
    title : String
    version : String
    default_locale : String
    macros : Map[String, Array[Node]]
    citation : Layout?
    bibliography : Layout?
    citation_sort : Array[SortKey]
    bibliography_sort : Array[SortKey]
    citation_disambiguate : Bool
    citation_collapse : String
    citation_delimiter : String
    }

    TextNode

    pub struct TextNode {
    variable : String
    value : String
    macro_name : String
    term : String
    form : String
    }

    XmlElem

    pub(all) struct XmlElem {
    name : String
    attrs : Map[String, String]
    children : Array[XmlNode]
    } derive(
    Debug
    )

    XmlElem::attr

    fn XmlElem::attr(self : XmlElem, key : String) -> String

    XmlElem::direct_text

    fn XmlElem::direct_text(self : XmlElem) -> String

    XmlElem::first

    fn XmlElem::first(self : XmlElem, name : String) -> XmlElem?

    XmlElem::has_attr

    fn XmlElem::has_attr(self : XmlElem, key : String) -> Bool

    XmlElem::kids

    fn XmlElem::kids(self : XmlElem) -> Array[XmlElem]

    XmlElem::kids_named

    fn XmlElem::kids_named(self : XmlElem, name : String) -> Array[XmlElem]

    XmlElem::text

    fn XmlElem::text(self : XmlElem) -> String

    XmlElem::to_repr

    XmlNode

    pub(all) enum XmlNode {
    Element(XmlElem)
    Text(String)
    } derive(
    Debug
    )

    XmlNode::to_repr

    assign_year_suffixes

    fn assign_year_suffixes(items : Array[Item]) -> Unit

    bibliography_key

    fn bibliography_key(item : Item) -> String

    bibliography_statistics

    fn bibliography_statistics(items : Array[Item]) -> Map[String, Int]

    citation_has_locator

    fn citation_has_locator(cites : Array[Cite]) -> Bool

    cite_items

    fn cite_items(items : Array[Item], cites : Array[Cite]) -> Array[Item]

    cite_to_plain_json

    fn cite_to_plain_json(cite : Cite) -> String

    cites_to_plain_json

    fn cites_to_plain_json(cites : Array[Cite]) -> String

    collapse_numeric_ranges

    fn collapse_numeric_ranges(values : Array[Int]) -> String

    compact_whitespace

    fn compact_whitespace(value : String) -> String

    compare_items_for_bibliography

    fn compare_items_for_bibliography(a : Item, b : Item) -> Int

    date_is_range

    fn date_is_range(date : CslDate) -> Bool

    date_range_text

    fn date_range_text(date : CslDate, delimiter? : String) -> String

    date_sort_stamp

    fn date_sort_stamp(date : CslDate) -> Int64

    deduplicate_items

    fn deduplicate_items(items : Array[Item]) -> Array[Item]

    first_nonempty_render

    fn first_nonempty_render(style_xml : String, items : Array[Item]) -> String

    format_edition

    fn format_edition(item : Item) -> String

    format_identifier

    fn format_identifier(item : Item, prefer_doi? : Bool) -> String

    format_localized_date

    fn format_localized_date(locale : Locale, date : CslDate, form? : String) -> String

    format_localized_month

    fn format_localized_month(locale : Locale, date : CslDate, form : String) -> String

    format_page_variable

    fn format_page_variable(item : Item, variable? : String) -> String

    format_volume_issue

    fn format_volume_issue(item : Item) -> String

    initials_for_name

    fn initials_for_name(name : CslName, marker? : String) -> String

    invert_name

    fn invert_name(name : CslName) -> String

    item_author_key

    fn item_author_key(item : Item) -> String

    item_author_year_key

    fn item_author_year_key(item : Item) -> String

    item_by_id

    fn item_by_id(items : Array[Item], id : String) -> Item?

    item_container_label

    fn item_container_label(item : Item) -> String

    item_display_title

    fn item_display_title(item : Item) -> String

    item_has_author

    fn item_has_author(item : Item) -> Bool

    item_has_editor

    fn item_has_editor(item : Item) -> Bool

    item_is_book

    fn item_is_book(item : Item) -> Bool

    item_is_online

    fn item_is_online(item : Item) -> Bool

    item_is_periodical

    fn item_is_periodical(item : Item) -> Bool

    item_summary

    fn item_summary(item : Item) -> String

    item_to_plain_json

    fn item_to_plain_json(item : Item) -> String

    items_to_plain_json

    fn items_to_plain_json(items : Array[Item]) -> String

    locale_debug

    fn locale_debug(locale : Locale) -> String

    locale_from_language

    fn locale_from_language(language : String) -> Locale

    locale_months

    fn locale_months(locale : Locale) -> Array[String]

    locale_terms

    fn locale_terms(locale : Locale) -> Array[String]

    localize_and

    fn localize_and(locale : Locale) -> String

    localize_et_al

    fn localize_et_al(locale : Locale) -> String

    localize_label

    fn localize_label(locale : Locale, variable : String, count : Int) -> String

    localize_name_delimiter

    fn localize_name_delimiter(locale : Locale, count : Int) -> String

    merge_citation_parts

    fn merge_citation_parts(main : String, cites : Array[Cite]) -> String

    merge_locales

    fn merge_locales(base : Locale, replacement : Locale) -> Locale

    name_family_key

    fn name_family_key(name : CslName) -> String

    name_given_key

    fn name_given_key(name : CslName) -> String

    name_list_equal

    fn name_list_equal(a : Array[CslName], b : Array[CslName]) -> Bool

    names_equal

    fn names_equal(a : CslName, b : CslName) -> Bool

    normalize_page_range

    fn normalize_page_range(value : String) -> String

    Page-range and citation helpers used by numeric and author-date styles.

    normalize_range_part

    fn normalize_range_part(value : String) -> String

    normalize_title

    fn normalize_title(value : String) -> String

    number_label

    fn number_label(variable : String, count : Int, short? : Bool) -> String

    page_range_end

    fn page_range_end(value : String) -> String

    page_range_start

    fn page_range_start(value : String) -> String

    parse_cites_json

    fn parse_cites_json(json_text : String) -> Result[Array[Cite], CslError]

    parse_item_json

    fn parse_item_json(value : Json) -> Result[Item, CslError]

    parse_items_json

    fn parse_items_json(json_text : String) -> Result[Array[Item], CslError]

    parse_locale_xml

    fn parse_locale_xml(xml : String) -> Result[Locale, CslError]

    parse_xml

    fn parse_xml(input : String) -> Result[XmlElem, CslError]

    Parse a CSL/XML document and return the root element.

    processor_capabilities

    fn processor_capabilities() -> Array[String]

    processor_empty_result

    fn processor_empty_result(style_xml : String) -> Bool

    processor_is_deterministic

    fn processor_is_deterministic(style_xml : String, items : Array[Item]) -> Bool

    processor_license

    fn processor_license() -> String

    processor_upstream

    fn processor_upstream() -> String

    processor_version

    fn processor_version() -> String

    render_citation_html

    fn render_citation_html(style_xml : String, items : Array[Item], cites : Array[Cite]) -> Result[String, CslError]

    render_citation_text

    fn render_citation_text(style_xml : String, items : Array[Item], cites : Array[Cite]) -> Result[String, CslError]

    render_cite_prefix

    fn render_cite_prefix(cite : Cite) -> String

    render_each_item

    fn render_each_item(style_xml : String, items : Array[Item]) -> Array[String]

    render_html

    fn render_html(style_xml : String, items : Array[Item]) -> Result[String, CslError]

    render_int_range

    fn render_int_range(start : Int, end : Int) -> String

    render_many

    fn render_many(styles : Array[String], items : Array[Item]) -> Array[Result[String, CslError]]

    render_one

    fn render_one(style_xml : String, item : Item) -> Result[String, CslError]

    render_text

    fn render_text(style_xml : String, items : Array[Item]) -> Result[String, CslError]

    render_text_json

    fn render_text_json(style_xml : String, items_json : String) -> Result[String, CslError]

    safe_render

    fn safe_render(style_xml : String, items : Array[Item]) -> String

    safe_render_html

    fn safe_render_html(style_xml : String, items : Array[Item]) -> String

    sentence_title

    fn sentence_title(value : String) -> String

    style_complexity

    fn style_complexity(style_xml : String) -> Map[String, Int]

    style_has_macro

    fn style_has_macro(style_xml : String, name : String) -> Bool

    style_is_author_date

    fn style_is_author_date(style_xml : String) -> Bool

    style_is_numeric

    fn style_is_numeric(style_xml : String) -> Bool

    style_macro_names

    fn style_macro_names(style_xml : String) -> Array[String]

    style_metadata

    fn style_metadata(style_xml : String) -> Map[String, String]

    style_render_preview

    fn style_render_preview(style_xml : String, item : Item) -> Map[String, String]

    style_supports_bibliography

    fn style_supports_bibliography(style_xml : String) -> Bool

    style_supports_citation

    fn style_supports_citation(style_xml : String) -> Bool

    title_case_words

    fn title_case_words(value : String) -> String

    validate_item

    fn validate_item(item : Item) -> Array[String]

    validate_items

    fn validate_items(items : Array[Item]) -> Map[String, Array[String]]

    validate_style

    fn validate_style(style_xml : String) -> Array[String]

    with_citation_numbers

    fn with_citation_numbers(items : Array[Item]) -> Array[Item]