mirror of
https://github.com/Ed94/Odin.git
synced 2026-06-20 12:44:59 -07:00
Merge branch 'master' into sysinfo
This commit is contained in:
+39
-5
@@ -38,18 +38,50 @@ sort :: proc(data: $T/[]$E) where ORD(E) {
|
||||
}
|
||||
}
|
||||
|
||||
// sort sorts a slice
|
||||
|
||||
sort_by_indices :: proc{ sort_by_indices_allocate, _sort_by_indices}
|
||||
|
||||
sort_by_indices_allocate :: proc(data: $T/[]$E, indices: []int, allocator := context.allocator) -> (sorted: T) {
|
||||
assert(len(data) == len(indices))
|
||||
sorted = make([]int, len(data), allocator)
|
||||
for v, i in indices {
|
||||
sorted[i] = data[v]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
_sort_by_indices :: proc(data, sorted: $T/[]$E, indices: []int) {
|
||||
assert(len(data) == len(indices))
|
||||
assert(len(data) == len(sorted))
|
||||
for v, i in indices {
|
||||
sorted[i] = data[v]
|
||||
}
|
||||
}
|
||||
|
||||
sort_by_indices_overwrite :: proc(data: $T/[]$E, indices: []int) {
|
||||
assert(len(data) == len(indices))
|
||||
temp := make([]int, len(data), context.allocator)
|
||||
defer delete(temp)
|
||||
for v, i in indices {
|
||||
temp[i] = data[v]
|
||||
}
|
||||
swap_with_slice(data, temp)
|
||||
}
|
||||
|
||||
// sort sorts a slice and returns a slice of the original indices
|
||||
// This sort is not guaranteed to be stable
|
||||
sort_with_indices :: proc(data: $T/[]$E, indices: []int) where ORD(E) {
|
||||
sort_with_indices :: proc(data: $T/[]$E, allocator := context.allocator) -> (indices: []int) where ORD(E) {
|
||||
indices = make([]int, len(data), allocator)
|
||||
when size_of(E) != 0 {
|
||||
if n := len(data); n > 1 {
|
||||
assert(len(data) == len(indices))
|
||||
for _, idx in indices {
|
||||
indices[idx] = idx
|
||||
}
|
||||
_quick_sort_general_with_indices(data, indices, 0, n, _max_depth(n), struct{}{}, .Ordered)
|
||||
}
|
||||
return indices
|
||||
}
|
||||
return indices
|
||||
}
|
||||
|
||||
// sort_by sorts a slice with a given procedure to test whether two values are ordered "i < j"
|
||||
@@ -64,16 +96,18 @@ sort_by :: proc(data: $T/[]$E, less: proc(i, j: E) -> bool) {
|
||||
|
||||
// sort_by sorts a slice with a given procedure to test whether two values are ordered "i < j"
|
||||
// This sort is not guaranteed to be stable
|
||||
sort_by_with_indices :: proc(data: $T/[]$E, indices: []int, less: proc(i, j: E) -> bool) {
|
||||
sort_by_with_indices :: proc(data: $T/[]$E, less: proc(i, j: E) -> bool, allocator := context.allocator) -> (indices : []int) {
|
||||
indices = make([]int, len(data), allocator)
|
||||
when size_of(E) != 0 {
|
||||
if n := len(data); n > 1 {
|
||||
assert(len(data) == len(indices))
|
||||
for _, idx in indices {
|
||||
indices[idx] = idx
|
||||
}
|
||||
_quick_sort_general_with_indices(data, indices, 0, n, _max_depth(n), less, .Less)
|
||||
return indices
|
||||
}
|
||||
}
|
||||
return indices
|
||||
}
|
||||
|
||||
sort_by_cmp :: proc(data: $T/[]$E, cmp: proc(i, j: E) -> Ordering) {
|
||||
|
||||
@@ -51,23 +51,20 @@ test_sort_with_indices :: proc(t: ^testing.T) {
|
||||
r := rand.create(seed)
|
||||
|
||||
vals := make([]u64, test_size)
|
||||
f_idx := make([]int, test_size) // Forward index, will be sorted
|
||||
r_idx := make([]int, test_size) // Reverse index
|
||||
|
||||
defer {
|
||||
delete(vals)
|
||||
delete(f_idx)
|
||||
delete(r_idx)
|
||||
}
|
||||
|
||||
// Set up test values
|
||||
for _, i in vals {
|
||||
vals[i] = rand.uint64(&r)
|
||||
f_idx[i] = i
|
||||
}
|
||||
|
||||
// Sort
|
||||
slice.sort_with_indices(vals, f_idx)
|
||||
f_idx := slice.sort_with_indices(vals)
|
||||
defer delete(f_idx)
|
||||
|
||||
// Verify sorted test values
|
||||
rand.init(&r, seed)
|
||||
@@ -94,4 +91,92 @@ test_sort_with_indices :: proc(t: ^testing.T) {
|
||||
last = v
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@test
|
||||
test_sort_by_indices :: proc(t: ^testing.T) {
|
||||
seed := rand.uint64()
|
||||
fmt.printf("Random seed: %v\n", seed)
|
||||
|
||||
// Test sizes are all prime.
|
||||
test_sizes :: []int{7, 13, 347, 1031, 10111, 100003}
|
||||
|
||||
for test_size in test_sizes {
|
||||
fmt.printf("Sorting %v random u64 values along with index.\n", test_size)
|
||||
|
||||
r := rand.create(seed)
|
||||
|
||||
vals := make([]u64, test_size)
|
||||
r_idx := make([]int, test_size) // Reverse index
|
||||
defer {
|
||||
delete(vals)
|
||||
delete(r_idx)
|
||||
}
|
||||
|
||||
// Set up test values
|
||||
for _, i in vals {
|
||||
vals[i] = rand.uint64(&r)
|
||||
}
|
||||
|
||||
// Sort
|
||||
f_idx := slice.sort_with_indices(vals)
|
||||
defer delete(f_idx)
|
||||
|
||||
// Verify sorted test values
|
||||
rand.init(&r, seed)
|
||||
|
||||
{
|
||||
indices := make([]int, test_size)
|
||||
defer delete(indices)
|
||||
for _, i in indices {
|
||||
indices[i] = i
|
||||
}
|
||||
|
||||
sorted_indices := slice.sort_by_indices(indices, f_idx)
|
||||
defer delete(sorted_indices)
|
||||
for v, i in sorted_indices {
|
||||
idx_pass := v == f_idx[i]
|
||||
expect(t, idx_pass, "Expected the sorted index to be the same as the result from sort_with_indices")
|
||||
if !idx_pass {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
indices := make([]int, test_size)
|
||||
defer delete(indices)
|
||||
for _, i in indices {
|
||||
indices[i] = i
|
||||
}
|
||||
|
||||
slice.sort_by_indices_overwrite(indices, f_idx)
|
||||
for v, i in indices {
|
||||
idx_pass := v == f_idx[i]
|
||||
expect(t, idx_pass, "Expected the sorted index to be the same as the result from sort_with_indices")
|
||||
if !idx_pass {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
indices := make([]int, test_size)
|
||||
swap := make([]int, test_size)
|
||||
defer {
|
||||
delete(indices)
|
||||
delete(swap)
|
||||
}
|
||||
for _, i in indices {
|
||||
indices[i] = i
|
||||
}
|
||||
|
||||
slice.sort_by_indices(indices, swap, f_idx)
|
||||
for v, i in swap {
|
||||
idx_pass := v == f_idx[i]
|
||||
expect(t, idx_pass, "Expected the sorted index to be the same as the result from sort_with_indices")
|
||||
if !idx_pass {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+8
-1
@@ -134,4 +134,11 @@ See also LICENSE in the `GGPO` directory itself.
|
||||
`botan.lib` is available under Botan's [BSD](https://botan.randombit.net/license.txt) license.
|
||||
|
||||
See also LICENSE in the `botan` directory itself.
|
||||
Includes full bindings as well as wrappers to match the `core:crypto` API.
|
||||
Includes full bindings as well as wrappers to match the `core:crypto` API.
|
||||
|
||||
## CommonMark
|
||||
|
||||
[CMark](https://github.com/commonmark/cmark) CommonMark parsing library.
|
||||
|
||||
See also LICENSE in the `commonmark` directory itself.
|
||||
Includes full bindings and Windows `.lib` and `.dll`.
|
||||
Vendored
+170
@@ -0,0 +1,170 @@
|
||||
Copyright (c) 2014, John MacFarlane
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
-----
|
||||
|
||||
houdini.h, houdini_href_e.c, houdini_html_e.c, houdini_html_u.c
|
||||
|
||||
derive from https://github.com/vmg/houdini (with some modifications)
|
||||
|
||||
Copyright (C) 2012 Vicent Martí
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
-----
|
||||
|
||||
buffer.h, buffer.c, chunk.h
|
||||
|
||||
are derived from code (C) 2012 Github, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
-----
|
||||
|
||||
utf8.c and utf8.c
|
||||
|
||||
are derived from utf8proc
|
||||
(<http://www.public-software-group.org/utf8proc>),
|
||||
(C) 2009 Public Software Group e. V., Berlin, Germany.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
-----
|
||||
|
||||
The normalization code in normalize.py was derived from the
|
||||
markdowntest project, Copyright 2013 Karl Dubost:
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 Karl Dubost
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
-----
|
||||
|
||||
The CommonMark spec (test/spec.txt) is
|
||||
|
||||
Copyright (C) 2014-15 John MacFarlane
|
||||
|
||||
Released under the Creative Commons CC-BY-SA 4.0 license:
|
||||
<http://creativecommons.org/licenses/by-sa/4.0/>.
|
||||
|
||||
-----
|
||||
|
||||
The test software in test/ is
|
||||
|
||||
Copyright (c) 2014, John MacFarlane
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
@echo off
|
||||
pushd W:\Odin-other\Odin-test
|
||||
call build.bat
|
||||
popd
|
||||
Vendored
+526
@@ -0,0 +1,526 @@
|
||||
/*
|
||||
Bindings against CMark (https://github.com/commonmark/cmark)
|
||||
|
||||
Original authors: John MacFarlane, Vicent Marti, Kārlis Gaņģis, Nick Wellnhofer.
|
||||
See LICENSE for license details.
|
||||
*/
|
||||
package commonmark
|
||||
|
||||
import "core:c"
|
||||
import "core:c/libc"
|
||||
import "core:runtime"
|
||||
|
||||
BINDING_VERSION :: Version_Info{major = 0, minor = 30, patch = 2}
|
||||
|
||||
when ODIN_OS == .Windows {
|
||||
foreign import lib {
|
||||
"cmark_static.lib",
|
||||
}
|
||||
} else when ODIN_OS == .Linux {
|
||||
foreign import lib {
|
||||
"libcmark.a",
|
||||
}
|
||||
}
|
||||
|
||||
Option :: enum c.int {
|
||||
Source_Position = 1, // Include a `data-sourcepos` attribute on all block elements.
|
||||
Hard_Breaks = 2, // Render `softbreak` as hard line breaks.
|
||||
Safe = 3, // Defined for API compatibility, now enabled by default.
|
||||
Unsafe = 17, // Render raw HTML and unsafe links (`javascript:`, `vbscript:`,
|
||||
// `file:`, and `data:`, except for `image/png`, `image/gif`,
|
||||
// `image/jpeg`, or `image/webp` mime types). By default,
|
||||
// raw HTML is replaced by a placeholder HTML comment. Unsafe
|
||||
// links are replaced by empty strings.
|
||||
No_Breaks = 4, // Render `softbreak` elements as spaces.
|
||||
Normalize = 8, // Legacy option, no effect.
|
||||
Validate_UTF8 = 9, // Validate UTF-8 input before parsing, replacing illegal
|
||||
// sequences with the replacement character U+FFFD.
|
||||
Smart = 10, // Convert straight quotes to curly, --- to em dashes, -- to en dashes.
|
||||
}
|
||||
Options :: bit_set[Option; c.int]
|
||||
|
||||
DEFAULT_OPTIONS :: Options{}
|
||||
|
||||
Node_Type :: enum u16 {
|
||||
// Error status
|
||||
None = 0,
|
||||
|
||||
/* Block */
|
||||
Document,
|
||||
Block_Quote,
|
||||
List,
|
||||
Item,
|
||||
Code_Block,
|
||||
HTML_Block,
|
||||
Custom_Block,
|
||||
Paragraph,
|
||||
Heading,
|
||||
Thematic_Break,
|
||||
|
||||
/* Inline */
|
||||
Text,
|
||||
Soft_Break,
|
||||
Line_Break,
|
||||
Code,
|
||||
HTML_Inline,
|
||||
Custom_Inline,
|
||||
Emph,
|
||||
Strong,
|
||||
Link,
|
||||
Image,
|
||||
|
||||
First_Block = Document,
|
||||
Last_Block = Thematic_Break,
|
||||
|
||||
First_Inline = Text,
|
||||
Last_Inline = Image,
|
||||
}
|
||||
|
||||
List_Type :: enum c.int {
|
||||
None,
|
||||
Bullet,
|
||||
Ordered,
|
||||
}
|
||||
|
||||
Delim_Type :: enum c.int {
|
||||
None,
|
||||
Period,
|
||||
Paren,
|
||||
}
|
||||
|
||||
// Version information
|
||||
Version_Info :: struct {
|
||||
patch: u8,
|
||||
minor: u8,
|
||||
major: u8,
|
||||
_: u8,
|
||||
}
|
||||
|
||||
@(default_calling_convention="c", link_prefix="cmark_")
|
||||
foreign lib {
|
||||
version :: proc() -> (res: Version_Info) ---
|
||||
version_string :: proc() -> (res: cstring) ---
|
||||
}
|
||||
|
||||
// Simple API
|
||||
@(default_calling_convention="c", link_prefix="cmark_")
|
||||
foreign lib {
|
||||
// Convert 'text' (assumed to be a UTF-8 encoded string with length `len`) from CommonMark Markdown to HTML
|
||||
// returning a null-terminated, UTF-8-encoded string. It is the caller's responsibility
|
||||
// to free the returned buffer.
|
||||
markdown_to_html :: proc(text: cstring, length: c.size_t, options: Options) -> (html: cstring) ---
|
||||
}
|
||||
|
||||
markdown_to_html_from_string :: proc(text: string, options: Options) -> (html: string) {
|
||||
return string(markdown_to_html(cstring(raw_data(text)), len(text), options))
|
||||
}
|
||||
|
||||
// Custom allocator - Defines the memory allocation functions to be used by CMark
|
||||
// when parsing and allocating a document tree
|
||||
Allocator :: struct {
|
||||
calloc: proc "c" (num: c.size_t, size: c.size_t) -> rawptr,
|
||||
realloc: proc "c" (ptr: rawptr, new_size: c.size_t) -> rawptr,
|
||||
free: proc "c" (ptr: rawptr),
|
||||
}
|
||||
|
||||
@(default_calling_convention="c", link_prefix="cmark_")
|
||||
foreign lib {
|
||||
// Returns a pointer to the default memory allocator.
|
||||
get_default_mem_allocator :: proc() -> (mem: ^Allocator) ---
|
||||
}
|
||||
|
||||
bufsize_t :: distinct i32
|
||||
|
||||
// Node creation, destruction, and tree traversal
|
||||
Node :: struct {
|
||||
mem: ^Allocator,
|
||||
|
||||
next: ^Node,
|
||||
prev: ^Node,
|
||||
parent: ^Node,
|
||||
first_child: ^Node,
|
||||
last_child: ^Node,
|
||||
|
||||
user_data: rawptr,
|
||||
data: [^]u8,
|
||||
len: bufsize_t,
|
||||
|
||||
start_line: c.int,
|
||||
start_column: c.int,
|
||||
end_line: c.int,
|
||||
end_column: c.int,
|
||||
|
||||
type: Node_Type,
|
||||
flags: Node_Flags,
|
||||
|
||||
as: struct #raw_union {
|
||||
list: List,
|
||||
code: Code,
|
||||
heading: Heading,
|
||||
link: Link,
|
||||
custom: Custom,
|
||||
html_block_type: c.int,
|
||||
},
|
||||
}
|
||||
|
||||
Node_Flag :: enum u16 {
|
||||
Open = 0,
|
||||
Last_Line_Blank = 1,
|
||||
Last_Line_Checked = 2,
|
||||
}
|
||||
Node_Flags :: bit_set[Node_Flag; u16]
|
||||
|
||||
List :: struct {
|
||||
marker_offset: c.int,
|
||||
padding: c.int,
|
||||
start: c.int,
|
||||
list_type: u8,
|
||||
delimiter: u8,
|
||||
bullet_char: u8,
|
||||
tight: c.bool,
|
||||
}
|
||||
|
||||
Code :: struct {
|
||||
info: cstring,
|
||||
fence_length: u8,
|
||||
fence_offset: u8,
|
||||
fence_char: u8,
|
||||
fenced: b8,
|
||||
}
|
||||
|
||||
Heading :: struct {
|
||||
internal_offset: c.int,
|
||||
level: i8,
|
||||
setext: c.bool,
|
||||
}
|
||||
|
||||
|
||||
Link :: struct {
|
||||
url: cstring,
|
||||
title: cstring,
|
||||
}
|
||||
|
||||
Custom :: struct {
|
||||
on_enter: cstring,
|
||||
on_exit: cstring,
|
||||
}
|
||||
|
||||
@(default_calling_convention="c", link_prefix="cmark_")
|
||||
foreign lib {
|
||||
// Creates a new node of type 'type'.
|
||||
// Note that the node may have other required properties, which it is the caller's responsibility
|
||||
// to assign.
|
||||
node_new :: proc(type: Node_Type) -> (node: ^Node) ---
|
||||
|
||||
// Same as `node_new`, but explicitly listing the memory allocator used to allocate the node.
|
||||
// Note: be sure to use the same allocator for every node in a tree, or bad things can happen.
|
||||
node_new_with_mem :: proc(type: Node_Type, mem: ^Allocator) -> (node: ^Node) ---
|
||||
|
||||
// Frees the memory allocated for a node and any children.
|
||||
node_free :: proc(node: ^Node) ---
|
||||
|
||||
/*
|
||||
Tree Traversal
|
||||
*/
|
||||
// Returns the next node in the sequence after `node`, or nil if there is none.
|
||||
node_next :: proc(node: ^Node) -> (next: ^Node) ---
|
||||
|
||||
// Returns the previous node in the sequence after `node`, or nil if there is none.
|
||||
node_previous :: proc(node: ^Node) -> (prev: ^Node) ---
|
||||
|
||||
// Returns the parent of `node`, or nil if there is none.
|
||||
node_parent :: proc(node: ^Node) -> (parent: ^Node) ---
|
||||
|
||||
// Returns the first child of `node`, or nil if `node` has no children.
|
||||
node_first_child :: proc(node: ^Node) -> (child: ^Node) ---
|
||||
|
||||
// Returns the last child of `node`, or nil if `node` has no children.
|
||||
node_last_child :: proc(node: ^Node) -> (child: ^Node) ---
|
||||
|
||||
}
|
||||
|
||||
Iter :: distinct rawptr
|
||||
|
||||
Event_Type :: enum c.int {
|
||||
None,
|
||||
Done,
|
||||
Enter,
|
||||
Exit,
|
||||
}
|
||||
|
||||
@(default_calling_convention="c", link_prefix="cmark_")
|
||||
foreign lib {
|
||||
// Creates a new iterator starting at 'root'. The current node and event
|
||||
// type are undefined until `iter_next` is called for the first time.
|
||||
// The memory allocated for the iterator should be released using
|
||||
// 'iter_free' when it is no longer needed.
|
||||
iter_new :: proc(root: ^Node) -> (iter: ^Iter) ---
|
||||
|
||||
// Frees the memory allocated for an iterator.
|
||||
iter_free :: proc(iter: ^Iter) ---
|
||||
|
||||
// Advances to the next node and returns the event type (`.Enter`, `.Exit`, `.Done`)
|
||||
iter_next :: proc(iter: ^Iter) -> (event_type: Event_Type) ---
|
||||
|
||||
// Returns the current node.
|
||||
iter_get_node :: proc(iter: ^Iter) -> (node: ^Node) ---
|
||||
|
||||
// Returns the current event type.
|
||||
iter_get_event_type :: proc(iter: ^Iter) -> (event_type: Event_Type) ---
|
||||
|
||||
// Returns the root node.
|
||||
iter_get_root :: proc(iter: ^Iter) -> (root: ^Node) ---
|
||||
|
||||
// Resets the iterator so that the current node is `current` and
|
||||
// the event type is `event_type`. The new current node must be a
|
||||
// descendant of the root node or the root node itself.
|
||||
iter_reset :: proc(iter: ^Iter, current: ^Node, event_type: Event_Type) ---
|
||||
}
|
||||
|
||||
// Accessors
|
||||
@(default_calling_convention="c", link_prefix="cmark_")
|
||||
foreign lib {
|
||||
// Returns the user data of `node`.
|
||||
node_get_user_data :: proc(node: ^Node) -> (user_data: rawptr) ---
|
||||
|
||||
// Sets arbitrary user data for `node`. Returns `true` on success, `false` on failure.
|
||||
node_set_user_data :: proc(node: ^Node, user_data: rawptr) -> (success: b32) ---
|
||||
|
||||
// Returns the type of `node`, or `.None` on error.
|
||||
node_get_type :: proc(node: ^Node) -> (node_type: Node_Type) ---
|
||||
|
||||
// Like `node_get_type`, but returns a string representation of the type, or "<unknown>".
|
||||
node_get_type_string :: proc(node: ^Node) -> (node_type: cstring) ---
|
||||
|
||||
// Returns the string contents of `node`, or an empty string if none is set.
|
||||
// Returns `nil` if called on a node that does not have string content.
|
||||
node_get_literal :: proc(node: ^Node) -> (content: cstring) ---
|
||||
|
||||
// Sets the string contents of `node`. Returns `true` on success, `false` on failure.
|
||||
node_set_literal :: proc(node: ^Node, content: cstring) -> (success: b32) ---
|
||||
|
||||
// Returns the heading level of `node`, or 0 if `node` is not a heading.
|
||||
node_get_heading_level :: proc(node: ^Node) -> (level: c.int) ---
|
||||
|
||||
// Sets the heading level of `node`. Returns `true` on success, `false` on failure.
|
||||
node_set_heading_level :: proc(node: ^Node, level: c.int) -> (success: b32) ---
|
||||
|
||||
// Returns the list type of `node`, or `.No_List` if not a list.
|
||||
node_get_list_type :: proc(node: ^Node) -> (list_type: List_Type) ---
|
||||
|
||||
// Sets the list type of `node`. Returns `true` on success, `false` on failure.
|
||||
node_set_list_type :: proc(node: ^Node, list_type: List_Type) -> (success: b32) ---
|
||||
|
||||
// Returns the list delimiter type of `node`, or `.No_Delim` if not a list.
|
||||
node_get_list_delim :: proc(node: ^Node) -> (delim_type: Delim_Type) ---
|
||||
|
||||
// Sets the delimiter type of `node`. Returns `true` on success, `false` on failure.
|
||||
node_set_list_delim :: proc(node: ^Node, delim_type: Delim_Type) -> (success: b32) ---
|
||||
|
||||
// Returns starting number of `node`, if it is an ordered list, otherwise 0.
|
||||
node_get_list_start :: proc(node: ^Node) -> (start: c.int) ---
|
||||
|
||||
// Sets starting number of `node`, if it is an ordered list.
|
||||
// Returns `true` on success, `false` on failure.
|
||||
node_set_list_start :: proc(node: ^Node, start: c.int) -> (success: b32) ---
|
||||
|
||||
// Returns `true` if `node` is a tight list, `false` otherwise.
|
||||
node_get_list_tight :: proc(node: ^Node) -> (tight: b32) ---
|
||||
|
||||
// Sets the "tightness" of a list. Returns `true` on success, `false` on failure.
|
||||
node_set_list_tight :: proc(node: ^Node, tight: b32) -> (success: b32) ---
|
||||
|
||||
// Returns the info string from a fenced code block.
|
||||
get_fence_info :: proc(node: ^Node) -> (fence_info: cstring) ---
|
||||
|
||||
// Sets the info string in a fenced code block, returning `true` on success and `false` on failure.
|
||||
node_set_fence_info :: proc(node: ^Node, fence_info: cstring) -> (success: b32) ---
|
||||
|
||||
// Returns the URL of a link or image `node`, or an empty string if no URL is set.
|
||||
// Returns nil if called on a node that is not a link or image.
|
||||
node_get_url :: proc(node: ^Node) -> (url: cstring) ---
|
||||
|
||||
// Sets the URL of a link or image `node`. Returns `true` on success, `false` on failure.
|
||||
node_set_url :: proc(node: ^Node, url: cstring) -> (success: b32) ---
|
||||
|
||||
// Returns the title of a link or image `node`, or an empty string if no title is set.
|
||||
// Returns nil if called on a node that is not a link or image.
|
||||
node_get_title :: proc(node: ^Node) -> (title: cstring) ---
|
||||
|
||||
// Sets the title of a link or image `node`. Returns `true` on success, `false` on failure.
|
||||
node_set_title :: proc(node: ^Node, title: cstring) -> (success: b32) ---
|
||||
|
||||
// Returns the literal "on enter" text for a custom `node`, or an empty string if no on_enter is set.
|
||||
// Returns nil if called on a non-custom node.
|
||||
node_get_on_enter :: proc(node: ^Node) -> (on_enter: cstring) ---
|
||||
|
||||
// Sets the literal text to render "on enter" for a custom `node`.
|
||||
// Any children of the node will be rendered after this text.
|
||||
// Returns `true` on success, `false`on failure.
|
||||
node_set_on_enter :: proc(node: ^Node, on_enter: cstring) -> (success: b32) ---
|
||||
|
||||
// Returns the line on which `node` begins.
|
||||
node_get_start_line :: proc(node: ^Node) -> (line: c.int) ---
|
||||
|
||||
// Returns the column at which `node` begins.
|
||||
node_get_start_column :: proc(node: ^Node) -> (column: c.int) ---
|
||||
|
||||
// Returns the line on which `node` ends.
|
||||
node_get_end_line :: proc(node: ^Node) -> (line: c.int) ---
|
||||
|
||||
// Returns the column at which `node` ends.
|
||||
node_get_end_column :: proc(node: ^Node) -> (column: c.int) ---
|
||||
}
|
||||
|
||||
// Tree Manipulation
|
||||
@(default_calling_convention="c", link_prefix="cmark_")
|
||||
foreign lib {
|
||||
// Unlinks a `node`, removing it from the tree, but not freeing its memory.
|
||||
// (Use `node_free` for that.)
|
||||
node_unlink :: proc(node: ^Node) ---
|
||||
|
||||
// Inserts 'sibling' before `node`. Returns `true` on success, `false` on failure.
|
||||
node_insert_before :: proc(node: ^Node, sibling: ^Node) -> (success: b32) ---
|
||||
|
||||
// Inserts 'sibling' after `node`. Returns `true` on success, `false` on failure.
|
||||
node_insert_after :: proc(node: ^Node, sibling: ^Node) -> (success: b32) ---
|
||||
|
||||
// Replaces 'oldnode' with 'newnode' and unlinks 'oldnode'
|
||||
// (but does not free its memory).
|
||||
// Returns `true` on success, `false` on failure.
|
||||
node_replace :: proc(old_node: ^Node, new_node: ^Node) -> (success: b32) ---
|
||||
|
||||
// Adds 'child' to the beginning of the children of `node`.
|
||||
// Returns `true` on success, `false` on failure.
|
||||
node_prepend_child :: proc(node: ^Node, child: ^Node) -> (success: b32) ---
|
||||
|
||||
// Adds 'child' to the end of the children of `node`.
|
||||
// Returns `true` on success, `false` on failure.
|
||||
node_append_child :: proc(node: ^Node, child: ^Node) -> (success: b32) ---
|
||||
|
||||
// Consolidates adjacent text nodes.
|
||||
consolidate_text_nodes :: proc(root: ^Node) ---
|
||||
}
|
||||
|
||||
Parser :: distinct rawptr
|
||||
|
||||
@(default_calling_convention="c", link_prefix="cmark_")
|
||||
foreign lib {
|
||||
// Creates a new parser object.
|
||||
parser_new :: proc(options: Options) -> (parser: ^Parser) ---
|
||||
|
||||
// Creates a new parser object with the given memory allocator.
|
||||
parser_new_with_mem :: proc(options: Options, mem: ^Allocator) -> (parser: ^Parser) ---
|
||||
|
||||
// Frees memory allocated for a parser object.
|
||||
parser_free :: proc(parser: ^Parser) ---
|
||||
|
||||
// Feeds a string of length 'len' to 'parser'.
|
||||
parser_feed :: proc(parser: ^Parser, buffer: [^]byte, len: c.size_t) ---
|
||||
|
||||
// Finish parsing and return a pointer to a tree of nodes.
|
||||
parser_finish :: proc(parser: ^Parser) -> (root: ^Node) ---
|
||||
|
||||
// Parse a CommonMark document in 'buffer' of length 'len'.
|
||||
// Returns a pointer to a tree of nodes. The memory allocated for
|
||||
// the node tree should be released using 'node_free' when it is no longer needed.
|
||||
parse_document :: proc(buffer: [^]byte, len: c.size_t, options: Options) -> (root: ^Node) ---
|
||||
|
||||
// Parse a CommonMark document in file 'f', returning a pointer to a tree of nodes.
|
||||
// The memory allocated for the node tree should be released using 'node_free'
|
||||
// when it is no longer needed.
|
||||
//
|
||||
// Called `parse_from_libc_file` so as not to confuse with Odin's file handling.
|
||||
|
||||
@(link_name = "parse_from_file")
|
||||
parse_from_libc_file :: proc(file: ^libc.FILE, options: Options) -> (root: ^Node) ---
|
||||
}
|
||||
|
||||
parser_feed_from_string :: proc "c" (parser: ^Parser, s: string) {
|
||||
parser_feed(parser, raw_data(s), len(s))
|
||||
}
|
||||
parse_document_from_string :: proc "c" (s: string, options: Options) -> (root: ^Node) {
|
||||
return parse_document(raw_data(s), len(s), options)
|
||||
}
|
||||
|
||||
// Rendering
|
||||
@(default_calling_convention="c", link_prefix="cmark_")
|
||||
foreign lib {
|
||||
// Render a `node` tree as XML.
|
||||
// It is the caller's responsibilityto free the returned buffer.
|
||||
render_xml :: proc(root: ^Node, options: Options) -> (xml: cstring) ---
|
||||
|
||||
// Render a `node` tree as an HTML fragment.
|
||||
// It is up to the user to add an appropriate header and footer.
|
||||
// It is the caller's responsibility to free the returned buffer.
|
||||
render_html :: proc(root: ^Node, options: Options) -> (html: cstring) ---
|
||||
|
||||
// Render a `node` tree as a groff man page, without the header.
|
||||
// It is the caller's responsibility to free the returned buffer.
|
||||
render_man :: proc(root: ^Node, options: Options, width: c.int) -> (groff: cstring) ---
|
||||
|
||||
// Render a `node` tree as a commonmark document.
|
||||
// It is the caller's responsibility to free the returned buffer.
|
||||
render_commonmark :: proc(root: ^Node, options: Options, width: c.int) -> (commonmark: cstring) ---
|
||||
|
||||
// Render a `node` tree as a LaTeX document.
|
||||
// It is the caller's responsibility to free the returned buffer.
|
||||
render_latex :: proc(root: ^Node, options: Options, width: c.int) -> (latex: cstring) ---
|
||||
}
|
||||
|
||||
// Helpers to free results from `render_*`.
|
||||
free_rawptr :: proc "c" (ptr: rawptr) {
|
||||
cmm := get_default_mem_allocator()
|
||||
cmm.free(ptr)
|
||||
}
|
||||
free_cstring :: proc "c" (str: cstring) {
|
||||
free_rawptr(rawptr(str))
|
||||
}
|
||||
free_string :: proc "c" (s: string) {
|
||||
free_rawptr(raw_data(s))
|
||||
}
|
||||
free :: proc{free_rawptr, free_cstring}
|
||||
|
||||
// Wrap CMark allocator as Odin allocator
|
||||
@(private)
|
||||
cmark_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> (res: []byte, err: runtime.Allocator_Error) {
|
||||
|
||||
cmark_alloc := cast(^Allocator)allocator_data
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
ptr := cmark_alloc.calloc(1, c.size_t(size))
|
||||
res = transmute([]byte)runtime.Raw_Slice{ptr, size}
|
||||
return res, nil
|
||||
|
||||
case .Free:
|
||||
cmark_alloc.free(old_memory)
|
||||
return nil, nil
|
||||
|
||||
case .Free_All:
|
||||
return nil, .Mode_Not_Implemented
|
||||
|
||||
case .Resize:
|
||||
new_ptr := cmark_alloc.realloc(old_memory, c.size_t(size))
|
||||
res = transmute([]byte)runtime.Raw_Slice{new_ptr, size}
|
||||
if size > old_size {
|
||||
runtime.mem_zero(raw_data(res[old_size:]), size - old_size)
|
||||
}
|
||||
return res, nil
|
||||
|
||||
case .Query_Features:
|
||||
return nil, nil
|
||||
|
||||
case .Query_Info:
|
||||
return nil, .Mode_Not_Implemented
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
get_default_mem_allocator_as_odin :: proc() -> runtime.Allocator {
|
||||
return runtime.Allocator{
|
||||
procedure = cmark_allocator_proc,
|
||||
data = rawptr(get_default_mem_allocator()),
|
||||
}
|
||||
}
|
||||
Vendored
BIN
Binary file not shown.
Vendored
+117
@@ -0,0 +1,117 @@
|
||||
// +ignore
|
||||
/*
|
||||
Bindings against CMark (https://github.com/commonmark/cmark)
|
||||
|
||||
Original authors: John MacFarlane, Vicent Marti, Kārlis Gaņģis, Nick Wellnhofer.
|
||||
See LICENSE for license details.
|
||||
*/
|
||||
package commonmark
|
||||
|
||||
/*
|
||||
Parsing - Simple interface:
|
||||
|
||||
```odin
|
||||
import cm "vendor:commonmark"
|
||||
|
||||
hellope_world :: proc() {
|
||||
fmt.printf("CMark version: %v\n", cm.version_string())
|
||||
|
||||
str := "Hellope *world*!"
|
||||
root := cm.parse_document(raw_data(str), len(str), cm.DEFAULT_OPTIONS)
|
||||
defer cm.node_free(root)
|
||||
|
||||
html := cm.render_html(root, cm.DEFAULT_OPTIONS)
|
||||
defer cm.free(html)
|
||||
|
||||
fmt.println(html)
|
||||
}
|
||||
```
|
||||
|
||||
Parsing - Streaming interface:
|
||||
|
||||
```odin
|
||||
import cm "vendor:commonmark"
|
||||
|
||||
streaming :: proc() {
|
||||
using cm
|
||||
|
||||
STR :: "Hellope *world*!\n\n"
|
||||
N :: 50
|
||||
STREAM_SIZE :: 42
|
||||
|
||||
str_buf: [len(STR) * N]u8
|
||||
for i in 0..<N {
|
||||
copy(str_buf[i*len(STR):], STR)
|
||||
}
|
||||
|
||||
parser := parser_new(DEFAULT_OPTIONS)
|
||||
defer parser_free(parser)
|
||||
|
||||
buf := str_buf[:]
|
||||
for len(buf) > STREAM_SIZE {
|
||||
parser_feed(parser, raw_data(buf), STREAM_SIZE)
|
||||
buf = buf[STREAM_SIZE:]
|
||||
}
|
||||
|
||||
if len(buf) > 0 {
|
||||
parser_feed(parser, raw_data(buf), len(buf))
|
||||
buf = buf[len(buf):]
|
||||
}
|
||||
|
||||
root := parser_finish(parser)
|
||||
defer cm.node_free(root)
|
||||
|
||||
html := cm.render_html(root, cm.DEFAULT_OPTIONS)
|
||||
defer cm.free(html)
|
||||
|
||||
fmt.println(html)
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
An iterator will walk through a tree of nodes, starting from a root
|
||||
node, returning one node at a time, together with information about
|
||||
whether the node is being entered or exited.
|
||||
|
||||
The iterator will first descend to a child node, if there is one.
|
||||
When there is no child, the iterator will go to the next sibling.
|
||||
When there is no next sibling, the iterator will return to the parent
|
||||
(but with an `Event_Type.Exit`).
|
||||
|
||||
The iterator will return `.Done` when it reaches the root node again.
|
||||
|
||||
One natural application is an HTML renderer, where an `.Enter` event
|
||||
outputs an open tag and an `.Exit` event outputs a close tag.
|
||||
|
||||
An iterator might also be used to transform an AST in some systematic
|
||||
way, for example, turning all level-3 headings into regular paragraphs.
|
||||
|
||||
```odin
|
||||
usage_example(root: ^Node) {
|
||||
ev_type: Event_Type
|
||||
iter := iter_new(root)
|
||||
defer iter_free(iter)
|
||||
for {
|
||||
ev_type = iter_next(iter)
|
||||
if ev_type == .Done do break
|
||||
cur := iter_get_node(iter)
|
||||
// Do something with `cur` and `ev_type`
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Iterators will never return `.Exit` events for leaf nodes,
|
||||
which are nodes of type:
|
||||
|
||||
* HTML_Block
|
||||
* Thematic_Break
|
||||
* Code_Block
|
||||
* Text
|
||||
* Soft_Break
|
||||
* Line_Break
|
||||
* Code
|
||||
* HTML_Inline
|
||||
|
||||
Nodes must only be modified after an `.Exit` event, or an `.Enter` event for
|
||||
leaf nodes.
|
||||
*/
|
||||
Reference in New Issue
Block a user