Compare commits

...
This repository has been archived on 2024-05-26. You can view files and clone it, but cannot push or open issues or pull requests.

30 Commits

Author SHA1 Message Date
Benedikt Peetz bbd6bd60bb
chore(c_test): Remove the POC
The feature is implemented, thus it has served it's purpose.
2024-05-20 15:31:09 +02:00
Benedikt Peetz ca0efecfde
chore(treewide): Add missing license headers 2024-05-20 15:31:08 +02:00
Benedikt Peetz 051e15b0ed
fix(example/main): Update to use the new result type 2024-05-20 15:31:06 +02:00
Benedikt Peetz a33aa053d2
feat(src): Add real and working support for results
Every `Result` is now -- on demand -- instantiated (i.e. the generic types
are replaced with the concretely specified ones). And can thus simply be
exported to c.
2024-05-20 15:31:05 +02:00
Benedikt Peetz 5d3afa3c5a
build(update.sh): Don't always perform the expensive `upgrade` 2024-05-20 15:31:04 +02:00
Benedikt Peetz 527727cbd3
docs(grammar.pdf): Regenerate with new landscape ebnf2pdf script 2024-05-20 15:31:02 +02:00
Benedikt Peetz 5a3f337057
fix(src/parsing): Remove the extra `error` attribute keyword
This keyword marked an enum as being an error, which is exactly what
`derive(Error)` already does. Thus this extra attribute keyword has been
removed and the `error` name has been used to rename the `msg` attribute
keyword, to be in-line with what `thiserror` does.
2024-05-20 15:31:01 +02:00
Benedikt Peetz d27eea711a
tests(error_derive): Init 2024-05-20 15:31:01 +02:00
Benedikt Peetz 4feae61a76
fix(parser/unchecked/attributes): Correctly enforce an identifier in `derive`
The ebnf grammar specifies an identifier (namely “Error” in the
`derive` attribute.), while the parser expected a string literal there.
2024-05-20 15:30:58 +02:00
Benedikt Peetz 9d14d2e061
refactor(example/main/.cargo/config): Add a `.toml` extension
Cargo is deprecating the old name (i.e. without the `.toml` extension).
2024-05-20 15:30:54 +02:00
Benedikt Peetz ecb8d4d2f0
build(treewide): Update 2024-05-20 15:30:54 +02:00
Benedikt Peetz 6a4c224c64
tests(results): Add a test for results 2024-05-20 15:30:53 +02:00
Benedikt Peetz 5dbc5e676d
docs(c_test): Add a poc regarding results in c 2024-05-20 15:30:51 +02:00
Benedikt Peetz 31493968b5 tests(src/types/newtypes): Test if a full round-trip works 2024-05-04 21:10:35 +02:00
Benedikt Peetz c2ef7d0a59 tests(tests): Update to new String wrapper 2024-05-04 10:40:17 +02:00
Benedikt Peetz df146b3fa6 chore(version): v0.1.1 2024-05-04 10:26:23 +02:00
Benedikt Peetz b750bb8495 chore(version): v0.1.0 2024-05-04 10:24:33 +02:00
Benedikt Peetz d57beeebd4 build(cog): Avoid building the project with nix, as this fails 2024-05-04 10:23:59 +02:00
Benedikt Peetz ec11a129bd fix(gitignore): Don't ignore the `Cargo.lock` file 2024-05-04 10:22:46 +02:00
Benedikt Peetz ddd9255c2e build(cog): Remove wrong toml header 2024-05-04 10:22:03 +02:00
Benedikt Peetz 07c38468ce chore(treewide): Renew copyright headers 2024-05-04 10:20:25 +02:00
Benedikt Peetz c7b8fb3f86 build(cog): Use new `renew_copyright_header` script 2024-05-04 10:19:57 +02:00
Benedikt Peetz 0a60c894d2 feat!(src/macros): Store allocation location in all strings
This makes it possible to automatically free the string in the correct
way expected by both c and rust.
Rust strings can now just be freed by the rust allocator, whilst c
strings are forgotten, so that the c allocator may free them.

BREAKING CHANGE: All usage of explicit mentions `std::string::String`
                 in your `handle_cmd` function will need to be replaced
                 by `trixy::types::newtypes::String`. Otherwise these
                 types should provide the same methods.
2024-05-04 10:12:20 +02:00
Benedikt Peetz 86427b0c1a refactor(src/macros/generate/host): Merge host generation in one module 2024-05-04 08:23:36 +02:00
Benedikt Peetz 0774ac2124 chore(Cargo.toml): Add further required metadata 2024-05-03 20:02:17 +02:00
Benedikt Peetz 475e7edadf chore(Cargo.toml): Add required metadata 2024-05-03 19:32:44 +02:00
Benedikt Peetz 366efd8331
docs(README): Mention the binary 2024-03-27 23:04:14 +01:00
Benedikt Peetz 1e078fa46d
tests(types): Update to represent a minimal change in the library 2024-03-27 23:03:45 +01:00
Benedikt Peetz 06c61ade54
docs(README): Add 2024-03-27 22:53:08 +01:00
Benedikt Peetz 6bed2a843a
fix(example/main): Ignore generated api.rs debugging file 2024-03-27 22:36:26 +01:00
56 changed files with 2149 additions and 592 deletions

4
.gitignore vendored
View File

@ -25,7 +25,3 @@
# dev env
.direnv
.ccls-cache
# trixy is a library
Cargo.lock

426
Cargo.lock generated Normal file
View File

@ -0,0 +1,426 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [
"memchr",
]
[[package]]
name = "anstream"
version = "0.6.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
[[package]]
name = "anstyle-parse"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19"
dependencies = [
"anstyle",
"windows-sys",
]
[[package]]
name = "bitflags"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
[[package]]
name = "clap"
version = "4.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
dependencies = [
"clap_builder",
"clap_derive",
]
[[package]]
name = "clap_builder"
version = "4.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_derive"
version = "4.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "clap_lex"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
[[package]]
name = "colorchoice"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
[[package]]
name = "convert_case"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "diff"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
[[package]]
name = "getopts"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
dependencies = [
"unicode-width",
]
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "is_terminal_polyfill"
version = "1.70.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
[[package]]
name = "libc"
version = "0.2.155"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
[[package]]
name = "log"
version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
[[package]]
name = "memchr"
version = "2.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
[[package]]
name = "pretty_assertions"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66"
dependencies = [
"diff",
"yansi",
]
[[package]]
name = "prettyplease"
version = "0.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e"
dependencies = [
"proc-macro2",
"syn",
]
[[package]]
name = "proc-macro2"
version = "1.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b"
dependencies = [
"unicode-ident",
]
[[package]]
name = "pulldown-cmark"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8746739f11d39ce5ad5c2520a9b75285310dbfe78c541ccf832d38615765aec0"
dependencies = [
"bitflags",
"getopts",
"memchr",
"pulldown-cmark-escape",
"unicase",
]
[[package]]
name = "pulldown-cmark-escape"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "007d8adb5ddab6f8e3f491ac63566a7d5002cc7ed73901f72057943fa71ae1ae"
[[package]]
name = "quote"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "syn"
version = "2.0.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ad3dee41f36859875573074334c200d1add8e4a87bb37113ebd31d926b7b11f"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "1.0.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "trixy"
version = "0.1.1"
dependencies = [
"clap",
"convert_case",
"libc",
"log",
"pretty_assertions",
"prettyplease",
"proc-macro2",
"pulldown-cmark",
"quote",
"regex",
"syn",
"thiserror",
]
[[package]]
name = "unicase"
version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
dependencies = [
"version_check",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "unicode-segmentation"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
[[package]]
name = "unicode-width"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6"
[[package]]
name = "utf8parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
[[package]]
name = "windows_i686_gnu"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
[[package]]
name = "windows_i686_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
[[package]]
name = "yansi"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"

View File

@ -20,29 +20,34 @@
[package]
name = "trixy"
version = "0.1.0"
version = "0.1.1"
edition = "2021"
description = "A rust crate used to generate multi-language apis for your application"
license = "LGPL-3.0-or-later"
repository = "https://git.nerdcult.net/trinitrix/trixy"
categories = ["config", "compilers", "development-tools::ffi"]
keywords = ["ffi", "c-ffi", "api"]
[dependencies]
clap = { version = "4.5.4", features = ["derive"], optional = true }
convert_case = { version = "0.6.0", optional = true }
proc-macro2 = { version = "1.0.79", optional = true }
quote = { version = "1.0.35", optional = true }
syn = { version = "2.0.55", features = [
proc-macro2 = { version = "1.0.82", optional = true }
quote = { version = "1.0.36", optional = true }
syn = { version = "2.0.64", features = [
"extra-traits",
"full",
"parsing",
], optional = true }
thiserror = { version = "1.0.58", optional = true }
pulldown-cmark = { version = "0.10.0", optional = true }
thiserror = { version = "1.0.61", optional = true }
pulldown-cmark = { version = "0.11.0", optional = true }
# macros
prettyplease = { version = "0.2.17", optional = true }
prettyplease = { version = "0.2.20", optional = true }
# parser
regex = { version = "1.10.4", optional = true }
# types
libc = { version = "0.2.153", optional = true }
libc = { version = "0.2.155", optional = true }
log = { version = "0.4.21", optional = true }
[dev-dependencies]

175
NEWS.md Normal file
View File

@ -0,0 +1,175 @@
<!--
Copyright (C) 2023 - 2024:
The Trinitrix Project <soispha@vhack.eu, antifallobst@systemausfall.org>
SPDX-License-Identifier: LGPL-3.0-or-later
This file is part of the Trixy crate for Trinitrix.
Trixy is free software: you can redistribute it and/or modify
it under the terms of the Lesser GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
and the Lesser GNU General Public License along with this program.
If not, see <https://www.gnu.org/licenses/>.
-->
# Changelog
All notable changes to this project will be documented in this file. See [conventional commits](https://www.conventionalcommits.org/) for commit guidelines.
______________________________________________________________________
## v0.1.0 - 2024-05-04
#### Bug Fixes
- **(bin/generate)** Actually only generate what was specified - (08df8e8) - *soispha*
- **(binary/parse)** Actually only perform the other steps, if asked for it - (4e3bdf2) - *soispha*
- **(docs/generate_docs)** Cd into the correct directory to make ebnf2pdf work - (9a0720e) - *soispha*
- **(example/main)** Ignore generated api.rs debugging file - (6bed2a8) - *soispha*
- **(generate/convert/host/rust/derive/structure)** Ignore `ptr` arg on todo - (bd0b3c7) - *soispha*
- **(gitignore)** Don't ignore the `Cargo.lock` file - (ec11a12) - Benedikt Peetz
- **(macros)** Mark the main generate function as must_use - (db5cd1a) - *soispha*
- **(macros)** Generate namespacesed types and convertible impls - (8b02d13) - *soispha*
- **(macros/c_api)** Add host generation support for structs and enums - (5fe757f) - *soispha*
- **(macros/c_api/header)** Format doc comments in a c-like way - (17fd954) - *soispha*
- **(macros/config/file_tree/markdown)** Change format to align with mdfmt - (5855d9b) - *soispha*
- **(macros/generate)** Correctly generate function types - (9a8ccc9) - *soispha*
- **(macros/generate/auxiliary/c/function)** Also add inputs when no output - (3d35d39) - *soispha*
- **(macros/generate/convert/auxiliary/c)** Correctly generate function types - (a66c687) - *soispha*
- **(macros/generate/convert/auxiliary/c/arguments)** Accept newlines - (bfeb620) - *soispha*
- **(macros/generate/host)** Add unified support for conversions - (b5f5ae8) - *soispha*
- **(macros/generate/host/c/function)** Actually call the callback function - (645b8d5) - *soispha*
- **(macros/generate/host/host/type)** The function type is a c pointer - (a58030c) - *soispha*
- **(parser/error)** Remove multiple errors from line number calc - (e512352) - *soispha*
- **(parser/lexing/error)** Improve the message of the `NoMatchesTaken` error - (ce1e4d8) - *soispha*
- **(parser/lexing/take_until_sucessive_match)** Add utf8 support - (fae3de8) - *soispha*
- **(parser/tokenizer)** Remove the tokenizer's death, when exposed to doc comments - (508dc2b) - *soispha*
- **(parser/unchecked/parse_structure)** Accept attributes on fields - (7a8dc66) - *soispha*
- **(scripts/renew_copyright_header)** Set correct template line lenght - (00a82ac) - *soispha*
- **(scripts/renew_copyright_header)** Add support for gitignore files - (9e7e9d7) - *soispha*
- **(scripts/renew_copyright_header)** Correctly handle shebangs - (3646e4a) - *soispha*
- **(scripts/renew_copyright_header)** Add per filetype handling - (3484ead) - *soispha*
- **(trixy)** Add thiserror dependency, as it's used in generated code - (e72c212) - *soispha*
- **(trixy)** Use public reexports - (b9e2641) - *soispha*
- **(trixy-macros)** Format the generated header with in the GNU style - (7c27cdc) - *soispha*
- **(trixy-parser)** Remove the `void` type - (a077ef1) - *soispha*
- **(trixy-types)** Conform to the api provided by the headers - (86b946b) - *soispha*
- **(trixy-types)** Rework c header files - (f699ca2) - *soispha*
- **(trixy/examples/main)** Improve c code - (5ce46a1) - *soispha*
- **(types)** Remove top-level support for generic types - (37ba451) - *soispha*
- **(types)** Improve support for generic rust types - (c2d21fd) - *soispha*
- **(types/error)** Add a pseudo error for infallible conversions - (9e37456) - *soispha*
- **(types/types_list)** Add the supported primitive types - (febb2de) - *soispha*
#### Build system
- **(.envrc)** Add `./target/debug` to PATH - (9233a13) - *soispha*
- **(.licensure.yml)** Update to really be usable - (cc50ec2) - *soispha*
- **(cog)** Avoid building the project with nix, as this fails - (d57beee) - Benedikt Peetz
- **(cog)** Remove wrong toml header - (ddd9255) - Benedikt Peetz
- **(cog)** Use new `renew_copyright_header` script - (c7b8fb3) - Benedikt Peetz
- **(cog.toml)** Use `nix fmt` (treefmt) instead of only `cargo fmt` - (86d0060) - *soispha*
- **(flake)** Update - (596197b) - *soispha*
- **(flake)** Update - (9630e10) - *soispha*
- **(flake)** Add treefmt - (1b8a33c) - *soispha*
- **(flake)** De-duplicate the `flake.lock` file - (3e59d53) - *soispha*
- **(licensure)** Add c headers to known files - (6e26bec) - *soispha*
- **(licensure)** Update to a better license template - (1a2267b) - *soispha*
- **(scripts/renew_copyright_header)** Init - (e9efc23) - *soispha*
- **(treewide)** Add relevant configuration for releases - (22904c9) - *soispha*
- **(update.sh)** Include the main example in the updates - (2e2028d) - *soispha*
- **(update.sh)** Add a shebang - (8104e80) - *soispha*
- **(update.sh)** Add an update script - (80c6dd4) - *soispha*
#### Documentation
- **(README)** Mention the binary - (366efd8) - *soispha*
- **(README)** Add - (06c61ad) - *soispha*
- **(example)** Update the example c and tri file to latest progress - (f688df1) - *soispha*
- **(example/main)** Update to include newly added callbacks - (7f6c76a) - *soispha*
- **(grammar.ebnf)** Don't reimplement already specified rules - (838181c) - *soispha*
- **(lib)** Add a description to the oneshot-senders - (e205ea4) - *soispha*
- **(macros/generate)** Improve error output - (73416da) - *soispha*
- **(parser/examples)** Add various examples (also the trinitrix main api) - (0ebbc43) - *soispha*
- **(parser/generate_docs)** Move shebang to first line - (1d49bdf) - *soispha*
- **(trixy-parser)** Update the railroad diagrams - (88aa3c3) - *soispha*
#### Features
- **(bin)** Add a binary, that helps in showing the generated code - (ec929da) - *soispha*
- **(example/valgrind)** Add a script to test the c example with valgrind - (eb31567) - *soispha*
- **(macros/c_api/header)** Add structs and enums to the c header - (6e72b7b) - *soispha*
- **(macros/config/file_tree)** Add support for parsing a FileTree from file - (89fd67c) - *soispha*
- **(macros/generate/convert/auxiliary)** Merge comments in c - (bb101c1) - *soispha*
- **(parser)** Associate a vector of nasps with every struct, enum, nasp - (a50936f) - *soispha*
- **(parser)** Add support for parsing function pointer types - (f73d3aa) - *soispha*
- **(parser)** Add support for parsing attributes - (add0d17) - *soispha*
- **(parser/bin)** Allow `lex` as an alias to `tokenize` - (21b0ce1) - *soispha*
- **(parser/lexing)** Desuger doc comments by running a regex on the file - (f1e9087) - *soispha*
- **(treewide)** Finalize basic c API - (e52f74b) - *soispha*
- **(treewide)** Add broken Vec<E>, Result\<T,E> and Option<T> types to c api - (b3c6a4c) - *soispha*
- **(treewide)** Provide a c api - (7d1a41a) - *soispha*
- **(trixy-macros)** Add rust host code generation - (ed96a50) - *soispha*
- **(trixy-parser)** Adapt Trixy to be a complete subset of rust - (b679987) - *soispha*
- **(types/try_from_impl)** Add a trixy::String from &str impl - (c69c00e) - *soispha*
#### Miscellaneous Chores
- **(.licensure.yml)** Remove unnecessary line in header comment - (dc000f2) - *soispha*
- **(.licensure.yml)** Add license spdx identifier - (7a95fa7) - *soispha*
- **(.licensure.yml)** Also add the license text to markdown files - (2f7be6f) - *soispha*
- **(Cargo.toml)** Add further required metadata - (0774ac2) - Benedikt Peetz
- **(Cargo.toml)** Add required metadata - (475e7ed) - Benedikt Peetz
- **(docs/generate_docs)** Add a `.sh` extension, so scripts recognize the ft - (a653dec) - *soispha*
- **(example)** Switch to full trinitrix API - (d2d18d9) - *soispha*
- **(parser/docs/grammar.pdf)** Update file - (c898c48) - *soispha*
- **(treewide)** Renew copyright headers - (07c3846) - Benedikt Peetz
- **(treewide)** Update license header - (5ab2bbb) - *soispha*
- **(treewide)** Add or update license header - (2b45995) - *soispha*
- **(treewide)** Add the new license header - (5a7bb00) - *soispha*
- **(treewide)** Remove old license header - (7ac6edb) - *soispha*
- **(treewide)** Move the trixy subcrates under one trixy crate - (21e1b75) - *soispha*
- **(treewide)** Add license headers - (0744c84) - *soispha*
- **(treewide)** Add license headers - (03f2450) - *soispha*
- Initial commit - (233fa3e) - soispha
#### Refactoring
- **(example/main/c)** Deduplicate error handling function - (cd5b0c9) - *soispha*
- **(macros/convertible_derive)** Generate the `Convertible` Trait impl - (18034e2) - *soispha*
- **(parser)** Use predictable names for tokens - (a4513e8) - *soispha*
- **(src/macros/generate/host)** Merge host generation in one module - (86427b0) - Benedikt Peetz
- **(treewide)** Rework file structure in `src/macros` - (eb7a901) - *soispha*
- **(types/headers/string)** Don't typedef a string type - (938e038) - *soispha*
#### Style
- **(flake)** Use yamlfmt instead of prettier - (4110f65) - *soispha*
- **(flake)** Add even more formatters - (fa6b046) - *soispha*
- **(treewide)** Format - (a766149) - *soispha*
- **(treewide)** format - (5ada3cb) - *soispha*
- **(treewide)** Reformat with treefmt (`nix fmt`) - (d904a19) - *soispha*
- **(treewide)** Reformat - (03f8eda) - *soispha*
#### Tests
- **(multiple)** Ignore, as it's not a important feature - (b1d47de) - *soispha*
- **(parser/checked/test)** Update test case - (0a6159e) - *soispha*
- **(scripts)** Add a script, which will help with generating and updating tests - (b85650e) - *soispha*
- **(tests)** Update `expected.md` files - (5a9de1a) - *soispha*
- **(tests)** Update - (475dde2) - *soispha*
- **(tests)** Add the positive tests (and delete the ones in rust) - (d48456d) - *soispha*
- **(trixy-parser)** Restore test functionality after nasp -> mod rename - (dc8a7ec) - *soispha*
- Add new multiline display for c auxiliary code - (055cf2d) - soispha
______________________________________________________________________
Changelog generated by [cocogitto](https://github.com/cocogitto/cocogitto).

68
README.md Normal file
View File

@ -0,0 +1,68 @@
<!--
Copyright (C) 2023 - 2024:
The Trinitrix Project <soispha@vhack.eu, antifallobst@systemausfall.org>
SPDX-License-Identifier: LGPL-3.0-or-later
This file is part of the Trixy crate for Trinitrix.
Trixy is free software: you can redistribute it and/or modify
it under the terms of the Lesser GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
and the Lesser GNU General Public License along with this program.
If not, see <https://www.gnu.org/licenses/>.
-->
# Trixy
A crate to generate a multi-language foreign function interface.
## Features
- Support for custom types, derived from a set of [primitive types](./src/types/mod.rs).
- Support for callbacks, thought c function pointers.
- Support for error handling, when checking a functions arguments (NULL pointer and such things).
## Supported languages:
- c
## Dependencies
Trixy only needs:
- `clang-format` to format the generated c header files.
## The Trixy Language
The API to generate is specified in a Trixy file. The language of these files is an subset
of rust and should thus be easy to learn and pick up.
A grammar file is provided [here](./docs/grammar.ebnf) encoded in [Extended Backus-Naur Form](https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form).
The grammar file is rendered as railroad diagrams in PDF [here](./docs/grammar.pdf), run [`./docs/generate_docs`](./docs/generate_docs.sh) to regenerate the PDF file.
## Testing
Trixy contains integration tests in the `./tests` directory. These are maintained with the
`./scripts/tests.sh` script. Take a look at it's `--help` output for further usage
information.
This crate also contains a binary, which can help with manual testing and inspecting of the
generated AST. The `cargo run --features 'build-binary' -- --help` output should provide
all required information.
Tests are (as of now) only positive, i.e. they can only test successful situations.
Failing test will probably be supported in the future.
## Contributing
We have a [nix flake](./flake.nix), which provides the required dependencies to work with Trixy and the associated scripts in the `./scripts` directory.
Please also make sure to run the `./scripts/renew_copyright_header.sh` after you added a new file, to ensure that every file contains a license header.

View File

@ -19,14 +19,12 @@
# If not, see <https://www.gnu.org/licenses/>.
tag_prefix = "v"
branch_whitelist = ["main"]
branch_whitelist = ["main", "prime"]
ignore_merge_commits = false
[commit_types]
pre_bump_hooks = [
"licensur -p -i", # update the license header in each file
"nix build", # verify the project builds
"./scripts/renew_copyright_header.sh", # update the license header in each file
# "nix build", # verify the project builds
"nix fmt", # format
"cargo set-version {{version}}", # bump version in Cargo.toml
]
@ -36,8 +34,6 @@ post_bump_hooks = [
"git push origin v{{version}}", # push the new tag to origin
]
[bump_profiles]
[changelog]
path = "NEWS.md"
remote = "git.nerdcult.net"

View File

@ -47,13 +47,14 @@ DocNamedType = {DocComment} {Attribute} NamedType;
# (* This is syntax sugar for a `DocAttribute` *)
DocComment = "///" {ANYTHING} LineEnding;
DocAttribute = "doc" "=" StringLiteral;
Attribute = "#" "[" AttributeValue "]" LineEnding;
AttributeValue = DeriveAttribute | DocAttribute | ErrorAttribute | MsgAttribute;
ErrorAttribute = "error";
MsgAttribute = "msg" "(" StringLiteral ")";
AttributeValue = DeriveAttribute | DocAttribute | ErrorAttribute;
DeriveAttribute = "derive" "(" "Error" ")";
DocAttribute = "doc" "=" StringLiteral;
ErrorAttribute = "error" "=" StringLiteral;
Comment = "//" [ NOT ("/" {ANYTHING} LineEnding) | "//"] {ANYTHING} LineEnding;
LineEnding = "\\n" | "\\r" | "\\r\\n";

Binary file not shown.

View File

@ -21,3 +21,5 @@
/target
/result
/dist
/src/bin/main/generated_api.rs

298
example/main/Cargo.lock generated Normal file
View File

@ -0,0 +1,298 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [
"memchr",
]
[[package]]
name = "bitflags"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "convert_case"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "getopts"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
dependencies = [
"unicode-width",
]
[[package]]
name = "libc"
version = "0.2.155"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
[[package]]
name = "libloading"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19"
dependencies = [
"cfg-if",
"windows-targets",
]
[[package]]
name = "log"
version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
[[package]]
name = "memchr"
version = "2.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
[[package]]
name = "prettyplease"
version = "0.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e"
dependencies = [
"proc-macro2",
"syn",
]
[[package]]
name = "proc-macro2"
version = "1.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b"
dependencies = [
"unicode-ident",
]
[[package]]
name = "pulldown-cmark"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8746739f11d39ce5ad5c2520a9b75285310dbfe78c541ccf832d38615765aec0"
dependencies = [
"bitflags",
"getopts",
"memchr",
"pulldown-cmark-escape",
"unicase",
]
[[package]]
name = "pulldown-cmark-escape"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "007d8adb5ddab6f8e3f491ac63566a7d5002cc7ed73901f72057943fa71ae1ae"
[[package]]
name = "quote"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
[[package]]
name = "syn"
version = "2.0.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ad3dee41f36859875573074334c200d1add8e4a87bb37113ebd31d926b7b11f"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "1.0.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "trixy"
version = "0.1.1"
dependencies = [
"convert_case",
"libc",
"log",
"prettyplease",
"proc-macro2",
"pulldown-cmark",
"quote",
"regex",
"syn",
"thiserror",
]
[[package]]
name = "trixy-example"
version = "0.1.0"
dependencies = [
"libloading",
"trixy",
]
[[package]]
name = "unicase"
version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
dependencies = [
"version_check",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "unicode-segmentation"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
[[package]]
name = "unicode-width"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "windows-targets"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
[[package]]
name = "windows_i686_gnu"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
[[package]]
name = "windows_i686_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"

View File

@ -88,14 +88,24 @@ plugin_main ()
// The API also has full support for custom types (the `Dog` and `TrainedDog`
// types are defined in the `api.tri` file)
struct Dog dog = { .name = "Bruce" };
struct TrainedDog tDog;
Result (TrainedDog, TrainingMistake) tDog;
if (!dogs.train_dog (&tDog, dog))
handle_error ();
println ("Dog %s is now trained with specialization %i", tDog.name,
tDog.training);
// Beware that you need to free them with the appropriate free functions
string_free (tDog.name);
if (tDog.tag == OK)
{
println ("Dog %s is now trained with specialization %i",
tDog.value.ok.name, tDog.value.ok.training);
// Beware that you need to free them with the appropriate free functions
string_free (tDog.value.ok.name);
}
else
{
eprintln (
"The dog could not be trained because of a training mistake: '%d'",
tDog.value.err)
}
// Trixy also supports functions (i.e. callbacks):
enum DogType output_dog;

View File

@ -1,56 +0,0 @@
/*
* Copyright (C) 2023 - 2024:
* The Trinitrix Project <soispha@vhack.eu, antifallobst@systemausfall.org>
* SPDX-License-Identifier: LGPL-3.0-or-later
*
* This file is part of the Trixy crate for Trinitrix.
*
* Trixy is free software: you can redistribute it and/or modify
* it under the terms of the Lesser GNU General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* and the Lesser GNU General Public License along with this program.
* If not, see <https://www.gnu.org/licenses/>.
*/
#include "../dist/interface.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define println(args...) \
printf ("\33[32;1m(plugin):\33[0m \33[34;1m"); \
printf (args); \
printf ("\n\33[0m"); \
fflush (stdout);
#define eprintln(args...) \
printf ("\33[32;1m(plugin):\33[0m\33[31;1m "); \
printf (args); \
printf ("\n\33[0m"); \
fflush (stdout);
void
handle_error ()
{
int error_length = last_error_length ();
char *error = malloc (error_length);
last_error_message (error, error_length);
eprintln ("Encountered error: %s", error);
free (error);
}
int
plugin_main ()
{
if (!trinitrix.api.room_message_send ("Hi!"))
handle_error ();
return EXIT_SUCCESS;
}

View File

@ -29,11 +29,11 @@ enum DogTraining {
Blind,
}
#[error]
#[derive(Error)]
enum TrainingMistake {
#[msg("The dog got bitten")]
#[error = "The dog got bitten"]
GotBitten,
#[msg("The trainer died")]
#[error = "The trainer died"]
Died,
}
@ -57,11 +57,16 @@ mod dogs {
name: String,
training: DogTraining,
}
/// Say hi to a name
fn hi(name: String) -> String;
/// Train a dog
fn train_dog(dog: Dog) -> TrainedDog;
fn train_dog(dog: Dog) -> Result<TrainedDog, TrainingMistake>;
/// Renames a dog, will return the same name again, if the dog didn't accept the new
/// name.
fn rename_dog(dog: Dog, new_name: String) -> Result<Dog, String>;
fn guess_my_favourite_dog(callback: fn(name: String, age: u32) -> Dog) -> DogType;
}

View File

@ -20,18 +20,21 @@
* If not, see <https://www.gnu.org/licenses/>.
*/
use std::{env, ffi::c_int, mem};
use std::{env, ffi::c_int};
use dogs::TrainedDog;
use dogs_c::TrainedDog_c;
use libloading::{Library, Symbol};
use trixy::types::{newtypes, traits::convert_trait::Convertible, String};
use crate::dogs::DogType;
include!(concat!(env!("OUT_DIR"), "/api.rs"));
// run `cargo r --bin api > ./src/bin/main/api.rs` to output the generated api
// mod api;
// pub use api::*;
// run `cargo run --bin api > ./src/bin/main/generated_api.rs` to output the generated api
// pub mod generated_api;
// TODO: Remove this crutch, when trixy accepts a submodule <2024-05-20>
// pub use generated_api::*;
fn handle_cmd(cmd: Commands) {
match cmd {
@ -40,36 +43,46 @@ fn handle_cmd(cmd: Commands) {
let output = format!("Hi {}!", name);
println!("(rust): {}", output);
trixy_output.send(output.into()).expect("Will work");
mem::forget(name);
}
dogs::Dogs::train_dog { trixy_output, dog } => {
dogs::Dogs::train_dog {
trixy_output,
dog: _,
} => {
trixy_output
.send(
TrainedDog {
name: "Willis".into(),
training: DogTraining::Wolf,
}
.try_into()
.unwrap(),
Into::<OurResult<TrainedDog_c, TrainingMistake_c>>::into(
Err(TrainingMistake::Died.into()), // ok variant
// Ok(TrainedDog {
// name: "Willis".into(),
// training: DogTraining::Wolf,
// }
// .into_ptr()),
)
.into_ptr(),
)
.unwrap();
mem::forget(dog);
}
dogs::Dogs::guess_my_favourite_dog {
trixy_output,
callback,
} => {
let fav_dog = callback("Frank".into(), 30);
let dog_name: String = fav_dog.name.clone().try_into().unwrap();
let dog_name: newtypes::String = fav_dog.name.clone().try_into().unwrap();
println!("(rust): They want a dog named: {}", dog_name);
trixy_output.send(DogType::Cat.into()).unwrap();
mem::forget(dog_name);
}
dogs::Dogs::rename_dog {
trixy_output,
dog: _,
new_name,
} => {
trixy_output
.send(Into::<OurResult<Dog_c, String>>::into(Err(new_name.into())).into_ptr())
.unwrap();
}
},
Commands::outstanding { name } => {
println!("(rust): {} is outstanding!", name);
mem::forget(name);
}
}
}

View File

@ -7,11 +7,11 @@
]
},
"locked": {
"lastModified": 1711407199,
"narHash": "sha256-A/nB4j3JHL51ztlMQdfKw6y8tUJJzai3bLsZUEEaBxY=",
"lastModified": 1716080827,
"narHash": "sha256-2R9LBCR8TlMuK4Md9ELwPVuwRQWI3pAh7Nj9e318Hk4=",
"owner": "ipetkov",
"repo": "crane",
"rev": "7e468a455506f2e65550e08dfd45092f0857a009",
"rev": "a7146b04405d93b24a1bac76d93270787872c8be",
"type": "github"
},
"original": {
@ -39,17 +39,17 @@
]
},
"locked": {
"lastModified": 1708194952,
"narHash": "sha256-xko2SsVmDxWLw5RZGUAy4LWuwTggpw66RfaSVZYajKI=",
"lastModified": 1716118706,
"narHash": "sha256-RXlWmKYk+pIPUWMsJCgARELUwJJ/IyENsOmDKhKmVgU=",
"ref": "refs/heads/prime",
"rev": "67f230acb0c83f3dd0d0f3489e7fd0c13d740bb5",
"revCount": 6,
"rev": "de896da1c03934cd0e2bd8da4cb1983a4e777f4c",
"revCount": 9,
"type": "git",
"url": "https://codeberg.org/soispha/ebnf2pdf.git"
"url": "https://codeberg.org/bpeetz/ebnf2pdf.git"
},
"original": {
"type": "git",
"url": "https://codeberg.org/soispha/ebnf2pdf.git"
"url": "https://codeberg.org/bpeetz/ebnf2pdf.git"
}
},
"ebnf2ps": {
@ -152,11 +152,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1711370797,
"narHash": "sha256-2xu0jVSjuKhN97dqc4bVtvEH52Rwh6+uyI1XCnzoUyI=",
"lastModified": 1716097317,
"narHash": "sha256-1UMrLtgzielG/Sop6gl6oTSM4pDt7rF9j9VuxhDWDlY=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "c726225724e681b3626acc941c6f95d2b0602087",
"rev": "8535fb92661f37ff9f0da3007fbc942f7d134b41",
"type": "github"
},
"original": {
@ -188,11 +188,11 @@
]
},
"locked": {
"lastModified": 1711419061,
"narHash": "sha256-+5M/czgYGqs/jKmi8bvYC+JUYboUKNTfkRiesXopeXQ=",
"lastModified": 1716085073,
"narHash": "sha256-3+9gI93XxszWA2+9S2xZfws1QArPX/MC6nahOGpcMB4=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "4c11d2f698ff1149f76b69e72852d5d75f492d0c",
"rev": "cfc8776011bd83508324115d353222475e1601c0",
"type": "github"
},
"original": {
@ -221,11 +221,11 @@
]
},
"locked": {
"lastModified": 1702394821,
"narHash": "sha256-wdq1KUZPUbPAbPXPqrkSjx9POS9XhCgSjIecq6KN7JI=",
"lastModified": 1710519017,
"narHash": "sha256-7C7onkhZiLwzrrf9XfzHEOjht9FmZfGABPbzE8XOYoI=",
"ref": "refs/heads/prime",
"rev": "552419325081adaded55effa255d6f967cfc5245",
"revCount": 118,
"rev": "24713f56f30e755553cac9497258ebc5d73036dc",
"revCount": 180,
"type": "git",
"url": "https://codeberg.org/soispha/shell_library.git"
},
@ -256,11 +256,11 @@
]
},
"locked": {
"lastModified": 1710781103,
"narHash": "sha256-nehQK/XTFxfa6rYKtbi8M1w+IU1v5twYhiyA4dg1vpg=",
"lastModified": 1715940852,
"narHash": "sha256-wJqHMg/K6X3JGAE9YLM0LsuKrKb4XiBeVaoeMNlReZg=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "7ee5aaac63c30d3c97a8c56efe89f3b2aa9ae564",
"rev": "2fba33a182602b9d49f0b2440513e5ee091d838b",
"type": "github"
},
"original": {

View File

@ -61,7 +61,7 @@
};
ebnf2pdf = {
url = "git+https://codeberg.org/soispha/ebnf2pdf.git";
url = "git+https://codeberg.org/bpeetz/ebnf2pdf.git";
inputs = {
nixpkgs.follows = "nixpkgs";
flake-utils.follows = "flake-utils";

View File

@ -25,7 +25,7 @@ use crate::parser::command_spec::{Attribute, DocIdentifier, Enumeration};
impl Enumeration {
pub fn to_auxiliary_c(&self) -> String {
let doc_comments: String = Attribute::to_auxiliary_c_merged(&self.attributes);
let ident = &self.identifier.to_auxiliary_c();
let ident = &self.identifier.to_auxiliary_c(&[]);
let states = self
.states
.iter()

View File

@ -24,15 +24,12 @@ use convert_case::{Case, Casing};
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use crate::parser::command_spec::{Identifier, Variant};
use crate::parser::command_spec::{Identifier, Type, Variant};
mod doc_identifier;
//
// pub use doc_identifier::*;
impl Identifier {
pub fn to_auxiliary_c(&self) -> TokenStream2 {
pub fn to_auxiliary_c(&self, generic_args: &[Type]) -> TokenStream2 {
let ident = self.to_rust_pascalized();
match &self.variant {
Variant::Structure { .. } => {
@ -46,10 +43,17 @@ impl Identifier {
enum #ident
}
}
Variant::Result { .. } => {
let ident = Type::mangle_result_name(generic_args);
quote! {
struct #ident
}
}
Variant::Primitive => match self.name.to_case(Case::Snake).as_str() {
"string" => {
quote! {
const char*
const char *
}
}
// Unsigned
@ -65,14 +69,15 @@ impl Identifier {
// Float
"f_32" => quote! { float },
"f_64" => quote! { double },
// Other (not yet imlemented)
// Others (not yet imlemented)
// ("Option", 1),
// ("Vec", 1),
// ("Result", 2),
other => {
todo!("'{}' is not yet supported", other)
}
},
other => {
unimplemented!("{:#?}", other)
}

View File

@ -24,7 +24,7 @@ use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote};
use crate::parser::command_spec::{
Attribute, Enumeration, Function, Identifier, Namespace, Structure,
Attribute, Enumeration, Function, Identifier, Namespace, Structure, Type,
};
impl Namespace {
@ -44,6 +44,12 @@ impl Namespace {
.map(Enumeration::to_auxiliary_c)
.collect::<Vec<String>>()
.join("\n");
let results: String = self
.select_types_pred(|(ident, _generics)| ident.name.as_str() == "Result")
.iter()
.map(|(_, generics)| Type::to_auxilary_c_result(generics).to_string())
.collect::<Vec<String>>()
.join("\n");
let functions: String = self
.functions
.iter()
@ -56,7 +62,7 @@ impl Namespace {
.map(|nasp| nasp.to_auxiliary_c(&nasps))
.collect();
format! {"{}\n{}\n{}\n{}", enumerations, structures, functions, namespaces}
format! {"{}\n{}\n{}\n{}\n{}", enumerations, structures, results, functions, namespaces}
}
pub fn to_auxiliary_c_full_struct_init(&self, namespaces: &Vec<&Identifier>) -> TokenStream2 {

View File

@ -25,7 +25,7 @@ use crate::parser::command_spec::{Attribute, DocNamedType, Structure};
impl Structure {
pub fn to_auxiliary_c(&self) -> String {
let doc_comments: String = Attribute::to_auxiliary_c_merged(&self.attributes);
let ident = self.identifier.to_auxiliary_c();
let ident = self.identifier.to_auxiliary_c(&[]);
let contents = self
.contents
.iter()

View File

@ -36,9 +36,9 @@ impl Type {
match self {
Type::Typical {
identifier,
generic_args: _,
generic_args,
} => {
let ident = identifier.to_auxiliary_c();
let ident = identifier.to_auxiliary_c(generic_args);
let output = if is_output {
quote! {
*
@ -78,4 +78,21 @@ impl Type {
}
}
}
pub fn to_auxilary_c_result(generic_args: &[Type]) -> TokenStream2 {
let ident = Type::mangle_result_name(&generic_args);
let generic_one = generic_args[0].to_auxiliary_c(false, None);
let generic_two = generic_args[1].to_auxiliary_c(false, None);
quote! {
struct #ident {
enum ResultTag tag;
union {
#generic_one ok;
#generic_two err;
} value;
};
}
}
}

View File

@ -20,12 +20,14 @@
* If not, see <https://www.gnu.org/licenses/>.
*/
use std::iter;
use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote};
use crate::{
macros::config::trixy::TrixyConfig,
parser::command_spec::{Enumeration, Identifier, Namespace, Structure},
parser::command_spec::{Enumeration, Identifier, Namespace, Structure, Type},
};
impl Namespace {
@ -48,11 +50,20 @@ impl Namespace {
let enumerations: TokenStream2 = self.enumerations.iter().map(Enumeration::to_c).collect();
let callback_function = format_ident!("{}", config.callback_function);
let all_results_types: TokenStream2 = self
.select_types_pred(|(identifier, _generic_args)| identifier.name.as_str() == "Result")
.iter()
.map(|(_, generics)| Type::to_c_result(&generics))
.collect();
quote! {
pub mod #ident {
#[allow(unused_imports)]
use crate :: #callback_function;
#all_results_types
#enumerations
#structures
#additional_functions
@ -60,4 +71,81 @@ impl Namespace {
#functions
}
}
/// Get all the types used in this namespaces that conform to the predicate.
/// The predicate get's the identifier of a type and the generic arguments passed.
pub fn select_types_pred(
&self,
pred: fn(&(Identifier, Vec<Type>)) -> bool,
) -> Vec<(Identifier, Vec<Type>)> {
fn filter_type(ty: &Type) -> Vec<(Identifier, Vec<Type>)> {
match ty {
Type::Typical {
identifier,
generic_args,
} => {
let mut output = vec![];
output.extend(generic_args.iter().map(|ga| filter_type(ga)).flatten());
output.push((identifier.to_owned(), generic_args.to_owned()));
// Stop recursion and return
output
}
Type::Function { inputs, output } => {
let mut full_output = vec![];
full_output.extend(
inputs
.iter()
.map(|na| &na.r#type)
.map(|ty| filter_type(&ty))
.flatten(),
);
if let Some(out) = output {
full_output.extend(filter_type(out))
}
full_output
}
}
}
let all_types = self.collect_types();
let all_result_types: Vec<_> = all_types
.iter()
.map(|r#type| filter_type(r#type))
.flatten()
.filter(pred)
.collect();
all_result_types
}
/// Collect a vector of all types used in this namespace
pub fn collect_types(&self) -> Vec<Type> {
let structures_types = self
.structures
.iter()
.map(|st| st.contents.iter().map(|dc| dc.r#type.clone()))
.flatten();
self.functions
.iter()
.map(|r#fn| {
r#fn.inputs
.iter()
.map(|nt| Some(nt.r#type.clone()))
.chain({
if let Some(output) = &r#fn.output {
iter::once(Some(output.clone()))
} else {
iter::once(None)
}
})
.filter_map(|x| x)
})
.flatten()
.chain(structures_types)
.collect()
}
}

View File

@ -21,16 +21,14 @@
*/
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use quote::{format_ident, quote};
use syn::Ident;
use crate::parser::command_spec::{Identifier, NamedType, Type};
mod doc_named_type;
mod named_type;
// pub use doc_named_type::*;
// pub use named_type::*;
impl Type {
pub fn to_c(&self) -> TokenStream2 {
match self {
@ -42,10 +40,84 @@ impl Type {
}
}
pub fn mangle_result_name(generic_args: &[Type]) -> Ident {
assert_eq!(generic_args.len(), 2);
format_ident!(
"result_{}_{}",
generic_args[0].to_string(),
generic_args[1].to_string()
)
}
pub fn to_c_function(inputs: &[NamedType], output: &Option<Box<Type>>) -> TokenStream2 {
Type::to_rust_function(&inputs, &output)
}
pub fn to_c_result(generic_args: &[Type]) -> TokenStream2 {
assert_eq!(generic_args.len(), 2);
let first_generic = generic_args[0].to_c();
let second_generic = generic_args[1].to_c();
let ident = Type::mangle_result_name(&generic_args);
let value_ident = format_ident!("{}_value", ident);
quote! {
#[allow(non_camel_case_types)]
#[repr(C)]
pub union #value_ident {
ok: std::mem::ManuallyDrop<#first_generic>,
err: std::mem::ManuallyDrop<#second_generic>,
}
#[allow(non_camel_case_types)]
#[repr(C)]
pub struct #ident {
tag: trixy::types::ResultTag,
value: #value_ident,
}
impl std::fmt::Debug for #ident {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.tag {
trixy::types::ResultTag::Ok => write!(f, "{:?}", unsafe { &self.value.ok }),
trixy::types::ResultTag::Err => write!(f, "{:?}", unsafe { &self.value.err }),
}
}
}
impl trixy::types::traits::convert_trait::Convertible
for crate::OurResult<#first_generic, #second_generic>
{
type Ptr = #ident;
fn into_ptr(self) -> Self::Ptr {
if self.value.is_ok() {
Self::Ptr {
tag: trixy::types::ResultTag::Ok,
value: #value_ident {
ok: std::mem::ManuallyDrop::new(self.value.expect("Is ok")),
},
}
} else {
Self::Ptr {
tag: trixy::types::ResultTag::Err,
value: #value_ident {
err: std::mem::ManuallyDrop::new(self.value.expect_err("Is err")),
},
}
}
}
fn from_ptr(_ptr: Self::Ptr) -> Result<Self, trixy::types::error::TypeConversionError> {
unreachable!("This should probably not be called?");
}
}
}
}
fn to_c_typical(identifier: &Identifier, generic_args: &Vec<Type>) -> TokenStream2 {
let trixy_build_in_types: Vec<&str> = crate::types::BASE_TYPES
.iter()
@ -78,19 +150,36 @@ impl Type {
#nasp_path #ident
}
} else {
debug_assert_eq!(trixy_build_in_types.len(), 1);
assert_eq!(trixy_build_in_types.len(), 1);
let type_name = trixy_build_in_types
.first()
.expect("The names should not be dublicated, this should be the only value");
.expect("The names should not be duplicated, this should be the only value");
match *type_name {
"Result" => {
let ident_ok = &generic_args.first().expect("This is a result").to_c();
let ident_err = &generic_args.last().expect("This is a result").to_c();
assert_eq!(generic_args.len(), 2);
let namespaces_path = &identifier.variant.to_c_path();
let nasp_path = if namespaces_path.is_empty() {
quote! {
crate ::
}
} else {
let path = namespaces_path;
quote! {
#path ::
}
};
let ident = format_ident!(
"result_{}_{}",
generic_args[0].to_string(),
generic_args[1].to_string()
);
quote! {
// eg: <Result<TrainedDog, TrainingMistake> as Convertible>::Ptr,
<Result<#ident_ok, #ident_err> as Convertible>::Ptr
#nasp_path #ident
}
}
"Option" => {

View File

@ -45,13 +45,12 @@ impl Variant {
let main_namespace;
match self {
Variant::Structure { namespace } => {
Variant::Structure { namespace }
| Variant::Enumeration { namespace }
| Variant::Result { namespace } => {
main_namespace = mangle_namespace_name(namespace);
}
Variant::Enumeration { namespace } => {
main_namespace = mangle_namespace_name(namespace);
}
_ => unreachable!("This should never be called"),
other => unreachable!("This should never be called on '{:#?}'", other),
}
Namespace::to_rust_path_owned(&main_namespace[..])
}

View File

@ -23,7 +23,7 @@
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use crate::parser::command_spec::{Attribute, Identifier};
use crate::parser::command_spec::{unchecked::DeriveValue, Attribute, Identifier};
impl Attribute {
pub fn to_rust(&self, _target: &Identifier) -> TokenStream2 {
@ -31,14 +31,17 @@ impl Attribute {
Attribute::doc(comment) => quote! {
#[doc = #comment]
},
Attribute::error => quote! {
// We simply use thiserror here
#[derive(trixy::__private::thiserror::Error)]
},
Attribute::msg(msg) => quote! {
Attribute::error(msg) => quote! {
#[error(#msg)]
},
Attribute::derive(_) => unimplemented!("Derive is not used as of now"),
Attribute::derive(attr) => {
match attr {
DeriveValue::Error => quote! {
// We simply use `thiserror` here
#[derive(trixy::__private::thiserror::Error)]
},
}
}
}
}
}

View File

@ -26,7 +26,6 @@ use quote::quote;
use crate::parser::command_spec::{Function, Identifier, NamedType, Type};
impl Function {
// was called function_identifier_to_rust
pub fn to_rust_identifier<F>(
&self,
input_fmt_fn: fn(&NamedType) -> TokenStream2,

View File

@ -61,32 +61,41 @@ impl Type {
}
}
pub fn to_rust_typical(identifier: &Identifier, generic_args: &Vec<Type>) -> TokenStream2 {
let ident = identifier.to_rust();
let namespaces_path = Variant::to_rust_path(&identifier.variant);
let nasp_path = if let Some(nasp_path) = Variant::to_rust_path(&identifier.variant) {
if nasp_path.is_empty() {
match identifier {
Identifier { name, variant: _ } if name == "String" => {
// We wrap over the string to ensure that we know where it was allocated.
quote! {
crate ::
}
} else {
let path = namespaces_path;
quote! {
#path ::
trixy::types::newtypes::String
}
}
} else {
quote! {}
};
_ => {
let ident = identifier.to_rust();
if generic_args.is_empty() {
quote! {
#nasp_path #ident
}
} else {
let generics: Vec<TokenStream2> = generic_args.iter().map(Type::to_rust).collect();
quote! {
#nasp_path #ident <#(#generics),*>
let nasp_path = if let Some(path) = &Variant::to_rust_path(&identifier.variant) {
if path.is_empty() {
quote! {
crate ::
}
} else {
quote! {
#path ::
}
}
} else {
quote! {}
};
if generic_args.is_empty() {
quote! {
#nasp_path #ident
}
} else {
let generics: Vec<TokenStream2> =
generic_args.iter().map(Type::to_rust).collect();
quote! {
#nasp_path #ident <#(#generics),*>
}
}
}
}
}

View File

@ -1,75 +0,0 @@
/*
* Copyright (C) 2023 - 2024:
* The Trinitrix Project <soispha@vhack.eu, antifallobst@systemausfall.org>
* SPDX-License-Identifier: LGPL-3.0-or-later
*
* This file is part of the Trixy crate for Trinitrix.
*
* Trixy is free software: you can redistribute it and/or modify
* it under the terms of the Lesser GNU General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* and the Lesser GNU General Public License along with this program.
* If not, see <https://www.gnu.org/licenses/>.
*/
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use crate::{
macros::config::trixy::TrixyConfig,
parser::command_spec::{CommandSpec, Enumeration, Structure},
};
/// This function generates the main c API provided by Trixy.
/// This works for example like this:
/// Turning this:
/// ```text
/// nasp trinitrix {
/// struct Callback {
/// func: String,
/// timeout: String,
/// };
///
/// enum CallbackPriority {
/// High,
/// Medium,
/// Low,
/// };
///
/// fn execute_callback(callback: Callback, priority: CallbackPriority);
/// }
/// ```
/// to this:
/// ```no_run
/// pub extern "C" fn exectute_callback(callback: Callback, priority: CallbackPriority) {
/// /* Here we simply call your handler function, with the command of the function */
/// }
/// ```
pub fn generate(trixy: &CommandSpec, config: &TrixyConfig) -> TokenStream2 {
let functions: TokenStream2 = trixy
.functions
.iter()
.map(|r#fn| r#fn.to_c(&config, &vec![]))
.collect();
let namespaced_functions: TokenStream2 = trixy
.namespaces
.iter()
.map(|nasp| nasp.to_c(&config, &vec![]))
.collect();
let structures: TokenStream2 = trixy.structures.iter().map(Structure::to_c).collect();
let enumerations: TokenStream2 = trixy.enumerations.iter().map(Enumeration::to_c).collect();
quote! {
#enumerations
#structures
#functions
#namespaced_functions
}
}

View File

@ -22,20 +22,79 @@
use crate::{
macros::{config::trixy::TrixyConfig, generate::host::format_rust},
parser::command_spec::CommandSpec,
parser::command_spec::{CommandSpec, Enumeration, Structure},
};
pub mod host;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
pub fn generate(trixy: &CommandSpec, config: &TrixyConfig) -> String {
let host_rust_code = host::generate(&trixy, &config);
let host_rust_code = generate_code(&trixy, &config);
let rust_code = format_rust(host_rust_code);
format!(
"\
/* C API */\n\
// Workaround rust rules when implementing a foreign trait on a foreign type
#[derive(Debug)]
pub struct OurResult<T, E> {{
value: Result<T, E>,
}}
impl<T, E> From<Result<T, E>> for OurResult<T, E> {{
fn from(value: Result<T, E>) -> Self {{
Self {{ value }}
}}
}}
/* The real C API */\n\
{}",
rust_code
)
}
/// This function generates the main c API provided by Trixy.
/// This works for example like this:
/// Turning this:
/// ```text
/// nasp trinitrix {
/// struct Callback {
/// func: String,
/// timeout: String,
/// };
///
/// enum CallbackPriority {
/// High,
/// Medium,
/// Low,
/// };
///
/// fn execute_callback(callback: Callback, priority: CallbackPriority);
/// }
/// ```
/// to this:
/// ```no_run
/// pub extern "C" fn exectute_callback(callback: Callback, priority: CallbackPriority) {
/// /* Here we simply call your handler function, with the command of the function */
/// }
/// ```
pub fn generate_code(trixy: &CommandSpec, config: &TrixyConfig) -> TokenStream2 {
let functions: TokenStream2 = trixy
.functions
.iter()
.map(|r#fn| r#fn.to_c(&config, &vec![]))
.collect();
let namespaced_functions: TokenStream2 = trixy
.namespaces
.iter()
.map(|nasp| nasp.to_c(&config, &vec![]))
.collect();
let structures: TokenStream2 = trixy.structures.iter().map(Structure::to_c).collect();
let enumerations: TokenStream2 = trixy.enumerations.iter().map(Enumeration::to_c).collect();
quote! {
#enumerations
#structures
#functions
#namespaced_functions
}
}

View File

@ -1,115 +0,0 @@
/*
* Copyright (C) 2023 - 2024:
* The Trinitrix Project <soispha@vhack.eu, antifallobst@systemausfall.org>
* SPDX-License-Identifier: LGPL-3.0-or-later
*
* This file is part of the Trixy crate for Trinitrix.
*
* Trixy is free software: you can redistribute it and/or modify
* it under the terms of the Lesser GNU General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* and the Lesser GNU General Public License along with this program.
* If not, see <https://www.gnu.org/licenses/>.
*/
//! This module is responsible for generating the rust code used to interface with the api.
//! That includes the structs and enums declared in the trixy file and the enum used to describe the
//! command being executed.
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use crate::{
macros::config::trixy::TrixyConfig,
parser::command_spec::{CommandSpec, Enumeration, Namespace, Structure},
};
/// This function turns, for example, the following trixy input into this rust code:
/// ```text
/// nasp trinitrix {
/// struct Callback {
/// func: String,
/// timeout: String,
/// };
///
/// enum CallbackPriority {
/// High,
/// Medium,
/// Low,
/// };
///
/// fn execute_callback(callback: Callback, priority: CallbackPriority) -> String;
/// }
/// ```
/// ```no_run
/// #[derive(Debug)]
/// pub enum Commands {
/// Trinitrix(trinitrix::Trinitrix),
/// }
/// pub mod trinitrix {
/// #[allow(non_camel_case_types)]
/// #[derive(Debug)]
/// struct Callback {
/// func: String,
/// timeout: String,
/// }
/// #[allow(non_camel_case_types)]
/// #[derive(Debug)]
/// enum CallbackPriority {
/// High,
/// Medium,
/// Low,
/// }
/// #[derive(Debug)]
/// pub enum Trinitrix {
/// #[allow(non_camel_case_types)]
/// execute_callback {
/// callback: Callback,
/// priority: CallbackPriority,
/// trixy_output: trixy::oneshot::channel<String>
/// },
/// }
/// }
/// ```
pub fn generate(trixy: &CommandSpec, _config: &TrixyConfig) -> TokenStream2 {
let modules: TokenStream2 = trixy
.namespaces
.iter()
.map(|nasp| nasp.to_rust_module(&vec![]))
.collect();
let structures: TokenStream2 = trixy.structures.iter().map(Structure::to_rust).collect();
let enumerations: TokenStream2 = trixy
.enumerations
.iter()
.map(Enumeration::to_rust)
.collect();
let functions: Vec<TokenStream2> = trixy
.functions
.iter()
.map(|r#fn| r#fn.to_rust(&[]))
.collect();
let namespace_modules: Vec<TokenStream2> = trixy
.namespaces
.iter()
.map(Namespace::to_rust_module_enum)
.collect();
quote! {
#structures
#enumerations
#[derive(Debug)]
pub enum Commands {
#(#functions,)*
#(#namespace_modules),*
}
#modules
}
}

View File

@ -20,14 +20,20 @@
* If not, see <https://www.gnu.org/licenses/>.
*/
use crate::parser::command_spec::CommandSpec;
//! This module is responsible for generating the rust code used to interface with the api.
//! That includes the structs and enums declared in the trixy file and the enum used to describe the
//! command being executed.
use crate::macros::{config::trixy::TrixyConfig, generate::host::format_rust};
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
pub mod host;
use crate::{
macros::{config::trixy::TrixyConfig, generate::host::format_rust},
parser::command_spec::{CommandSpec, Enumeration, Namespace, Structure},
};
pub fn generate(trixy: &CommandSpec, config: &TrixyConfig) -> String {
let host_rust_code = host::generate(&trixy, &config);
let host_rust_code = generate_code(&trixy, &config);
let rust_code = format_rust(host_rust_code);
@ -38,3 +44,85 @@ pub fn generate(trixy: &CommandSpec, config: &TrixyConfig) -> String {
rust_code
)
}
/// This function turns, for example, the following trixy input into this rust code:
/// ```text
/// mod trinitrix {
/// struct Callback {
/// func: String,
/// timeout: String,
/// };
///
/// enum CallbackPriority {
/// High,
/// Medium,
/// Low,
/// };
///
/// fn execute_callback(callback: Callback, priority: CallbackPriority) -> String;
/// }
/// ```
/// ```no_run
/// #[derive(Debug)]
/// pub enum Commands {
/// Trinitrix(trinitrix::Trinitrix),
/// }
/// pub mod trinitrix {
/// #[allow(non_camel_case_types)]
/// #[derive(Debug)]
/// struct Callback {
/// func: String,
/// timeout: String,
/// }
/// #[allow(non_camel_case_types)]
/// #[derive(Debug)]
/// enum CallbackPriority {
/// High,
/// Medium,
/// Low,
/// }
/// #[derive(Debug)]
/// pub enum Trinitrix {
/// #[allow(non_camel_case_types)]
/// execute_callback {
/// callback: Callback,
/// priority: CallbackPriority,
/// trixy_output: trixy::oneshot::channel<String>
/// },
/// }
/// }
/// ```
pub fn generate_code(trixy: &CommandSpec, _config: &TrixyConfig) -> TokenStream2 {
let modules: TokenStream2 = trixy
.namespaces
.iter()
.map(|nasp| nasp.to_rust_module(&vec![]))
.collect();
let structures: TokenStream2 = trixy.structures.iter().map(Structure::to_rust).collect();
let enumerations: TokenStream2 = trixy
.enumerations
.iter()
.map(Enumeration::to_rust)
.collect();
let functions: Vec<TokenStream2> = trixy
.functions
.iter()
.map(|r#fn| r#fn.to_rust(&[]))
.collect();
let namespace_modules: Vec<TokenStream2> = trixy
.namespaces
.iter()
.map(Namespace::to_rust_module_enum)
.collect();
quote! {
#structures
#enumerations
#[derive(Debug)]
pub enum Commands {
#(#functions,)*
#(#namespace_modules),*
}
#modules
}
}

View File

@ -36,9 +36,14 @@ pub enum Variant {
Enumeration {
namespace: Vec<Identifier>,
},
Result {
namespace: Vec<Identifier>,
},
Namespace,
/// The first (implicit) namespace, containing everything
RootNamespace,
Function,
Primitive,
NamedType,
@ -59,7 +64,7 @@ impl Variant {
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct Namespace {
pub name: Identifier,
@ -89,21 +94,21 @@ impl From<Namespace> for CommandSpec {
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct Structure {
pub identifier: Identifier,
pub contents: Vec<DocNamedType>,
pub attributes: Vec<Attribute>,
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct Enumeration {
pub identifier: Identifier,
pub states: Vec<DocIdentifier>,
pub attributes: Vec<Attribute>,
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct Function {
pub identifier: Identifier,
pub inputs: Vec<NamedType>,
@ -206,7 +211,7 @@ pub struct NamedType {
pub r#type: Type,
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct DocNamedType {
pub name: Identifier,
pub r#type: Type,
@ -246,20 +251,19 @@ impl TokenKind {
pub enum Attribute {
#[allow(non_camel_case_types)]
doc(String),
#[allow(non_camel_case_types)]
derive(DeriveValue),
#[allow(non_camel_case_types)]
error,
#[allow(non_camel_case_types)]
msg(String),
error(String),
}
impl From<unchecked::Attribute> for Attribute {
fn from(value: unchecked::Attribute) -> Self {
match value {
unchecked::Attribute::doc { content: name, .. } => Self::doc(name.content),
unchecked::Attribute::derive { value, .. } => Self::derive(value),
unchecked::Attribute::error { .. } => Self::error,
unchecked::Attribute::msg { content, .. } => Self::msg(content.content),
unchecked::Attribute::error { content, .. } => Self::error(content.content),
}
}
}

View File

@ -76,12 +76,12 @@ pub enum Attribute {
content: StringLiteral,
span: TokenSpan,
},
#[allow(non_camel_case_types)]
derive { value: DeriveValue, span: TokenSpan },
#[allow(non_camel_case_types)]
error { span: TokenSpan },
#[allow(non_camel_case_types)]
msg {
error {
content: StringLiteral,
span: TokenSpan,
},
@ -93,7 +93,6 @@ impl Display for Attribute {
Attribute::doc { .. } => f.write_str("doc"),
Attribute::derive { .. } => f.write_str("derive"),
Attribute::error { .. } => f.write_str("error"),
Attribute::msg { .. } => f.write_str("msg"),
}
}
}
@ -104,7 +103,6 @@ impl Attribute {
Attribute::doc { span, .. } => *span,
Attribute::derive { span, .. } => *span,
Attribute::error { span, .. } => *span,
Attribute::msg { span, .. } => *span,
}
}
}

View File

@ -264,7 +264,7 @@ pub enum Keyword {
/// Keywords used in attributes: (#[<keyword>(<value>)])
#[derive(Debug, PartialEq, PartialOrd, Ord, Eq, Clone, Copy)]
pub enum AttributeKeyword {
/// Derive a trait
/// Derive a trait (only `Error` for now)
#[allow(non_camel_case_types)]
derive,
@ -272,13 +272,9 @@ pub enum AttributeKeyword {
#[allow(non_camel_case_types)]
doc,
/// Mark the beginning of an error
/// Add an error message
#[allow(non_camel_case_types)]
error,
/// Encompass an error message
#[allow(non_camel_case_types)]
msg,
}
impl Display for Keyword {
@ -298,7 +294,6 @@ impl Display for AttributeKeyword {
AttributeKeyword::derive => f.write_str("derive"),
AttributeKeyword::doc => f.write_str("doc"),
AttributeKeyword::error => f.write_str("error"),
AttributeKeyword::msg => f.write_str("msg"),
}
}
}

View File

@ -272,7 +272,6 @@ fn tokenize_ident(text: &str) -> Result<(TokenKind, usize), LexingError> {
"derive" => TokenKind::AttributeKeyword(AttributeKeyword::derive),
"doc" => TokenKind::AttributeKeyword(AttributeKeyword::doc),
"error" => TokenKind::AttributeKeyword(AttributeKeyword::error),
"msg" => TokenKind::AttributeKeyword(AttributeKeyword::msg),
other => TokenKind::Identifier(other.to_string()),
};

View File

@ -159,7 +159,7 @@ impl Parser {
let mut functions = vec![];
for function in namespace.functions {
functions.push(self.process_function(function)?);
functions.push(self.process_function(function, &previous_namespaces)?);
}
let mut namespaces = vec![];
for namespace in namespace.namespaces {
@ -187,14 +187,15 @@ impl Parser {
fn process_function(
&mut self,
mut function: UncheckedFunction,
parent_namespaces: &Vec<Identifier>,
) -> Result<Function, ParsingError> {
let identifier = mem::take(&mut function.identifier.kind).to_identifier(Variant::Function);
let mut inputs = vec![];
for input in function.inputs {
inputs.push(self.process_named_type(input)?);
inputs.push(self.process_named_type(input, parent_namespaces)?);
}
let output = if let Some(r#type) = function.output {
Some(self.process_type(r#type)?)
Some(self.process_type(r#type, parent_namespaces)?)
} else {
None
};
@ -243,7 +244,7 @@ impl Parser {
mem::take(&mut state.token.kind).to_identifier(Variant::DocNamedType);
DocIdentifier {
name: ident.name,
attributes: take_attrs! {state, doc, msg},
attributes: take_attrs! {state, doc, error},
variant: Variant::DocNamedType,
}
})
@ -252,7 +253,7 @@ impl Parser {
Ok(Enumeration {
identifier,
states,
attributes: take_attrs! {enumeration, doc, derive, error},
attributes: take_attrs! {enumeration, doc, derive},
})
}
@ -269,7 +270,7 @@ impl Parser {
});
let mut contents = vec![];
for named_type in structure.contents {
contents.push(self.process_doc_named_type(named_type)?);
contents.push(self.process_doc_named_type(named_type, parent_namespaces)?);
}
Ok(Structure {
@ -282,19 +283,21 @@ impl Parser {
fn process_named_type(
&mut self,
mut named_type: UncheckedNamedType,
parent_namespaces: &Vec<Identifier>,
) -> Result<NamedType, ParsingError> {
let name: Identifier =
mem::take(&mut named_type.name.kind).to_identifier(Variant::NamedType);
let r#type: Type = self.process_type(named_type.r#type)?;
let r#type: Type = self.process_type(named_type.r#type, parent_namespaces)?;
Ok(NamedType { name, r#type })
}
fn process_doc_named_type(
&mut self,
mut doc_named_type: UncheckedDocNamedType,
parent_namespaces: &Vec<Identifier>,
) -> Result<DocNamedType, ParsingError> {
let name: Identifier =
mem::take(&mut doc_named_type.name.kind).to_identifier(Variant::DocNamedType);
let r#type: Type = self.process_type(doc_named_type.r#type)?;
let r#type: Type = self.process_type(doc_named_type.r#type, parent_namespaces)?;
Ok(DocNamedType {
name,
r#type,
@ -302,14 +305,18 @@ impl Parser {
})
}
fn process_type(&mut self, r#type: UncheckedType) -> Result<Type, ParsingError> {
fn process_type(
&mut self,
r#type: UncheckedType,
parent_namespaces: &Vec<Identifier>,
) -> Result<Type, ParsingError> {
match r#type {
UncheckedType::Typical {
identifier,
generic_args,
} => self.process_typical_type(identifier, generic_args),
} => self.process_typical_type(identifier, generic_args, parent_namespaces),
UncheckedType::Function { inputs, output } => {
self.process_function_type(inputs, output)
self.process_function_type(inputs, output, parent_namespaces)
}
}
}
@ -318,6 +325,7 @@ impl Parser {
&mut self,
mut type_identifier: Token,
generic_args: Vec<UncheckedType>,
parent_namespaces: &Vec<Identifier>,
) -> Result<Type, ParsingError> {
fn match_to_vector_struct(matches: Vec<&UncheckedStructure>) -> Vec<Identifier> {
matches
@ -401,7 +409,13 @@ impl Parser {
.iter()
.any(|(ident, _)| ident == &identifier.name)
{
variant = Variant::Primitive;
if &identifier.name == "Result" {
variant = Variant::Result {
namespace: parent_namespaces.clone(),
};
} else {
variant = Variant::Primitive;
}
} else {
return Err(ParsingError::TypeNotDeclared {
r#type: identifier,
@ -461,7 +475,7 @@ impl Parser {
let mut new_generic_args: Vec<checked::Type> = vec![];
for generic_arg in generic_args {
new_generic_args.push(self.process_type(generic_arg)?);
new_generic_args.push(self.process_type(generic_arg, parent_namespaces)?);
}
Ok(Type::Typical {
@ -473,15 +487,16 @@ impl Parser {
&mut self,
inputs: Vec<UncheckedNamedType>,
output: Option<Box<UncheckedType>>,
parent_namespaces: &Vec<Identifier>,
) -> Result<Type, ParsingError> {
let inputs = inputs
.into_iter()
.map(|input| self.process_named_type(input))
.map(|input| self.process_named_type(input, parent_namespaces))
.collect::<Result<Vec<NamedType>, ParsingError>>()?;
let mut new_output = None;
if let Some(output) = output {
new_output = Some(Box::new(self.process_type(*output)?));
new_output = Some(Box::new(self.process_type(*output, parent_namespaces)?));
}
Ok(Type::Function {

View File

@ -176,14 +176,6 @@ impl Parser {
}
}
fn parse_bracket_string_literal(&mut self) -> Result<StringLiteral, ParsingError> {
self.expect(token![CurvedBracketOpen])?;
let string_literal = self.expect(token![StringLiteral])?;
self.expect(token![CurvedBracketClose])?;
let string_literal = Into::<StringLiteral>::into(string_literal);
Ok(string_literal)
}
fn parse_attribute_value(&mut self) -> Result<Attribute, ParsingError> {
let ident = self.expect(token![AttributeKeyword])?;
let span = *ident.span();
@ -193,14 +185,26 @@ impl Parser {
let attribute = match keyword {
AttributeKeyword::derive => {
let string_literal = self.parse_bracket_string_literal()?;
match string_literal.content.as_str() {
self.expect(token![CurvedBracketOpen])?;
let string_literal_token = self.expect(token![Identifier])?;
let string_literal =
if let TokenKind::Identifier(ident) = string_literal_token.kind() {
ident
} else {
unreachable! {"The token is a identifier, as checked by the `expect`"};
};
self.expect(token![CurvedBracketClose])?;
match string_literal.as_str() {
"Error" => Ok(Attribute::derive {
value: DeriveValue::Error,
span,
}),
_ => Err(error::ParsingError::WrongDeriveValue {
specified: string_literal,
specified: StringLiteral {
content: string_literal.to_owned(),
span: string_literal_token.span,
},
}),
}
}
@ -208,18 +212,16 @@ impl Parser {
self.expect(token![=])?;
let string_literal = self.expect(token![StringLiteral])?;
let string_literal = Into::<StringLiteral>::into(string_literal);
if self.expect_peek(token![PoundSign]) {
dbg!(&self.token_stream);
}
Ok(Attribute::doc {
content: string_literal,
span,
})
}
AttributeKeyword::error => Ok(Attribute::error { span }),
AttributeKeyword::msg => {
let string_literal = self.parse_bracket_string_literal()?;
Ok(Attribute::msg {
AttributeKeyword::error => {
self.expect(token![=])?;
let string_literal = self.expect(token![StringLiteral])?;
let string_literal = Into::<StringLiteral>::into(string_literal);
Ok(Attribute::error {
content: string_literal,
span,
})

View File

@ -0,0 +1,44 @@
/*
* Copyright (C) 2023 - 2024:
* The Trinitrix Project <soispha@vhack.eu, antifallobst@systemausfall.org>
* SPDX-License-Identifier: LGPL-3.0-or-later
*
* This file is part of the Trixy crate for Trinitrix.
*
* Trixy is free software: you can redistribute it and/or modify
* it under the terms of the Lesser GNU General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* and the Lesser GNU General Public License along with this program.
* If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef TRIXY_RESULT_H
#define TRIXY_RESULT_H
/**
* @brief A rust-like result type
*
* @detail
* This macro effectively generates a type name from the two generic
* parameters. The generated type name is always globally unique.
*/
#define Result(ok, err) struct result_##ok##_##err
/**
* @brief The possibly states of a Result.
*/
enum ResultTag
{
OK,
ERR,
};
#endif // TRIXY_RESULT_H

View File

@ -22,6 +22,7 @@
//! Trixy contains the types used by the [`trixy-macros`] crate to provide ffi safe types
pub mod error;
pub mod newtypes;
pub mod traits;
pub mod types_list;
@ -39,7 +40,7 @@ macro_rules! header {
// NOTE: Every type here must have the [`Convertible`] implemented on it (@soispha)
// And be added to the `convert/c/auxiliary/idendentifier/mod.rs` file
// And add it to the types list (`./types_list.rs`)
pub const BASE_TYPES: [(&'static std::primitive::str, usize); 11] = [
pub const BASE_TYPES: [(&'static std::primitive::str, usize); 12] = [
// Unsigned
("u8", 0),
("u16", 0),
@ -55,15 +56,19 @@ pub const BASE_TYPES: [(&'static std::primitive::str, usize); 11] = [
("f64", 0),
// Other
("String", 0),
("Result", 2),
// FIXME(@soispha): These work, but the generated code is not really ideal <2024-03-26>
// ("Option", 1),
// ("Vec", 1),
// ("Result", 2),
];
/// The first value is the file name, the second it's contents
pub const C_TYPE_HEADER: [(&'static std::primitive::str, &'static std::primitive::str); 3] =
[header!("errno.h"), header!("string.h"), header!("vec.h")];
pub const C_TYPE_HEADER: [(&'static std::primitive::str, &'static std::primitive::str); 4] = [
header!("errno.h"),
header!("string.h"),
header!("vec.h"),
header!("result.h"),
];
pub fn header_names() -> std::string::String {
C_TYPE_HEADER

141
src/types/newtypes/mod.rs Normal file
View File

@ -0,0 +1,141 @@
/*
* Copyright (C) 2023 - 2024:
* The Trinitrix Project <soispha@vhack.eu, antifallobst@systemausfall.org>
* SPDX-License-Identifier: LGPL-3.0-or-later
*
* This file is part of the Trixy crate for Trinitrix.
*
* Trixy is free software: you can redistribute it and/or modify
* it under the terms of the Lesser GNU General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* and the Lesser GNU General Public License along with this program.
* If not, see <https://www.gnu.org/licenses/>.
*/
//! This module contains wrapper types for know rust types, where trixy needs to store additional
//! information
use std::{fmt::Display, mem, string};
#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct String {
pub(crate) data: Option<string::String>,
pub(crate) alloc_location: AllocLocation,
}
#[derive(Clone, Hash, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) enum AllocLocation {
#[default]
Rust,
C,
}
impl From<&str> for String {
fn from(value: &str) -> Self {
Self {
// It's cloned here:
data: Some(value.to_owned()),
// The string is always allocated in rust, as we clone it above this comment
alloc_location: AllocLocation::Rust,
}
}
}
impl From<string::String> for String {
fn from(value: string::String) -> Self {
Self {
data: Some(value),
// We just assume that every std String is actually allocated in rust
alloc_location: AllocLocation::Rust,
}
}
}
impl Display for String {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl String {
pub fn as_str(&self) -> &str {
&self
.data
.as_ref()
.expect("The string should only be empty when it's dropped")
}
pub fn into_std_string(mut self) -> string::String {
match self.alloc_location {
AllocLocation::C => {
// Re-allocate the string in rust
let c_alloc_string = mem::take(&mut self.data).expect("It should only be Some");
let rust_alloc_string = c_alloc_string.clone();
// This is here because of the same reason as in the drop impl: we just can't free
// a string allocated in c.
mem::forget(c_alloc_string);
rust_alloc_string
}
AllocLocation::Rust => {
let string = mem::take(&mut self.data).expect("Will always be some");
string
}
}
}
}
impl Drop for String {
fn drop(&mut self) {
match self.alloc_location {
AllocLocation::C => {
let string = mem::take(&mut self.data);
// C needs to free it's strings by itself. We cannot do the job of the c allocator,
// thus just forget about it here
mem::forget(string)
}
AllocLocation::Rust => {
// A rust allocated string can be free normally. We don't need to do anything here.
}
}
}
}
#[cfg(test)]
mod tests {
use crate::types::types_list;
use super::String;
#[test]
fn test_string_round_trip() {
let start = "HI! I'm a nice string".to_owned();
let wrapper: String = start.clone().into();
assert_eq!(&start, wrapper.as_str());
assert_eq!(start, wrapper.to_string());
assert_eq!(start, wrapper.into_std_string());
}
#[test]
fn test_string_round_trip_through_c() {
let start = "HI! I'm a nice string".to_owned();
let c_string: types_list::String = start.clone().into();
let wrapper: String = Into::<types_list::String>::into(c_string)
.try_into()
.unwrap();
assert_eq!(&start, wrapper.as_str());
assert_eq!(start, wrapper.to_string());
assert_eq!(start, wrapper.into_std_string());
}
}

View File

@ -129,121 +129,93 @@ pub extern "C" fn string_free(ptr: *const c_char) {
CString::drop_ptr(ptr);
}
impl<T: Convertible> Convertible for Option<T> {
type Ptr = *const <T as Convertible>::Ptr;
// impl<T: Convertible> Convertible for Option<T> {
// type Ptr = *const <T as Convertible>::Ptr;
//
// fn into_ptr(self) -> Self::Ptr {
// if let Some(inner) = self {
// &inner.into_ptr()
// } else {
// ptr::null()
// }
// }
//
// fn from_ptr(_: Self::Ptr) -> Result<Self, error::TypeConversionError> {
// warn!(
// "
// This should never be called, as this option type resolves
// to a null pointer or to a pointer to the real value
// (e. g. a `crate::Vec<T>`)
// "
// );
// Ok(Option::default())
// }
// }
fn into_ptr(self) -> Self::Ptr {
if let Some(inner) = self {
&inner.into_ptr()
} else {
ptr::null()
}
}
fn from_ptr(_: Self::Ptr) -> Result<Self, error::TypeConversionError> {
warn!(
"
This should never be called, as this option type resolves
to a null pointer or to a pointer to the real value
(e. g. a `crate::Vec<T>`)
"
);
Ok(Option::default())
}
}
impl<T: Convertible, E: Error> Convertible for Result<T, E> {
type Ptr = *const <T as Convertible>::Ptr;
fn into_ptr(self) -> Self::Ptr {
match self {
Ok(ok) => &ok.into_ptr(),
Err(err) => {
errno::set(TypeConversionError::ResultWasErr {
original_message: err.to_string(),
});
ptr::null()
}
}
}
fn from_ptr(_: Self::Ptr) -> Result<Self, error::TypeConversionError> {
warn!(
"
This should never be called, as this result type resolves
to a null pointer (and a set error) or to a pointer to
the real value (e. g. a `crate::Vec<T>`)
"
);
todo!()
// Ok(Result::Ok(T::default()))
}
}
impl<T: Convertible> Convertible for Vec<T> {
type Ptr = crate::types::Vec<<T as Convertible>::Ptr>;
fn into_ptr(self) -> Self::Ptr {
let data_vec: Vec<_> = self.into_iter().map(|val| val.into_ptr()).collect();
let data_vec = ManuallyDrop::new(data_vec);
Self::Ptr {
data: data_vec.as_ptr(),
length: data_vec.len(),
capacity: data_vec.capacity(),
}
}
fn from_ptr(ptr: Self::Ptr) -> Result<Self, error::TypeConversionError> {
if ptr.data.is_null() {
return Err(TypeConversionError::NullPointer);
}
// TODO(@soispha): Add this, when this feature is stable <2024-02-17>
// if !ptr.data.is_aligned() {
// return Err(TypeConversionError::NotAligned);
// }
let base_vec = unsafe {
// SAFETY:
// We simply hope that c treated our vector as read-only and didn't modify it.
// See the SAFETY section of the String implementation for more detail.
Vec::from_raw_parts(
ptr.data as *mut <T as Convertible>::Ptr,
ptr.length,
ptr.capacity,
)
};
let vec: Vec<_> = base_vec
.into_iter()
.map(|val| <T as Convertible>::from_ptr(val))
.collect::<Result<Vec<_>, _>>()?;
Ok(vec)
}
}
macro_rules! make_vec_free {
($value:ident, $name:ident) => {
#[no_mangle]
pub extern "C" fn $name(ptr: crate::types::Vec<<$value as Convertible>::Ptr>) {
Vec::<$value>::drop_ptr(ptr);
}
};
}
// Unsigned
make_vec_free!(u8, vec_free_u8);
make_vec_free!(u16, vec_free_u16);
make_vec_free!(u32, vec_free_u32);
make_vec_free!(u64, vec_free_u64);
// Signed
make_vec_free!(i8, vec_free_i8);
make_vec_free!(i16, vec_free_i16);
make_vec_free!(i32, vec_free_i32);
make_vec_free!(i64, vec_free_i64);
// Float
make_vec_free!(f32, vec_free_f32);
make_vec_free!(f64, vec_free_f64);
// Other
make_vec_free!(String, vec_free_String);
// impl<T: Convertible> Convertible for Vec<T> {
// type Ptr = crate::types::Vec<<T as Convertible>::Ptr>;
//
// fn into_ptr(self) -> Self::Ptr {
// let data_vec: Vec<_> = self.into_iter().map(|val| val.into_ptr()).collect();
// let data_vec = ManuallyDrop::new(data_vec);
// Self::Ptr {
// data: data_vec.as_ptr(),
// length: data_vec.len(),
// capacity: data_vec.capacity(),
// }
// }
//
// fn from_ptr(ptr: Self::Ptr) -> Result<Self, error::TypeConversionError> {
// if ptr.data.is_null() {
// return Err(TypeConversionError::NullPointer);
// }
// // TODO(@soispha): Add this, when this feature is stable <2024-02-17>
// // if !ptr.data.is_aligned() {
// // return Err(TypeConversionError::NotAligned);
// // }
// let base_vec = unsafe {
// // SAFETY:
// // We simply hope that c treated our vector as read-only and didn't modify it.
// // See the SAFETY section of the String implementation for more detail.
// Vec::from_raw_parts(
// ptr.data as *mut <T as Convertible>::Ptr,
// ptr.length,
// ptr.capacity,
// )
// };
//
// let vec: Vec<_> = base_vec
// .into_iter()
// .map(|val| <T as Convertible>::from_ptr(val))
// .collect::<Result<Vec<_>, _>>()?;
// Ok(vec)
// }
// }
//
// macro_rules! make_vec_free {
// ($value:ident, $name:ident) => {
// #[no_mangle]
// pub extern "C" fn $name(ptr: crate::types::Vec<<$value as Convertible>::Ptr>) {
// Vec::<$value>::drop_ptr(ptr);
// }
// };
// }
//
// // Unsigned
// make_vec_free!(u8, vec_free_u8);
// make_vec_free!(u16, vec_free_u16);
// make_vec_free!(u32, vec_free_u32);
// make_vec_free!(u64, vec_free_u64);
// // Signed
// make_vec_free!(i8, vec_free_i8);
// make_vec_free!(i16, vec_free_i16);
// make_vec_free!(i32, vec_free_i32);
// make_vec_free!(i64, vec_free_i64);
// // Float
// make_vec_free!(f32, vec_free_f32);
// make_vec_free!(f64, vec_free_f64);
// // Other
// make_vec_free!(String, vec_free_String);
// FIXME(@soispha): Find a way to support these <2024-02-27>
// make_vec_free!(Option<?>, vec_free_Option);

View File

@ -20,32 +20,51 @@
* If not, see <https://www.gnu.org/licenses/>.
*/
use crate::types::String;
use crate::types::{
newtypes::{self, AllocLocation},
types_list,
};
use std::ffi::CString;
use super::convert_trait::Convertible;
impl From<std::string::String> for String {
impl From<std::string::String> for types_list::String {
fn from(value: std::string::String) -> Self {
let cstring =
CString::new(value).expect("The input is a valid String, this always succeeds");
let c_char = cstring.into_ptr();
String(c_char)
types_list::String(c_char)
}
}
/// Plainly here for convenience
impl From<&str> for String {
impl From<&str> for types_list::String {
fn from(value: &str) -> Self {
value.to_owned().into()
}
}
impl TryFrom<String> for std::string::String {
type Error = crate::types::error::TypeConversionError;
fn try_from(value: String) -> Result<Self, Self::Error> {
let cstring = CString::from_ptr(value.0)?;
let string = cstring.into_string()?;
Ok(string)
impl From<newtypes::String> for types_list::String {
fn from(value: newtypes::String) -> Self {
// NOTE: This is not really performant, but necessary as we otherwise would need c to
// differentiate between c allocated strings (which are freed by `free`) and rust allocated strings
// (which are freed by `string_free`) <2024-05-04>
let value_string = value.into_std_string();
value_string.into()
}
}
impl TryFrom<types_list::String> for newtypes::String {
type Error = crate::types::error::TypeConversionError;
fn try_from(value: types_list::String) -> Result<Self, Self::Error> {
let cstring = CString::from_ptr(value.0)?;
let string = cstring.into_string()?;
Ok(newtypes::String {
data: Some(string),
// TODO: You could of course do a rust-only round trip, but I think it's reasonable to
// assume, that all types_list::String values come from C <2024-05-04>
alloc_location: AllocLocation::C,
})
}
}

View File

@ -23,24 +23,30 @@
// NOTE(@soispha): All types specified here *MUST* be include in the BASE_TYPES constant, otherwise
// they are not usable from Trixy code <2023-12-25>
// NOTE(@soispha): All types added here must also have an added free impl for the vector type (./traits/convert_trait.rs) <2024-02-27>
use std::ffi::c_char;
#[derive(Debug, Clone)]
#[repr(C)]
pub struct String(pub(crate) *const c_char);
/// The tag used to tag the c result Enumerations
#[derive(Debug)]
#[repr(C)]
pub struct Vec<T> {
/// You should cast this value to it's supposed type (readable from the Trixy api definition
/// file)
pub(crate) data: *const T,
pub(crate) length: usize,
pub(crate) capacity: usize,
pub enum ResultTag {
Ok,
Err,
}
// #[derive(Debug)]
// #[repr(C)]
// pub struct Vec<T> {
// /// You should cast this value to it's supposed type (readable from the Trixy api definition
// /// file)
// pub(crate) data: *const T,
// pub(crate) length: usize,
// pub(crate) capacity: usize,
// }
// Unsigned
pub use std::primitive::u16;
pub use std::primitive::u32;

View File

@ -15,7 +15,10 @@ pub mod trinitrix {
pub enum Trinitrix {
/// Attribute doc comment, but very ##" "## "# " escaped
#[allow(non_camel_case_types)]
hi { trixy_output: trixy::oneshot::Sender<trixy::types::String>, name: String },
hi {
trixy_output: trixy::oneshot::Sender<trixy::types::String>,
name: trixy::types::newtypes::String,
},
}
}
/* C API */

View File

@ -15,7 +15,10 @@ pub mod trinitrix {
pub enum Trinitrix {
/// Second doc comment
#[allow(non_camel_case_types)]
hi { trixy_output: trixy::oneshot::Sender<trixy::types::String>, name: String },
hi {
trixy_output: trixy::oneshot::Sender<trixy::types::String>,
name: trixy::types::newtypes::String,
},
}
}
/* C API */

View File

@ -0,0 +1,75 @@
# Host files
File path: `out/dir/api.rs`
```rust
// Host code
/* Rust API */
#[derive(trixy::__private::thiserror::Error)]
#[allow(non_camel_case_types)]
#[derive(Debug)]
pub enum ErrorOne {
#[error("I'm an error")]
Error,
#[error("I'm also an error'")]
ErrorB,
}
impl From<crate::ErrorOne_c> for ErrorOne {
fn from(value: crate::ErrorOne_c) -> Self {
match value {
crate::ErrorOne_c::Error => Self::Error,
crate::ErrorOne_c::ErrorB => Self::ErrorB,
}
}
}
#[derive(Debug)]
pub enum Commands {}
/* C API */
#[derive(trixy::__private::thiserror::Error)]
#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Debug)]
pub enum ErrorOne_c {
#[error("I'm an error")]
Error,
#[error("I'm also an error'")]
ErrorB,
}
impl From<crate::ErrorOne> for ErrorOne_c {
fn from(value: crate::ErrorOne) -> Self {
match value {
crate::ErrorOne::Error => Self::Error,
crate::ErrorOne::ErrorB => Self::ErrorB,
}
}
}
// vim: filetype=rust
```
# Auxiliary files
File path: `dist/interface.h`
```c
#if !defined TRIXY_MAIN_HEADER
#define TRIXY_MAIN_HEADER
#include "errno.h"
#include "string.h"
#include "vec.h"
/**
*/
enum ErrorOne
{
/**
*/
Error,
/**
*/
ErrorB,
};
#endif // if !defined TRIXY_MAIN_HEADER
// vim: filetype=c
```

View File

@ -0,0 +1,34 @@
/*
* Copyright (C) 2023 - 2024:
* The Trinitrix Project <soispha@vhack.eu, antifallobst@systemausfall.org>
* SPDX-License-Identifier: LGPL-3.0-or-later
*
* This file is part of the Trixy crate for Trinitrix.
*
* Trixy is free software: you can redistribute it and/or modify
* it under the terms of the Lesser GNU General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* and the Lesser GNU General Public License along with this program.
* If not, see <https://www.gnu.org/licenses/>.
*/
#[derive(Error)]
enum ErrorOne {
#[error = "I'm an error"]
Error,
#[error = "I'm also an error'"]
ErrorB,
// TODO: We also need to assert, that all enum veriants have the error attribute. <2024-05-19>
}
// Trixy is sort of a subset of rust
// vim: syntax=rust

View File

@ -0,0 +1,39 @@
/*
* Copyright (C) 2023 - 2024:
* The Trinitrix Project <soispha@vhack.eu, antifallobst@systemausfall.org>
* SPDX-License-Identifier: LGPL-3.0-or-later
*
* This file is part of the Trixy crate for Trinitrix.
*
* Trixy is free software: you can redistribute it and/or modify
* it under the terms of the Lesser GNU General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* and the Lesser GNU General Public License along with this program.
* If not, see <https://www.gnu.org/licenses/>.
*/
use pretty_assertions::assert_eq;
use trixy::macros::config::{file_tree::FileTree, trixy::TrixyConfig};
#[test]
pub fn error_derive() {
let input = include_str!("./expected.md");
let expected: FileTree = input.parse().unwrap();
let config = TrixyConfig::new("callback_function")
.out_dir_path("out/dir")
.trixy_path("./tests/error_derive/input.tri")
.dist_dir_path("dist")
.add_c_headers(false);
let actual = config.generate();
assert_eq!(expected, actual);
}

View File

@ -18,7 +18,10 @@ pub mod trinitrix {
pub enum Trinitrix {
///I \n also \n contain \n them
#[allow(non_camel_case_types)]
hi { trixy_output: trixy::oneshot::Sender<trixy::types::String>, name: String },
hi {
trixy_output: trixy::oneshot::Sender<trixy::types::String>,
name: trixy::types::newtypes::String,
},
}
}
/* C API */

0
tests/result/expected.md Normal file
View File

38
tests/result/input.tri Normal file
View File

@ -0,0 +1,38 @@
/*
* Copyright (C) 2023 - 2024:
* The Trinitrix Project <soispha@vhack.eu, antifallobst@systemausfall.org>
* SPDX-License-Identifier: LGPL-3.0-or-later
*
* This file is part of the Trixy crate for Trinitrix.
*
* Trixy is free software: you can redistribute it and/or modify
* it under the terms of the Lesser GNU General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* and the Lesser GNU General Public License along with this program.
* If not, see <https://www.gnu.org/licenses/>.
*/
mod trinitrix {
#[derive(Error)]
enum GetBError {
#[error = "Could not do A!"]
A,
#[error = "Failed on calling b"]
B,
}
fn get_a() -> Result<u32, String>;
fn get_b() -> Result<String, GetBError>;
}
// Trixy is sort of a subset of rust
// vim: syntax=rust

39
tests/result/main.rs Normal file
View File

@ -0,0 +1,39 @@
/*
* Copyright (C) 2023 - 2024:
* The Trinitrix Project <soispha@vhack.eu, antifallobst@systemausfall.org>
* SPDX-License-Identifier: LGPL-3.0-or-later
*
* This file is part of the Trixy crate for Trinitrix.
*
* Trixy is free software: you can redistribute it and/or modify
* it under the terms of the Lesser GNU General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* and the Lesser GNU General Public License along with this program.
* If not, see <https://www.gnu.org/licenses/>.
*/
use pretty_assertions::assert_eq;
use trixy::macros::config::{file_tree::FileTree, trixy::TrixyConfig};
#[test]
pub fn result() {
let input = include_str!("./expected.md");
let expected: FileTree = input.parse().unwrap();
let config = TrixyConfig::new("callback_function")
.out_dir_path("out/dir")
.trixy_path("./tests/result/input.tri")
.dist_dir_path("dist")
.add_c_headers(false);
let actual = config.generate();
assert_eq!(expected, actual);
}

View File

@ -34,7 +34,7 @@ pub mod trinitrix {
/// Send a message to the current room
/// The send message is interpreted literally.
#[allow(non_camel_case_types)]
room_message_send { message: String },
room_message_send { message: trixy::types::newtypes::String },
Ui(ui::Ui),
Keymaps(keymaps::Keymaps),
Raw(raw::Raw),
@ -105,15 +105,22 @@ pub mod trinitrix {
pub enum Keymaps {
/// Add a new keymapping
#[allow(non_camel_case_types)]
add { mode: String, key: String, callback: extern "C" fn() },
add {
mode: trixy::types::newtypes::String,
key: trixy::types::newtypes::String,
callback: extern "C" fn(),
},
/// Remove a keymapping
///
/// Does nothing, if the keymapping doesn't exists yet
#[allow(non_camel_case_types)]
remove { mode: String, key: String },
remove {
mode: trixy::types::newtypes::String,
key: trixy::types::newtypes::String,
},
/// List declared keymappings
#[allow(non_camel_case_types)]
get { mode: String },
get { mode: trixy::types::newtypes::String },
}
}
/// Functions only used internally within Trinitrix
@ -122,17 +129,17 @@ pub mod trinitrix {
pub enum Raw {
/// Send an error to the default error output
#[allow(non_camel_case_types)]
raise_error { error_message: String },
raise_error { error_message: trixy::types::newtypes::String },
/// Send output to the default output
/// This is mainly used to display the final
/// output of evaluated lua commands.
#[allow(non_camel_case_types)]
display_output { output_message: String },
display_output { output_message: trixy::types::newtypes::String },
/// Input a character without checking for possible keymaps
/// If the current state does not expect input, this character is ignored
/// The encoding is the same as in the `trinitrix.api.keymaps` commands
#[allow(non_camel_case_types)]
send_input_unprocessed { input: String },
send_input_unprocessed { input: trixy::types::newtypes::String },
Private(__private::Private),
}
/// This namespace is used to store some command specific data (like functions, as

View File

@ -28,7 +28,7 @@ pub mod test {
}
}
fn from_ptr(
ptr: Self::Ptr,
_ptr: Self::Ptr,
) -> Result<Self, trixy::types::error::TypeConversionError> {
todo!()
}

View File

@ -19,9 +19,14 @@
# and the Lesser GNU General Public License along with this program.
# If not, see <https://www.gnu.org/licenses/>.
cargo update && cargo upgrade
update() {
[ "$1" = "upgrade" ] && cargo upgrade
cargo update
}
update "$@"
cd ./example/main || {
echo "Main example is missing"
exit 1
}
cargo update && cargo upgrade
update "$@"