Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
7 changes: 1 addition & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,14 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive

- name: Install dependencies
run: sudo apt-get update && sudo apt-get install -y cmake build-essential
run: sudo apt-get update && sudo apt-get install -y build-essential

- name: Install V
uses: vlang/setup-v@v1
with:
check-latest: true

- name: Build lexbor
run: make setup

- name: Run tests
run: make test
4 changes: 0 additions & 4 deletions .gitmodules

This file was deleted.

12 changes: 4 additions & 8 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
.PHONY: setup test bench bench-selectors bench-parse bench-jsoup example clean install
.PHONY: generate test bench bench-selectors bench-parse bench-jsoup example install

# Build lexbor static library
setup:
bash setup.sh
# Regenerate unity build files from vendored lexbor source tree
generate:
bash generate_unity.sh

# Run all tests
test:
Expand All @@ -27,10 +27,6 @@ bench-jsoup:
example:
v -path "$(CURDIR)/..|@vlib|@vmodules" run examples/basic.v

# Clean lexbor build artifacts
clean:
rm -rf lexbor/build

# Symlink into V modules
install:
ln -sf $(CURDIR) ~/.vmodules/vsoup
15 changes: 6 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,22 +44,18 @@ println(doc.html())

```sh
v install marcalc.vsoup
cd ~/.vmodules/$USER/vsoup
bash setup.sh
```

The setup script will automatically clone [Lexbor](https://lexbor.com) v2.6.0 and build it as a static library. This only needs to be done once.

**Prerequisites:** C compiler (cc/gcc/clang) and CMake.

### From source

```sh
git clone --recurse-submodules https://github.com/marcalc/vsoup.git
git clone https://github.com/marcalc/vsoup.git
cd vsoup
make setup
make test
```

[Lexbor](https://lexbor.com) v2.6.0 is vendored and compiled directly by V — no CMake or separate build step required.

## API Reference

### Parsing
Expand Down Expand Up @@ -219,6 +215,7 @@ defer { doc2.free() }
```
vsoup
├── bindings.v # C FFI declarations (lexbor)
├── lexbor_*.c # Per-module unity builds — V compiles lexbor directly
├── helpers.v # C↔V conversion, serialization, selector cache
├── vsoup.v # parse(), parse_file(), connect()
├── document.v # Document struct
Expand All @@ -227,7 +224,7 @@ vsoup
├── node_type.v # NodeType enum
├── connection.v # HTTP client
├── c_shims.c/h # Compatibility shims for lexbor v2.6.0
└── lexbor/ # Vendored lexbor v2.6.0 (git submodule)
└── lexbor/ # Vendored lexbor v2.6.0 source
```

**Key design decisions:**
Expand Down
14 changes: 12 additions & 2 deletions bindings.v
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
module vsoup

// Compiler flags for lexbor static library
// Compile lexbor directly — no CMake required.
// Per-module unity builds (lexbor_*.c) + two files compiled separately
// to avoid static symbol conflicts across translation units.
#flag -DLEXBOR_STATIC
#flag -I @VMODROOT
#flag -I @VMODROOT/lexbor/source
#flag @VMODROOT/lexbor/build/liblexbor_static.a
#flag linux -lm
#flag @VMODROOT/lexbor/lexbor_core.c
#flag @VMODROOT/lexbor/lexbor_dom.c
#flag @VMODROOT/lexbor/lexbor_html.c
#flag @VMODROOT/lexbor/source/lexbor/html/interface.c
#flag @VMODROOT/lexbor/lexbor_css.c
#flag @VMODROOT/lexbor/source/lexbor/css/value.c
#flag @VMODROOT/lexbor/lexbor_extra.c
#flag @VMODROOT/c_shims.c

// Include the main lexbor headers
Expand Down
159 changes: 159 additions & 0 deletions generate_unity.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
#!/bin/bash
set -e

#
# generate_unity.sh — Auto-generate per-module lexbor unity build files.
#
# Mirrors CMake's GLOB_RECURSE approach: scans lexbor/source/lexbor/*/
# for .c files and generates unity .c files grouped by module.
#
# Run this after updating the lexbor submodule to regenerate.
#

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
LEXBOR_SOURCE="$SCRIPT_DIR/lexbor/source"

if [ ! -d "$LEXBOR_SOURCE/lexbor" ]; then
echo "Error: lexbor source not found at $LEXBOR_SOURCE"
echo "Run 'bash setup.sh' first to fetch the lexbor source."
exit 1
fi

# ── Configuration ─────────────────────────────────────────────────────
#
# Module → unity file grouping.
# Modules that share static symbol names must be in separate groups.
#
# Known conflicts (lexbor v2.6.0):
# - dom/interfaces/document.c vs style/dom/interfaces/document.c
# → dom and style are in separate groups
# - html/interface.c vs html/interfaces/document.c (via interface_res.h)
# → html/interface.c excluded from unity, compiled separately
# - css/value.c vs css/syntax/syntax.c
# → css/value.c excluded from unity, compiled separately

# Files excluded from unity builds due to static symbol conflicts.
# These are compiled as separate translation units via #flag in bindings.v.
EXCLUDED="lexbor/html/interface.c lexbor/css/value.c"

# ── Helpers ───────────────────────────────────────────────────────────

is_excluded() {
for excl in $EXCLUDED; do
if [ "$1" = "$excl" ]; then
return 0
fi
done
return 1
}

# find_sources MODULE — glob all .c files for a module (like CMake's GLOB_RECURSE)
find_sources() {
find "$LEXBOR_SOURCE/lexbor/$1" -name "*.c" -type f 2>/dev/null | sort
}

# write_includes OUTPUT MODULE... — append #include lines for each module's .c files
write_includes() {
local out="$1"; shift
for mod in "$@"; do
if [ ! -d "$LEXBOR_SOURCE/lexbor/$mod" ]; then
echo " Warning: module '$mod' not found, skipping"
continue
fi
echo "/* $mod */" >> "$out"
find_sources "$mod" | while read -r filepath; do
local rel="${filepath#$LEXBOR_SOURCE/}"
if is_excluded "$rel"; then
echo "/* excluded: $rel (separate TU) */" >> "$out"
else
echo "#include \"$rel\"" >> "$out"
fi
done
echo "" >> "$out"
done
}

# write_header OUTPUT MODULES_DESC — write the file header
write_header() {
cat > "$1" <<EOF
/*
* Auto-generated by generate_unity.sh — do not edit manually.
* Lexbor modules: $2
*/

#define LEXBOR_STATIC
EOF
}

# ── Generate each group ───────────────────────────────────────────────

echo "Generating lexbor unity build files from $LEXBOR_SOURCE ..."

# ── core (+ platform ports) ──

OUT="$SCRIPT_DIR/lexbor/lexbor_core.c"
write_header "$OUT" "core"
cat >> "$OUT" <<'EOF'

#ifndef _DEFAULT_SOURCE
#define _DEFAULT_SOURCE
#endif
#ifndef _BSD_SOURCE
#define _BSD_SOURCE
#endif

EOF
write_includes "$OUT" core
cat >> "$OUT" <<'EOF'
/* ports */
#ifdef _WIN32
EOF
find "$LEXBOR_SOURCE/lexbor/ports/windows_nt" -name "*.c" -type f | sort | while read -r f; do
echo " #include \"${f#$LEXBOR_SOURCE/}\"" >> "$OUT"
done
echo "#else" >> "$OUT"
find "$LEXBOR_SOURCE/lexbor/ports/posix" -name "*.c" -type f | sort | while read -r f; do
echo " #include \"${f#$LEXBOR_SOURCE/}\"" >> "$OUT"
done
echo "#endif" >> "$OUT"
echo " Generated $OUT"

# ── dom ──

OUT="$SCRIPT_DIR/lexbor/lexbor_dom.c"
write_header "$OUT" "dom"
echo "" >> "$OUT"
write_includes "$OUT" dom
echo " Generated $OUT"

# ── html (interface.c excluded → separate TU) ──

OUT="$SCRIPT_DIR/lexbor/lexbor_html.c"
write_header "$OUT" "html"
echo "" >> "$OUT"
write_includes "$OUT" html
echo " Generated $OUT"

# ── css (value.c excluded → separate TU) ──

OUT="$SCRIPT_DIR/lexbor/lexbor_css.c"
write_header "$OUT" "css"
echo "" >> "$OUT"
write_includes "$OUT" css
echo " Generated $OUT"

# ── extra (all remaining modules) ──

OUT="$SCRIPT_DIR/lexbor/lexbor_extra.c"
write_header "$OUT" "ns tag selectors encoding engine style punycode unicode url utils"
echo "" >> "$OUT"
write_includes "$OUT" ns tag selectors encoding engine style punycode unicode url utils
echo " Generated $OUT"

# ── Summary ──

echo ""
echo "Done. Excluded files (compiled as separate TUs via bindings.v):"
for f in $EXCLUDED; do
echo " - $f"
done
1 change: 0 additions & 1 deletion lexbor
Submodule lexbor deleted from e142a9
46 changes: 46 additions & 0 deletions lexbor/lexbor_core.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Auto-generated by generate_unity.sh — do not edit manually.
* Lexbor modules: core
*/

#define LEXBOR_STATIC

#ifndef _DEFAULT_SOURCE
#define _DEFAULT_SOURCE
#endif
#ifndef _BSD_SOURCE
#define _BSD_SOURCE
#endif

/* core */
#include "lexbor/core/array.c"
#include "lexbor/core/array_obj.c"
#include "lexbor/core/avl.c"
#include "lexbor/core/bst.c"
#include "lexbor/core/bst_map.c"
#include "lexbor/core/conv.c"
#include "lexbor/core/diyfp.c"
#include "lexbor/core/dobject.c"
#include "lexbor/core/dtoa.c"
#include "lexbor/core/hash.c"
#include "lexbor/core/in.c"
#include "lexbor/core/mem.c"
#include "lexbor/core/mraw.c"
#include "lexbor/core/plog.c"
#include "lexbor/core/print.c"
#include "lexbor/core/serialize.c"
#include "lexbor/core/shs.c"
#include "lexbor/core/str.c"
#include "lexbor/core/strtod.c"
#include "lexbor/core/utils.c"

/* ports */
#ifdef _WIN32
#include "lexbor/ports/windows_nt/lexbor/core/fs.c"
#include "lexbor/ports/windows_nt/lexbor/core/memory.c"
#include "lexbor/ports/windows_nt/lexbor/core/perf.c"
#else
#include "lexbor/ports/posix/lexbor/core/fs.c"
#include "lexbor/ports/posix/lexbor/core/memory.c"
#include "lexbor/ports/posix/lexbor/core/perf.c"
#endif
34 changes: 34 additions & 0 deletions lexbor/lexbor_css.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Auto-generated by generate_unity.sh — do not edit manually.
* Lexbor modules: css
*/

#define LEXBOR_STATIC

/* css */
#include "lexbor/css/at_rule.c"
#include "lexbor/css/at_rule/state.c"
#include "lexbor/css/css.c"
#include "lexbor/css/declaration.c"
#include "lexbor/css/log.c"
#include "lexbor/css/parser.c"
#include "lexbor/css/property.c"
#include "lexbor/css/property/state.c"
#include "lexbor/css/rule.c"
#include "lexbor/css/selectors/pseudo.c"
#include "lexbor/css/selectors/pseudo_state.c"
#include "lexbor/css/selectors/selector.c"
#include "lexbor/css/selectors/selectors.c"
#include "lexbor/css/selectors/state.c"
#include "lexbor/css/state.c"
#include "lexbor/css/stylesheet.c"
#include "lexbor/css/syntax/anb.c"
#include "lexbor/css/syntax/parser.c"
#include "lexbor/css/syntax/state.c"
#include "lexbor/css/syntax/syntax.c"
#include "lexbor/css/syntax/token.c"
#include "lexbor/css/syntax/tokenizer.c"
#include "lexbor/css/syntax/tokenizer/error.c"
#include "lexbor/css/unit.c"
/* excluded: lexbor/css/value.c (separate TU) */

25 changes: 25 additions & 0 deletions lexbor/lexbor_dom.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Auto-generated by generate_unity.sh — do not edit manually.
* Lexbor modules: dom
*/

#define LEXBOR_STATIC

/* dom */
#include "lexbor/dom/collection.c"
#include "lexbor/dom/exception.c"
#include "lexbor/dom/interface.c"
#include "lexbor/dom/interfaces/attr.c"
#include "lexbor/dom/interfaces/cdata_section.c"
#include "lexbor/dom/interfaces/character_data.c"
#include "lexbor/dom/interfaces/comment.c"
#include "lexbor/dom/interfaces/document.c"
#include "lexbor/dom/interfaces/document_fragment.c"
#include "lexbor/dom/interfaces/document_type.c"
#include "lexbor/dom/interfaces/element.c"
#include "lexbor/dom/interfaces/event_target.c"
#include "lexbor/dom/interfaces/node.c"
#include "lexbor/dom/interfaces/processing_instruction.c"
#include "lexbor/dom/interfaces/shadow_root.c"
#include "lexbor/dom/interfaces/text.c"

Loading
Loading