diff --git a/packages/preview/rivet/0.2.0/CHANGELOG.md b/packages/preview/rivet/0.2.0/CHANGELOG.md new file mode 100644 index 000000000..4dd368640 --- /dev/null +++ b/packages/preview/rivet/0.2.0/CHANGELOG.md @@ -0,0 +1,21 @@ +# Changelog + +## [v0.2.0] - 2025-02-23 +- updated CeTZ to 0.3.2 +- updated to Typst 0.13.0 +- fixed missing bit index on dependencies +- updated docs (Tidy, codelst -> codly) + +## [v0.1.0] - 2024-10-02 +- prepared for publication in Typst Universe + +## [v0.0.2] - 2024-06-15 +### Added +- `width` parameter to `schema.render` for easier integration +- `all-bit-i` config option +- colored ranges +- format specification in the manual + +## [v0.0.1] - 2024-05-19 +- initial version +- ported all features from the [python package](https://git.kb28.ch/HEL/rivet/) \ No newline at end of file diff --git a/packages/preview/rivet/0.2.0/LICENSE b/packages/preview/rivet/0.2.0/LICENSE new file mode 100644 index 000000000..74e2338b6 --- /dev/null +++ b/packages/preview/rivet/0.2.0/LICENSE @@ -0,0 +1,73 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright 2024 HEL + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/packages/preview/rivet/0.2.0/README.md b/packages/preview/rivet/0.2.0/README.md new file mode 100644 index 000000000..64cc5d1d8 --- /dev/null +++ b/packages/preview/rivet/0.2.0/README.md @@ -0,0 +1,41 @@ +# rivet-typst + +RIVET _(Register / Instruction Visualizer & Explainer Tool)_ is a [Typst](https://typst.app) package for visualizing binary instructions or describing the contents of a register, using the [CeTZ](https://typst.app/universe/package/cetz) package. + +It is based on the [homonymous Python script](https://git.kb28.ch/HEL/rivet/) + +## Examples + + + + + + + + + + + + + +
+ + + +
A bit of eveything
+ + + +
RISC-V memory instructions (blueprint)
+ +*Click on the example image to jump to the code.* + +## Usage +For more information, see the [manual](manual.pdf) + +To use this package, simply import `schema` from [rivet](https://typst.app/universe/package/rivet) and call `schema.load` to parse a schema description. Then use `schema.render` to render it, et voilĂ  ! +```typ +#import "@preview/rivet:0.2.0": schema +#let doc = schema.load("path/to/schema.yaml") +#schema.render(doc) +``` \ No newline at end of file diff --git a/packages/preview/rivet/0.2.0/docs/config.typ b/packages/preview/rivet/0.2.0/docs/config.typ new file mode 100644 index 000000000..2046f8c71 --- /dev/null +++ b/packages/preview/rivet/0.2.0/docs/config.typ @@ -0,0 +1,63 @@ +/// Creates a dictionary of all configuration parameters +/// +/// - default-font-family (str): The default font family +/// - default-font-size (length): The absolute default font size +/// - italic-font-family (str): The italic font family (for value descriptions) +/// - italic-font-size (length): The absolute italic font size +/// - background (color): The diagram background color +/// - text-color (color): The default color used to display text +/// - link-color (color): The color used to display links and arrows +/// - bit-i-color (color): The color used to display bit indices +/// - border-color (color): The color used to display borders +/// - bit-width (float): The width of a bit +/// - bit-height (float): The height of a bit +/// - description-margin (float): The margin between descriptions +/// - dash-length (float): The length of individual dashes (for dashed lines) +/// - dash-space (float): The space between two dashes (for dashed lines) +/// - arrow-size (float): The size of arrow heads +/// - margins (tuple): TODO -> remove +/// - arrow-margin (float): The margin between arrows and the structures they link +/// - values-gap (float): The gap between individual values +/// - arrow-label-distance (float): The distance between arrows and their labels +/// - force-descs-on-side (bool): If true, descriptions are placed on the side of the structure, otherwise, they are placed as close as possible to the bit +/// - left-labels (bool): If true, descriptions are put on the left, otherwise, they default to the right hand side +/// - width (float): TODO -> remove +/// - height (float): TODO -> remove +/// - full-page (bool): If true, the page will be resized to fit the diagram and take the background color +/// - all-bit-i (bool): If true, all bit indices will be rendered, otherwise, only the ends of each range will be displayed +/// -> dictionary +#let config( + default-font-family: "Ubuntu Mono", + default-font-size: 15pt, + italic-font-family: "Ubuntu Mono", + italic-font-size: 12pt, + background: white, + text-color: black, + link-color: black, + bit-i-color: black, + border-color: black, + bit-width: 30, + bit-height: 30, + description-margin: 10, + dash-length: 6, + dash-space: 4, + arrow-size: 10, + margins: (20, 20, 20, 20), + arrow-margin: 4, + values-gap: 5, + arrow-label-distance: 5, + force-descs-on-side: false, + left-labels: false, + width: 1200, + height: 800, + full-page: false, + all-bit-i: true +) = {} + +/// Dark theme config +/// - ..args (any): see @@config() +#let dark(..args) = {} + +/// Blueprint theme config +/// - ..args (any): see @@config() +#let blueprint(..args) = {} \ No newline at end of file diff --git a/packages/preview/rivet/0.2.0/docs/examples.typ b/packages/preview/rivet/0.2.0/docs/examples.typ new file mode 100644 index 000000000..1e6c99b9c --- /dev/null +++ b/packages/preview/rivet/0.2.0/docs/examples.typ @@ -0,0 +1,72 @@ +#import "../src/lib.typ": schema +#import "../src/util.typ" + +#let example-preamble = "import \"../src/lib.typ\": *;" + +#let example(src, show-src: true, vertical: false, fill: true) = { + src = src.text.trim() + let full-src = example-preamble + src + let body = eval(full-src) + + block(width: 100%, + align(center, + box( + stroke: black + 1pt, + radius: .5em, + fill: if fill {orange.lighten(95%)} else {none}, + if show-src { + let src-block = align(left, raw(src, lang: "typc")) + table( + columns: if vertical {1} else {2}, + inset: 1em, + align: horizon + center, + stroke: none, + body, + if vertical {table.hline()} else {table.vline()}, src-block + ) + } else { + table( + inset: 1em, + body + ) + } + ) + ) + ) +} + +#let config-config = example(raw(" +let ex = schema.load(```yaml +structures: + main: + bits: 4 + ranges: + 3-0: + name: default +```) +schema.render(ex, config: config.config()) +")) + +#let config-dark = example(raw(" +let ex = schema.load(```yaml +structures: + main: + bits: 4 + ranges: + 3-0: + name: dark +```) +schema.render(ex, config: config.dark()) +")) + +#let config-blueprint = example(raw(" +let ex = schema.load(```yaml +structures: + main: + bits: 4 + ranges: + 3-0: + name: blueprint +```) +schema.render(ex, config: config.blueprint()) +")) \ No newline at end of file diff --git a/packages/preview/rivet/0.2.0/docs/schema.typ b/packages/preview/rivet/0.2.0/docs/schema.typ new file mode 100644 index 000000000..48d539e22 --- /dev/null +++ b/packages/preview/rivet/0.2.0/docs/schema.typ @@ -0,0 +1,19 @@ +/// Loads a schema from a file or a raw block. +/// This function returns a dictionary of structures +/// +/// Supported formats: #schema.valid-extensions.map(e => raw("." + e)).join(", ") +/// - path-or-schema (str, raw, dictionary): +/// #list( +/// [If it is a string, defines the path to load.\ #emoji.warning Warning: this will only work if this package is part of your project, as packages installed in the `@local` or `@preview` namespace cannot access project files], +/// [If it is a raw block, its content is directly parsed (the block's language will define the format to use)], +/// [If it is a dictionary, it directly defines the schema structure] +/// ) +/// -> dictionary +#let load(path-or-schema) = {} + +/// Renders the given schema +/// This functions +/// - schema (dictionary): A schema dictionary, as returned by @@load() +/// - config (auto, dictionary): The configuration parameters, as returned by #doc-ref("config.config") +/// - width (ratio, length): The width of the generated figure +#let render(schema, config: auto, width: 100%) = {} \ No newline at end of file diff --git a/packages/preview/rivet/0.2.0/gallery/example1.pdf b/packages/preview/rivet/0.2.0/gallery/example1.pdf new file mode 100644 index 000000000..ca2330e67 Binary files /dev/null and b/packages/preview/rivet/0.2.0/gallery/example1.pdf differ diff --git a/packages/preview/rivet/0.2.0/gallery/example1.png b/packages/preview/rivet/0.2.0/gallery/example1.png new file mode 100644 index 000000000..6ad84475b Binary files /dev/null and b/packages/preview/rivet/0.2.0/gallery/example1.png differ diff --git a/packages/preview/rivet/0.2.0/gallery/example1.typ b/packages/preview/rivet/0.2.0/gallery/example1.typ new file mode 100644 index 000000000..bfb9ca06f --- /dev/null +++ b/packages/preview/rivet/0.2.0/gallery/example1.typ @@ -0,0 +1,6 @@ +#import "../src/lib.typ": schema, config + +#let example = schema.load("/gallery/example1.yaml") +#schema.render(example, config: config.config( + full-page: true +)) \ No newline at end of file diff --git a/packages/preview/rivet/0.2.0/gallery/example1.yaml b/packages/preview/rivet/0.2.0/gallery/example1.yaml new file mode 100644 index 000000000..2ca79645a --- /dev/null +++ b/packages/preview/rivet/0.2.0/gallery/example1.yaml @@ -0,0 +1,73 @@ +structures: + main: + bits: 32 + ranges: + 31-28: + name: cond + 27: + name: 0 + 26: + name: 1 + 25: + name: I + 24: + name: P + description: pre / post indexing bit + values: + 0: post, add offset after transfer + 1: pre, add offset before transfer + 23: + name: U + description: up / down bit + values: + 0: down, subtract offset from base + 1: up, addition offset to base + 22: + name: B + description: byte / word bit + values: + 0: transfer word quantity + 1: transfer byte quantity + 21: + name: W + description: write-back bit + values: + 0: no write-back + 1: write address into base + 20: + name: L + description: load / store bit + values: + 0: store to memory + 1: load from memory + 19-16: + name: Rn + description: base register + 15-12: + name: Rd + description: source / destination register + 11-0: + name: offset + depends-on: 25 + values: + 0: + description: offset is an immediate value + structure: immediateOffset + 1: + description: offset is a register + structure: registerOffset + immediateOffset: + bits: 12 + ranges: + 11-0: + name: 12-bit immediate offset + description: unsigned number + registerOffset: + bits: 12 + ranges: + 11-4: + name: shift + description: shift applied to Rm + 3-0: + name: Rm + description: offset register diff --git a/packages/preview/rivet/0.2.0/gallery/example2.pdf b/packages/preview/rivet/0.2.0/gallery/example2.pdf new file mode 100644 index 000000000..20678dab5 Binary files /dev/null and b/packages/preview/rivet/0.2.0/gallery/example2.pdf differ diff --git a/packages/preview/rivet/0.2.0/gallery/example2.png b/packages/preview/rivet/0.2.0/gallery/example2.png new file mode 100644 index 000000000..19ef68afe Binary files /dev/null and b/packages/preview/rivet/0.2.0/gallery/example2.png differ diff --git a/packages/preview/rivet/0.2.0/gallery/example2.typ b/packages/preview/rivet/0.2.0/gallery/example2.typ new file mode 100644 index 000000000..012dd9b6c --- /dev/null +++ b/packages/preview/rivet/0.2.0/gallery/example2.typ @@ -0,0 +1,7 @@ +#import "../src/lib.typ": schema, config + +#let example = schema.load("/gallery/example2.yaml") +#schema.render(example, config: config.blueprint( + full-page: true, + left-labels: true +)) \ No newline at end of file diff --git a/packages/preview/rivet/0.2.0/gallery/example2.yaml b/packages/preview/rivet/0.2.0/gallery/example2.yaml new file mode 100644 index 000000000..778fac222 --- /dev/null +++ b/packages/preview/rivet/0.2.0/gallery/example2.yaml @@ -0,0 +1,78 @@ +structures: + main: + bits: 32 + ranges: + 31-20: + name: src + depends-on: 5 + values: + 0: + description: + structure: srcImmediate + 1: + description: + structure: srcRegister + 19-15: + name: rs1 + 14-12: + name: funct3 + description: function modifier + values: + 000: byte + 001: half-word + "010": word + 100: upper byte (load only) + 101: upper half (load only) + 11-7: + name: dst + depends-on: 5 + values: + 0: + description: + structure: dstRegister + 1: + description: + structure: dstImmediate + 6: + name: 0 + 5: + name: I + 4: + name: 0 + 3: + name: 0 + 2: + name: 0 + 1: + name: 1 + 0: + name: 1 + + srcImmediate: + bits: 12 + ranges: + 11-0: + name: src + description: source memory address + srcRegister: + bits: 12 + ranges: + 11-5: + name: dstU + description: destination address upper bits + 4-0: + name: rs2 + description: source register + + dstImmediate: + bits: 5 + ranges: + 4-0: + name: destL + description: destination address lower bits + dstRegister: + bits: 5 + ranges: + 4-0: + name: rd + description: destination register \ No newline at end of file diff --git a/packages/preview/rivet/0.2.0/gallery/example3.pdf b/packages/preview/rivet/0.2.0/gallery/example3.pdf new file mode 100644 index 000000000..d5156f404 Binary files /dev/null and b/packages/preview/rivet/0.2.0/gallery/example3.pdf differ diff --git a/packages/preview/rivet/0.2.0/gallery/example3.png b/packages/preview/rivet/0.2.0/gallery/example3.png new file mode 100644 index 000000000..d2d70e8fa Binary files /dev/null and b/packages/preview/rivet/0.2.0/gallery/example3.png differ diff --git a/packages/preview/rivet/0.2.0/gallery/example3.typ b/packages/preview/rivet/0.2.0/gallery/example3.typ new file mode 100644 index 000000000..cf04b4333 --- /dev/null +++ b/packages/preview/rivet/0.2.0/gallery/example3.typ @@ -0,0 +1,23 @@ +#import "../src/lib.typ": schema, config + +#let example = schema.load("/gallery/example1.yaml") +//#schema.render(example) + += Chapter 1 +#lorem(50) + += Chapter 2 +#lorem(50) + +== Section 2.1 + +#lorem(20) + +#figure( + schema.render(example, config: config.config(all-bit-i: false)), + caption: "Test schema" +) + +#lorem(20) + += Chapter 3 \ No newline at end of file diff --git a/packages/preview/rivet/0.2.0/gallery/example3.yaml b/packages/preview/rivet/0.2.0/gallery/example3.yaml new file mode 100644 index 000000000..1cc11ba4d --- /dev/null +++ b/packages/preview/rivet/0.2.0/gallery/example3.yaml @@ -0,0 +1,12 @@ +structures: + main: + bits: 32 + ranges: + 31-24: + name: op + 23-16: + name: r1 + 15-8: + name: r2 + 7-0: + name: r3 \ No newline at end of file diff --git a/packages/preview/rivet/0.2.0/gallery/riscv/alu_instr.yaml b/packages/preview/rivet/0.2.0/gallery/riscv/alu_instr.yaml new file mode 100644 index 000000000..b1b0cd4af --- /dev/null +++ b/packages/preview/rivet/0.2.0/gallery/riscv/alu_instr.yaml @@ -0,0 +1,60 @@ +structures: + main: + bits: 32 + ranges: + 31-20: + name: op2 + depends-on: 5 + values: + 0: + description: second operand is an immediate value + structure: immediateOp + 1: + description: second operand is a register + structure: registerOp + 19-15: + name: rs1 + 14-12: + name: funct3 + description: operation + values: + 000: add / sub + 100: xor + 110: or + 111: and + 001: sl + 101: sr + 11-7: + name: rd + 6: + name: 0 + 5: + name: I + 4: + name: 1 + 3: + name: 0 + 2: + name: 0 + 1: + name: 1 + 0: + name: 1 + immediateOp: + bits: 12 + ranges: + 11-0: + name: 12-bit immediate value + description: signed number + registerOp: + bits: 12 + ranges: + 11-5: + name: funct7 + description: function modifier + values: + 0000000: default (add, srl) + "0100000": sub, sra + 4-0: + name: rs2 + description: second register operand diff --git a/packages/preview/rivet/0.2.0/gallery/riscv/branch_instr.yaml b/packages/preview/rivet/0.2.0/gallery/riscv/branch_instr.yaml new file mode 100644 index 000000000..e919d24e5 --- /dev/null +++ b/packages/preview/rivet/0.2.0/gallery/riscv/branch_instr.yaml @@ -0,0 +1,34 @@ +structures: + main: + bits: 32 + ranges: + 31-25: + name: imm + 24-20: + name: rs2 + 19-15: + name: rs1 + 14-12: + name: funct3 + description: function modifier + values: + 000: if equal + 001: if not equal + 100: if less + 101: if greater or equal + 11-7: + name: imm + 6: + name: 1 + 5: + name: 1 + 4: + name: 0 + 3: + name: 0 + 2: + name: 0 + 1: + name: 1 + 0: + name: 1 \ No newline at end of file diff --git a/packages/preview/rivet/0.2.0/gallery/riscv/mem_instr.yaml b/packages/preview/rivet/0.2.0/gallery/riscv/mem_instr.yaml new file mode 100644 index 000000000..778fac222 --- /dev/null +++ b/packages/preview/rivet/0.2.0/gallery/riscv/mem_instr.yaml @@ -0,0 +1,78 @@ +structures: + main: + bits: 32 + ranges: + 31-20: + name: src + depends-on: 5 + values: + 0: + description: + structure: srcImmediate + 1: + description: + structure: srcRegister + 19-15: + name: rs1 + 14-12: + name: funct3 + description: function modifier + values: + 000: byte + 001: half-word + "010": word + 100: upper byte (load only) + 101: upper half (load only) + 11-7: + name: dst + depends-on: 5 + values: + 0: + description: + structure: dstRegister + 1: + description: + structure: dstImmediate + 6: + name: 0 + 5: + name: I + 4: + name: 0 + 3: + name: 0 + 2: + name: 0 + 1: + name: 1 + 0: + name: 1 + + srcImmediate: + bits: 12 + ranges: + 11-0: + name: src + description: source memory address + srcRegister: + bits: 12 + ranges: + 11-5: + name: dstU + description: destination address upper bits + 4-0: + name: rs2 + description: source register + + dstImmediate: + bits: 5 + ranges: + 4-0: + name: destL + description: destination address lower bits + dstRegister: + bits: 5 + ranges: + 4-0: + name: rd + description: destination register \ No newline at end of file diff --git a/packages/preview/rivet/0.2.0/gallery/riscv/riscv.pdf b/packages/preview/rivet/0.2.0/gallery/riscv/riscv.pdf new file mode 100644 index 000000000..ca9bca8a7 Binary files /dev/null and b/packages/preview/rivet/0.2.0/gallery/riscv/riscv.pdf differ diff --git a/packages/preview/rivet/0.2.0/gallery/riscv/riscv.typ b/packages/preview/rivet/0.2.0/gallery/riscv/riscv.typ new file mode 100644 index 000000000..0802ad9ad --- /dev/null +++ b/packages/preview/rivet/0.2.0/gallery/riscv/riscv.typ @@ -0,0 +1,14 @@ +#import "../../src/lib.typ": * + +#let conf = config.config( + full-page: true, + left-labels: true +) +#let alu = schema.load("/gallery/riscv/alu_instr.yaml") +#schema.render(alu, config: conf) + +#let branch = schema.load("/gallery/riscv/branch_instr.yaml") +#schema.render(branch, config: conf) + +#let mem = schema.load("/gallery/riscv/mem_instr.yaml") +#schema.render(mem, config: conf) diff --git a/packages/preview/rivet/0.2.0/gallery/test.json b/packages/preview/rivet/0.2.0/gallery/test.json new file mode 100644 index 000000000..f247beabe --- /dev/null +++ b/packages/preview/rivet/0.2.0/gallery/test.json @@ -0,0 +1,111 @@ +{ + "structures": { + "main": { + "bits": 32, + "ranges": { + "31-28": { + "name": "cond" + }, + "27": { + "name": "0" + }, + "26": { + "name": "1" + }, + "25": { + "name": "I" + }, + "24": { + "name": "P", + "description": "pre / post indexing bit", + "values": { + "0": "post, add offset after transfer", + "1": "pre, add offset before transfer" + } + }, + "23": { + "name": "U", + "description": "up / down bit", + "values": { + "0": "down, subtract offset from base", + "1": "up, addition offset to base" + } + }, + "22": { + "name": "B", + "description": "byte / word bit", + "values": { + "0": "transfer word quantity", + "1": "transfer byte quantity" + } + }, + "21": { + "name": "W", + "description": "write-back bit", + "values": { + "0": "no write-back", + "1": "write address into base" + } + }, + "20": { + "name": "L", + "description": "load / store bit", + "values": { + "0": "store to memory", + "1": "load from memory" + } + }, + "19-16": { + "name": "Rn", + "description": "base register" + }, + "15-12": { + "name": "Rd", + "description": "source / destination register" + }, + "11-0": { + "name": "offset", + "depends-on": "25", + "values": { + "0": { + "description": "offset is an immediate value", + "structure": "immediateOffset" + }, + "1": { + "description": "offset is a register", + "structure": "registerOffset" + } + } + } + } + }, + "immediateOffset": { + "bits": 12, + "ranges": { + "11-0": { + "name": "12-bit immediate offset", + "description": "unsigned number" + } + } + }, + "registerOffset": { + "bits": 12, + "ranges": { + "11-4": { + "name": "shift", + "description": "shift applied to Rm" + }, + "3-0": { + "name": "Rm", + "description": "offset register" + } + } + } + }, + "colors": { + "main": { + "31-28": "#FF0000", + "11-4": [34, 176, 43] + } + } +} diff --git a/packages/preview/rivet/0.2.0/gallery/test.pdf b/packages/preview/rivet/0.2.0/gallery/test.pdf new file mode 100644 index 000000000..aa076ba04 Binary files /dev/null and b/packages/preview/rivet/0.2.0/gallery/test.pdf differ diff --git a/packages/preview/rivet/0.2.0/gallery/test.typ b/packages/preview/rivet/0.2.0/gallery/test.typ new file mode 100644 index 000000000..515a8464a --- /dev/null +++ b/packages/preview/rivet/0.2.0/gallery/test.typ @@ -0,0 +1,139 @@ +#import "../src/lib.typ": * + +#let test-yaml = schema.load("/gallery/test.yaml") +#schema.render(test-yaml, config: config.config( + full-page: true +)) + +#let test-json = schema.load("/gallery/test.json") +#schema.render(test-json, config: config.blueprint( + full-page: true +)) + +#let test-xml = schema.load("/gallery/test.xml") +#schema.render(test-xml, config: config.dark( + full-page: true +)) + +#let test-raw = schema.load(```yaml +structures: + main: + bits: 4 + ranges: + 3-0: + name: test +```) +#schema.render(test-raw, config: config.config( + full-page: true +)) + +#let test-typ = schema.load(( + structures: ( + main: ( + bits: 32, + ranges: ( + "31-28": (name: "cond"), + "27": (name: "0"), + "26": (name: "1"), + "25": (name: "I"), + "24": ( + name: "P", + description: "pre / post indexing bit", + values: ( + "0": "post, add offset after transfer", + "1": "pre, add offset before transfer" + ) + ), + "23": ( + name: "U", + description: "up / down bit", + values: ( + "0": "down, subtract offset from base", + "1": "up, addition offset to base" + ) + ), + "22": ( + name: "B", + description: "byte / word bit", + values: ( + "0": "transfer word quantity", + "1": "transfer byte quantity" + ) + ), + "21": ( + name: "W", + description: "write-back bit", + values: ( + "0": "no write-back", + "1": "write address into base" + ) + ), + "20": ( + name: "L", + description: "load / store bit", + values: ( + "0": "store to memory", + "1": "load from memory" + ) + ), + "19-16": ( + name: "Rn", + description: "base register" + ), + "15-12": ( + name: "Rd", + description: "source / destination register" + ), + "11-0": ( + name: "offset", + depends-on: "25", + values: ( + "0": ( + description: "offset is an immediate value", + structure: "immediateOffset" + ), + "1": ( + description: "offset is a register", + structure: "registerOffset" + ) + ) + ) + ) + ), + immediateOffset: ( + bits: 12, + ranges: ( + "11-0": ( + name: "12-bit immediate offset", + description: "unsigned number" + ) + ) + ), + registerOffset: ( + bits: 12, + ranges: ( + "11-4": ( + name: "shift", + description: "shift applied to Rm" + ), + "3-0": ( + name: "Rm", + description: "offset register" + ) + ) + ) + ), + colors: ( + main: ( + "31-28": red, + "11-4": green + ), + registerOffset: ( + "11-4": rgb(240, 140, 80) + ) + ) +)) + +#schema.render(test-typ, config: config.config( + full-page: true +)) \ No newline at end of file diff --git a/packages/preview/rivet/0.2.0/gallery/test.xml b/packages/preview/rivet/0.2.0/gallery/test.xml new file mode 100644 index 000000000..4a8b71631 --- /dev/null +++ b/packages/preview/rivet/0.2.0/gallery/test.xml @@ -0,0 +1,71 @@ + + + + + + + + pre / post indexing bit + + post, add offset after transfer + pre, add offset before transfer + + + + up / down bit + + down, subtract offset from base + up, addition offset to base + + + + byte / word bit + + transfer word quantity + transfer byte quantity + + + + write-back bit + + no write-back + write address into base + + + + load / store bit + + store to memory + load from memory + + + + base register + + + source / destination register + + + + offset is an immediate value + offset is a register + + + + + + unsigned number + + + + + shift applied to Rm + + + offset register + + + + + + \ No newline at end of file diff --git a/packages/preview/rivet/0.2.0/gallery/test.yaml b/packages/preview/rivet/0.2.0/gallery/test.yaml new file mode 100644 index 000000000..f7119ae0e --- /dev/null +++ b/packages/preview/rivet/0.2.0/gallery/test.yaml @@ -0,0 +1,81 @@ +structures: + main: + bits: 32 + ranges: + 31-28: + name: cond + 27: + name: 0 + 26: + name: 1 + 25: + name: I + 24: + name: P + description: pre / post indexing bit + values: + 0: post, add offset after transfer + 1: pre, add offset before transfer + 23: + name: U + description: up / down bit + values: + 0: down, subtract offset from base + 1: up, addition offset to base + 22: + name: B + description: byte / word bit + values: + 0: transfer word quantity + 1: transfer byte quantity + 21: + name: W + description: write-back bit + values: + 0: no write-back + 1: write address into base + 20: + name: L + description: load / store bit + values: + 0: store to memory + 1: load from memory + 19-16: + name: Rn + description: base register + 15-12: + name: Rd + description: source / destination register + 11-0: + name: offset + depends-on: 25 + values: + 0: + description: offset is an immediate value + structure: immediateOffset + 1: + description: offset is a register + structure: registerOffset + immediateOffset: + bits: 12 + ranges: + 11-0: + name: 12-bit immediate offset + description: unsigned number + registerOffset: + bits: 12 + ranges: + 11-4: + name: shift + description: shift applied to Rm + 3-0: + name: Rm + description: offset register + +colors: + main: + 31-28: "#3EFA6B" + 25-23: + - 100 + - 150 + - 200 \ No newline at end of file diff --git a/packages/preview/rivet/0.2.0/justfile b/packages/preview/rivet/0.2.0/justfile new file mode 100644 index 000000000..f0c0b97c5 --- /dev/null +++ b/packages/preview/rivet/0.2.0/justfile @@ -0,0 +1,12 @@ +# Local Variables: +# mode: makefile +# End: +gallery_dir := "./gallery" +set shell := ["bash", "-uc"] + +manual: + typst c manual.typ manual.pdf + +gallery: + for f in "{{gallery_dir}}"/*.typ; do typst c --root . "$f" "${f/typ/pdf}"; done + for f in "{{gallery_dir}}"/example*.typ; do typst c --root . "$f" "${f/typ/png}"; done diff --git a/packages/preview/rivet/0.2.0/manual.pdf b/packages/preview/rivet/0.2.0/manual.pdf new file mode 100644 index 000000000..8597825d6 Binary files /dev/null and b/packages/preview/rivet/0.2.0/manual.pdf differ diff --git a/packages/preview/rivet/0.2.0/manual.typ b/packages/preview/rivet/0.2.0/manual.typ new file mode 100644 index 000000000..a97b4498d --- /dev/null +++ b/packages/preview/rivet/0.2.0/manual.typ @@ -0,0 +1,308 @@ +#import "@preview/tidy:0.4.1" +#import "@preview/codly:1.2.0": codly-init, codly +#import "@preview/codly-languages:0.1.7": codly-languages +#import "@preview/showybox:2.0.4": showybox +#import "src/lib.typ" +#import "src/schema.typ" +#import "docs/examples.typ" + +#show: codly-init + +#codly(languages: codly-languages) + +#set heading(numbering: (..num) => if num.pos().len() < 4 { + numbering("1.1", ..num) +}) + +#set page(numbering: "1/1", header: align(right)[rivet #sym.dash.em v#lib.version]) + +#let doc-ref(target, full: false, var: false) = { + let (module, func) = target.split(".") + let label-name = module + "-" + func + let display-name = func + if full { + display-name = target + } + if not var { + label-name += "()" + display-name += "()" + } + link(label(label-name), raw(display-name)) +} + +#let note(it) = showybox( + title: "Note", + title-style: ( + color: white, + weight: "bold" + ), + frame: ( + title-color: blue.lighten(30%), + border-color: blue.darken(40%) + ), + it +) + +#show link: set text(blue) + +#let sch = schema.load(```yaml +structures: + main: + bits: 5 + ranges: + 4: + name: R + description: Register + 3: + name: I + description: Instruction + 2: + name: V + description: Visualizer + 1: + name: E + description: Explainer + 0: + name: T + description: Tool +```) +#align(center, schema.render(sch, width: 50%, config: lib.config.config(left-labels: true))) +#v(1fr) +#box( + width: 100%, + stroke: black, + inset: 1em, + outline(indent: auto, depth: 3) +) +#pagebreak(weak: true) + += Introduction + +This package provides a way to make beautiful register diagrams using the CeTZ package. It can be used to document Assembly instructions or binary registers + +This is a port of the #link("https://git.kb28.ch/HEL/rivet")[homonymous Python script] for Typst. + += Usage + +Simply import `schema` from #link("src/lib.typ") and call `schema.load` to parse a schema description. Then use `schema.render` to render it, et voilĂ  ! +#pad(left: 1em)[```typ +#import "@preview/rivet:0.1.0": schema +#let doc = schema.load("path/to/schema.yaml") +#schema.render(doc) +```] + += Format + +This section describes the structure of a schema definition. The examples given use the JSON syntax. For examples in different formats, see #link("https://git.kb28.ch/HEL/rivet-typst/src/branch/main/gallery/test.yaml")[test.yaml], #link("https://git.kb28.ch/HEL/rivet-typst/src/branch/main/gallery/test.json")[test.json] and #link("https://git.kb28.ch/HEL/rivet-typst/src/branch/main/gallery/test.xml")[test.xml]. You can also directly define a schema using Typst dictionaries and arrays. + +Since the XML format is quite different from the other, you might find it helpful to look at the examples on GitHub to get familiar with it. + +== Main layout + +A schema contains a dictionary of structures. The must be at least one defined structure named "main". + +It can also optionnaly contain a "colors" dictionary. More details about this in #link()[Colors] + +```json +{ + "structures": { + "main": { + ... + }, + "struct1": { + ... + }, + "struct2": { + ... + }, + ... + } +} +``` + +#pagebreak(weak: true) + +== Structure + +A structure has a given number of bits and one or multiple ranges. Each range of bits can have a name, a description and / or values with special meaning (see #link()[Range]). A range's structure can also depend on another range's value (see #link()[Dependencies]). + +The range name (or key) defines the left- and rightmost bits (e.g. `7-4` goes from bit 7 down to bit 4). Bits are displayed in big-endian, i.e. the leftmost bit has the highest value. + +```json +"main": { + "bits": 8, + "ranges": { + "7-4": { + ... + }, + "3-2": { + ... + }, + "1": { + ... + }, + "0": { + ... + } + } +} +``` + +== Range + +A range represents a group of consecutive bits. It can have a name (displayed in the bit cells), a description (displayed under the structure) and / or values. + +For values depending on other ranges, see #link()[Dependencies]. + +#note[ + In YAML, make sure to wrap values in quotes because some values can be interpreted as octal notation (e.g. 010 #sym.arrow.r 8) +] + +```json +"3-2": { + "name": "op", + "description": "Logical operation", + "values": { + "00": "AND", + "01": "OR", + "10": "XOR", + "11": "NAND" + } +} +``` + +#pagebreak(weak: true) + +== Dependencies + +The structure of one range may depend on the value of another. To represent this situation, first indicate on the child range the range on which it depends. + +Then, in its values, indicate which structure to use. A description can also be added (displayed above the horizontal dependency arrow) + +```json +"7-4": { + ... + "depends-on": "0", + "values": { + "0": { + "description": "immediate value", + "structure": "immediateValue" + }, + "1": { + "description": "value in register", + "structure": "registerValue" + } + } +} +``` + +Finally, add the sub-structures to the structure dictionary: + +```json +{ + "structures": { + "main": { + ... + }, + "immediateValue": { + "bits": 4, + ... + }, + "registerValue": { + "bits": 4, + ... + }, + ... + } +} +``` + +#pagebreak(weak: true) + +== Colors + +You may want to highlight some ranges to make your diagram more readable. For this, you can use colors. Colors may be defined in a separate dictionary, at the same level as the "structures" dictionary: + +```json +{ + "structures": { + ... + }, + "colors": { + ... + } +} +``` + +It can contain color definitions for any number of ranges. For each range, you may then define a dictionary mapping bit ranges to a particular color: + +```json +"colors": { + "main": { + "31-28": "#ABCDEF", + "27-20": "12,34,56" + }, + "registerValue": { + "19-10": [12, 34, 56] + } +} +``` + +Valid color formats are: +- hex string starting with `#`, e.g. `"#23fa78"` +- array of three integers (only JSON, YAML and Typst), e.g. `[35, 250, 120]` +- string of three comma-separated integers (useful for XML), e.g. `"35,250,120"` +- a Typst color (only Typst), e.g. `colors.green` or `rgb(35, 250, 120)` + +#note[ + The XML format implements colors a bit differently. Instead of having a "colors" dictionary, color definitions are directly put on the same level as structure definitions. For this, you can use a `color` node with the attributes "structure", "color", "start" and "end", like so: + ```xml + + + ... + + ... + + + + ``` +] + +#pagebreak(weak: true) + += Config presets + +Aside from the default config, some example presets are also provided: +- #doc-ref("config.config", full: true): the default theme, black on white + #examples.config-config +- #doc-ref("config.dark", full: true): a dark theme, with white text and lines on a black background + #examples.config-dark +- #doc-ref("config.blueprint", full: true): a blueprint theme, with white text and lines on a blue background + #examples.config-blueprint + +#pagebreak(weak: true) + += Reference + +#let doc-config = tidy.parse-module( + read("docs/config.typ"), + name: "config", + old-syntax: true, + scope: ( + doc-ref: doc-ref + ) +) +#tidy.show-module(doc-config, sort-functions: false) + +#pagebreak() + +#let doc-schema = tidy.parse-module( + read("docs/schema.typ"), + name: "schema", + old-syntax: true, + scope: ( + schema: schema, + doc-ref: doc-ref + ) +) +#tidy.show-module(doc-schema, sort-functions: false) \ No newline at end of file diff --git a/packages/preview/rivet/0.2.0/src/config.typ b/packages/preview/rivet/0.2.0/src/config.typ new file mode 100644 index 000000000..f3868ad7a --- /dev/null +++ b/packages/preview/rivet/0.2.0/src/config.typ @@ -0,0 +1,80 @@ +#let config( + default-font-family: "Ubuntu Mono", + default-font-size: 15pt, + italic-font-family: "Ubuntu Mono", + italic-font-size: 12pt, + background: white, + text-color: black, + link-color: black, + bit-i-color: black, + border-color: black, + bit-width: 30, + bit-height: 30, + description-margin: 10, + dash-length: 6, + dash-space: 4, + arrow-size: 10, + margins: (20, 20, 20, 20), + arrow-margin: 4, + values-gap: 5, + arrow-label-distance: 5, + force-descs-on-side: false, + left-labels: false, + width: 1200, + height: 800, + full-page: false, + all-bit-i: true +) = { + return ( + default-font-family: default-font-family, + default-font-size: default-font-size, + italic-font-family: italic-font-family, + italic-font-size: italic-font-size, + background: background, + text-color: text-color, + link-color: link-color, + bit-i-color: bit-i-color, + border-color: border-color, + bit-width: bit-width, + bit-height: bit-height, + description-margin: description-margin, + dash-length: dash-length, + dash-space: dash-space, + arrow-size: arrow-size, + margins: margins, + arrow-margin: arrow-margin, + values-gap: values-gap, + arrow-label-distance: arrow-label-distance, + force-descs-on-side: force-descs-on-side, + left-labels: left-labels, + width: width, + height: height, + full-page: full-page, + all-bit-i: all-bit-i + ) +} + +#let dark = config.with( + background: rgb(24, 24, 24), + text-color: rgb(216, 216, 216), + link-color: rgb(150, 150, 150), + bit-i-color: rgb(180, 180, 180), + border-color: rgb(180, 180, 180) +) + +#let blueprint = config.with( + background: rgb(53, 77, 158), + text-color: rgb(231, 236, 249), + link-color: rgb(169, 193, 228), + bit-i-color: rgb(214, 223, 244), + border-color: rgb(214, 223, 244) +) + +#let transparent = config.with( + background: rgb(0, 0, 0, 0), + text-color: rgb(128, 128, 128), + link-color: rgb(128, 128, 128), + bit-i-color: rgb(128, 128, 128), + border-color: rgb(128, 128, 128) +) + diff --git a/packages/preview/rivet/0.2.0/src/lib.typ b/packages/preview/rivet/0.2.0/src/lib.typ new file mode 100644 index 000000000..6d7aa1d7e --- /dev/null +++ b/packages/preview/rivet/0.2.0/src/lib.typ @@ -0,0 +1,4 @@ +#let version = version(0,2,0) + +#import "config.typ" +#import "schema.typ" \ No newline at end of file diff --git a/packages/preview/rivet/0.2.0/src/range.typ b/packages/preview/rivet/0.2.0/src/range.typ new file mode 100644 index 000000000..96a7ce400 --- /dev/null +++ b/packages/preview/rivet/0.2.0/src/range.typ @@ -0,0 +1,65 @@ +#import "util.typ" + +#let key(start, end) = { + return str(start) + "->" + str(end) +} + +#let bits(range) = { + return range.end - range.start + 1 +} + +#let parse-span(span) = { + let start-end = span.split("-") + if start-end.len() == 1 { + start-end.push(start-end.first()) + } + let start = int(start-end.last()) + let end = int(start-end.first()) + return (start, end) +} + +#let make( + start, + end, + name, + description: "", + values: none, + depends-on: none +) = { + return ( + start: start, + end: end, + name: name, + description: description, + values: values, + depends-on: depends-on, + last-value-y: -1 + ) +} + +#let load(start, end, data) = { + let values = none + let bits = end - start + 1 + + if "values" in data { + values = (:) + for (val, desc) in data.values { + val = util.z-fill(val, bits) + values.insert(val, desc) + } + } + + let depends-on = data.at("depends-on", default: none) + if depends-on != none { + depends-on = parse-span(str(depends-on)) + } + + return make( + start, + end, + str(data.name), + description: data.at("description", default: ""), + values: values, + depends-on: depends-on + ) +} \ No newline at end of file diff --git a/packages/preview/rivet/0.2.0/src/renderer.typ b/packages/preview/rivet/0.2.0/src/renderer.typ new file mode 100644 index 000000000..94f4b0ebd --- /dev/null +++ b/packages/preview/rivet/0.2.0/src/renderer.typ @@ -0,0 +1,540 @@ +#import "@preview/cetz:0.3.2": canvas, draw + +#import "range.typ" as rng +#import "structure.typ" +#import "vec.typ" + +#let draw-rect(color, x, y, width, height, thickness: 0) = { + let fill = none + let stroke = color + thickness * 1pt + if thickness == 0 { + fill = color + stroke = none + } + draw.rect((x, -y), (x + width, -y - height), fill: fill, stroke: stroke) +} + +#let draw-text( + txt, + color, + x, + y, + anchor: "center", + font: none, + italic: false, + size: 1em, + fill: none +) = { + let text-params = (:) + if font != none { + text-params.insert("font", font) + } + if italic { + text-params.insert("style", "italic") + } + + draw.content( + (x, -y), + text(txt, fill: color, size: size, ..text-params), + anchor: anchor, + stroke: none + ) +} + +#let draw-line(color, a, b) = { + let (x0, y0) = a + let (x1, y1) = b + draw.line((x0, -y0), (x1, -y1), stroke: color) +} + +#let draw-lines(color, ..pts) = { + let pts = pts.pos().map(pt => (pt.at(0), -pt.at(1))) + draw.line(..pts, stroke: color) +} + +#let draw-poly(color, ..pts, thickness: 0) = { + let pts = pts.pos().map(pt => (pt.at(0), -pt.at(1))) + let params = ( + stroke: (paint: color, thickness: thickness), + fill: none + ) + if thickness == 0 { + params = ( + stroke: none, + fill: color + ) + } + draw.line(..pts, ..params) +} + +#let draw-underbracket(config, start, end, bits-y) = { + let bit-w = config.bit-width + let bit-h = config.bit-height + + let x0 = start + bit-w / 2 + let x1 = end - bit-w / 2 + let y0 = bits-y + bit-h * 1.25 + let y1 = bits-y + bit-h * 1.5 + + let col = config.link-color + draw-lines(col, (x0, y0), (x0, y1), (x1, y1), (x1, y0)) +} + +#let draw-link( + config, + start-x, + start-y, + end-x, + end-y +) = { + let bit-h = config.bit-height + let arrow-margin = config.arrow-margin + + if end-x > start-x { + end-x -= arrow-margin + } else { + end-x += arrow-margin + } + + draw-lines( + config.link-color, + (start-x, start-y + bit-h * 1.5), + (start-x, end-y + bit-h / 2), + (end-x, end-y + bit-h / 2), + ) +} + +#let draw-values(config, values, desc-x, desc-y) = { + let shapes = () + let txt-col = config.text-color + let bit-w = config.bit-height // Why ? I don't remember + let gap = config.values-gap + + for (val, desc) in values.pairs().sorted(key: p => p.first()) { + desc-y += gap + let txt = val + " = " + desc + shapes += draw-text( + txt, txt-col, desc-x + bit-w / 2, desc-y, + anchor: "north-west", + font: config.italic-font-family, + italic: true, + size: config.italic-font-size + ) + + desc-y += config.italic-font-size / 1.2pt + } + + return (shapes, desc-x, desc-y) +} + +#let draw-description( + config, + range_, + start-x, + start-y, + width, + desc-x, + desc-y +) = { + let shapes = () + let bit-w = config.bit-width + let bit-h = config.bit-height + + if config.left-labels { + desc-x = calc.min(desc-x, start-x + width / 2 - bit-w) + } else { + desc-x = calc.max(desc-x, start-x + width / 2 + bit-w) + } + + shapes += draw-underbracket(config, start-x, start-x + width, start-y) + + let mid-x = start-x + width / 2 + shapes += draw-link(config, mid-x, start-y, desc-x, desc-y) + + let txt-x = desc-x + + if config.left-labels { + txt-x -= range_.description.len() * config.default-font-size / 2pt + } + + shapes += draw-text( + range_.description, + config.text-color, + txt-x, desc-y + bit-h / 2, + anchor: "west" + ) + + desc-y += config.default-font-size / 0.75pt + + if range_.values != none and range_.depends-on == none { + let shapes_ + (shapes_, _, desc-y) = draw-values(config, range_.values, txt-x, desc-y) + shapes += shapes_ + } + + desc-y += config.description-margin + + return (shapes, desc-x, desc-y) +} + +#let draw-arrow(config, start-x, start-y, end-x, end-y, label: "") = { + let shapes = () + let dash-len = config.dash-length + let dash-space = config.dash-space + let arrow-size = config.arrow-size + let link-col = config.link-color + let txt-col = config.text-color + let arrow-label-dist = config.arrow-label-distance + + let start = vec.vec(start-x, start-y) + let end = vec.vec(end-x, end-y) + let start-end = vec.sub(end, start) + let d = vec.normalize(start-end) + + let dashes = int(vec.mag(start-end) / (dash-len + dash-space)) + + for i in range(dashes) { + let a = vec.add( + start, + vec.mul(d, i * (dash-len + dash-space)) + ) + let b = vec.add( + a, + vec.mul(d, dash-len) + ) + + shapes += draw-line(link-col, (a.x, a.y), (b.x, b.y)) + } + + let n = vec.vec(d.y, -d.x) + let width = arrow-size / 1.5 + let p1 = vec.sub( + end, + vec.sub( + vec.mul(d, arrow-size), + vec.mul(n, width) + ) + ) + let p2 = vec.sub( + end, + vec.add( + vec.mul(d, arrow-size), + vec.mul(n, width) + ) + ) + + shapes += draw-poly( + link-col, + (end.x, end.y), + (p1.x, p1.y), + (p2.x, p2.y) + ) + + if label != "" { + shapes += draw-text( + label, + txt-col, + (start.x + end.x) / 2, + (start.y + end.y) / 2 + arrow-label-dist, + anchor: "north" + ) + } + + return shapes +} + +#let draw-dependency( + draw-struct, config, + struct, schema, bits-x, bits-y, range_, desc-x, desc-y +) = { + let shapes = () + + let bit-w = config.bit-width + let bit-h = config.bit-height + let arrow-margin = config.arrow-margin + + let start-i = struct.bits - range_.end - 1 + let start-x = bits-x + start-i * bit-w + let width = rng.bits(range_) * bit-w + + shapes += draw-underbracket(config, start-x, start-x + width, bits-y) + let depend-key = rng.key(..range_.depends-on) + let depend-range = struct.ranges.at(depend-key) + let prev-range-y = bits-y + bit-h * 1.5 + + let prev-depend-y = if depend-range.last-value-y == -1 { + bits-y + bit-h * 1.5 + } else { + depend-range.last-value-y + } + + let depend-start-i = struct.bits - depend-range.end - 1 + let depend-start-x = bits-x + depend-start-i * bit-w + let depend-width = rng.bits(depend-range) * bit-w + let depend-mid = depend-start-x + depend-width / 2 + shapes += draw-underbracket(config, depend-start-x, depend-start-x + depend-width, bits-y) + + for (val, data) in range_.values.pairs().sorted(key: p => p.first()) { + shapes += draw-arrow(config, depend-mid, prev-depend-y, depend-mid, desc-y - arrow-margin) + + let val-ranges = (:) + for i in range(rng.bits(depend-range)) { + val-ranges.insert( + str(depend-range.end - i), + (name: val.at(i)) + ) + } + + let val-struct = ( + bits: rng.bits(depend-range), + start: depend-range.start, + ranges: val-ranges + ) + val-struct = structure.load("", val-struct) + + let shapes_ + (shapes_, ..) = draw-struct(config, val-struct, schema, ox: depend-start-x, oy: desc-y) + shapes += shapes_ + + let y = desc-y + bit-h * 1.5 + + let x1 + let x2 + + // Arrow from left to right + if depend-range.end > range_.start { + x1 = depend-start-x + depend-width + arrow-margin + x2 = start-x - arrow-margin + + // Arrow from right to left + } else { + x1 = depend-start-x - arrow-margin + x2 = start-x + width + arrow-margin + } + + shapes += draw-arrow(config, x1, y, x2, y, label: data.description) + shapes += draw-arrow(config, + start-x + width - bit-w, + prev-range-y, + start-x + width - bit-w, + desc-y + bit-h - arrow-margin + ) + + prev-depend-y = desc-y + bit-h * 2 + arrow-margin + prev-range-y = prev-depend-y + depend-range.last-value-y = prev-depend-y + + (shapes_, desc-y) = draw-struct(config, schema.structures.at(data.structure), schema, ox: start-x, oy: desc-y) + shapes += shapes_ + } + + struct.ranges.at(depend-key) = depend-range + + return (shapes, desc-x, desc-y, struct) +} + +#let draw-structure(config, struct, schema, ox: 0, oy: 0) = { + let shapes + let colors = schema.at("colors", default: (:)) + let bg-col = config.background + let txt-col = config.text-color + let border-col = config.border-color + let bit-w = config.bit-width + let bit-h = config.bit-height + + let (bits-x, bits-y) = (ox, oy + bit-h) + let bits-width = struct.bits * bit-w + let start-bit = struct.start + let bit-colors = (:) + for i in range(struct.bits) { + bit-colors.insert(str(i), bg-col) + } + if struct.name in colors { + for (s, col) in colors.at(struct.name) { + let (start, end) = rng.parse-span(s) + for i in range(start, end + 1) { + let real-i = struct.bits - i - 1 + start-bit + bit-colors.insert(str(real-i), col) + } + } + } + let range-boundaries = () + for r in struct.ranges.values() { + let i = struct.bits - r.end - 1 + start-bit + range-boundaries.push(i) + } + + // Draw colors + for i in range(struct.bits) { + let bit-x = ox + i * bit-w + shapes += draw-rect(bit-colors.at(str(i)), bit-x, bits-y, bit-w+1, bit-h) + } + + // Draw rectangle around structure + shapes += draw-rect(border-col, bits-x, bits-y, bits-width, bit-h, thickness: 2) + + let indices = range(struct.start, struct.start + struct.bits) + if not config.all-bit-i { + indices = () + for r in struct.ranges.values() { + indices.push(r.start) + indices.push(r.end) + } + } + + for i in range(struct.bits) { + let bit-x = ox + i * bit-w + let real-i = struct.bits - i - 1 + start-bit + + if real-i in indices { + shapes += draw-text( + str(real-i), + txt-col, + bit-x + bit-w / 2, + oy + bit-h / 2 + ) + } + + // Draw separator + if i != 0 and not i in range-boundaries { + shapes += draw-line(border-col, (bit-x, bits-y), (bit-x, bits-y + bit-h * 0.2)) + shapes += draw-line(border-col, (bit-x, bits-y + bit-h * 0.8), (bit-x, bits-y + bit-h)) + } + } + + let ranges = structure.get-sorted-ranges(struct) + if config.left-labels { + ranges = ranges.rev() + } + + let desc-x + if config.force-descs-on-side { + desc-x = config.margins.at(3) + structures.main.bits * bit-w + if config.left-labels { + desc-x = config.width - desc-x + } + } else { + desc-x = ox + if config.left-labels { + desc-x += struct.bits * bit-w + } + } + + let desc-y = bits-y + bit-h * 2 + + // Names + simple descriptions + for range_ in ranges { + let start-i = struct.bits - range_.end + start-bit - 1 + let start-x = bits-x + start-i * bit-w + let width = rng.bits(range_) * bit-w + + let name-x = start-x + width / 2 + let name-y = bits-y + bit-h / 2 + + shapes += draw-line(border-col, (start-x, bits-y), (start-x, bits-y + bit-h)) + shapes += draw-text(range_.name, txt-col, name-x, name-y, fill: bg-col) + + if range_.description != "" { + let shapes_ + (shapes_, desc-x, desc-y) = draw-description( + config, range_, start-x, bits-y, width, desc-x, desc-y + ) + shapes += shapes_ + } + } + + // Dependencies + for range_ in ranges { + if range_.values() != none and range_.depends-on != none { + let shapes_ + (shapes_, desc-x, desc-y, struct) = draw-dependency( + draw-structure, config, + struct, schema, bits-x, bits-y, range_, desc-x, desc-y, + ) + shapes += shapes_ + } + } + + return (shapes, desc-y) +} + +#let render(config, schema, width: 100%) = { + set text( + font: config.default-font-family, + size: config.default-font-size + ) + + let main = schema.structures.main + let ox = config.margins.at(3) + if config.left-labels { + ox = config.width - ox - main.bits * config.bit-width + } + + let params = if config.full-page { + ( + width: auto, + height: auto, + fill: config.background, + margin: 0cm + ) + } else { + (:) + } + + set page(..params) + + let cnvs = canvas(length: 1pt, background: config.background, { + let (shapes, _) = draw-structure( + config, main, schema, + ox: ox, + oy: config.margins.at(0) + ) + // Workaround for margins + draw.group(name: "g", padding: config.margins, shapes) + draw.line( + "g.north-west", + "g.north-east", + "g.south-east", + "g.south-west", + stroke: none, + fill: none + ) + }) + + if config.full-page { + cnvs + } else { + layout(size => { + let m = measure(cnvs) + let w = m.width + let h = m.height + let base-w = if type(width) == ratio { + size.width * width + } else { + width + } + let r = if w == 0 { + 0 + } else { + base-w / w + } + + let new-w = w * r + let new-h = h * r + r *= 100% + + box( + width: new-w, + height: new-h, + scale(x: r, y: r, cnvs, reflow: true) + ) + }) + } +} + +#let make(config) = { + return ( + config: config, + render: render.with(config) + ) +} \ No newline at end of file diff --git a/packages/preview/rivet/0.2.0/src/schema.typ b/packages/preview/rivet/0.2.0/src/schema.typ new file mode 100644 index 000000000..70282730c --- /dev/null +++ b/packages/preview/rivet/0.2.0/src/schema.typ @@ -0,0 +1,92 @@ +#import "config.typ" as conf +#import "renderer.typ" +#import "structure.typ" +#import "xml-loader.typ" + +#let valid-extensions = ("yaml", "json", "xml") + +#let parse-file(path) = { + let ext = path.split(".").last() + + if not ext in valid-extensions { + let fmts = valid-extensions.map(fmt => "." + fmt).join(", ") + fmts = "(" + fmts + ")" + panic("." + ext + " files are not supported. Valid formats: " + fmts) + } + + if ext == "yaml" { + return yaml(path) + } else if ext == "json" { + return json(path) + } else if ext == "xml" { + return xml-loader.load(path) + } +} + +#let parse-raw(schema) = { + let lang = schema.lang + let content = bytes(schema.text) + if not lang in valid-extensions { + let fmts = valid-extensions.join(", ") + fmts = "(" + fmts + ")" + panic("Unsupported format '" + lang + "'. Valid formats: " + fmts) + } + + if lang == "yaml" { + return yaml(content) + } else if lang == "json" { + return json(content) + } else if lang == "xml" { + return xml-loader.parse(xml(content).first()) + } +} + +#let load(path-or-schema) = { + let schema = if type(path-or-schema) == str { + parse-file(path-or-schema) + } else if type(path-or-schema) == dictionary { + path-or-schema + } else { + parse-raw(path-or-schema) + } + + if "colors" in schema { + for struct in schema.colors.keys() { + for (span, col) in schema.colors.at(struct) { + if type(col) == str { + if col.starts-with("#") { + col = rgb(col) + } else { + let (r, g, b) = col.split(",").map(v => int(v)) + col = rgb(r, g, b) + } + } else if type(col) == array { + col = rgb(..col) + } else if type(col) != color { + panic("Invalid color format") + } + schema.colors.at(struct).at(span) = col + } + } + } else { + schema.insert("colors", (:)) + } + + let structures = (:) + for (id, data) in schema.structures { + id = str(id) + structures.insert(id, structure.load(id, data)) + } + return ( + structures: structures, + colors: schema.at("colors", default: (:)) + ) +} + +#let render(schema, width: 100%, config: auto) = { + if config == auto { + config = conf.config() + } + let renderer_ = renderer.make(config) + (renderer_.render)(schema, width: width) +} \ No newline at end of file diff --git a/packages/preview/rivet/0.2.0/src/structure.typ b/packages/preview/rivet/0.2.0/src/structure.typ new file mode 100644 index 000000000..f5cfa5b68 --- /dev/null +++ b/packages/preview/rivet/0.2.0/src/structure.typ @@ -0,0 +1,57 @@ +#import "range.typ" as rng +#import "util.typ" + +#let make( + name, + bits, + ranges, + start: 0 +) = { + return ( + name: name, + bits: bits, + ranges: ranges, + start: start + ) +} + +#let load(id, data) = { + let struct = (id: id) + let ranges = (:) + + for (range-span, range-data) in data.ranges { + let (start, end) = rng.parse-span(str(range-span)) + ranges.insert( + rng.key(start, end), + rng.load(start, end, range-data) + ) + } + + let ranges2 = (:) + for (k, range_) in ranges { + if range_.values != none and range_.depends-on != none { + let depends-key = rng.key(..range_.depends-on) + let depends-range = ranges.at(depends-key) + let bits = rng.bits(depends-range) + let values = (:) + for (v, d) in range_.values { + v = util.z-fill(str(int(v)), bits) + values.insert(v, d) + } + range_.values = values + } + ranges2.insert(k, range_) + } + + return make( + id, + int(data.bits), + ranges2, + start: data.at("start", default: 0) + ) +} + +#let get-sorted-ranges(struct) = { + let ranges = struct.ranges.values() + return ranges.sorted(key: r => r.end) +} \ No newline at end of file diff --git a/packages/preview/rivet/0.2.0/src/util.typ b/packages/preview/rivet/0.2.0/src/util.typ new file mode 100644 index 000000000..c00508573 --- /dev/null +++ b/packages/preview/rivet/0.2.0/src/util.typ @@ -0,0 +1,4 @@ +#let z-fill(string, length) = { + let filled = "0" * length + string + return filled.slice(-length) +} \ No newline at end of file diff --git a/packages/preview/rivet/0.2.0/src/vec.typ b/packages/preview/rivet/0.2.0/src/vec.typ new file mode 100644 index 000000000..be367866a --- /dev/null +++ b/packages/preview/rivet/0.2.0/src/vec.typ @@ -0,0 +1,44 @@ +#let vec(x, y) = { + return (x: x, y: y) +} + +#let add(v1, v2) = { + return vec( + v1.x + v2.x, + v1.y + v2.y + ) +} + +#let sub(v1, v2) = { + return vec( + v1.x - v2.x, + v1.y - v2.y + ) +} + +#let mul(v, f) = { + return vec( + v.x * f, + v.y * f + ) +} + +#let div(v, f) = { + return vec( + v.x / f, + v.y / f + ) +} + +#let mag(v) = { + return calc.sqrt(v.x * v.x + v.y * v.y) +} + +#let normalize(v) = { + let m = mag(v) + + if m == 0 { + return (x: 0, y: 0) + } + return div(v, m) +} \ No newline at end of file diff --git a/packages/preview/rivet/0.2.0/src/xml-loader.typ b/packages/preview/rivet/0.2.0/src/xml-loader.typ new file mode 100644 index 000000000..9513766d5 --- /dev/null +++ b/packages/preview/rivet/0.2.0/src/xml-loader.typ @@ -0,0 +1,117 @@ +#let find(elmt, tag) = { + if not "children" in elmt { + return none + } + + return elmt.children.find(e => "tag" in e and e.tag == tag) +} + +#let find-all(elmt, tag) = { + if not "children" in elmt { + return () + } + + return elmt.children.filter(e => "tag" in e and e.tag == tag) +} + +#let parse-values(elmt) = { + let values = (:) + let case-elmts = find-all(elmt, "case") + + for case-elmt in case-elmts { + let val = case-elmt.attrs.value + let desc = case-elmt.children.first() + let struct = none + if "structure" in case-elmt.attrs { + struct = case-elmt.attrs.structure + } + + values.insert(val, + if struct != none { + ( + description: desc, + structure: struct + ) + } else { + desc + } + ) + } + + return values +} + +#let parse-range(elmt) = { + let range_ = ( + name: elmt.attrs.name + ) + let desc = none + if "children" in elmt { + desc = find(elmt, "description") + } + + if desc != none { + range_.insert("description", desc.children.first()) + } + + let values-elmt = find(elmt, "values") + if values-elmt != none { + range_.insert("values", parse-values(values-elmt)) + } + + if "depends-on" in elmt.attrs { + range_.insert("depends-on", elmt.attrs.depends-on) + } + + return range_ +} + +#let parse-structure(elmt) = { + let ranges = (:) + let range-elmts = elmt.children.filter(e => "tag" in e and e.tag == "range") + + for range-elmt in range-elmts { + let span = range-elmt.attrs.end + "-" + range-elmt.attrs.start + ranges.insert(span, parse-range(range-elmt)) + } + + return ( + bits: elmt.attrs.bits, + ranges: ranges + ) +} + +#let parse(content) = { + let struct-elmts = content.children.filter(e => "tag" in e and e.tag == "structure") + let color-elmts = content.children.filter(e => "tag" in e and e.tag == "color") + + let structures = (:) + let colors = (:) + + for struct-elmt in struct-elmts { + structures.insert( + struct-elmt.attrs.id, + parse-structure(struct-elmt) + ) + } + + for color-elmt in color-elmts { + let struct = color-elmt.attrs.structure + if not struct in colors { + colors.insert(struct, (:)) + } + + let span = color-elmt.attrs.end + "-" + color-elmt.attrs.start + colors.at(struct).insert(span, color-elmt.attrs.color) + } + + return ( + structures: structures, + colors: colors + ) +} + +#let load(path) = { + let content = xml(path).first() + return parse(content) +} \ No newline at end of file diff --git a/packages/preview/rivet/0.2.0/typst.toml b/packages/preview/rivet/0.2.0/typst.toml new file mode 100644 index 000000000..a0c5417ef --- /dev/null +++ b/packages/preview/rivet/0.2.0/typst.toml @@ -0,0 +1,14 @@ +[package] +name = "rivet" +version = "0.2.0" +compiler = "0.13.0" +repository = "https://git.kb28.ch/HEL/rivet-typst" +entrypoint = "src/lib.typ" +authors = [ + "Louis Heredero " +] +categories = [ "visualization" ] +license = "Apache-2.0" +description = "Register / Instruction Visualizer & Explainer Tool with Typst, using CeTZ" +keywords = [ "assembly", "instruction", "binary" ] +exclude = [ "gallery", "justfile", "docs" ] \ No newline at end of file