use std::collections::BTreeMap;
use std::fmt::Write;
use typelisp_front::check::registry::{AdtDef, AdtKind, FnSig, Namespace};
use typelisp_front::check::repr::Repr;
use typelisp_front::check::Registry;
use typelisp_front::types::Path;
use typelisp_front::Type;
pub const DOC_DIR: &str = "docs/dev/api_version";
pub const LATEST_FILE: &str = "latest_api_signature.md";
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct AbiVersion {
pub major: u32,
pub minor: u32,
pub patch: u32,
}
impl AbiVersion {
pub fn parse(text: &str) -> Option<AbiVersion> {
let mut parts = text.split('.').map(|part| part.parse::<u32>().ok());
let version = AbiVersion { major: parts.next()??, minor: parts.next()??, patch: parts.next()?? };
parts.next().is_none().then_some(version)
}
pub fn current() -> AbiVersion {
AbiVersion::parse(typelisp_abi::ABI_VERSION)
.unwrap_or_else(|| panic!("typelisp-abi names `{}`, not MAJOR.MINOR.PATCH", typelisp_abi::ABI_VERSION))
}
pub fn is_of_this_package(self) -> bool {
let (major, minor) = package_major_minor();
self.major == major && self.minor == minor
}
pub fn next(self) -> AbiVersion {
let next = if self.is_of_this_package() {
AbiVersion { patch: self.patch + 1, ..self }
} else {
let (major, minor) = package_major_minor();
AbiVersion { major, minor, patch: 0 }
};
assert!(next > self, "the typelisp version {} is older than ABI version {}", env!("CARGO_PKG_VERSION"), self);
next
}
}
impl std::fmt::Display for AbiVersion {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
}
}
fn package_major_minor() -> (u32, u32) {
let mut parts = env!("CARGO_PKG_VERSION").split('.').map(|part| part.parse::<u32>().ok());
match (parts.next().flatten(), parts.next().flatten()) {
(Some(major), Some(minor)) => (major, minor),
_ => panic!("the typelisp version `{}` does not start with MAJOR.MINOR", env!("CARGO_PKG_VERSION")),
}
}
pub fn history_file(version: AbiVersion) -> String {
format!("history/api_{}.md", version)
}
pub fn history_file_version(name: &str) -> Option<AbiVersion> {
AbiVersion::parse(name.strip_prefix("api_")?.strip_suffix(".md")?)
}
pub fn document(version: AbiVersion, description: &str) -> String {
format!(
"# ABI バージョン {}\n\n\
`scripts/regen-abi-version.sh` が生成した文書。手で編集しない。\n\n\
コンパイラが生成するコードが、ABI バージョン {} のランタイムライブラリについて仮定していることの一覧。\n\
ここに書かれていない前提(シムが引数をどう扱うか、生成コードがヒープを直接読む箇所など)は含まない。\n\n\
内容ハッシュ (FNV-1a 64): `{:016x}`\n\n{}",
version,
version,
fnv1a(description),
description
)
}
pub fn parse_document(text: &str) -> Option<(AbiVersion, u64, &str)> {
let version = AbiVersion::parse(text.strip_prefix("# ABI バージョン ")?.split('\n').next()?)?;
let hash_at = text.find("内容ハッシュ (FNV-1a 64): `")? + "内容ハッシュ (FNV-1a 64): `".len();
let hash = u64::from_str_radix(text.get(hash_at..hash_at + 16)?, 16).ok()?;
let body = &text[text.find("\n## ")? + 1..];
Some((version, hash, body))
}
pub fn fnv1a(text: &str) -> u64 {
text.bytes().fold(0xcbf2_9ce4_8422_2325, |h, b| (h ^ b as u64).wrapping_mul(0x0000_0100_0000_01b3))
}
pub fn describe() -> String {
let mut out = String::new();
constants(&mut out);
vocabulary(&mut out);
let reg = Registry::with_builtins();
let mut defs = Defs::default();
collect(®.root, &mut Vec::new(), &mut defs);
reprs(&mut out, &defs);
shims(&mut out);
types(&mut out, &defs);
functions(&mut out, &defs);
out
}
macro_rules! const_table {
($out:expr, $title:literal, $module:path, [$($name:ident),+ $(,)?]) => {{
use $module as m;
writeln!($out, "## {}\n\n| 名前 | 値 |\n|---|---|", $title).unwrap();
$(writeln!($out, "| {} | {:?} |", stringify!($name), m::$name).unwrap();)+
$out.push('\n');
}};
}
fn constants(out: &mut String) {
const_table!(out, "呼び出し規約 (typelisp_abi)", typelisp_abi, [
STATUS_RETURN, STATUS_CALL, STATUS_SUSPEND, STATUS_UNWIND,
BODY_ABI_CLASSIC, BODY_ABI_COROUTINE,
FRAME_VALUE_SLOT, FRAME_HANDLER_SLOT, FRAME_RESERVED_SLOTS,
]);
const_table!(out, "中断の種類 (typelisp_abi::call_state)", typelisp_abi::call_state, [
SUSPEND_YIELD, SUSPEND_SLEEP, SUSPEND_WAIT, SUSPEND_SAFEPOINT,
SUSPEND_CHAN_NEW, SUSPEND_CHAN_LEN, SUSPEND_CHAN_CAP, SUSPEND_CHAN_CLOSE,
SUSPEND_CHAN_SEND, SUSPEND_CHAN_RECV, SUSPEND_CHAN_SELECT,
SUSPEND_IO, SUSPEND_IO_FOR, SUSPEND_TASK, SUSPEND_THREAD, SUSPEND_MAIN,
]);
const_table!(out, "生成コードの本体の ABI (typelisp::compile)", crate::compile, [EMITTED_BODY_ABI]);
const_table!(out, "値の表現 (typelisp_mem::tagged)", typelisp_mem::tagged, [
LAYOUT, LAYOUT_THREE_BIT, LAYOUT_FIXNUM_ONE_BIT, LAYOUT_OPTION_NICHE,
FIXNUM_SHIFT, FIXNUM_MIN, FIXNUM_MAX, FIXNUM_MASK, FIXNUM_TAG,
LOW_MASK, TAG_CONS, TAG_SYMBOL, TAG_BOXED, TAG_SMALL,
SMALL_MASK, SMALL_SHIFT, TAG_IMMEDIATE, TAG_CHAR, TAG_STR, TAG_PATH,
BOXED_SHIFT, IMMEDIATE_NIL, IMMEDIATE_FALSE, IMMEDIATE_TRUE, NIL_WORD,
]);
const_table!(out, "C メモリ (typelisp_rt::c_mem)", typelisp_rt::c_mem, [
KIND_I8, KIND_I16, KIND_I32, KIND_U8, KIND_U16, KIND_U32, KIND_C_LONG, KIND_C_ULONG,
KIND_F32, KIND_F64, KIND_BOOL, KIND_PTR, DESC_FIELD, DESC_PAIR,
]);
const_table!(out, "終了コード (typelisp_rt)", typelisp_rt, [EXIT_CODE_PANIC, EXIT_CODE_SUCCESS]);
const_table!(out, "ダンプ形式 (typelisp_front::dump)", typelisp_front::dump, [FORMAT_VERSION, CONTAINER_VERSION]);
}
fn vocabulary(out: &mut String) {
out.push_str("## well-known シンボル (typelisp_mem::BUILTIN_SYMBOLS)\n\n| 番号 | 名前 |\n|---|---|\n");
for (i, name) in typelisp_mem::BUILTIN_SYMBOLS.iter().enumerate() {
writeln!(out, "| {} | `{}` |", i, name).unwrap();
}
out.push_str("\n## 組み込み型キー (typelisp_mem::BUILTIN_TYPE_KEYS)\n\n| 番号 | 型キー |\n|---|---|\n");
for (i, key) in typelisp_mem::BUILTIN_TYPE_KEYS.iter().enumerate() {
writeln!(out, "| {} | `{}` |", i, key).unwrap();
}
out.push_str("\n## 実行時が組み立てる値の型キー (typelisp_rt)\n\n| 表 | 関数 | 型キー |\n|---|---|---|\n");
let tables: [(&str, &[(&str, &str)]); 5] = [
("sys_builtin::RESULT_KEYS", typelisp_rt::sys_builtin::RESULT_KEYS),
("stream_builtin::RESULT_KEYS", typelisp_rt::stream_builtin::RESULT_KEYS),
("stream_builtin::INNER_KEYS", typelisp_rt::stream_builtin::INNER_KEYS),
("net_builtin::RESULT_KEYS", typelisp_rt::net_builtin::RESULT_KEYS),
("net_builtin::INNER_KEYS", typelisp_rt::net_builtin::INNER_KEYS),
];
for (table, rows) in tables {
for (name, key) in rows {
writeln!(out, "| {} | `{}` | `{}` |", table, name, key).unwrap();
}
}
writeln!(out, "| readtable::READER_MACRO_OPTION_KEY | | `{}` |", typelisp_rt::readtable::READER_MACRO_OPTION_KEY).unwrap();
out.push('\n');
}
#[derive(Default)]
struct Defs {
types: BTreeMap<String, AdtDef>,
fns: BTreeMap<String, (Path, FnSig)>,
}
fn collect(ns: &Namespace, at: &mut Vec<String>, defs: &mut Defs) {
for def in ns.types.values().filter(|d| d.builtin) {
defs.types.insert(def.name.to_string(), def.clone());
}
for (name, sig) in ns.fns.iter().filter(|(_, s)| s.builtin) {
let mut segs = at.clone();
segs.push(name.clone());
let path = Path::from_segments(segs);
defs.fns.insert(path.to_string(), (path, sig.clone()));
}
for (name, child) in &ns.modules {
at.push(name.clone());
collect(child, at, defs);
at.pop();
}
}
fn reprs(out: &mut String, defs: &Defs) {
let kinds: BTreeMap<String, AdtKind> = defs.types.iter().map(|(k, d)| (k.clone(), d.kind)).collect();
let kind_of = |p: &Path| kinds.get(&p.to_string()).copied();
let mut seen: BTreeMap<String, Type> = BTreeMap::new();
let mut note = |t: &Type| {
seen.entry(t.to_string()).or_insert_with(|| t.clone());
};
for (_, sig) in defs.fns.values() {
sig_types(sig, &mut note);
}
for def in defs.types.values() {
for v in &def.variants {
v.fields.iter().for_each(&mut note);
}
for f in def.assoc.values() {
sig_types(&f.sig, &mut note);
}
}
out.push_str("## 型ごとの表現 (Repr)\n\n| 型 | 型キー | Repr | field_kind | binding_kind |\n|---|---|---|---|---|\n");
for (name, ty) in &seen {
let repr = Repr::of_by(ty, &kind_of);
writeln!(
out,
"| `{}` | `{}` | {} | {} | {} |",
name,
typelisp_front::type_key::type_key_of_type(ty),
repr.tag(),
repr.field_kind(),
repr.binding_kind()
)
.unwrap();
}
out.push('\n');
}
fn sig_types(sig: &FnSig, note: &mut impl FnMut(&Type)) {
sig.params.iter().for_each(&mut *note);
note(&sig.ret);
if let Some(rest) = &sig.rest {
note(rest);
}
for p in sig.optionals.iter().chain(&sig.keys) {
note(&p.decl_ty);
}
}
fn shims(out: &mut String) {
const CLASSIC: &str = "i64 (ptr, i32)";
let mut rows: BTreeMap<&str, &str> = super::externs::rt_extern_functions().iter().map(|(n, _)| (*n, CLASSIC)).collect();
for (name, ty) in [("rt_heap_init", CLASSIC), ("rt_run_entry", "i64 (i64)"), ("rt_drive_body", CLASSIC)] {
rows.insert(name, ty);
}
out.push_str("## ランタイム関数\n\n| シンボル | LLVM の型 |\n|---|---|\n");
for (name, ty) in rows {
writeln!(out, "| `{}` | `{}` |", name, ty).unwrap();
}
out.push('\n');
}
fn types(out: &mut String, defs: &Defs) {
out.push_str("## 組み込み型\n\n");
for (name, def) in &defs.types {
let kind = match def.kind {
AdtKind::Sum => "sum",
AdtKind::Struct => "struct",
};
writeln!(out, "### `{}` ({}, 型引数 [{}])\n", name, kind, def.params.join(", ")).unwrap();
for (i, v) in def.variants.iter().enumerate() {
let fields: Vec<String> = v.fields.iter().map(|t| format!("`{}`", t)).collect();
writeln!(out, "- 変種 {} `{}`: {}", i, v.name, fields.join(", ")).unwrap();
}
if !def.field_names.is_empty() {
writeln!(out, "- フィールド名: {}", def.field_names.iter().map(|f| format!("`{}`", f)).collect::<Vec<_>>().join(", ")).unwrap();
}
for (method, f) in def.assoc.iter().filter(|(_, f)| f.builtin) {
let receiver = if f.instance { "インスタンス" } else { "関連関数" };
writeln!(out, "- {} `{}`: {}", receiver, method, sig_text(&f.sig)).unwrap();
}
out.push('\n');
}
}
fn functions(out: &mut String, defs: &Defs) {
out.push_str("## 組み込み関数\n\n| 関数 | シム | シグネチャ |\n|---|---|---|\n");
for (name, (path, sig)) in &defs.fns {
let shim = super::externs::builtin_shim(path).map(|s| format!("`{}`", s)).unwrap_or_default();
writeln!(out, "| `{}` | {} | {} |", name, shim, sig_text(sig).replace('|', "\\|")).unwrap();
}
out.push('\n');
}
fn sig_text(sig: &FnSig) -> String {
let mut params: Vec<String> = sig.params.iter().map(|t| t.to_string()).collect();
if !sig.optionals.is_empty() {
params.push("&optional".to_string());
params.extend(sig.optionals.iter().map(|p| format!("{}: {}", p.name, p.decl_ty)));
}
if !sig.keys.is_empty() {
params.push("&key".to_string());
params.extend(sig.keys.iter().map(|p| format!("{}: {}", p.name, p.decl_ty)));
}
if let Some(rest) = &sig.rest {
params.push(format!("&rest {}", rest));
}
let generics = if sig.type_params.is_empty() { String::new() } else { format!("<{}> ", sig.type_params.join(", ")) };
let mut text = format!("`{}({}) -> {}`", generics, params.join(", "), sig.ret);
for (param, bounds) in &sig.bounds {
for b in bounds {
write!(text, " where `{}: {}`", param, b.trait_path).unwrap();
for (assoc, ty) in &b.assoc {
write!(text, " (`{}` = `{}`)", assoc, ty).unwrap();
}
}
}
text
}